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,106 +4,15 @@ import { transformedImageVaultAssetSchema } from "@scandic-hotels/common/utils/i
import { LoyaltyPageEnum } from "../../../enums/loyaltyPage"
import { discriminatedUnionArray } from "../../../utils/discriminatedUnion"
import {
cardGridRefsSchema,
cardsGridSchema,
} from "../schemas/blocks/cardsGrid"
import {
contentRefsSchema as blockContentRefsSchema,
contentSchema as blockContentSchema,
} from "../schemas/blocks/content"
import {
dynamicContentRefsSchema,
dynamicContentSchema as blockDynamicContentSchema,
} from "../schemas/blocks/dynamicContent"
import {
shortcutsRefsSchema,
shortcutsSchema,
} from "../schemas/blocks/shortcuts"
import {
contentRefsSchema as sidebarContentRefsSchema,
contentSchema as sidebarContentSchema,
} from "../schemas/sidebar/content"
import { cardsGridSchema } from "../schemas/blocks/cardsGrid"
import { contentSchema as blockContentSchema } from "../schemas/blocks/content"
import { dynamicContentSchema as blockDynamicContentSchema } from "../schemas/blocks/dynamicContent"
import { shortcutsSchema } from "../schemas/blocks/shortcuts"
import { contentSchema as sidebarContentSchema } from "../schemas/sidebar/content"
import { dynamicContentSchema as sidebarDynamicContentSchema } from "../schemas/sidebar/dynamicContent"
import {
joinLoyaltyContactRefsSchema,
joinLoyaltyContactSchema,
} from "../schemas/sidebar/joinLoyaltyContact"
import { joinLoyaltyContactSchema } from "../schemas/sidebar/joinLoyaltyContact"
import { systemSchema } from "../schemas/system"
// LoyaltyPage Refs
const extendedCardGridRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.CardsGrid),
})
.merge(cardGridRefsSchema)
const extendedContentRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.Content),
})
.merge(blockContentRefsSchema)
const extendedDynamicContentRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.DynamicContent),
})
.merge(dynamicContentRefsSchema)
const extendedShortcutsRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.Shortcuts),
})
.merge(shortcutsRefsSchema)
const blocksRefsSchema = z.discriminatedUnion("__typename", [
extendedCardGridRefsSchema,
extendedContentRefsSchema,
extendedDynamicContentRefsSchema,
extendedShortcutsRefsSchema,
])
const contentSidebarRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.sidebar.Content),
})
.merge(sidebarContentRefsSchema)
const extendedJoinLoyaltyContactRefsSchema = z
.object({
__typename: z.literal(
LoyaltyPageEnum.ContentStack.sidebar.JoinLoyaltyContact
),
})
.merge(joinLoyaltyContactRefsSchema)
const sidebarRefsSchema = z.discriminatedUnion("__typename", [
contentSidebarRefsSchema,
extendedJoinLoyaltyContactRefsSchema,
z.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.sidebar.DynamicContent),
}),
])
export const loyaltyPageRefsSchema = z.object({
loyalty_page: z.object({
blocks: discriminatedUnionArray(blocksRefsSchema.options).optional(),
sidebar: discriminatedUnionArray(sidebarRefsSchema.options)
.optional()
.transform((data) => {
if (data) {
return data.filter(
(block) =>
block.__typename !==
LoyaltyPageEnum.ContentStack.sidebar.DynamicContent
)
}
return data
}),
system: systemSchema,
}),
})
// LoyaltyPage
export const extendedCardsGridSchema = z
.object({

View File

@@ -2,78 +2,20 @@ import { createCounter } from "@scandic-hotels/common/telemetry"
import { router } from "../../.."
import { notFoundError } from "../../../errors"
import {
GetLoyaltyPage,
GetLoyaltyPageRefs,
} from "../../../graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
import { GetLoyaltyPage } from "../../../graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
import { request } from "../../../graphql/request"
import { contentstackExtendedProcedureUID } from "../../../procedures"
import {
generateRefsResponseTag,
generateTag,
generateTagsFromAssetSystem,
generateTagsFromSystem,
} from "../../../utils/generateTag"
import { loyaltyPageRefsSchema, loyaltyPageSchema } from "./output"
import { getConnections } from "./utils"
import { generateTag } from "../../../utils/generateTag"
import { loyaltyPageSchema } from "./output"
import type {
GetLoyaltyPageRefsSchema,
GetLoyaltyPageSchema,
} from "../../../types/loyaltyPage"
import type { GetLoyaltyPageSchema } from "../../../types/loyaltyPage"
import type { TrackingPageData } from "../../types"
export const loyaltyPageQueryRouter = router({
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
const { lang, uid } = ctx
const getLoyaltyPageRefsCounter = createCounter(
"trpc.contentstack.loyaltyPage.get.refs"
)
const metricsGetLoyaltyPageRefs = getLoyaltyPageRefsCounter.init({
lang,
uid,
})
metricsGetLoyaltyPageRefs.start()
const variables = { locale: lang, uid }
const refsResponse = await request<GetLoyaltyPageRefsSchema>(
GetLoyaltyPageRefs,
variables,
{
key: generateRefsResponseTag(lang, uid),
ttl: "max",
}
)
if (!refsResponse.data) {
metricsGetLoyaltyPageRefs.noDataError()
throw notFoundError({
message: "GetLoyaltyPageRefs returned no data",
errorDetails: { ...variables },
})
}
const validatedLoyaltyPageRefs = loyaltyPageRefsSchema.safeParse(
refsResponse.data
)
if (!validatedLoyaltyPageRefs.success) {
metricsGetLoyaltyPageRefs.validationError(validatedLoyaltyPageRefs.error)
return null
}
metricsGetLoyaltyPageRefs.success()
const { connections, assetConnections } = getConnections(
validatedLoyaltyPageRefs.data
)
const tags = [
generateTagsFromSystem(lang, connections),
generateTagsFromAssetSystem(assetConnections),
generateTag(lang, validatedLoyaltyPageRefs.data.loyalty_page.system.uid),
].flat()
const cacheKey = generateTag(lang, uid)
const getLoyaltyPageCounter = createCounter(
"trpc.contentstack.loyaltyPage.get"
@@ -82,11 +24,12 @@ export const loyaltyPageQueryRouter = router({
metricsGetLoyaltyPage.start()
const variables = { locale: lang, uid }
const response = await request<GetLoyaltyPageSchema>(
GetLoyaltyPage,
variables,
{
key: tags,
key: `${cacheKey}:loyaltyPage`,
ttl: "max",
}
)

View File

@@ -1,72 +0,0 @@
import { LoyaltyPageEnum } from "../../../enums/loyaltyPage"
import type { LoyaltyPageRefs } from "../../../types/loyaltyPage"
import type { AssetSystem, System } from "../schemas/system"
export function getConnections({ loyalty_page }: LoyaltyPageRefs) {
const connections: System["system"][] = [loyalty_page.system]
const assetConnections: AssetSystem[] = []
if (loyalty_page.blocks) {
loyalty_page.blocks.forEach((block) => {
switch (block.__typename) {
case LoyaltyPageEnum.ContentStack.blocks.CardsGrid:
if (block.cards_grid.length) {
connections.push(...block.cards_grid)
}
break
case LoyaltyPageEnum.ContentStack.blocks.Content:
if (block?.content?.length) {
block.content.forEach((contentBlock) => {
if ("system" in contentBlock) {
assetConnections.push(contentBlock)
} else {
connections.push(contentBlock)
}
})
}
break
case LoyaltyPageEnum.ContentStack.blocks.DynamicContent:
if (block.dynamic_content.link) {
connections.push(block.dynamic_content.link)
}
break
case LoyaltyPageEnum.ContentStack.blocks.Shortcuts:
if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts.filter((c) => !!c))
}
break
default:
break
}
})
}
if (loyalty_page.sidebar) {
loyalty_page.sidebar.forEach((block) => {
switch (block?.__typename) {
case LoyaltyPageEnum.ContentStack.sidebar.Content:
if (block?.content?.length) {
block.content.forEach((contentBlock) => {
if ("system" in contentBlock) {
assetConnections.push(contentBlock)
} else {
connections.push(contentBlock)
}
})
}
break
case LoyaltyPageEnum.ContentStack.sidebar.JoinLoyaltyContact:
if (block.join_loyalty_contact?.button) {
connections.push(block.join_loyalty_contact.button)
}
break
default:
break
}
})
}
return { connections, assetConnections }
}