import { metrics } from "@opentelemetry/api" import { GetContactConfig } from "@/lib/graphql/Query/ContactConfig.graphql" import { GetCurrentFooter, GetCurrentFooterRef, } from "@/lib/graphql/Query/CurrentFooter.graphql" import { GetCurrentHeader, GetCurrentHeaderRef, } from "@/lib/graphql/Query/CurrentHeader.graphql" import { GetHeader, GetHeaderRef } from "@/lib/graphql/Query/Header.graphql" import { request } from "@/lib/graphql/request" import { notFound } from "@/server/errors/trpc" import { contentstackBaseProcedure, router } from "@/server/trpc" import { generateRefsResponseTag, generateTag } from "@/utils/generateTag" import { langInput } from "./input" import { type ContactConfigData, CurrentHeaderData, CurrentHeaderDataRaw, CurrentHeaderRefDataRaw, FooterDataRaw, FooterRefDataRaw, HeaderData, HeaderDataRaw, HeaderRefDataRaw, InternalExternalLink, MenuItem, SubmenuItem, validateContactConfigSchema, validateCurrentHeaderConfigSchema, validateFooterConfigSchema, validateHeaderSchema, } from "./output" import { makeLinkObjectFromInternalExternalLink } from "./utils" 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" ) // OpenTelemetry metrics: Header const getHeaderRefCounter = meter.createCounter( "trpc.contentstack.header.ref.get" ) const getHeaderRefSuccessCounter = meter.createCounter( "trpc.contentstack.header.ref.get-success" ) const getHeaderRefFailCounter = 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" ) // 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" ) 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( 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] }), header: contentstackBaseProcedure.query(async ({ ctx }) => { const { lang } = ctx getHeaderRefCounter.add(1, { lang }) console.info( "contentstack.header.ref start", JSON.stringify({ query: { lang } }) ) // TODO: Add better ref types and error handling for responseRef const responseRef = await request(GetHeaderRef, { locale: lang, }) getCurrentHeaderCounter.add(1, { lang }) console.info( "contentstack.header start", JSON.stringify({ query: { lang } }) ) const response = await request( GetHeader, { locale: lang }, { tags: [ generateTag(lang, responseRef.data.all_header.items[0].system.uid), ], } ) if (!response.data) { const notFoundError = notFound(response) getHeaderFailCounter.add(1, { lang, error_type: "not_found", error: JSON.stringify({ code: notFoundError.code }), }) console.error( "contentstack.header not found error", JSON.stringify({ query: { lang }, error: { code: notFoundError.code }, }) ) throw notFoundError } const validatedHeaderConfig = validateHeaderSchema.safeParse(response.data) if (!validatedHeaderConfig.success) { getHeaderFailCounter.add(1, { lang, error_type: "validation_error", error: JSON.stringify(validatedHeaderConfig.error), }) console.error( "contentstack.header validation error", JSON.stringify({ query: { lang }, error: validatedHeaderConfig.error, }) ) return null } getHeaderSuccessCounter.add(1, { lang }) console.info( "contentstack.header success", JSON.stringify({ query: { lang } }) ) const data = validatedHeaderConfig.data.all_header.items[0] const topLink = makeLinkObjectFromInternalExternalLink(data.top_link) const menuItems: MenuItem[] = data.menu_items.map((menuItem) => { let link = null let seeAllLink = null let submenu: SubmenuItem[] = [] if (!menuItem.submenu.length) { link = !menuItem.submenu.length ? makeLinkObjectFromInternalExternalLink(menuItem.link) : null seeAllLink = makeLinkObjectFromInternalExternalLink( menuItem.see_all_link ) } else { submenu = menuItem.submenu.map(({ title, links }) => ({ title: title, links: links.map( (link) => makeLinkObjectFromInternalExternalLink( link ) as InternalExternalLink ), })) } return { title: menuItem.title, link, seeAllLink, submenu, } }) const headerData: HeaderData = { topLink, menuItems } return headerData }), currentHeader: contentstackBaseProcedure .input(langInput) .query(async ({ input }) => { getCurrentHeaderRefCounter.add(1, { lang: input.lang }) console.info( "contentstack.currentHeader.ref start", JSON.stringify({ query: { lang: input.lang } }) ) const responseRef = await request( GetCurrentHeaderRef, { locale: input.lang, }, { cache: "force-cache", next: { tags: [generateRefsResponseTag(input.lang, "current_header")], }, } ) getCurrentHeaderCounter.add(1, { lang: input.lang }) console.info( "contentstack.currentHeader start", JSON.stringify({ query: { lang: input.lang }, }) ) const currentHeaderUID = responseRef.data.all_current_header.items[0].system.uid // There's currently no error handling/validation for the responseRef, should it be added? const response = await request( GetCurrentHeader, { locale: input.lang }, { cache: "force-cache", next: { tags: [generateTag(input.lang, currentHeaderUID)], }, } ) if (!response.data) { const notFoundError = notFound(response) getCurrentHeaderFailCounter.add(1, { lang: input.lang, error_type: "not_found", error: JSON.stringify({ code: notFoundError.code }), }) console.error( "contentstack.currentHeader not found error", JSON.stringify({ query: { lang: input.lang, }, error: { code: notFoundError.code }, }) ) throw notFoundError } const validatedHeaderConfig = validateCurrentHeaderConfigSchema.safeParse( response.data ) if (!validatedHeaderConfig.success) { getCurrentHeaderFailCounter.add(1, { lang: input.lang, error_type: "validation_error", error: JSON.stringify(validatedHeaderConfig.error), }) console.error( "contentstack.currentHeader validation error", JSON.stringify({ query: { lang: input.lang, }, error: validatedHeaderConfig.error, }) ) return null } getCurrentHeaderSuccessCounter.add(1, { lang: input.lang }) console.info( "contentstack.currentHeader success", JSON.stringify({ query: { lang: input.lang }, }) ) const logo = validatedHeaderConfig.data.all_current_header.items[0].logoConnection .edges?.[0]?.node return { ...validatedHeaderConfig.data.all_current_header.items[0], logo, } as CurrentHeaderData }), footer: contentstackBaseProcedure .input(langInput) .query(async ({ input }) => { getFooterRefCounter.add(1, { lang: input.lang }) console.info( "contentstack.footer.ref start", JSON.stringify({ query: { lang: input.lang } }) ) const responseRef = await request( GetCurrentFooterRef, { locale: input.lang, }, { cache: "force-cache", next: { tags: [generateRefsResponseTag(input.lang, "current_footer")], }, } ) // There's currently no error handling/validation for the responseRef, should it be added? getFooterCounter.add(1, { lang: input.lang }) console.info( "contentstack.footer start", JSON.stringify({ query: { lang: input.lang, }, }) ) const currentFooterUID = responseRef.data.all_current_footer.items[0].system.uid const response = await request( GetCurrentFooter, { locale: input.lang, }, { cache: "force-cache", next: { tags: [generateTag(input.lang, currentFooterUID)], }, } ) if (!response.data) { const notFoundError = notFound(response) getFooterFailCounter.add(1, { lang: input.lang, error_type: "not_found", error: JSON.stringify({ code: notFoundError.code }), }) console.error( "contentstack.footer not found error", JSON.stringify({ query: { lang: input.lang, }, error: { code: notFoundError.code }, }) ) throw notFoundError } const validatedFooterConfig = validateFooterConfigSchema.safeParse( response.data ) if (!validatedFooterConfig.success) { getFooterFailCounter.add(1, { lang: input.lang, error_type: "validation_error", error: JSON.stringify(validatedFooterConfig.error), }) console.error( "contentstack.footer validation error", JSON.stringify({ query: { lang: input.lang }, error: validatedFooterConfig.error, }) ) return null } getFooterSuccessCounter.add(1, { lang: input.lang }) console.info( "contentstack.footer success", JSON.stringify({ query: { lang: input.lang } }) ) return validatedFooterConfig.data.all_current_footer.items[0] }), })