Merge branch 'develop' into feature/tracking
This commit is contained in:
@@ -14,7 +14,8 @@ import { removeMultipleSlashes } from "@/utils/url"
|
||||
|
||||
import { systemSchema } from "../schemas/system"
|
||||
|
||||
import { Image } from "@/types/image"
|
||||
import { AlertTypeEnum } from "@/types/enums/alert"
|
||||
import type { Image } from "@/types/image"
|
||||
|
||||
// Help me write this zod schema based on the type ContactConfig
|
||||
export const validateContactConfigSchema = z.object({
|
||||
@@ -39,10 +40,12 @@ export const validateContactConfigSchema = z.object({
|
||||
phone: z.object({
|
||||
number: z.string().nullable(),
|
||||
name: z.string().nullable(),
|
||||
footnote: z.string().nullable(),
|
||||
}),
|
||||
phone_loyalty: z.object({
|
||||
number: z.string().nullable(),
|
||||
name: z.string().nullable(),
|
||||
footnote: z.string().nullable(),
|
||||
}),
|
||||
visiting_address: z.object({
|
||||
zip: z.string().nullable(),
|
||||
@@ -366,16 +369,16 @@ export const validateFooterConfigSchema = z
|
||||
all_footer: z.object({
|
||||
items: z.array(
|
||||
z.object({
|
||||
main_links: validateLinks,
|
||||
main_links: validateLinks.default([]),
|
||||
app_downloads: z.object({
|
||||
title: z.string(),
|
||||
links: validateLinksWithType,
|
||||
links: validateLinksWithType.default([]),
|
||||
}),
|
||||
secondary_links: validateSecondaryLinks,
|
||||
secondary_links: validateSecondaryLinks.default([]),
|
||||
social_media: z.object({
|
||||
links: validateLinksWithType,
|
||||
links: validateLinksWithType.default([]),
|
||||
}),
|
||||
tertiary_links: validateLinks,
|
||||
tertiary_links: validateLinks.default([]),
|
||||
})
|
||||
),
|
||||
}),
|
||||
@@ -415,25 +418,31 @@ export const validateFooterRefConfigSchema = z.object({
|
||||
items: z
|
||||
.array(
|
||||
z.object({
|
||||
main_links: z.array(
|
||||
z.object({
|
||||
pageConnection: pageConnectionRefs,
|
||||
})
|
||||
),
|
||||
secondary_links: z.array(
|
||||
z.object({
|
||||
links: z.array(
|
||||
z.object({
|
||||
pageConnection: pageConnectionRefs,
|
||||
})
|
||||
),
|
||||
})
|
||||
),
|
||||
tertiary_links: z.array(
|
||||
z.object({
|
||||
pageConnection: pageConnectionRefs,
|
||||
})
|
||||
),
|
||||
main_links: z
|
||||
.array(
|
||||
z.object({
|
||||
pageConnection: pageConnectionRefs,
|
||||
})
|
||||
)
|
||||
.nullable(),
|
||||
secondary_links: z
|
||||
.array(
|
||||
z.object({
|
||||
links: z.array(
|
||||
z.object({
|
||||
pageConnection: pageConnectionRefs,
|
||||
})
|
||||
),
|
||||
})
|
||||
)
|
||||
.nullable(),
|
||||
tertiary_links: z
|
||||
.array(
|
||||
z.object({
|
||||
pageConnection: pageConnectionRefs,
|
||||
})
|
||||
)
|
||||
.nullable(),
|
||||
system: systemSchema,
|
||||
})
|
||||
)
|
||||
@@ -538,6 +547,7 @@ export const headerRefsSchema = z
|
||||
})
|
||||
|
||||
const linkUnionSchema = z.discriminatedUnion("__typename", [
|
||||
pageLinks.accountPageSchema,
|
||||
pageLinks.contentPageSchema,
|
||||
pageLinks.hotelPageSchema,
|
||||
pageLinks.loyaltyPageSchema,
|
||||
@@ -660,3 +670,166 @@ export const headerSchema = z
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export const alertSchema = z
|
||||
.object({
|
||||
type: z.nativeEnum(AlertTypeEnum),
|
||||
text: z.string(),
|
||||
heading: z.string(),
|
||||
phone_contact: z.object({
|
||||
display_text: z.string(),
|
||||
phone_number: z.string().nullable(),
|
||||
footnote: z.string().nullable(),
|
||||
}),
|
||||
has_link: z.boolean(),
|
||||
link: linkAndTitleSchema,
|
||||
has_sidepeek_button: z.boolean(),
|
||||
sidepeek_button: z.object({
|
||||
cta_text: z.string(),
|
||||
}),
|
||||
sidepeek_content: z.object({
|
||||
heading: z.string(),
|
||||
content: z.object({
|
||||
json: z.any(),
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z
|
||||
.discriminatedUnion("__typename", [
|
||||
pageLinks.accountPageSchema,
|
||||
pageLinks.contentPageSchema,
|
||||
pageLinks.hotelPageSchema,
|
||||
pageLinks.loyaltyPageSchema,
|
||||
])
|
||||
.transform((data) => {
|
||||
const link = pageLinks.transform(data)
|
||||
if (link) {
|
||||
return link
|
||||
}
|
||||
return data
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
.transform(
|
||||
({
|
||||
type,
|
||||
heading,
|
||||
text,
|
||||
phone_contact,
|
||||
has_link,
|
||||
link,
|
||||
has_sidepeek_button,
|
||||
sidepeek_button,
|
||||
sidepeek_content,
|
||||
}) => {
|
||||
const hasLink = has_link && link.link
|
||||
return {
|
||||
type,
|
||||
text,
|
||||
heading,
|
||||
phoneContact:
|
||||
phone_contact.display_text && phone_contact.phone_number
|
||||
? {
|
||||
displayText: phone_contact.display_text,
|
||||
phoneNumber: phone_contact.phone_number,
|
||||
footnote: phone_contact.footnote,
|
||||
}
|
||||
: null,
|
||||
hasSidepeekButton: !!has_sidepeek_button,
|
||||
link: hasLink
|
||||
? {
|
||||
url: link.link.url,
|
||||
title: link.title,
|
||||
}
|
||||
: null,
|
||||
sidepeekButton:
|
||||
!hasLink && has_sidepeek_button ? sidepeek_button : null,
|
||||
sidepeekContent:
|
||||
!hasLink && has_sidepeek_button ? sidepeek_content : null,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const siteConfigSchema = z
|
||||
.object({
|
||||
all_site_config: z.object({
|
||||
items: z
|
||||
.array(
|
||||
z.object({
|
||||
sitewide_alert: z.object({
|
||||
booking_widget_disabled: z.boolean(),
|
||||
alertConnection: z.object({
|
||||
edges: z
|
||||
.array(
|
||||
z.object({
|
||||
node: alertSchema,
|
||||
})
|
||||
)
|
||||
.max(1),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
)
|
||||
.max(1),
|
||||
}),
|
||||
})
|
||||
.transform((data) => {
|
||||
if (!data.all_site_config.items.length) {
|
||||
return {
|
||||
sitewideAlert: null,
|
||||
bookingWidgetDisabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
const { sitewide_alert } = data.all_site_config.items[0]
|
||||
|
||||
return {
|
||||
sitewideAlert: sitewide_alert.alertConnection.edges[0]?.node || null,
|
||||
bookingWidgetDisabled: sitewide_alert.booking_widget_disabled,
|
||||
}
|
||||
})
|
||||
|
||||
const sidepeekContentRefSchema = z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.discriminatedUnion("__typename", [
|
||||
pageLinks.accountPageRefSchema,
|
||||
pageLinks.contentPageRefSchema,
|
||||
pageLinks.hotelPageRefSchema,
|
||||
pageLinks.loyaltyPageRefSchema,
|
||||
]),
|
||||
})
|
||||
),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const alertConnectionRefSchema = z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.object({
|
||||
link: linkRefsSchema,
|
||||
sidepeek_content: sidepeekContentRefSchema,
|
||||
}),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const siteConfigRefSchema = z.object({
|
||||
all_site_config: z.object({
|
||||
items: z.array(
|
||||
z.object({
|
||||
sitewide_alert: z.object({
|
||||
alertConnection: alertConnectionRefSchema,
|
||||
}),
|
||||
system: systemSchema,
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { GetContactConfig } from "@/lib/graphql/Query/ContactConfig.graphql"
|
||||
import {
|
||||
GetCurrentFooter,
|
||||
@@ -11,6 +10,10 @@ import {
|
||||
} from "@/lib/graphql/Query/Current/Header.graphql"
|
||||
import { GetFooter, GetFooterRef } from "@/lib/graphql/Query/Footer.graphql"
|
||||
import { GetHeader, GetHeaderRef } from "@/lib/graphql/Query/Header.graphql"
|
||||
import {
|
||||
GetSiteConfig,
|
||||
GetSiteConfigRef,
|
||||
} from "@/lib/graphql/Query/SiteConfig.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||
@@ -31,13 +34,50 @@ import {
|
||||
type GetCurrentHeaderData,
|
||||
headerRefsSchema,
|
||||
headerSchema,
|
||||
siteConfigRefSchema,
|
||||
siteConfigSchema,
|
||||
validateContactConfigSchema,
|
||||
validateCurrentFooterConfigSchema,
|
||||
validateCurrentHeaderConfigSchema,
|
||||
validateFooterConfigSchema,
|
||||
validateFooterRefConfigSchema,
|
||||
} from "./output"
|
||||
import { getConnections, getFooterConnections } from "./utils"
|
||||
import {
|
||||
getContactConfigCounter,
|
||||
getContactConfigFailCounter,
|
||||
getContactConfigSuccessCounter,
|
||||
getCurrentFooterCounter,
|
||||
getCurrentFooterFailCounter,
|
||||
getCurrentFooterRefCounter,
|
||||
getCurrentFooterSuccessCounter,
|
||||
getCurrentHeaderCounter,
|
||||
getCurrentHeaderFailCounter,
|
||||
getCurrentHeaderRefCounter,
|
||||
getCurrentHeaderSuccessCounter,
|
||||
getFooterCounter,
|
||||
getFooterFailCounter,
|
||||
getFooterRefCounter,
|
||||
getFooterRefFailCounter,
|
||||
getFooterRefSuccessCounter,
|
||||
getFooterSuccessCounter,
|
||||
getHeaderCounter,
|
||||
getHeaderFailCounter,
|
||||
getHeaderRefsCounter,
|
||||
getHeaderRefsFailCounter,
|
||||
getHeaderRefsSuccessCounter,
|
||||
getHeaderSuccessCounter,
|
||||
getSiteConfigCounter,
|
||||
getSiteConfigFailCounter,
|
||||
getSiteConfigRefCounter,
|
||||
getSiteConfigRefFailCounter,
|
||||
getSiteConfigRefSuccessCounter,
|
||||
getSiteConfigSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import {
|
||||
getAlertPhoneContactData,
|
||||
getConnections,
|
||||
getFooterConnections,
|
||||
} from "./utils"
|
||||
|
||||
import type {
|
||||
FooterDataRaw,
|
||||
@@ -47,157 +87,77 @@ import type {
|
||||
GetHeader as GetHeaderData,
|
||||
GetHeaderRefs,
|
||||
} from "@/types/trpc/routers/contentstack/header"
|
||||
import type {
|
||||
GetSiteConfigData,
|
||||
GetSiteConfigRefData,
|
||||
} from "@/types/trpc/routers/contentstack/siteConfig"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.base")
|
||||
// OpenTelemetry metrics: ContactConfig
|
||||
const getContactConfigCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get"
|
||||
)
|
||||
const getContactConfigSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get-success"
|
||||
)
|
||||
const getContactConfigFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get-fail"
|
||||
)
|
||||
// OpenTelemetry metrics: CurrentHeader
|
||||
const getCurrentHeaderRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get"
|
||||
)
|
||||
const getCurrentHeaderRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get-success"
|
||||
)
|
||||
const getCurrentHeaderRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get-fail"
|
||||
)
|
||||
const getCurrentHeaderCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get"
|
||||
)
|
||||
const getCurrentHeaderSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get-success"
|
||||
)
|
||||
const getCurrentHeaderFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get-fail"
|
||||
)
|
||||
async function getContactConfig(lang: Lang) {
|
||||
getContactConfigCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.contactConfig start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
const response = await request<ContactConfigData>(
|
||||
GetContactConfig,
|
||||
{
|
||||
locale: lang,
|
||||
},
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [`${lang}:contact`],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: Header
|
||||
const getHeaderRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get"
|
||||
)
|
||||
const getHeaderRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get-success"
|
||||
)
|
||||
const getHeaderRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get-fail"
|
||||
)
|
||||
const getHeaderCounter = meter.createCounter("trpc.contentstack.header.get")
|
||||
const getHeaderSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.get-success"
|
||||
)
|
||||
const getHeaderFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.get-fail"
|
||||
)
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
|
||||
// OpenTelemetry metrics: CurrentHeader
|
||||
const getCurrentFooterRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get"
|
||||
)
|
||||
const getCurrentFooterRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get-success"
|
||||
)
|
||||
const getCurrentFooterRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get-fail"
|
||||
)
|
||||
const getCurrentFooterCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get"
|
||||
)
|
||||
const getCurrentFooterSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get-success"
|
||||
)
|
||||
const getCurrentFooterFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get-fail"
|
||||
)
|
||||
getContactConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
|
||||
// OpenTelemetry metrics: Footer
|
||||
const getFooterRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get"
|
||||
)
|
||||
const getFooterRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get-success"
|
||||
)
|
||||
const getFooterRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get-fail"
|
||||
)
|
||||
const getFooterCounter = meter.createCounter("trpc.contentstack.footer.get")
|
||||
const getFooterSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.get-success"
|
||||
)
|
||||
const getFooterFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.get-fail"
|
||||
)
|
||||
console.error(
|
||||
"contentstack.config not found error",
|
||||
JSON.stringify({ query: { lang }, error: { code: notFoundError.code } })
|
||||
)
|
||||
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedContactConfigConfig = validateContactConfigSchema.safeParse(
|
||||
response.data
|
||||
)
|
||||
|
||||
if (!validatedContactConfigConfig.success) {
|
||||
getContactConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedContactConfigConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.contactConfig validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedContactConfigConfig.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
getContactConfigSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.contactConfig success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
||||
}
|
||||
|
||||
export const baseQueryRouter = router({
|
||||
contact: contentstackBaseProcedure.query(async ({ ctx }) => {
|
||||
const { lang } = ctx
|
||||
getContactConfigCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.contactConfig start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
const response = await request<ContactConfigData>(
|
||||
GetContactConfig,
|
||||
{
|
||||
locale: lang,
|
||||
},
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [`${lang}:contact`],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
|
||||
getContactConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"contentstack.config not found error",
|
||||
JSON.stringify({ query: { lang }, error: { code: notFoundError.code } })
|
||||
)
|
||||
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedContactConfigConfig = validateContactConfigSchema.safeParse(
|
||||
response.data
|
||||
)
|
||||
|
||||
if (!validatedContactConfigConfig.success) {
|
||||
getContactConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedContactConfigConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.contactConfig validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedContactConfigConfig.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
getContactConfigSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.contactConfig success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
||||
return await getContactConfig(ctx.lang)
|
||||
}),
|
||||
header: contentstackBaseProcedure.query(async ({ ctx }) => {
|
||||
const { lang } = ctx
|
||||
@@ -652,4 +612,149 @@ export const baseQueryRouter = router({
|
||||
|
||||
return validatedFooterConfig.data
|
||||
}),
|
||||
siteConfig: contentstackBaseProcedure.query(async ({ ctx }) => {
|
||||
const { lang } = ctx
|
||||
|
||||
getSiteConfigRefCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.siteConfig.ref start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
const responseRef = await request<GetSiteConfigRefData>(
|
||||
GetSiteConfigRef,
|
||||
{
|
||||
locale: lang,
|
||||
},
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [generateRefsResponseTag(lang, "siteConfig")],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!responseRef.data) {
|
||||
const notFoundError = notFound(responseRef)
|
||||
getSiteConfigRefFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.siteConfig.refs not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedSiteConfigRef = siteConfigRefSchema.safeParse(
|
||||
responseRef.data
|
||||
)
|
||||
|
||||
if (!validatedSiteConfigRef.success) {
|
||||
getSiteConfigRefFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedSiteConfigRef.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.siteConfig.refs validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: validatedSiteConfigRef.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
getSiteConfigRefSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.siteConfig.refs success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
|
||||
getSiteConfigCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.siteConfig start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
const [siteConfigResponse, contactConfig] = await Promise.all([
|
||||
request<GetSiteConfigData>(
|
||||
GetSiteConfig,
|
||||
{
|
||||
locale: lang,
|
||||
},
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [`${lang}:siteConfig`],
|
||||
},
|
||||
}
|
||||
),
|
||||
getContactConfig(lang),
|
||||
])
|
||||
|
||||
if (!siteConfigResponse.data) {
|
||||
const notFoundError = notFound(siteConfigResponse)
|
||||
|
||||
getSiteConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"contentstack.siteConfig not found error",
|
||||
JSON.stringify({ query: { lang }, error: { code: notFoundError.code } })
|
||||
)
|
||||
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedSiteConfig = siteConfigSchema.safeParse(
|
||||
siteConfigResponse.data
|
||||
)
|
||||
|
||||
if (!validatedSiteConfig.success) {
|
||||
getSiteConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedSiteConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.siteConfig validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedSiteConfig.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
getSiteConfigSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.siteConfig success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
|
||||
const { sitewideAlert } = validatedSiteConfig.data
|
||||
|
||||
return {
|
||||
...validatedSiteConfig.data,
|
||||
sitewideAlert: sitewideAlert
|
||||
? {
|
||||
...sitewideAlert,
|
||||
phoneContact: contactConfig
|
||||
? getAlertPhoneContactData(sitewideAlert, contactConfig)
|
||||
: null,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
112
server/routers/contentstack/base/telemetry.ts
Normal file
112
server/routers/contentstack/base/telemetry.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.base")
|
||||
// OpenTelemetry metrics: ContactConfig
|
||||
export const getContactConfigCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get"
|
||||
)
|
||||
export const getContactConfigSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get-success"
|
||||
)
|
||||
export const getContactConfigFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get-fail"
|
||||
)
|
||||
// OpenTelemetry metrics: CurrentHeader
|
||||
export const getCurrentHeaderRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get"
|
||||
)
|
||||
export const getCurrentHeaderRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get-success"
|
||||
)
|
||||
export const getCurrentHeaderRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get-fail"
|
||||
)
|
||||
export const getCurrentHeaderCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get"
|
||||
)
|
||||
export const getCurrentHeaderSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get-success"
|
||||
)
|
||||
export const getCurrentHeaderFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: Header
|
||||
export const getHeaderRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get"
|
||||
)
|
||||
export const getHeaderRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get-success"
|
||||
)
|
||||
export const getHeaderRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get-fail"
|
||||
)
|
||||
export const getHeaderCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.get"
|
||||
)
|
||||
export const getHeaderSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.get-success"
|
||||
)
|
||||
export const getHeaderFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.get-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: CurrentFooter
|
||||
export const getCurrentFooterRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get"
|
||||
)
|
||||
export const getCurrentFooterRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get-success"
|
||||
)
|
||||
export const getCurrentFooterRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get-fail"
|
||||
)
|
||||
export const getCurrentFooterCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get"
|
||||
)
|
||||
export const getCurrentFooterSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get-success"
|
||||
)
|
||||
export const getCurrentFooterFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: Footer
|
||||
export const getFooterRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get"
|
||||
)
|
||||
export const getFooterRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get-success"
|
||||
)
|
||||
export const getFooterRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get-fail"
|
||||
)
|
||||
export const getFooterCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.get"
|
||||
)
|
||||
export const getFooterSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.get-success"
|
||||
)
|
||||
export const getFooterFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.get-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: SiteConfig
|
||||
export const getSiteConfigRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.ref.get"
|
||||
)
|
||||
export const getSiteConfigRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.ref.get-success"
|
||||
)
|
||||
export const getSiteConfigRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.ref.get-fail"
|
||||
)
|
||||
export const getSiteConfigCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.get"
|
||||
)
|
||||
export const getSiteConfigSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.get-success"
|
||||
)
|
||||
export const getSiteConfigFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.get-fail"
|
||||
)
|
||||
@@ -1,11 +1,12 @@
|
||||
import type {
|
||||
FooterLinkItem,
|
||||
FooterRefDataRaw,
|
||||
} from "@/types/components/footer/footer"
|
||||
import { System } from "@/types/requests/system"
|
||||
import { Edges } from "@/types/requests/utils/edges"
|
||||
import { NodeRefs } from "@/types/requests/utils/refs"
|
||||
import { getValueFromContactConfig } from "@/utils/contactConfig"
|
||||
|
||||
import type { FooterRefDataRaw } from "@/types/components/footer/footer"
|
||||
import type { System } from "@/types/requests/system"
|
||||
import type { Edges } from "@/types/requests/utils/edges"
|
||||
import type { NodeRefs } from "@/types/requests/utils/refs"
|
||||
import type { HeaderRefs } from "@/types/trpc/routers/contentstack/header"
|
||||
import type { Alert } from "@/types/trpc/routers/contentstack/siteConfig"
|
||||
import type { ContactConfig } from "./output"
|
||||
|
||||
export function getConnections({ header }: HeaderRefs) {
|
||||
const connections: System["system"][] = [header.system]
|
||||
@@ -68,3 +69,21 @@ export function getFooterConnections(refs: FooterRefDataRaw) {
|
||||
|
||||
return connections
|
||||
}
|
||||
|
||||
export function getAlertPhoneContactData(
|
||||
alert: Alert,
|
||||
contactConfig: ContactConfig
|
||||
) {
|
||||
if (alert.phoneContact) {
|
||||
const { displayText, phoneNumber, footnote } = alert.phoneContact
|
||||
|
||||
return {
|
||||
displayText,
|
||||
phoneNumber: getValueFromContactConfig(phoneNumber, contactConfig),
|
||||
footnote: footnote
|
||||
? getValueFromContactConfig(footnote, contactConfig)
|
||||
: null,
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -39,6 +39,18 @@ import {
|
||||
joinLoyaltyContactRefsSchema,
|
||||
joinLoyaltyContactSchema,
|
||||
} from "../schemas/sidebar/joinLoyaltyContact"
|
||||
import {
|
||||
quickLinksRefschema,
|
||||
quickLinksSchema,
|
||||
} from "../schemas/sidebar/quickLinks"
|
||||
import {
|
||||
scriptedCardRefschema,
|
||||
scriptedCardsSchema,
|
||||
} from "../schemas/sidebar/scriptedCard"
|
||||
import {
|
||||
teaserCardRefschema,
|
||||
teaserCardsSchema,
|
||||
} from "../schemas/sidebar/teaserCard"
|
||||
import { systemSchema } from "../schemas/system"
|
||||
|
||||
import { ContentPageEnum } from "@/types/enums/contentPage"
|
||||
@@ -122,10 +134,31 @@ export const contentPageJoinLoyaltyContact = z
|
||||
})
|
||||
.merge(joinLoyaltyContactSchema)
|
||||
|
||||
export const contentPageSidebarScriptedCard = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.ScriptedCard),
|
||||
})
|
||||
.merge(scriptedCardsSchema)
|
||||
|
||||
export const contentPageSidebarTeaserCard = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.TeaserCard),
|
||||
})
|
||||
.merge(teaserCardsSchema)
|
||||
|
||||
export const contentPageSidebarQuicklinks = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.QuickLinks),
|
||||
})
|
||||
.merge(quickLinksSchema)
|
||||
|
||||
export const sidebarSchema = z.discriminatedUnion("__typename", [
|
||||
contentPageSidebarContent,
|
||||
contentPageSidebarDynamicContent,
|
||||
contentPageJoinLoyaltyContact,
|
||||
contentPageSidebarScriptedCard,
|
||||
contentPageSidebarTeaserCard,
|
||||
contentPageSidebarQuicklinks,
|
||||
])
|
||||
|
||||
const navigationLinksSchema = z
|
||||
@@ -241,9 +274,30 @@ const contentPageSidebarJoinLoyaltyContactRef = z
|
||||
})
|
||||
.merge(joinLoyaltyContactRefsSchema)
|
||||
|
||||
const contentPageSidebarScriptedCardRef = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.ScriptedCard),
|
||||
})
|
||||
.merge(scriptedCardRefschema)
|
||||
|
||||
const contentPageSidebarTeaserCardRef = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.TeaserCard),
|
||||
})
|
||||
.merge(teaserCardRefschema)
|
||||
|
||||
const contentPageSidebarQuickLinksRef = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.QuickLinks),
|
||||
})
|
||||
.merge(quickLinksRefschema)
|
||||
|
||||
const contentPageSidebarRefsItem = z.discriminatedUnion("__typename", [
|
||||
contentPageSidebarContentRef,
|
||||
contentPageSidebarJoinLoyaltyContactRef,
|
||||
contentPageSidebarScriptedCardRef,
|
||||
contentPageSidebarTeaserCardRef,
|
||||
contentPageSidebarQuickLinksRef,
|
||||
])
|
||||
|
||||
const contentPageHeaderRefs = z.object({
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { GetContentPageRefs } from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||
import {
|
||||
GetContentPageBlocksRefs,
|
||||
GetContentPageRefs,
|
||||
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
|
||||
@@ -41,18 +44,30 @@ export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
const refsResponse = await request<GetContentPageRefsSchema>(
|
||||
GetContentPageRefs,
|
||||
{ locale: lang, uid },
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [generateTag(lang, uid)],
|
||||
},
|
||||
}
|
||||
)
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
const [mainRefsResponse, blockRefsResponse] = await Promise.all([
|
||||
request<GetContentPageRefsSchema>(
|
||||
GetContentPageRefs,
|
||||
{ locale: lang, uid },
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [generateTag(lang, uid)],
|
||||
},
|
||||
}
|
||||
),
|
||||
request<GetContentPageRefsSchema>(
|
||||
GetContentPageBlocksRefs,
|
||||
{ locale: lang, uid },
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [generateTag(lang, uid)],
|
||||
},
|
||||
}
|
||||
),
|
||||
])
|
||||
if (!mainRefsResponse.data) {
|
||||
const notFoundError = notFound(mainRefsResponse)
|
||||
getContentPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
@@ -73,8 +88,14 @@ export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
||||
)
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
return refsResponse.data
|
||||
const responseData = {
|
||||
...mainRefsResponse.data,
|
||||
content_page: {
|
||||
...mainRefsResponse.data.content_page,
|
||||
blocks: blockRefsResponse.data.content_page.blocks,
|
||||
},
|
||||
}
|
||||
return responseData
|
||||
}
|
||||
|
||||
export function validateContentPageRefs(
|
||||
@@ -171,17 +192,30 @@ export function getConnections({ content_page }: ContentPageRefs) {
|
||||
connections.push(...block.content)
|
||||
}
|
||||
break
|
||||
|
||||
case ContentPageEnum.ContentStack.sidebar.JoinLoyaltyContact:
|
||||
if (block.join_loyalty_contact?.button) {
|
||||
connections.push(block.join_loyalty_contact.button)
|
||||
}
|
||||
break
|
||||
case ContentPageEnum.ContentStack.sidebar.ScriptedCard:
|
||||
if (block.scripted_card?.length) {
|
||||
connections.push(...block.scripted_card)
|
||||
}
|
||||
break
|
||||
case ContentPageEnum.ContentStack.sidebar.TeaserCard:
|
||||
if (block.teaser_card?.length) {
|
||||
connections.push(...block.teaser_card)
|
||||
}
|
||||
break
|
||||
case ContentPageEnum.ContentStack.sidebar.QuickLinks:
|
||||
if (block.shortcuts.shortcuts.length) {
|
||||
connections.push(...block.shortcuts.shortcuts)
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return connections
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ export const hotelFaqSchema = z
|
||||
.object({
|
||||
questions: accordionItemsSchema,
|
||||
})
|
||||
.optional(),
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
.transform((data) => {
|
||||
const array = []
|
||||
@@ -51,7 +52,7 @@ export const hotelFaqRefsSchema = z
|
||||
.optional()
|
||||
.default(HotelPageEnum.ContentStack.blocks.Faq),
|
||||
global_faqConnection: globalAccordionConnectionRefs.optional(),
|
||||
specific_faq: specificAccordionConnectionRefs.optional(),
|
||||
specific_faq: specificAccordionConnectionRefs.optional().nullable(),
|
||||
})
|
||||
.transform((data) => {
|
||||
const array = []
|
||||
|
||||
@@ -4,11 +4,7 @@ import * as pageLinks from "@/server/routers/contentstack/schemas/pageLinks"
|
||||
|
||||
import { BlocksEnums } from "@/types/enums/blocks"
|
||||
|
||||
export const shortcutsSchema = z.object({
|
||||
typename: z
|
||||
.literal(BlocksEnums.block.Shortcuts)
|
||||
.optional()
|
||||
.default(BlocksEnums.block.Shortcuts),
|
||||
export const shortcutsBlockSchema = z.object({
|
||||
shortcuts: z
|
||||
.object({
|
||||
subtitle: z.string().nullable(),
|
||||
@@ -62,6 +58,15 @@ export const shortcutsSchema = z.object({
|
||||
}),
|
||||
})
|
||||
|
||||
export const shortcutsSchema = z
|
||||
.object({
|
||||
typename: z
|
||||
.literal(BlocksEnums.block.Shortcuts)
|
||||
.optional()
|
||||
.default(BlocksEnums.block.Shortcuts),
|
||||
})
|
||||
.merge(shortcutsBlockSchema)
|
||||
|
||||
export const shortcutsRefsSchema = z.object({
|
||||
shortcuts: z.object({
|
||||
shortcuts: z
|
||||
|
||||
@@ -11,6 +11,11 @@ const metaData = z.object({
|
||||
Value: z.string().nullable(),
|
||||
})
|
||||
|
||||
export const focalPointSchema = z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
})
|
||||
|
||||
/**
|
||||
* Defines a media asset, original or conversion
|
||||
*/
|
||||
@@ -94,6 +99,7 @@ export const imageVaultAssetSchema = z.object({
|
||||
* Name of the user that added the asset to ImageVault
|
||||
*/
|
||||
AddedBy: z.string(),
|
||||
FocalPoint: focalPointSchema.optional(),
|
||||
})
|
||||
|
||||
export const imageVaultAssetTransformedSchema = imageVaultAssetSchema.transform(
|
||||
@@ -124,6 +130,7 @@ export const imageVaultAssetTransformedSchema = imageVaultAssetSchema.transform(
|
||||
height: mediaConversion.Height,
|
||||
aspectRatio,
|
||||
},
|
||||
focalPoint: rawData.FocalPoint || { x: 50, y: 50 },
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
|
||||
import { ContentEnum } from "@/types/enums/content"
|
||||
import { SidebarEnums } from "@/types/enums/sidebar"
|
||||
import { System } from "@/types/requests/system"
|
||||
|
||||
export const contentSchema = z.object({
|
||||
typename: z
|
||||
|
||||
16
server/routers/contentstack/schemas/sidebar/quickLinks.ts
Normal file
16
server/routers/contentstack/schemas/sidebar/quickLinks.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { shortcutsBlockSchema, shortcutsRefsSchema } from "../blocks/shortcuts"
|
||||
|
||||
import { SidebarEnums } from "@/types/enums/sidebar"
|
||||
|
||||
export const quickLinksSchema = z
|
||||
.object({
|
||||
typename: z
|
||||
.literal(SidebarEnums.blocks.QuickLinks)
|
||||
.optional()
|
||||
.default(SidebarEnums.blocks.QuickLinks),
|
||||
})
|
||||
.merge(shortcutsBlockSchema)
|
||||
|
||||
export const quickLinksRefschema = shortcutsRefsSchema
|
||||
66
server/routers/contentstack/schemas/sidebar/scriptedCard.ts
Normal file
66
server/routers/contentstack/schemas/sidebar/scriptedCard.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import {
|
||||
cardBlockRefsSchema,
|
||||
cardBlockSchema,
|
||||
transformCardBlock,
|
||||
transformCardBlockRefs,
|
||||
} from "../blocks/cardsGrid"
|
||||
|
||||
import { SidebarEnums } from "@/types/enums/sidebar"
|
||||
|
||||
export const scriptedCardsSchema = z.object({
|
||||
typename: z
|
||||
.literal(SidebarEnums.blocks.ScriptedCard)
|
||||
.optional()
|
||||
.default(SidebarEnums.blocks.ScriptedCard),
|
||||
scripted_card: z
|
||||
.object({
|
||||
theme: z
|
||||
.enum([
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"primaryDim",
|
||||
"primaryDark",
|
||||
"primaryInverted",
|
||||
"primaryStrong",
|
||||
])
|
||||
.nullable(),
|
||||
scripted_cardConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: cardBlockSchema,
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
.transform((data) => {
|
||||
return {
|
||||
theme: data.theme,
|
||||
...transformCardBlock(data.scripted_cardConnection.edges[0].node),
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
export const scriptedCardRefschema = z.object({
|
||||
scripted_card: z
|
||||
.object({
|
||||
scripted_cardConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: cardBlockRefsSchema,
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
.transform((data) => {
|
||||
let card = null
|
||||
if (data.scripted_cardConnection.edges.length) {
|
||||
card = transformCardBlockRefs(
|
||||
data.scripted_cardConnection.edges[0].node
|
||||
)
|
||||
}
|
||||
return card
|
||||
}),
|
||||
})
|
||||
54
server/routers/contentstack/schemas/sidebar/teaserCard.ts
Normal file
54
server/routers/contentstack/schemas/sidebar/teaserCard.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import {
|
||||
teaserCardBlockRefsSchema,
|
||||
teaserCardBlockSchema,
|
||||
transformCardBlockRefs,
|
||||
transformTeaserCardBlock,
|
||||
} from "../blocks/cardsGrid"
|
||||
|
||||
import { SidebarEnums } from "@/types/enums/sidebar"
|
||||
|
||||
export const teaserCardsSchema = z.object({
|
||||
typename: z
|
||||
.literal(SidebarEnums.blocks.TeaserCard)
|
||||
.optional()
|
||||
.default(SidebarEnums.blocks.TeaserCard),
|
||||
teaser_card: z
|
||||
.object({
|
||||
theme: z.enum(["featured", "default"]).nullable().default("default"),
|
||||
teaser_cardConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: teaserCardBlockSchema,
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
.transform((data) => {
|
||||
return {
|
||||
...transformTeaserCardBlock(data.teaser_cardConnection.edges[0].node),
|
||||
theme: data.theme,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
export const teaserCardRefschema = z.object({
|
||||
teaser_card: z
|
||||
.object({
|
||||
teaser_cardConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: teaserCardBlockRefsSchema,
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
.transform((data) => {
|
||||
let card = null
|
||||
if (data.teaser_cardConnection.edges.length) {
|
||||
card = transformCardBlockRefs(data.teaser_cardConnection.edges[0].node)
|
||||
}
|
||||
return card
|
||||
}),
|
||||
})
|
||||
Reference in New Issue
Block a user