Files
web/packages/trpc/lib/routers/contentstack/metadata/query.ts
Anton Gunnarsson 4e1cb01b84 Merged in chore/cleanup-after-trpc-migration (pull request #2457)
Chore/cleanup after trpc migration

* Clean up TODOs

* Rename REDEMPTION constant to SEARCH_TYPE_REDEMPTION

* Update dependencies

Remove unused deps from scandic-web
Add missing deps to trpc package

* Update self-referencing imports

* Remove unused variables from scandic-web env

* Fix missing graphql-tag package

* Actually fix

* Remove unused env var


Approved-by: Christian Andolf
Approved-by: Linus Flood
2025-06-30 12:08:19 +00:00

251 lines
8.9 KiB
TypeScript

import { cache } from "react"
import { createCounter } from "@scandic-hotels/common/telemetry"
import { router } from "../../.."
import { PageContentTypeEnum } from "../../../enums/contentType"
import { notFound } from "../../../errors"
import { GetAccountPageMetadata } from "../../../graphql/Query/AccountPage/Metadata.graphql"
import { GetCampaignOverviewPageMetadata } from "../../../graphql/Query/CampaignOverviewPage/Metadata.graphql"
import { GetCampaignPageMetadata } from "../../../graphql/Query/CampaignPage/Metadata.graphql"
import { GetCollectionPageMetadata } from "../../../graphql/Query/CollectionPage/Metadata.graphql"
import { GetContentPageMetadata } from "../../../graphql/Query/ContentPage/Metadata.graphql"
import { GetDestinationCityPageMetadata } from "../../../graphql/Query/DestinationCityPage/Metadata.graphql"
import { GetDestinationCountryPageMetadata } from "../../../graphql/Query/DestinationCountryPage/Metadata.graphql"
import { GetDestinationOverviewPageMetadata } from "../../../graphql/Query/DestinationOverviewPage/Metadata.graphql"
import { GetHotelPageMetadata } from "../../../graphql/Query/HotelPage/Metadata.graphql"
import { GetLoyaltyPageMetadata } from "../../../graphql/Query/LoyaltyPage/Metadata.graphql"
import { GetStartPageMetadata } from "../../../graphql/Query/StartPage/Metadata.graphql"
import { request } from "../../../graphql/request"
import { contentStackUidWithServiceProcedure } from "../../../procedures"
import { generateTag } from "../../../utils/generateTag"
import { getHotel } from "../../hotels/utils"
import { getUrlsOfAllLanguages } from "../languageSwitcher/utils"
import { getMetadataInput } from "./input"
import { getNonContentstackUrls, rawMetadataSchema } from "./output"
import { affix, getCityData, getCountryData } from "./utils"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { LanguageSwitcherData } from "../../../types/languageSwitcher"
import type { RawMetadataSchema } from "./output"
const fetchMetadata = cache(async function fetchMemoizedMetadata<T>(
query: string,
{ uid, lang }: { uid: string; lang: Lang }
) {
const getMetadataCounter = createCounter("trpc.contentstack", "metadata.get")
const metricsGetMetadata = getMetadataCounter.init({ lang, uid })
metricsGetMetadata.start()
const response = await request<T>(
query,
{ locale: lang, uid },
{
key: generateTag(lang, uid, affix),
ttl: "max",
}
)
if (!response.data) {
const notFoundError = notFound(response)
metricsGetMetadata.noDataError()
throw notFoundError
}
metricsGetMetadata.success()
return response.data
})
type Alternates = {
canonical?: string | null
languages?: Record<string, string>
}
type Robots = {
index?: boolean
follow?: boolean
}
export const metadataQueryRouter = router({
get: contentStackUidWithServiceProcedure
.input(getMetadataInput)
.query(async ({ ctx, input }) => {
const variables = {
lang: ctx.lang,
uid: ctx.uid,
}
let urls: LanguageSwitcherData | null = null
if (
input.subpage ||
input.filterFromUrl ||
!ctx.uid ||
!ctx.contentType
) {
urls = getNonContentstackUrls(ctx.lang, `${ctx.lang}/${ctx.pathname}`)
} else {
urls = await getUrlsOfAllLanguages(ctx.lang, ctx.uid, ctx.contentType)
}
let data: unknown = null
switch (ctx.contentType) {
case PageContentTypeEnum.accountPage:
const accountPageResponse = await fetchMetadata<{
account_page: RawMetadataSchema
}>(GetAccountPageMetadata, variables)
data = accountPageResponse.account_page
break
case PageContentTypeEnum.campaignOverviewPage:
const campaignOverviewPageResponse = await fetchMetadata<{
campaign_overview_page: RawMetadataSchema
}>(GetCampaignOverviewPageMetadata, variables)
data = campaignOverviewPageResponse.campaign_overview_page
break
case PageContentTypeEnum.campaignPage:
const campaignPageResponse = await fetchMetadata<{
campaign_page: RawMetadataSchema
}>(GetCampaignPageMetadata, variables)
data = campaignPageResponse.campaign_page
break
case PageContentTypeEnum.collectionPage:
const collectionPageResponse = await fetchMetadata<{
collection_page: RawMetadataSchema
}>(GetCollectionPageMetadata, variables)
data = collectionPageResponse.collection_page
break
case PageContentTypeEnum.contentPage:
const contentPageResponse = await fetchMetadata<{
content_page: RawMetadataSchema
}>(GetContentPageMetadata, variables)
data = contentPageResponse.content_page
break
case PageContentTypeEnum.destinationOverviewPage:
const destinationOverviewPageResponse = await fetchMetadata<{
destination_overview_page: RawMetadataSchema
}>(GetDestinationOverviewPageMetadata, variables)
data = destinationOverviewPageResponse.destination_overview_page
break
case PageContentTypeEnum.destinationCountryPage:
const destinationCountryPageResponse = await fetchMetadata<{
destination_country_page: RawMetadataSchema
}>(GetDestinationCountryPageMetadata, variables)
const countryData = await getCountryData(
destinationCountryPageResponse.destination_country_page,
input,
ctx.serviceToken,
ctx.lang
)
data = {
...destinationCountryPageResponse.destination_country_page,
destinationData: countryData,
}
break
case PageContentTypeEnum.destinationCityPage:
const destinationCityPageResponse = await fetchMetadata<{
destination_city_page: RawMetadataSchema
}>(GetDestinationCityPageMetadata, variables)
const cityData = await getCityData(
destinationCityPageResponse.destination_city_page,
input,
ctx.serviceToken,
ctx.lang
)
data = {
...destinationCityPageResponse.destination_city_page,
destinationData: cityData,
}
break
case PageContentTypeEnum.loyaltyPage:
const loyaltyPageResponse = await fetchMetadata<{
loyalty_page: RawMetadataSchema
}>(GetLoyaltyPageMetadata, variables)
data = loyaltyPageResponse.loyalty_page
break
case PageContentTypeEnum.hotelPage:
const hotelPageResponse = await fetchMetadata<{
hotel_page: RawMetadataSchema
}>(GetHotelPageMetadata, variables)
const hotelPageData = hotelPageResponse.hotel_page
const hotelData = hotelPageData.hotel_page_id
? await getHotel(
{
hotelId: hotelPageData.hotel_page_id,
isCardOnlyPayment: false,
language: ctx.lang,
},
ctx.serviceToken
)
: null
data = {
...hotelPageData,
...(hotelData
? {
hotelData: hotelData.hotel,
additionalHotelData: hotelData.additionalData,
hotelRestaurants: hotelData.restaurants,
}
: {}),
subpageUrl: input.subpage,
}
break
case PageContentTypeEnum.startPage:
const startPageResponse = await fetchMetadata<{
start_page: RawMetadataSchema
}>(GetStartPageMetadata, variables)
data = startPageResponse.start_page
break
default:
break
}
let alternates: Alternates | null = null
let robots: Robots | null = null
if (urls) {
const languages: Record<string, string> = {}
Object.entries(urls).forEach(([lang, { url }]) => {
languages[lang] = url
})
const canonical = urls[ctx.lang]?.url
alternates = {
canonical,
languages,
}
}
if (!data) {
alternates = null
}
if (input.noIndex) {
robots = {
index: false,
follow: false,
}
}
const transformMetadataCounter = createCounter(
"trpc.contentstack",
"metadata.transform"
)
const metricsTransformMetadata = transformMetadataCounter.init()
metricsTransformMetadata.start()
const rawMetadataResult = await rawMetadataSchema.safeParseAsync(data)
if (!rawMetadataResult.success) {
metricsTransformMetadata.validationError(rawMetadataResult.error)
return { rawMetadata: null, robots: null, alternates: null }
}
metricsTransformMetadata.success()
return { rawMetadata: rawMetadataResult.data, robots, alternates }
}),
})