import { metrics } from "@opentelemetry/api" import { cache } from "react" import { GetMyPagesBreadcrumbs, GetMyPagesBreadcrumbsRefs, } from "@/lib/graphql/Query/Breadcrumbs/AccountPage.graphql" import { GetCollectionPageBreadcrumbs, GetCollectionPageBreadcrumbsRefs, } from "@/lib/graphql/Query/Breadcrumbs/CollectionPage.graphql" import { GetContentPageBreadcrumbs, GetContentPageBreadcrumbsRefs, } from "@/lib/graphql/Query/Breadcrumbs/ContentPage.graphql" import { GetDestinationOverviewPageBreadcrumbs, GetDestinationOverviewPageBreadcrumbsRefs, } from "@/lib/graphql/Query/Breadcrumbs/DestinationOverviewPage.graphql" import { GetHotelPageBreadcrumbs, GetHotelPageBreadcrumbsRefs, } from "@/lib/graphql/Query/Breadcrumbs/HotelPage.graphql" import { GetLoyaltyPageBreadcrumbs, GetLoyaltyPageBreadcrumbsRefs, } from "@/lib/graphql/Query/Breadcrumbs/LoyaltyPage.graphql" import { request } from "@/lib/graphql/request" import { notFound } from "@/server/errors/trpc" import { contentstackExtendedProcedureUID, router } from "@/server/trpc" import { breadcrumbsRefsSchema, breadcrumbsSchema } from "./output" import { getTags } from "./utils" import { PageContentTypeEnum } from "@/types/requests/contentType" import type { BreadcrumbsRefsSchema, RawBreadcrumbsSchema, } from "@/types/trpc/routers/contentstack/breadcrumbs" import type { Lang } from "@/constants/languages" const meter = metrics.getMeter("trpc.breadcrumbs") // OpenTelemetry metrics const getBreadcrumbsRefsCounter = meter.createCounter( "trpc.contentstack.breadcrumbs.refs.get" ) const getBreadcrumbsRefsSuccessCounter = meter.createCounter( "trpc.contentstack.breadcrumbs.refs.get-success" ) const getBreadcrumbsRefsFailCounter = meter.createCounter( "trpc.contentstack.breadcrumbs.refs.get-fail" ) const getBreadcrumbsCounter = meter.createCounter( "trpc.contentstack.breadcrumbs.get" ) const getBreadcrumbsSuccessCounter = meter.createCounter( "trpc.contentstack.breadcrumbs.get-success" ) const getBreadcrumbsFailCounter = meter.createCounter( "trpc.contentstack.breadcrumbs.get-fail" ) interface BreadcrumbsPageData { dataKey: keyof T refQuery: string query: string } const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs( { dataKey, refQuery, query }: BreadcrumbsPageData, { uid, lang }: { uid: string; lang: Lang } ) { getBreadcrumbsRefsCounter.add(1, { lang, uid }) console.info( "contentstack.breadcrumbs refs get start", JSON.stringify({ query: { lang, uid } }) ) const refsResponse = await request<{ [K in keyof T]: BreadcrumbsRefsSchema }>( refQuery, { locale: lang, uid } ) const validatedRefsData = breadcrumbsRefsSchema.safeParse( refsResponse.data[dataKey] ) if (!validatedRefsData.success) { getBreadcrumbsRefsFailCounter.add(1, { error_type: "validation_error", error: JSON.stringify(validatedRefsData.error), }) console.error( "contentstack.breadcrumbs refs validation error", JSON.stringify({ error: validatedRefsData.error, }) ) return [] } getBreadcrumbsRefsSuccessCounter.add(1, { lang, uid }) console.info( "contentstack.breadcrumbs refs get success", JSON.stringify({ query: { lang, uid } }) ) const tags = getTags(validatedRefsData.data, lang) getBreadcrumbsCounter.add(1, { lang, uid }) console.info( "contentstack.breadcrumbs get start", JSON.stringify({ query: { lang, uid } }) ) const response = await request( query, { locale: lang, uid }, { cache: "force-cache", next: { tags }, } ) if (!response.data) { const notFoundError = notFound(response) getBreadcrumbsFailCounter.add(1, { lang, uid, error_type: "not_found", error: JSON.stringify({ code: notFoundError.code }), }) console.error( "contentstack.breadcrumbs get not found error", JSON.stringify({ query: { lang, uid }, error: { code: notFoundError.code }, }) ) throw notFoundError } const validatedBreadcrumbs = breadcrumbsSchema.safeParse( response.data[dataKey] ) if (!validatedBreadcrumbs.success) { getBreadcrumbsFailCounter.add(1, { error_type: "validation_error", error: JSON.stringify(validatedBreadcrumbs.error), }) console.error( "contentstack.breadcrumbs validation error", JSON.stringify({ error: validatedBreadcrumbs.error, }) ) return [] } getBreadcrumbsSuccessCounter.add(1, { lang, uid }) console.info( "contentstack.breadcrumbs get success", JSON.stringify({ query: { lang, uid } }) ) return validatedBreadcrumbs.data }) export const breadcrumbsQueryRouter = router({ get: contentstackExtendedProcedureUID.query(async ({ ctx }) => { const variables = { lang: ctx.lang, uid: ctx.uid, } switch (ctx.contentType) { case PageContentTypeEnum.accountPage: return await getBreadcrumbs<{ account_page: RawBreadcrumbsSchema }>( { dataKey: "account_page", refQuery: GetMyPagesBreadcrumbsRefs, query: GetMyPagesBreadcrumbs, }, variables ) case PageContentTypeEnum.collectionPage: return await getBreadcrumbs<{ collection_page: RawBreadcrumbsSchema }>( { dataKey: "collection_page", refQuery: GetCollectionPageBreadcrumbsRefs, query: GetCollectionPageBreadcrumbs, }, variables ) case PageContentTypeEnum.contentPage: return await getBreadcrumbs<{ content_page: RawBreadcrumbsSchema }>( { dataKey: "content_page", refQuery: GetContentPageBreadcrumbsRefs, query: GetContentPageBreadcrumbs, }, variables ) case PageContentTypeEnum.destinationOverviewPage: return await getBreadcrumbs<{ destination_overview_page: RawBreadcrumbsSchema }>( { dataKey: "destination_overview_page", refQuery: GetDestinationOverviewPageBreadcrumbsRefs, query: GetDestinationOverviewPageBreadcrumbs, }, variables ) case PageContentTypeEnum.hotelPage: return await getBreadcrumbs<{ hotel_page: RawBreadcrumbsSchema }>( { dataKey: "hotel_page", refQuery: GetHotelPageBreadcrumbsRefs, query: GetHotelPageBreadcrumbs, }, variables ) case PageContentTypeEnum.loyaltyPage: return await getBreadcrumbs<{ loyalty_page: RawBreadcrumbsSchema }>( { dataKey: "loyalty_page", refQuery: GetLoyaltyPageBreadcrumbsRefs, query: GetLoyaltyPageBreadcrumbs, }, variables ) default: return [] } }), })