Merged in feat/sw-2863-move-contentstack-router-to-trpc-package (pull request #2389)

feat(SW-2863): Move contentstack router to trpc package

* Add exports to packages and lint rule to prevent relative imports

* Add env to trpc package

* Add eslint to trpc package

* Apply lint rules

* Use direct imports from trpc package

* Add lint-staged config to trpc

* Move lang enum to common

* Restructure trpc package folder structure

* WIP first step

* update internal imports in trpc

* Fix most errors in scandic-web

Just 100 left...

* Move Props type out of trpc

* Fix CategorizedFilters types

* Move more schemas in hotel router

* Fix deps

* fix getNonContentstackUrls

* Fix import error

* Fix entry error handling

* Fix generateMetadata metrics

* Fix alertType enum

* Fix duplicated types

* lint:fix

* Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package

* Fix broken imports

* Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package


Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-06-26 07:53:01 +00:00
parent 0263ab8c87
commit 002d093af4
921 changed files with 3112 additions and 3008 deletions
@@ -0,0 +1,4 @@
import { mergeRouters } from "../../.."
import { languageSwitcherQueryRouter } from "./query"
export const languageSwitcherRouter = mergeRouters(languageSwitcherQueryRouter)
@@ -0,0 +1,8 @@
import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
export const getLanguageSwitcherInput = z.object({
lang: z.nativeEnum(Lang),
pathName: z.string(),
})
@@ -0,0 +1,15 @@
import { z } from "zod"
const link = z
.object({ url: z.string().optional(), isExternal: z.boolean() })
.optional()
.nullable()
export const validateLanguageSwitcherData = z.object({
da: link,
de: link,
en: link,
fi: link,
no: link,
sv: link,
})
@@ -0,0 +1,30 @@
import { publicProcedure } from "@scandic-hotels/trpc/procedures"
import { router } from "../../.."
import { getUidAndContentTypeByPath } from "../../../services/cms/getUidAndContentTypeByPath"
import { getNonContentstackUrls } from "../metadata/output"
import { getLanguageSwitcherInput } from "./input"
import { getUrlsOfAllLanguages } from "./utils"
import type { LanguageSwitcherData } from "../../../types/languageSwitcher"
export const languageSwitcherQueryRouter = router({
get: publicProcedure
.input(getLanguageSwitcherInput)
.query(async ({ input }) => {
const { pathName, lang } = input
const { uid, contentType } = await getUidAndContentTypeByPath(pathName)
let urls: LanguageSwitcherData | null = null
if (!uid || !contentType) {
urls = getNonContentstackUrls(lang, pathName)
} else {
urls = await getUrlsOfAllLanguages(lang, uid, contentType)
}
return {
lang,
urls,
}
}),
})
@@ -0,0 +1,203 @@
import { Lang } from "@scandic-hotels/common/constants/language"
import { createCounter } from "@scandic-hotels/common/telemetry"
import { removeTrailingSlash } from "@scandic-hotels/common/utils/url"
import { internalServerError } from "@scandic-hotels/trpc/errors"
import { PageContentTypeEnum } from "../../../enums/contentType"
import { batchRequest } from "../../../graphql/batchRequest"
import {
GetDaDeEnUrlsAccountPage,
GetFiNoSvUrlsAccountPage,
} from "../../../graphql/Query/AccountPage/AccountPage.graphql"
import {
GetDaDeEnUrlsCampaignOverviewPage,
GetFiNoSvUrlsCampaignOverviewPage,
} from "../../../graphql/Query/CampaignOverviewPage/CampaignOverviewPage.graphql"
import {
GetDaDeEnUrlsCampaignPage,
GetFiNoSvUrlsCampaignPage,
} from "../../../graphql/Query/CampaignPage/CampaignPage.graphql"
import {
GetDaDeEnUrlsCollectionPage,
GetFiNoSvUrlsCollectionPage,
} from "../../../graphql/Query/CollectionPage/CollectionPage.graphql"
import {
GetDaDeEnUrlsContentPage,
GetFiNoSvUrlsContentPage,
} from "../../../graphql/Query/ContentPage/ContentPage.graphql"
import {
GetDaDeEnUrlsCurrentBlocksPage,
GetFiNoSvUrlsCurrentBlocksPage,
} from "../../../graphql/Query/Current/LanguageSwitcher.graphql"
import {
GetDaDeEnUrlsDestinationCityPage,
GetFiNoSvUrlsDestinationCityPage,
} from "../../../graphql/Query/DestinationCityPage/DestinationCityPage.graphql"
import {
GetDaDeEnUrlsDestinationCountryPage,
GetFiNoSvUrlsDestinationCountryPage,
} from "../../../graphql/Query/DestinationCountryPage/DestinationCountryPage.graphql"
import {
GetDaDeEnUrlsDestinationOverviewPage,
GetFiNoSvUrlsDestinationOverviewPage,
} from "../../../graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql"
import {
GetDaDeEnUrlsHotelPage,
GetFiNoSvUrlsHotelPage,
} from "../../../graphql/Query/HotelPage/HotelPage.graphql"
import {
GetDaDeEnUrlsLoyaltyPage,
GetFiNoSvUrlsLoyaltyPage,
} from "../../../graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
import {
GetDaDeEnUrlsStartPage,
GetFiNoSvUrlsStartPage,
} from "../../../graphql/Query/StartPage/StartPage.graphql"
import { generateTag } from "../../../utils/generateTag"
import { validateLanguageSwitcherData } from "./output"
import type {
LanguageSwitcherData,
LanguageSwitcherQueryDataRaw,
} from "../../../types/languageSwitcher"
export const languageSwitcherAffix = "languageSwitcher"
export async function getUrlsOfAllLanguages(
lang: Lang,
uid: string,
contentType: string
) {
const getLanguageSwitcherCounter = createCounter(
"trpc.contentstack",
"languageSwitcher.get"
)
const metricsGetLanguageSwitcher = getLanguageSwitcherCounter.init({
lang,
uid,
contentType,
})
metricsGetLanguageSwitcher.start()
const variables = { uid }
const tagsDaDeEn = [
generateTag(Lang.da, uid, languageSwitcherAffix),
generateTag(Lang.de, uid, languageSwitcherAffix),
generateTag(Lang.en, uid, languageSwitcherAffix),
]
const tagsFiNoSv = [
generateTag(Lang.fi, uid, languageSwitcherAffix),
generateTag(Lang.no, uid, languageSwitcherAffix),
generateTag(Lang.sv, uid, languageSwitcherAffix),
]
let daDeEnDocument = null
let fiNoSvDocument = null
switch (contentType) {
case PageContentTypeEnum.accountPage:
daDeEnDocument = GetDaDeEnUrlsAccountPage
fiNoSvDocument = GetFiNoSvUrlsAccountPage
break
case PageContentTypeEnum.currentBlocksPage:
daDeEnDocument = GetDaDeEnUrlsCurrentBlocksPage
fiNoSvDocument = GetFiNoSvUrlsCurrentBlocksPage
break
case PageContentTypeEnum.campaignOverviewPage:
daDeEnDocument = GetDaDeEnUrlsCampaignOverviewPage
fiNoSvDocument = GetFiNoSvUrlsCampaignOverviewPage
break
case PageContentTypeEnum.campaignPage:
daDeEnDocument = GetDaDeEnUrlsCampaignPage
fiNoSvDocument = GetFiNoSvUrlsCampaignPage
break
case PageContentTypeEnum.loyaltyPage:
daDeEnDocument = GetDaDeEnUrlsLoyaltyPage
fiNoSvDocument = GetFiNoSvUrlsLoyaltyPage
break
case PageContentTypeEnum.hotelPage:
daDeEnDocument = GetDaDeEnUrlsHotelPage
fiNoSvDocument = GetFiNoSvUrlsHotelPage
break
case PageContentTypeEnum.contentPage:
daDeEnDocument = GetDaDeEnUrlsContentPage
fiNoSvDocument = GetFiNoSvUrlsContentPage
break
case PageContentTypeEnum.collectionPage:
daDeEnDocument = GetDaDeEnUrlsCollectionPage
fiNoSvDocument = GetFiNoSvUrlsCollectionPage
break
case PageContentTypeEnum.destinationOverviewPage:
daDeEnDocument = GetDaDeEnUrlsDestinationOverviewPage
fiNoSvDocument = GetFiNoSvUrlsDestinationOverviewPage
break
case PageContentTypeEnum.destinationCountryPage:
daDeEnDocument = GetDaDeEnUrlsDestinationCountryPage
fiNoSvDocument = GetFiNoSvUrlsDestinationCountryPage
break
case PageContentTypeEnum.destinationCityPage:
daDeEnDocument = GetDaDeEnUrlsDestinationCityPage
fiNoSvDocument = GetFiNoSvUrlsDestinationCityPage
break
case PageContentTypeEnum.startPage:
daDeEnDocument = GetDaDeEnUrlsStartPage
fiNoSvDocument = GetFiNoSvUrlsStartPage
break
default:
console.error(`type: [${contentType}]`)
console.error(`Trying to get a content type that is not supported`)
throw internalServerError()
}
if (!daDeEnDocument || !fiNoSvDocument) {
throw internalServerError()
}
const response = await batchRequest<LanguageSwitcherQueryDataRaw>([
{
document: daDeEnDocument,
variables,
cacheOptions: {
ttl: "max",
key: tagsDaDeEn,
},
},
{
document: fiNoSvDocument,
variables,
cacheOptions: {
ttl: "max",
key: tagsFiNoSv,
},
},
])
const urls = Object.keys(response.data).reduce<LanguageSwitcherData>(
(acc, key) => {
const item = response.data[key as Lang]
const url = item
? item.web?.original_url || removeTrailingSlash(`/${key}${item.url}`)
: undefined
return {
...acc,
[key]: { url, isExternal: !!item?.web?.original_url },
}
},
{}
)
const validatedLanguageSwitcherData =
validateLanguageSwitcherData.safeParse(urls)
if (!validatedLanguageSwitcherData.success) {
metricsGetLanguageSwitcher.validationError(
validatedLanguageSwitcherData.error
)
return null
}
metricsGetLanguageSwitcher.success()
return urls
}