Skip to content

Commit 72aedb5

Browse files
committed
fix: preserve Umbraco content aliases with regenerated types
1 parent 081d9e6 commit 72aedb5

4 files changed

Lines changed: 97 additions & 7 deletions

File tree

frontend/src/data/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { components } from '@/data/umbraco/deliveryApiSchema';
2+
import type { UmbracoContent } from '@/data/umbraco/types';
23
import type { Overwrite } from '@/util';
34

4-
type Content = components['schemas']['IApiContentResponseModel'];
5+
type Content = UmbracoContent;
56

67
export type Conference = Extract<Content, { contentType: 'conference' }>;
78

frontend/src/data/umbraco/getItemByPath.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { umbracoClient } from '@/data/umbraco/client';
22
import type { paths } from '@/data/umbraco/deliveryApiSchema';
33
import type {
4+
RawUmbracoContent,
45
UmbracoClientOptions,
56
UmbracoContent,
67
} from '@/data/umbraco/types';
8+
import { normalizeUmbracoContent } from '@/data/umbraco/types';
79

810
type GetItemByPathOptions =
911
paths['/umbraco/delivery/api/v2/content/item/{path}']['get']['parameters']['query'] &
@@ -19,14 +21,14 @@ export async function getItemByPathOrDefault(
1921
return;
2022
}
2123

22-
return data;
24+
return normalizeUmbracoContent(data);
2325
}
2426

2527
export async function getItemByPath(
2628
path: string,
2729
{ requestOptions = {}, ...options }: GetItemByPathOptions = {},
2830
): Promise<{
29-
data?: UmbracoContent;
31+
data?: RawUmbracoContent;
3032
error?: unknown;
3133
response: Response;
3234
}> {

frontend/src/data/umbraco/getItems.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { umbracoClient } from '@/data/umbraco/client';
22
import type { paths } from '@/data/umbraco/deliveryApiSchema';
33
import type {
4+
RawUmbracoContentCollection,
45
UmbracoClientOptions,
56
UmbracoContentCollection,
67
} from '@/data/umbraco/types';
8+
import { normalizeUmbracoContentCollection } from '@/data/umbraco/types';
79

810
type GetItemsOptions = NonNullable<
911
paths['/umbraco/delivery/api/v2/content']['get']['parameters']['query'] &
@@ -19,14 +21,14 @@ export async function getItemsOrDefault(
1921
return { total: 0, items: [] };
2022
}
2123

22-
return data;
24+
return normalizeUmbracoContentCollection(data);
2325
}
2426

2527
export async function getItems({
2628
requestOptions,
2729
...options
2830
}: GetItemsOptions): Promise<{
29-
data?: UmbracoContentCollection;
31+
data?: RawUmbracoContentCollection;
3032
error?: unknown;
3133
response: Response;
3234
}> {

frontend/src/data/umbraco/types.ts

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,97 @@
11
import type { components } from '@/data/umbraco/deliveryApiSchema';
22

3-
export type UmbracoContent = components['schemas']['IApiContentResponseModel'];
4-
export type UmbracoContentCollection =
3+
const CONTENT_TYPE_MAP = {
4+
PageContentModel: 'page',
5+
PageContentResponseModel: 'page',
6+
SessionContentModel: 'session',
7+
SessionContentResponseModel: 'session',
8+
SessionsContentModel: 'sessions',
9+
SessionsContentResponseModel: 'sessions',
10+
SpeakerContentModel: 'speaker',
11+
SpeakerContentResponseModel: 'speaker',
12+
SpeakersContentModel: 'speakers',
13+
SpeakersContentResponseModel: 'speakers',
14+
SponsorsContentModel: 'sponsors',
15+
SponsorsContentResponseModel: 'sponsors',
16+
TrackContentModel: 'track',
17+
TrackContentResponseModel: 'track',
18+
} as const;
19+
20+
type ContentTypeMap = {
21+
[K in keyof typeof CONTENT_TYPE_MAP]: (typeof CONTENT_TYPE_MAP)[K];
22+
};
23+
24+
type NormalizeContentType<T extends string> = T extends keyof ContentTypeMap
25+
? ContentTypeMap[T]
26+
: T;
27+
28+
type NormalizeUmbracoType<T> = T extends (infer U)[]
29+
? NormalizeUmbracoType<U>[]
30+
: T extends readonly (infer U)[]
31+
? readonly NormalizeUmbracoType<U>[]
32+
: T extends object
33+
? {
34+
[K in keyof T]: K extends 'contentType'
35+
? T[K] extends string
36+
? NormalizeContentType<T[K]>
37+
: T[K]
38+
: NormalizeUmbracoType<T[K]>;
39+
}
40+
: T;
41+
42+
export type UmbracoContent = NormalizeUmbracoType<
43+
components['schemas']['IApiContentResponseModel']
44+
>;
45+
export type RawUmbracoContent =
46+
components['schemas']['IApiContentResponseModel'];
47+
export type UmbracoContentCollection = NormalizeUmbracoType<
48+
components['schemas']['PagedIApiContentResponseModel']
49+
>;
50+
export type RawUmbracoContentCollection =
551
components['schemas']['PagedIApiContentResponseModel'];
652

753
export type ContentTypes = UmbracoContent;
854
export type ContentTypeKeys = ContentTypes['contentType'];
955

56+
function isRecord(value: unknown): value is Record<string, unknown> {
57+
return typeof value === 'object' && value !== null;
58+
}
59+
60+
function normalizeValue(value: unknown): unknown {
61+
if (Array.isArray(value)) {
62+
return value.map(normalizeValue);
63+
}
64+
65+
if (!isRecord(value)) {
66+
return value;
67+
}
68+
69+
return Object.fromEntries(
70+
Object.entries(value).map(([key, entry]) => {
71+
if (key === 'contentType' && typeof entry === 'string') {
72+
return [
73+
key,
74+
CONTENT_TYPE_MAP[entry as keyof ContentTypeMap] ?? entry,
75+
];
76+
}
77+
78+
return [key, normalizeValue(entry)];
79+
}),
80+
);
81+
}
82+
83+
export function normalizeUmbracoContent(
84+
content: RawUmbracoContent,
85+
): UmbracoContent {
86+
return normalizeValue(content) as UmbracoContent;
87+
}
88+
89+
export function normalizeUmbracoContentCollection(
90+
collection: RawUmbracoContentCollection,
91+
): UmbracoContentCollection {
92+
return normalizeValue(collection) as UmbracoContentCollection;
93+
}
94+
1095
export type UmbracoClientOptions = {
1196
requestOptions?: {
1297
next?: RequestInit['next'];

0 commit comments

Comments
 (0)