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

View File

@@ -0,0 +1,4 @@
import { mergeRouters } from "../../.."
import { accountPageQueryRouter } from "./query"
export const accountPageRouter = mergeRouters(accountPageQueryRouter)

View File

@@ -0,0 +1,90 @@
import { z } from "zod"
import { AccountPageEnum } from "../../../types/accountPageEnum"
import { discriminatedUnionArray } from "../../../utils/discriminatedUnion"
import {
dynamicContentRefsSchema,
dynamicContentSchema,
} from "../schemas/blocks/dynamicContent"
import {
shortcutsRefsSchema,
shortcutsSchema,
} from "../schemas/blocks/shortcuts"
import { textContentSchema } from "../schemas/blocks/textContent"
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
import { systemSchema } from "../schemas/system"
const accountPageDynamicContent = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.DynamicContent),
})
.merge(dynamicContentSchema)
const accountPageShortcuts = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.ShortCuts),
})
.merge(shortcutsSchema)
const accountPageTextContent = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.TextContent),
})
.merge(textContentSchema)
export const blocksSchema = z.discriminatedUnion("__typename", [
accountPageDynamicContent,
accountPageShortcuts,
accountPageTextContent,
])
export const accountPageSchema = z.object({
account_page: z.object({
content: discriminatedUnionArray(blocksSchema.options),
heading: z.string().nullable(),
preamble: z.string().nullable(),
hero_image: tempImageVaultAssetSchema,
hero_image_active: z
.boolean()
.nullable()
.transform((val) => val ?? false),
title: z.string(),
url: z.string(),
system: systemSchema.merge(
z.object({
created_at: z.string(),
updated_at: z.string(),
})
),
}),
trackingProps: z.object({
url: z.string(),
}),
})
const accountPageDynamicContentRefs = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.DynamicContent),
})
.merge(dynamicContentRefsSchema)
const accountPageShortcutsRefs = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.ShortCuts),
})
.merge(shortcutsRefsSchema)
const accountPageContentItemRefs = z.discriminatedUnion("__typename", [
z.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.TextContent),
}),
accountPageDynamicContentRefs,
accountPageShortcutsRefs,
])
export const accountPageRefsSchema = z.object({
account_page: z.object({
content: discriminatedUnionArray(accountPageContentItemRefs.options),
system: systemSchema,
}),
})

View File

@@ -0,0 +1,126 @@
import { createCounter } from "@scandic-hotels/common/telemetry"
import { notFound } from "@scandic-hotels/trpc/errors"
import { contentstackExtendedProcedureUID } from "@scandic-hotels/trpc/procedures"
import { router } from "../../.."
import {
GetAccountPage,
GetAccountPageRefs,
} from "../../../graphql/Query/AccountPage/AccountPage.graphql"
import { request } from "../../../graphql/request"
import {
generateRefsResponseTag,
generateTag,
generateTagsFromSystem,
} from "../../../utils/generateTag"
import { accountPageRefsSchema, accountPageSchema } from "./output"
import { getConnections } from "./utils"
import type {
GetAccountPageRefsSchema,
GetAccountPageSchema,
} from "../../../types/accountPage"
import type { TrackingPageData } from "../../types"
export const accountPageQueryRouter = router({
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
const { lang, uid } = ctx
const getAccountPageRefsCounter = createCounter(
"trpc.contentstack",
"accountPage.get.refs"
)
const metricsRefs = getAccountPageRefsCounter.init({ lang, uid })
metricsRefs.start()
const refsResponse = await request<GetAccountPageRefsSchema>(
GetAccountPageRefs,
{
locale: lang,
uid,
},
{
key: generateRefsResponseTag(lang, uid),
ttl: "max",
}
)
if (!refsResponse.data) {
const notFoundError = notFound(refsResponse)
metricsRefs.noDataError()
throw notFoundError
}
const validatedAccountPageRefs = accountPageRefsSchema.safeParse(
refsResponse.data
)
if (!validatedAccountPageRefs.success) {
metricsRefs.validationError(validatedAccountPageRefs.error)
return null
}
const connections = getConnections(validatedAccountPageRefs.data)
const tags = [
generateTagsFromSystem(lang, connections),
generateTag(lang, validatedAccountPageRefs.data.account_page.system.uid),
].flat()
metricsRefs.success()
const getAccountPageCounter = createCounter(
"trpc.contentstack",
"accountPage.get"
)
const metrics = getAccountPageCounter.init({ lang, uid })
metrics.start()
const response = await request<GetAccountPageSchema>(
GetAccountPage,
{
locale: lang,
uid,
},
{
key: tags,
ttl: "max",
}
)
if (!response.data) {
const notFoundError = notFound(response)
metrics.noDataError()
throw notFoundError
}
const validatedAccountPage = accountPageSchema.safeParse(response.data)
if (!validatedAccountPage.success) {
metrics.validationError(validatedAccountPage.error)
return null
}
metrics.success()
const parsedtitle = response.data.account_page.title
.replaceAll(" ", "")
.toLowerCase()
const tracking: TrackingPageData = {
pageId: validatedAccountPage.data.account_page.system.uid,
domainLanguage: lang,
publishDate: validatedAccountPage.data.account_page.system.updated_at,
createDate: validatedAccountPage.data.account_page.system.created_at,
channel: "scandic-friends",
pageType: `member${parsedtitle}page`,
pageName: validatedAccountPage.data.trackingProps.url,
siteSections: validatedAccountPage.data.trackingProps.url,
siteVersion: "new-web",
}
return {
accountPage: validatedAccountPage.data.account_page,
tracking,
}
}),
})

View File

@@ -0,0 +1,29 @@
import { AccountPageEnum } from "../../../types/accountPageEnum"
import type { AccountPageRefs } from "../../../types/accountPage"
import type { System } from "../schemas/system"
export function getConnections({ account_page }: AccountPageRefs) {
const connections: System["system"][] = [account_page.system]
if (account_page.content) {
account_page.content.forEach((block) => {
switch (block.__typename) {
case AccountPageEnum.ContentStack.blocks.ShortCuts: {
if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts)
}
break
}
case AccountPageEnum.ContentStack.blocks.DynamicContent: {
if (block.dynamic_content.link) {
connections.push(block.dynamic_content.link)
}
break
}
}
})
}
return connections
}