Files
web/packages/trpc/lib/routers/contentstack/schemas/pageLinks.ts
Linus Flood 5fc93472f4 Merged in feat/rework-contentstack (pull request #3493)
Feat(SW-3708): refactor contentstack fetching (removing all refs) and cache invalidation

* Remove all REFS

* Revalidate correct language

* PR fixes

* PR fixes

* Throw when errors from contentstack api


Approved-by: Joakim Jäderberg
2026-01-27 12:38:36 +00:00

206 lines
5.8 KiB
TypeScript

import { z } from "zod"
import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
import { ContentEnum } from "../../../types/content"
import { safeUnion } from "../base/utils"
import { systemSchema } from "./system"
export const pageLinkSchema = z.object({
system: systemSchema,
title: z.string().optional().default(""),
url: z.string().optional().default(""),
})
export const accountPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.AccountPage),
})
.merge(pageLinkSchema)
export const extendedPageLinkSchema = pageLinkSchema.merge(
z.object({
web: z
.object({
original_url: z.string().optional().default(""),
})
.default({ original_url: "" }),
})
)
export const campaignOverviewPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.CampaignOverviewPage),
})
.merge(extendedPageLinkSchema)
export const collectionPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.CollectionPage),
})
.merge(extendedPageLinkSchema)
export const contentPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.ContentPage),
})
.merge(extendedPageLinkSchema)
export const destinationCityPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.DestinationCityPage),
})
.merge(pageLinkSchema)
export const campaignPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.CampaignPage),
})
.merge(pageLinkSchema)
export const destinationCountryPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.DestinationCountryPage),
})
.merge(pageLinkSchema)
export const destinationOverviewPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.DestinationOverviewPage),
})
.merge(pageLinkSchema)
export const hotelPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.HotelPage),
})
.merge(pageLinkSchema)
export const loyaltyPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.LoyaltyPage),
})
.merge(extendedPageLinkSchema)
export const startPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.StartPage),
})
.merge(pageLinkSchema)
export const promoCampaignPageSchema = z
.object({
__typename: z.literal(ContentEnum.blocks.PromoCampaignPage),
})
.merge(pageLinkSchema)
export const rawLinkUnionSchema = z.discriminatedUnion("__typename", [
accountPageSchema,
campaignOverviewPageSchema,
campaignPageSchema,
collectionPageSchema,
contentPageSchema,
destinationCityPageSchema,
destinationCountryPageSchema,
destinationOverviewPageSchema,
hotelPageSchema,
loyaltyPageSchema,
startPageSchema,
promoCampaignPageSchema,
])
export const linkUnionSchema = safeUnion(rawLinkUnionSchema)
type Data =
| z.output<typeof accountPageSchema>
| z.output<typeof campaignOverviewPageSchema>
| z.output<typeof campaignPageSchema>
| z.output<typeof collectionPageSchema>
| z.output<typeof contentPageSchema>
| z.output<typeof destinationCityPageSchema>
| z.output<typeof destinationCountryPageSchema>
| z.output<typeof destinationOverviewPageSchema>
| z.output<typeof hotelPageSchema>
| z.output<typeof loyaltyPageSchema>
| z.output<typeof startPageSchema>
| z.output<typeof promoCampaignPageSchema>
| Object
export function transformPageLink(data: Data) {
if (data && "__typename" in data) {
switch (data.__typename) {
case ContentEnum.blocks.AccountPage:
case ContentEnum.blocks.CampaignOverviewPage:
case ContentEnum.blocks.CampaignPage:
case ContentEnum.blocks.DestinationCityPage:
case ContentEnum.blocks.DestinationCountryPage:
case ContentEnum.blocks.DestinationOverviewPage:
case ContentEnum.blocks.HotelPage:
case ContentEnum.blocks.StartPage:
case ContentEnum.blocks.PromoCampaignPage:
return {
__typename: data.__typename,
system: data.system,
title: data.title,
url: removeMultipleSlashes(`/${data.system.locale}/${data.url}`),
}
case ContentEnum.blocks.CollectionPage:
case ContentEnum.blocks.ContentPage:
case ContentEnum.blocks.LoyaltyPage:
// TODO: Once all links use this transform
// `web` can be removed and not to be worried
// about throughout the application
return {
__typename: data.__typename,
system: data.system,
title: data.title,
url: data.web?.original_url
? data.web.original_url
: removeMultipleSlashes(`/${data.system.locale}/${data.url}`),
web: data.web,
}
}
}
}
export const internalOrExternalLinkSchema = z
.object({
is_contentstack_link: z.boolean().nullish(),
external_link: z
.object({
href: nullableStringValidator,
title: z.string().nullish(),
})
.nullish(),
title: nullableStringValidator,
linkConnection: z.object({
edges: z.array(
z.object({
node: linkUnionSchema.transform((data) => {
const link = transformPageLink(data)
if (link) {
return link
}
return data
}),
})
),
}),
})
.transform(
({ is_contentstack_link, external_link, linkConnection, title }) => {
if (is_contentstack_link !== false && linkConnection.edges.length) {
const linkRef = linkConnection.edges[0].node
return {
title: title || linkRef.title,
url: linkRef.url,
}
} else if (is_contentstack_link === false && external_link?.href) {
return {
title: title || external_link.title || "",
url: external_link.href,
}
}
}
)