fix(SW-194): add refs for hotel pages

This commit is contained in:
Matilda Landström
2024-10-09 16:33:14 +02:00
parent 99d537d40c
commit e234225c4b
8 changed files with 307 additions and 14 deletions

View File

@@ -1,4 +1,7 @@
#import "../../AccountPage/Ref.graphql"
#import "../../ContentPage/Ref.graphql"
#import "../../HotelPage/Ref.graphql"
#import "../../LoyaltyPage/Ref.graphql"
fragment AccordionBlockRefs on Accordion {
questions {
@@ -7,7 +10,10 @@ fragment AccordionBlockRefs on Accordion {
edges {
node {
__typename
...AccountPageRef
...ContentPageRef
...HotelPageRef
...LoyaltyPageRef
}
}
}

View File

@@ -63,6 +63,60 @@ query GetHotelPage($locale: String!, $uid: String!) {
}
}
}
system {
...System
created_at
updated_at
}
}
}
query GetHotelPageRefs($locale: String!, $uid: String!) {
hotel_page(locale: $locale, uid: $uid) {
faq {
global_faqConnection {
edges {
node {
...AccordionBlockRefs
}
}
}
specific_faq {
questions {
answer {
embedded_itemsConnection {
edges {
node {
__typename
...AccountPageRef
...ContentPageRef
...HotelPageRef
...LoyaltyPageRef
}
}
}
}
}
}
}
content {
__typename
... on HotelPageContentUpcomingActivitiesCard {
upcoming_activities_card {
hotel_page_activities_content_pageConnection {
edges {
node {
__typename
...ContentPageRef
}
}
}
}
}
}
system {
...System
}
}
}

View File

@@ -2,8 +2,12 @@ import { z } from "zod"
import { discriminatedUnionArray } from "@/lib/discriminatedUnion"
import { activitiesCardSchema } from "../schemas/blocks/activitiesCard"
import { accordionSchema } from "../schemas/blocks/faq"
import {
activitiesCardRefSchema,
activitiesCardSchema,
} from "../schemas/blocks/activitiesCard"
import { accordionRefsSchema,accordionSchema } from "../schemas/blocks/faq"
import { systemSchema } from "../schemas/system"
import { HotelPageEnum } from "@/types/enums/hotelPage"
@@ -24,5 +28,30 @@ export const hotelPageSchema = z.object({
hotel_page_id: z.string(),
title: z.string(),
url: z.string(),
system: systemSchema.merge(
z.object({
created_at: z.string(),
updated_at: z.string(),
})
),
}),
})
/** REFS */
const hotelPageActiviesCardRefs = z
.object({
__typename: z.literal(HotelPageEnum.ContentStack.blocks.ActivitiesCard),
})
.merge(activitiesCardRefSchema)
const hotelPageBlockRefsItem = z.discriminatedUnion("__typename", [
hotelPageActiviesCardRefs,
])
export const hotelPageRefsSchema = z.object({
hotel_page: z.object({
content: discriminatedUnionArray(hotelPageBlockRefsItem.options).nullable(),
faq: accordionRefsSchema.nullable(),
system: systemSchema,
}),
})

View File

@@ -0,0 +1,142 @@
import { metrics } from "@opentelemetry/api"
import { Lang } from "@/constants/languages"
import { GetHotelPageRefs } from "@/lib/graphql/Query/HotelPage/HotelPage.graphql"
import { request } from "@/lib/graphql/request"
import { notFound } from "@/server/errors/trpc"
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
import { hotelPageRefsSchema } from "./output"
import { HotelPageEnum } from "@/types/enums/hotelPage"
import { System } from "@/types/requests/system"
import {
GetHotelPageRefsSchema,
HotelPageRefs,
} from "@/types/trpc/routers/contentstack/hotelPage"
const meter = metrics.getMeter("trpc.hotelPage")
// OpenTelemetry metrics: HotelPage
export const getHotelPageCounter = meter.createCounter(
"trpc.contentstack.hotelPage.get"
)
const getHotelPageRefsCounter = meter.createCounter(
"trpc.contentstack.hotelPage.get"
)
const getHotelPageRefsFailCounter = meter.createCounter(
"trpc.contentstack.hotelPage.get-fail"
)
const getHotelPageRefsSuccessCounter = meter.createCounter(
"trpc.contentstack.hotelPage.get-success"
)
export async function fetchHotelPageRefs(lang: Lang, uid: string) {
getHotelPageRefsCounter.add(1, { lang, uid })
console.info(
"contentstack.hotelPage.refs start",
JSON.stringify({
query: { lang, uid },
})
)
const refsResponse = await request<GetHotelPageRefsSchema>(
GetHotelPageRefs,
{ locale: lang, uid },
{
cache: "force-cache",
next: {
tags: [generateTag(lang, uid)],
},
}
)
if (!refsResponse.data) {
const notFoundError = notFound(refsResponse)
getHotelPageRefsFailCounter.add(1, {
lang,
uid,
error_type: "http_error",
error: JSON.stringify({
code: notFoundError.code,
}),
})
console.error(
"contentstack.hotelPage.refs not found error",
JSON.stringify({
query: {
lang,
uid,
},
error: { code: notFoundError.code },
})
)
throw notFoundError
}
return refsResponse.data
}
export function validateHotelPageRefs(
data: GetHotelPageRefsSchema,
lang: Lang,
uid: string
) {
const validatedData = hotelPageRefsSchema.safeParse(data)
if (!validatedData.success) {
getHotelPageRefsFailCounter.add(1, {
lang,
uid,
error_type: "validation_error",
error: JSON.stringify(validatedData.error),
})
console.error(
"contentstack.hotelPage.refs validation error",
JSON.stringify({
query: { lang, uid },
error: validatedData.error,
})
)
return null
}
getHotelPageRefsSuccessCounter.add(1, { lang, uid })
console.info(
"contentstack.hotelPage.refs success",
JSON.stringify({
query: { lang, uid },
})
)
return validatedData.data
}
export function generatePageTags(
validatedData: HotelPageRefs,
lang: Lang
): string[] {
const connections = getConnections(validatedData)
return [
generateTagsFromSystem(lang, connections),
generateTag(lang, validatedData.hotel_page.system.uid),
].flat()
}
export function getConnections({ hotel_page }: HotelPageRefs) {
const connections: System["system"][] = [hotel_page.system]
if (hotel_page.content) {
hotel_page.content.forEach((block) => {
switch (block.__typename) {
case HotelPageEnum.ContentStack.blocks.ActivitiesCard: {
if (block.upcoming_activities_card.length) {
connections.push(...block.upcoming_activities_card)
}
break
}
}
if (hotel_page.faq) {
connections.push(...hotel_page.faq)
}
})
}
return connections
}

View File

@@ -8,7 +8,7 @@ import { tempImageVaultAssetSchema } from "../imageVault"
import { HotelPageEnum } from "@/types/enums/hotelPage"
export const activitiesCard = z.object({
export const activitiesCardSchema = z.object({
typename: z
.literal(HotelPageEnum.ContentStack.blocks.ActivitiesCard)
.optional()
@@ -57,3 +57,25 @@ export const activitiesCard = z.object({
}
}),
})
export const activitiesCardRefSchema = z.object({
upcoming_activities_card: z
.object({
hotel_page_activities_content_pageConnection: z.object({
edges: z.array(
z.object({
node: z.discriminatedUnion("__typename", [
pageLinks.contentPageRefSchema,
]),
})
),
}),
})
.transform((data) => {
return (
data.hotel_page_activities_content_pageConnection.edges.flatMap(
({ node }) => node.system
) || []
)
}),
})

View File

@@ -45,6 +45,10 @@ export const accordionSchema = z
export const accordionRefsSchema = z
.object({
__typename: z
.literal(HotelPageEnum.ContentStack.blocks.Faq)
.optional()
.default(HotelPageEnum.ContentStack.blocks.Faq),
global_faqConnection: globalFaqConnectionRefs.optional(),
specific_faq: specificFaqConnectionRefs.optional(),
})
@@ -66,5 +70,5 @@ export const accordionRefsSchema = z
)
) || []
)
return { faq: array.flat(2) }
return array.flat(2)
})

View File

@@ -1,5 +1,6 @@
import { metrics } from "@opentelemetry/api"
import { Lang } from "@/constants/languages"
import * as api from "@/lib/api"
import { GetHotelPage } from "@/lib/graphql/Query/HotelPage/HotelPage.graphql"
import { request } from "@/lib/graphql/request"
@@ -18,6 +19,12 @@ import {
import { toApiLang } from "@/server/utils"
import { hotelPageSchema } from "../contentstack/hotelPage/output"
import {
fetchHotelPageRefs,
generatePageTags,
getHotelPageCounter,
validateHotelPageRefs,
} from "../contentstack/hotelPage/utils"
import {
getHotelInputSchema,
getHotelsAvailabilityInputSchema,
@@ -70,14 +77,38 @@ const roomsAvailabilityFailCounter = meter.createCounter(
"trpc.hotel.availability.rooms-fail"
)
async function getContentstackData(
locale: string,
uid: string | null | undefined
) {
const response = await request<GetHotelPageData>(GetHotelPage, {
locale,
uid,
})
async function getContentstackData(lang: Lang, uid?: string | null) {
if (!uid) {
return null
}
const contentPageRefsData = await fetchHotelPageRefs(lang, uid)
const contentPageRefs = validateHotelPageRefs(contentPageRefsData, lang, uid)
if (!contentPageRefs) {
return null
}
const tags = generatePageTags(contentPageRefs, lang)
getHotelPageCounter.add(1, { lang, uid })
console.info(
"contentstack.contentPage start",
JSON.stringify({
query: { lang, uid },
})
)
const response = await request<GetHotelPageData>(
GetHotelPage,
{
locale: lang,
uid,
},
{
cache: "force-cache",
next: {
tags,
},
}
)
if (!response.data) {
throw notFound(response)
@@ -86,7 +117,7 @@ async function getContentstackData(
const hotelPageData = hotelPageSchema.safeParse(response.data)
if (!hotelPageData.success) {
console.error(
`Failed to validate Hotel Page - (uid: ${uid}, lang: ${locale})`
`Failed to validate Hotel Page - (uid: ${uid}, lang: ${lang})`
)
console.error(hotelPageData.error)
return null

View File

@@ -2,14 +2,19 @@ import { z } from "zod"
import {
contentBlock,
hotelPageRefsSchema,
hotelPageSchema,
} from "@/server/routers/contentstack/hotelPage/output"
import { activitiesCardSchema } from "@/server/routers/contentstack/schemas/blocks/activitiesCard"
// Will be extended once we introduce more functionality to our entries.
export interface GetHotelPageData extends z.input<typeof hotelPageSchema> {}
export interface HotelPage extends z.output<typeof hotelPageSchema> {}
export interface ActivitiesCard extends z.output<typeof activitiesCardSchema> {}
export type ActivityCard = ActivitiesCard["upcoming_activities_card"]
export interface ContentBlock extends z.output<typeof contentBlock> {}
export interface GetHotelPageRefsSchema
extends z.input<typeof hotelPageRefsSchema> {}
export interface HotelPageRefs extends z.output<typeof hotelPageRefsSchema> {}