Merged in feat/SW-1745-language-switcher-non-contentstack-pages (pull request #1501)

feat(SW-1745): Query for language switcher returns just current pathname for non-contentstack pages except for hotelreservation paths

* feat(SW-1745): Query for language switcher returns just current pathname for non-contentstack pages except for hotelreservation paths


Approved-by: Linus Flood
This commit is contained in:
Erik Tiekstra
2025-03-10 09:06:19 +00:00
parent 1009ea87c9
commit 19bb965298
11 changed files with 267 additions and 260 deletions

View File

@@ -61,6 +61,7 @@
"As our Close Friend": "Som vores nære ven",
"As our {level}": "Som vores {level}",
"As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Da dette er et ophold med flere værelser, skal annullereringen gennemføres af personen, der har booket opholdet. Bedes du ringe til vores kundeservice på 08-517 517 00, hvis du har brug for yderligere hjælp.",
"At a cost": "Mod betaling",
"At latest": "Senest",
"At the hotel": "På hotellet",
"Attractions": "Attraktioner",

View File

@@ -61,6 +61,7 @@
"As our Close Friend": "Als unser enger Freund",
"As our {level}": "Als unser {level}",
"As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Da dies ein Mehrzimmer-Aufenthalt ist, muss die Stornierung von der Person, die die Buchung getätigt hat, durchgeführt werden. Bitte rufen Sie uns unter der Telefonnummer 08-517 517 00 an, wenn Sie weitere Hilfe benötigen.",
"At a cost": "Gegen Gebühr",
"At latest": "Spätestens",
"At the hotel": "Im Hotel",
"Attraction": "Attraktion",

View File

@@ -64,6 +64,7 @@
"As our Close Friend": "As our Close Friend",
"As our {level}": "As our {level}",
"As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.",
"At a cost": "At a cost",
"At latest": "At latest",
"At the hotel": "At the hotel",
"Attractions": "Attractions",

View File

@@ -60,6 +60,7 @@
"As our Close Friend": "Läheisenä ystävänämme",
"As our {level}": "{level}-etu",
"As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Koska tämä on monihuoneinen majoitus, peruutus on tehtävä henkilölle, joka teki varauksen. Ota yhteyttä asiakaspalveluun apua varten, jos tarvitset lisää apua.",
"At a cost": "Maksua vastaan",
"At latest": "Viimeistään",
"At the hotel": "Hotellissa",
"Attractions": "Nähtävyydet",

View File

@@ -60,6 +60,7 @@
"As our Close Friend": "Som vår nære venn",
"As our {level}": "Som vår {level}",
"As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Som dette er et ophold med flere rom, må annullereringen gjøres av personen som booket opholdet. Vennligst ring 08-517 517 00 til vår kundeservice hvis du trenger mer hjelp.",
"At a cost": "Mot betaling",
"At latest": "Senest",
"At the hotel": "På hotellet",
"Attractions": "Attraksjoner",

View File

@@ -60,6 +60,7 @@
"As our Close Friend": "Som vår nära vän",
"As our {level}": "Som vår {level}",
"As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Då detta är en vistelse med flera rum måste avbokningen göras av personen som bokade vistelsen. Kontakta vår kundsupport på 08-517 517 00 om du behöver mer hjälp.",
"At a cost": "Mot en kostnad",
"At latest": "Senast",
"At the hotel": "På hotellet",
"Attractions": "Sevärdheter",

View File

@@ -112,12 +112,6 @@ export const getCurrentFooter = cache(async function getMemoizedCurrentFooter(
return serverClient().contentstack.base.currentFooter({ lang })
})
export const getLanguageSwitcher = cache(
async function getMemoizedLanguageSwitcher() {
return serverClient().contentstack.languageSwitcher.get()
}
)
export const getSiteConfig = cache(async function getMemoizedSiteConfig(
lang: Lang
) {

View File

@@ -2,9 +2,7 @@ import { z } from "zod"
import { Lang } from "@/constants/languages"
export const getLanguageSwitcherInput = z
.object({
lang: z.nativeEnum(Lang),
pathName: z.string(),
})
.optional()
export const getLanguageSwitcherInput = z.object({
lang: z.nativeEnum(Lang),
pathName: z.string(),
})

View File

@@ -1,277 +1,47 @@
import { metrics } from "@opentelemetry/api"
import { Lang } from "@/constants/languages"
import { baseUrls } from "@/constants/routes/baseUrls"
import { findMyBooking } from "@/constants/routes/findMyBooking"
import { batchRequest } from "@/lib/graphql/batchRequest"
import {
GetDaDeEnUrlsAccountPage,
GetFiNoSvUrlsAccountPage,
} from "@/lib/graphql/Query/AccountPage/AccountPage.graphql"
import {
GetDaDeEnUrlsCollectionPage,
GetFiNoSvUrlsCollectionPage,
} from "@/lib/graphql/Query/CollectionPage/CollectionPage.graphql"
import {
GetDaDeEnUrlsContentPage,
GetFiNoSvUrlsContentPage,
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
import {
GetDaDeEnUrlsCurrentBlocksPage,
GetFiNoSvUrlsCurrentBlocksPage,
} from "@/lib/graphql/Query/Current/LanguageSwitcher.graphql"
import {
GetDaDeEnUrlsDestinationCityPage,
GetFiNoSvUrlsDestinationCityPage,
} from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPage.graphql"
import {
GetDaDeEnUrlsDestinationCountryPage,
GetFiNoSvUrlsDestinationCountryPage,
} from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPage.graphql"
import {
GetDaDeEnUrlsDestinationOverviewPage,
GetFiNoSvUrlsDestinationOverviewPage,
} from "@/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql"
import {
GetDaDeEnUrlsHotelPage,
GetFiNoSvUrlsHotelPage,
} from "@/lib/graphql/Query/HotelPage/HotelPage.graphql"
import {
GetDaDeEnUrlsLoyaltyPage,
GetFiNoSvUrlsLoyaltyPage,
} from "@/lib/graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
import {
GetDaDeEnUrlsStartPage,
GetFiNoSvUrlsStartPage,
} from "@/lib/graphql/Query/StartPage/StartPage.graphql"
import { internalServerError } from "@/server/errors/trpc"
import { hotelreservation } from "@/constants/routes/hotelReservation"
import { publicProcedure, router } from "@/server/trpc"
import { getUidAndContentTypeByPath } from "@/services/cms/getUidAndContentTypeByPath"
import { generateTag } from "@/utils/generateTag"
import { getLanguageSwitcherInput } from "./input"
import { validateLanguageSwitcherData } from "./output"
import { languageSwitcherAffix } from "./utils"
import { getUrlsOfAllLanguages } from "./utils"
import { PageContentTypeEnum } from "@/types/requests/contentType"
import type {
LanguageSwitcherData,
LanguageSwitcherQueryDataRaw,
} from "@/types/requests/languageSwitcher"
interface LanguageSwitcherVariables {
contentType: string
uid: string
}
const meter = metrics.getMeter("trpc.contentstack.languageSwitcher")
const getLanguageSwitcherCounter = meter.createCounter(
"trpc.contentstack.languageSwitcher.get"
)
const getLanguageSwitcherSuccessCounter = meter.createCounter(
"trpc.contentstack.languageSwitcher.get-success"
)
const getLanguageSwitcherFailCounter = meter.createCounter(
"trpc.contentstack.languageSwitcher.get-fail"
)
async function getLanguageSwitcher(options: LanguageSwitcherVariables) {
const variables = { uid: options.uid }
const tagsDaDeEn = [
generateTag(Lang.da, options.uid, languageSwitcherAffix),
generateTag(Lang.de, options.uid, languageSwitcherAffix),
generateTag(Lang.en, options.uid, languageSwitcherAffix),
]
const tagsFiNoSv = [
generateTag(Lang.fi, options.uid, languageSwitcherAffix),
generateTag(Lang.no, options.uid, languageSwitcherAffix),
generateTag(Lang.sv, options.uid, languageSwitcherAffix),
]
let daDeEnDocument = null
let fiNoSvDocument = null
switch (options.contentType) {
case PageContentTypeEnum.accountPage:
daDeEnDocument = GetDaDeEnUrlsAccountPage
fiNoSvDocument = GetFiNoSvUrlsAccountPage
break
case PageContentTypeEnum.currentBlocksPage:
daDeEnDocument = GetDaDeEnUrlsCurrentBlocksPage
fiNoSvDocument = GetFiNoSvUrlsCurrentBlocksPage
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: [${options.contentType}]`)
console.error(`Trying to get a content type that is not supported`)
throw internalServerError()
}
if (daDeEnDocument && fiNoSvDocument) {
return await batchRequest<LanguageSwitcherQueryDataRaw>([
{
document: daDeEnDocument,
variables,
options: {
cache: "force-cache",
next: {
tags: tagsDaDeEn,
},
},
},
{
document: fiNoSvDocument,
variables,
options: {
cache: "force-cache",
next: {
tags: tagsFiNoSv,
},
},
},
])
}
throw internalServerError()
}
import type { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
import type { Lang } from "@/constants/languages"
export const languageSwitcherQueryRouter = router({
get: publicProcedure
.input(getLanguageSwitcherInput)
.query(async ({ input, ctx }) => {
let uid = ctx.uid
let contentType = ctx.contentType
let lang = ctx.lang ?? input?.lang
.query(async ({ input }) => {
const { pathName, lang } = input
const { uid, contentType } = await getUidAndContentTypeByPath(pathName)
if (input) {
const data = await getUidAndContentTypeByPath(input.pathName)
uid = data.uid
contentType = data.contentType ?? ctx.contentType
}
if (!uid || !lang) {
if (!uid || !contentType) {
// we have pages that are not currently routed within contentstack context,
// therefor this fix is needed for some of these pages
if (input && Object.values(findMyBooking).includes(input.pathName)) {
const urls: Record<string, { url: string }> = {}
if (Object.values(findMyBooking).includes(pathName)) {
const urls: LanguageSwitcherData = {}
return {
lang,
urls: Object.entries(findMyBooking).reduce((acc, [lang, url]) => {
acc[lang] = { url }
acc[lang as Lang] = { url }
return urls
}, urls),
}
}
return { lang: lang, urls: baseUrls }
if (pathName.startsWith(hotelreservation(lang))) {
return { lang, urls: baseUrls }
}
return { lang, urls: { [lang]: { url: pathName } } }
}
getLanguageSwitcherCounter.add(1, {
uid: uid,
lang: lang,
contentType: contentType,
})
console.info(
"contentstack.languageSwitcher start",
JSON.stringify({
query: {
uid: uid,
lang: lang,
contentType: contentType,
},
})
)
const res = await getLanguageSwitcher({
contentType: contentType!,
uid: uid,
})
const urls = await getUrlsOfAllLanguages(lang, uid, contentType)
const urls = Object.keys(res.data).reduce<LanguageSwitcherData>(
(acc, key) => {
const item = res.data[key as Lang]
const url = item
? item.web?.original_url || `/${key}${item.url}`
: undefined
return {
...acc,
[key]: { url, isExternal: !!item?.web?.original_url },
}
},
{} as LanguageSwitcherData
)
const validatedLanguageSwitcherData =
validateLanguageSwitcherData.safeParse(urls)
if (!validatedLanguageSwitcherData.success) {
getLanguageSwitcherFailCounter.add(1, {
uid: uid,
lang: lang,
contentType: contentType,
error_type: "validation_error",
error: JSON.stringify(validatedLanguageSwitcherData.error),
})
console.error(
"contentstack.languageSwitcher validation error",
JSON.stringify({
query: {
uid: uid,
lang: lang,
contentType: contentType,
},
error: validatedLanguageSwitcherData.error,
})
)
return null
}
getLanguageSwitcherSuccessCounter.add(1, {
uid: uid,
lang: lang,
contentType: contentType,
})
console.info(
"contentstack.languageSwitcher success",
JSON.stringify({
query: {
uid: uid,
lang: lang,
contentType: contentType,
},
})
)
return {
lang: lang,
lang,
urls,
}
}),

View File

@@ -0,0 +1,12 @@
import { metrics } from "@opentelemetry/api"
const meter = metrics.getMeter("trpc.contentstack.languageSwitcher")
export const getLanguageSwitcherCounter = meter.createCounter(
"trpc.contentstack.languageSwitcher.get"
)
export const getLanguageSwitcherSuccessCounter = meter.createCounter(
"trpc.contentstack.languageSwitcher.get-success"
)
export const getLanguageSwitcherFailCounter = meter.createCounter(
"trpc.contentstack.languageSwitcher.get-fail"
)

View File

@@ -1 +1,228 @@
import { Lang } from "@/constants/languages"
import { batchRequest } from "@/lib/graphql/batchRequest"
import {
GetDaDeEnUrlsAccountPage,
GetFiNoSvUrlsAccountPage,
} from "@/lib/graphql/Query/AccountPage/AccountPage.graphql"
import {
GetDaDeEnUrlsCollectionPage,
GetFiNoSvUrlsCollectionPage,
} from "@/lib/graphql/Query/CollectionPage/CollectionPage.graphql"
import {
GetDaDeEnUrlsContentPage,
GetFiNoSvUrlsContentPage,
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
import {
GetDaDeEnUrlsCurrentBlocksPage,
GetFiNoSvUrlsCurrentBlocksPage,
} from "@/lib/graphql/Query/Current/LanguageSwitcher.graphql"
import {
GetDaDeEnUrlsDestinationCityPage,
GetFiNoSvUrlsDestinationCityPage,
} from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPage.graphql"
import {
GetDaDeEnUrlsDestinationCountryPage,
GetFiNoSvUrlsDestinationCountryPage,
} from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPage.graphql"
import {
GetDaDeEnUrlsDestinationOverviewPage,
GetFiNoSvUrlsDestinationOverviewPage,
} from "@/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql"
import {
GetDaDeEnUrlsHotelPage,
GetFiNoSvUrlsHotelPage,
} from "@/lib/graphql/Query/HotelPage/HotelPage.graphql"
import {
GetDaDeEnUrlsLoyaltyPage,
GetFiNoSvUrlsLoyaltyPage,
} from "@/lib/graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
import {
GetDaDeEnUrlsStartPage,
GetFiNoSvUrlsStartPage,
} from "@/lib/graphql/Query/StartPage/StartPage.graphql"
import { internalServerError } from "@/server/errors/trpc"
import { generateTag } from "@/utils/generateTag"
import { validateLanguageSwitcherData } from "./output"
import {
getLanguageSwitcherCounter,
getLanguageSwitcherFailCounter,
getLanguageSwitcherSuccessCounter,
} from "./telemetry"
import { PageContentTypeEnum } from "@/types/requests/contentType"
import type {
LanguageSwitcherData,
LanguageSwitcherQueryDataRaw,
} from "@/types/requests/languageSwitcher"
export const languageSwitcherAffix = "languageSwitcher"
export async function getUrlsOfAllLanguages(
lang: Lang,
uid: string,
contentType: string
) {
getLanguageSwitcherCounter.add(1, {
uid,
lang,
contentType,
})
console.info(
"contentstack.languageSwitcher start",
JSON.stringify({
query: {
uid,
lang,
contentType,
},
})
)
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.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,
options: {
cache: "force-cache",
next: {
tags: tagsDaDeEn,
},
},
},
{
document: fiNoSvDocument,
variables,
options: {
cache: "force-cache",
next: {
tags: 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 || `/${key}${item.url}`
: undefined
return {
...acc,
[key]: { url, isExternal: !!item?.web?.original_url },
}
},
{}
)
const validatedLanguageSwitcherData =
validateLanguageSwitcherData.safeParse(urls)
if (!validatedLanguageSwitcherData.success) {
getLanguageSwitcherFailCounter.add(1, {
uid,
lang,
contentType,
error_type: "validation_error",
error: JSON.stringify(validatedLanguageSwitcherData.error),
})
console.error(
"contentstack.languageSwitcher validation error",
JSON.stringify({
query: {
uid,
lang,
contentType,
},
error: validatedLanguageSwitcherData.error,
})
)
return null
}
getLanguageSwitcherSuccessCounter.add(1, {
uid,
lang,
contentType,
})
console.info(
"contentstack.languageSwitcher success",
JSON.stringify({
query: {
uid,
lang,
contentType,
},
})
)
return urls
}