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
@@ -6,21 +6,11 @@ import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import { DestinationCountryPageEnum } from "../../../types/destinationCountryPage"
import { discriminatedUnionArray } from "../../../utils/discriminatedUnion"
import {
accordionRefsSchema,
accordionSchema,
} from "../schemas/blocks/accordion"
import { contentRefsSchema, contentSchema } from "../schemas/blocks/content"
import {
destinationFiltersRefsSchema,
transformedDestinationFiltersSchema,
} from "../schemas/destinationFilters"
import { accordionSchema } from "../schemas/blocks/accordion"
import { contentSchema } from "../schemas/blocks/content"
import { transformedDestinationFiltersSchema } from "../schemas/destinationFilters"
import { mapLocationSchema } from "../schemas/mapLocation"
import {
linkRefsUnionSchema,
linkUnionSchema,
transformPageLink,
} from "../schemas/pageLinks"
import { linkUnionSchema, transformPageLink } from "../schemas/pageLinks"
import { systemSchema } from "../schemas/system"
import type { ImageVaultAsset } from "@scandic-hotels/common/utils/imageVault"
@@ -131,47 +121,3 @@ export const countryPageUrlsSchema = z
.transform(
({ all_destination_country_page }) => all_destination_country_page.items
)
/** REFS */
const destinationCountryPageContentRefs = z
.object({
__typename: z.literal(
DestinationCountryPageEnum.ContentStack.blocks.Content
),
})
.merge(contentRefsSchema)
const destinationCountryPageAccordionRefs = z
.object({
__typename: z.literal(
DestinationCountryPageEnum.ContentStack.blocks.Accordion
),
})
.merge(accordionRefsSchema)
const blocksRefsSchema = z.discriminatedUnion("__typename", [
destinationCountryPageAccordionRefs,
destinationCountryPageContentRefs,
])
export const destinationCountryPageRefsSchema = z.object({
destination_country_page: z.object({
sidepeek_content: z
.object({
content: z
.object({
embedded_itemsConnection: z.object({
edges: z.array(
z.object({
node: linkRefsUnionSchema,
})
),
}),
})
.nullish(),
})
.nullish(),
blocks: discriminatedUnionArray(blocksRefsSchema.options).nullable(),
seo_filters: destinationFiltersRefsSchema,
system: systemSchema,
}),
})
@@ -2,73 +2,26 @@ import { createCounter } from "@scandic-hotels/common/telemetry"
import { router } from "../../.."
import { notFoundError } from "../../../errors"
import {
GetDestinationCountryPage,
GetDestinationCountryPageRefs,
} from "../../../graphql/Query/DestinationCountryPage/DestinationCountryPage.graphql"
import { GetDestinationCountryPage } from "../../../graphql/Query/DestinationCountryPage/DestinationCountryPage.graphql"
import { request } from "../../../graphql/request"
import {
contentStackBaseWithServiceProcedure,
contentstackExtendedProcedureUID,
} from "../../../procedures"
import { ApiCountry } from "../../../types/country"
import { generateRefsResponseTag } from "../../../utils/generateTag"
import { generateTag } from "../../../utils/generateTag"
import { getCityPagesInput } from "./input"
import {
destinationCountryPageRefsSchema,
destinationCountryPageSchema,
} from "./output"
import { generatePageTags, getCityPages } from "./utils"
import { destinationCountryPageSchema } from "./output"
import { getCityPages } from "./utils"
import type {
GetDestinationCountryPageData,
GetDestinationCountryPageRefsSchema,
} from "../../../types/destinationCountryPage"
import type { GetDestinationCountryPageData } from "../../../types/destinationCountryPage"
import type { TrackingPageData } from "../../types"
export const destinationCountryPageQueryRouter = router({
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
const { lang, uid } = ctx
const getDestinationCountryPageRefsCounter = createCounter(
"trpc.contentstack.destinationCountryPage.get.refs"
)
const metricsGetDestinationCountryPageRefs =
getDestinationCountryPageRefsCounter.init({ lang, uid })
metricsGetDestinationCountryPageRefs.start()
const variables = { locale: lang, uid }
const refsResponse = await request<GetDestinationCountryPageRefsSchema>(
GetDestinationCountryPageRefs,
variables,
{
key: generateRefsResponseTag(lang, uid),
ttl: "max",
}
)
if (!refsResponse.data) {
metricsGetDestinationCountryPageRefs.noDataError()
throw notFoundError({
message: "GetDestinationCountryPageRefs returned no data",
errorDetails: variables,
})
}
const validatedRefsData = destinationCountryPageRefsSchema.safeParse(
refsResponse.data
)
if (!validatedRefsData.success) {
metricsGetDestinationCountryPageRefs.validationError(
validatedRefsData.error
)
return null
}
metricsGetDestinationCountryPageRefs.success()
const tags = generatePageTags(validatedRefsData.data, lang)
const cacheKey = generateTag(lang, uid)
const getDestinationCountryPageCounter = createCounter(
"trpc.contentstack.destinationCountryPage.get"
@@ -78,11 +31,12 @@ export const destinationCountryPageQueryRouter = router({
metricsGetDestinationCountryPage.start()
const variables = { locale: lang, uid }
const response = await request<GetDestinationCountryPageData>(
GetDestinationCountryPage,
variables,
{
key: tags,
key: `${cacheKey}:destinationCountryPage`,
ttl: "max",
}
)
@@ -4,8 +4,7 @@ import { GetDestinationCityListData } from "../../../graphql/Query/DestinationCi
import { GetCountryPageUrls } from "../../../graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql"
import { request } from "../../../graphql/request"
import { ApiCountry } from "../../../types/country"
import { DestinationCountryPageEnum } from "../../../types/destinationCountryPage"
import { generateTag, generateTagsFromSystem } from "../../../utils/generateTag"
import { generateTag } from "../../../utils/generateTag"
import { getCitiesByCountry } from "../../hotels/services/getCitiesByCountry"
import { destinationCityListDataSchema } from "../destinationCityPage/output"
import { countryPageUrlsSchema } from "./output"
@@ -14,60 +13,7 @@ import type { Country } from "@scandic-hotels/common/constants/country"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { GetDestinationCityListDataResponse } from "../../../types/destinationCityPage"
import type {
DestinationCountryPageRefs,
GetCountryPageUrlsData,
} from "../../../types/destinationCountryPage"
import type { System } from "../schemas/system"
export function generatePageTags(
validatedData: DestinationCountryPageRefs,
lang: Lang
): string[] {
const connections = getConnections(validatedData)
return [
generateTagsFromSystem(lang, connections),
generateTag(lang, validatedData.destination_country_page.system.uid),
].flat()
}
export function getConnections({
destination_country_page,
}: DestinationCountryPageRefs) {
const connections: System["system"][] = [destination_country_page.system]
if (destination_country_page.blocks) {
destination_country_page.blocks.forEach((block) => {
switch (block.__typename) {
case DestinationCountryPageEnum.ContentStack.blocks.Accordion: {
if (block.accordion.length) {
connections.push(...block.accordion.filter((c) => !!c))
}
break
}
case DestinationCountryPageEnum.ContentStack.blocks.Content:
{
if (block?.content?.length) {
// TS has trouble infering the filtered types
// @ts-ignore
connections.push(...block.content)
}
}
break
}
})
}
if (destination_country_page.sidepeek_content) {
destination_country_page.sidepeek_content?.content?.embedded_itemsConnection.edges.forEach(
({ node }) => {
if (node.system) {
connections.push(node.system)
}
}
)
}
return connections
}
import type { GetCountryPageUrlsData } from "../../../types/destinationCountryPage"
export async function getCityListDataByCityIdentifier(
lang: Lang,