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
This commit is contained in:
Linus Flood
2026-01-27 12:38:36 +00:00
parent a5e214f783
commit 5fc93472f4
193 changed files with 489 additions and 9018 deletions

View File

@@ -4,30 +4,15 @@ import { transformedImageVaultAssetSchema } from "@scandic-hotels/common/utils/i
import { CollectionPageEnum } from "../../../types/collectionPage"
import { discriminatedUnionArray } from "../../../utils/discriminatedUnion"
import {
cardGridRefsSchema,
cardsGridSchema,
} from "../schemas/blocks/cardsGrid"
import {
dynamicContentRefsSchema,
dynamicContentSchema as blockDynamicContentSchema,
} from "../schemas/blocks/dynamicContent"
import {
shortcutsRefsSchema,
shortcutsSchema,
} from "../schemas/blocks/shortcuts"
import { uspGridRefsSchema, uspGridSchema } from "../schemas/blocks/uspGrid"
import {
videoCardRefsSchema,
videoCardSchema,
} from "../schemas/blocks/videoCard"
import {
linkAndTitleSchema,
linkConnectionRefs,
} from "../schemas/linkConnection"
import { cardsGridSchema } from "../schemas/blocks/cardsGrid"
import { dynamicContentSchema as blockDynamicContentSchema } from "../schemas/blocks/dynamicContent"
import { shortcutsSchema } from "../schemas/blocks/shortcuts"
import { uspGridSchema } from "../schemas/blocks/uspGrid"
import { videoCardSchema } from "../schemas/blocks/videoCard"
import { linkAndTitleSchema } from "../schemas/linkConnection"
import { internalOrExternalLinkSchema } from "../schemas/pageLinks"
import { systemSchema } from "../schemas/system"
import { transformedVideoSchema, videoRefSchema } from "../schemas/video"
import { transformedVideoSchema } from "../schemas/video"
// Block schemas
export const collectionPageCards = z
@@ -116,60 +101,3 @@ export const collectionPageSchema = z.object({
url: z.string(),
}),
})
/** REFS */
const collectionPageCardsRefs = z
.object({
__typename: z.literal(CollectionPageEnum.ContentStack.blocks.CardsGrid),
})
.merge(cardGridRefsSchema)
const collectionPageShortcutsRefs = z
.object({
__typename: z.literal(CollectionPageEnum.ContentStack.blocks.Shortcuts),
})
.merge(shortcutsRefsSchema)
const collectionPageUspGridRefs = z
.object({
__typename: z.literal(CollectionPageEnum.ContentStack.blocks.UspGrid),
})
.merge(uspGridRefsSchema)
const collectionPageDynamicContentRefs = z
.object({
__typename: z.literal(
CollectionPageEnum.ContentStack.blocks.DynamicContent
),
})
.merge(dynamicContentRefsSchema)
const collectionPageVideoCardRefs = z
.object({
__typename: z.literal(CollectionPageEnum.ContentStack.blocks.VideoCard),
})
.merge(videoCardRefsSchema)
const collectionPageBlockRefsItem = z.discriminatedUnion("__typename", [
collectionPageShortcutsRefs,
collectionPageDynamicContentRefs,
collectionPageCardsRefs,
collectionPageUspGridRefs,
collectionPageVideoCardRefs,
])
const collectionPageHeaderRefs = z.object({
navigation_links: z.array(linkConnectionRefs),
top_primary_button: linkConnectionRefs.nullable(),
})
export const collectionPageRefsSchema = z.object({
collection_page: z.object({
hero_video: videoRefSchema.nullish(),
header: collectionPageHeaderRefs,
blocks: discriminatedUnionArray(
collectionPageBlockRefsItem.options
).nullable(),
system: systemSchema,
}),
})

View File

@@ -4,12 +4,8 @@ import { router } from "../../.."
import { GetCollectionPage } from "../../../graphql/Query/CollectionPage/CollectionPage.graphql"
import { request } from "../../../graphql/request"
import { contentstackExtendedProcedureUID } from "../../../procedures"
import { generateTag } from "../../../utils/generateTag"
import { collectionPageSchema } from "./output"
import {
fetchCollectionPageRefs,
generatePageTags,
validateCollectionPageRefs,
} from "./utils"
import type { GetCollectionPageSchema } from "../../../types/collectionPage"
import type { TrackingPageData } from "../../types"
@@ -18,17 +14,7 @@ export const collectionPageQueryRouter = router({
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
const { lang, uid } = ctx
const collectionPageRefsData = await fetchCollectionPageRefs(lang, uid)
const collectionPageRefs = validateCollectionPageRefs(
collectionPageRefsData,
lang,
uid
)
if (!collectionPageRefs) {
return null
}
const tags = generatePageTags(collectionPageRefs, lang)
const cacheKey = generateTag(lang, uid)
const getCollectionPageCounter = createCounter(
"trpc.contentstack.collectionPage.get"
@@ -44,7 +30,7 @@ export const collectionPageQueryRouter = router({
GetCollectionPage,
{ locale: lang, uid },
{
key: tags,
key: `${cacheKey}:collectionPage`,
ttl: "max",
}
)

View File

@@ -1,144 +0,0 @@
import { createCounter } from "@scandic-hotels/common/telemetry"
import { notFoundError } from "../../../errors"
import { GetCollectionPageRefs } from "../../../graphql/Query/CollectionPage/CollectionPage.graphql"
import { request } from "../../../graphql/request"
import {
CollectionPageEnum,
type CollectionPageRefs,
type GetCollectionPageRefsSchema,
} from "../../../types/collectionPage"
import {
generateRefsResponseTag,
generateTag,
generateTagsFromAssetSystem,
generateTagsFromSystem,
} from "../../../utils/generateTag"
import { collectionPageRefsSchema } from "./output"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { AssetSystem, System } from "../schemas/system"
export async function fetchCollectionPageRefs(lang: Lang, uid: string) {
const getCollectionPageRefsCounter = createCounter(
"trpc.contentstack.collectionPage.get.refs"
)
const metricsGetCollectionPageRefs = getCollectionPageRefsCounter.init({
lang,
uid,
})
metricsGetCollectionPageRefs.start()
const cacheKey = generateRefsResponseTag(lang, uid)
const variables = {
locale: lang,
uid,
}
const refsResponse = await request<GetCollectionPageRefsSchema>(
GetCollectionPageRefs,
variables,
{
key: cacheKey,
ttl: "max",
}
)
if (!refsResponse.data) {
metricsGetCollectionPageRefs.noDataError()
throw notFoundError({
message: "GetCollectionPageRefs returned no data",
errorDetails: variables,
})
}
return refsResponse.data
}
export function validateCollectionPageRefs(
data: GetCollectionPageRefsSchema,
lang: Lang,
uid: string
) {
const getCollectionPageRefsCounter = createCounter(
"trpc.contentstack.collectionPage.get.refs"
)
const metricsGetCollectionPageRefs = getCollectionPageRefsCounter.init({
lang,
uid,
})
const validatedData = collectionPageRefsSchema.safeParse(data)
if (!validatedData.success) {
metricsGetCollectionPageRefs.validationError(validatedData.error)
return null
}
metricsGetCollectionPageRefs.success()
return validatedData.data
}
export function generatePageTags(
validatedData: CollectionPageRefs,
lang: Lang
): string[] {
const { connections, assetConnections } = getConnections(validatedData)
return [
generateTagsFromSystem(lang, connections),
generateTagsFromAssetSystem(assetConnections),
generateTag(lang, validatedData.collection_page.system.uid),
].flat()
}
export function getConnections({ collection_page }: CollectionPageRefs) {
const connections: System["system"][] = [collection_page.system]
const assetConnections: AssetSystem[] = []
if (collection_page.hero_video?.sourceConnection.edges[0]) {
assetConnections.push(
collection_page.hero_video.sourceConnection.edges[0].node
)
}
if (collection_page.blocks) {
collection_page.blocks.forEach((block) => {
const typeName = block.__typename
switch (typeName) {
case CollectionPageEnum.ContentStack.blocks.Shortcuts:
if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts.filter((c) => !!c))
}
break
case CollectionPageEnum.ContentStack.blocks.CardsGrid:
if (block.cards_grid.length) {
connections.push(...block.cards_grid)
}
break
case CollectionPageEnum.ContentStack.blocks.UspGrid:
if (block.usp_grid.length) {
connections.push(...block.usp_grid.filter((c) => !!c))
}
break
case CollectionPageEnum.ContentStack.blocks.VideoCard:
if (block.video_card?.system) {
connections.push(block.video_card.system)
}
if (block.video_card?.video.sourceConnection.edges[0]) {
assetConnections.push(
block.video_card.video.sourceConnection.edges[0].node
)
}
break
case CollectionPageEnum.ContentStack.blocks.DynamicContent:
break
default:
const _exhaustiveCheck: never = typeName
break
}
})
}
return { connections, assetConnections }
}