From f2068ac3256d6ffc9a5e3dc78457bbaf0a69908f Mon Sep 17 00:00:00 2001 From: Pontus Dreij Date: Mon, 9 Sep 2024 10:22:17 +0200 Subject: [PATCH] feat(SW-187): refactor footer query --- components/Footer/index.tsx | 6 +- server/routers/contentstack/base/output.ts | 11 +- server/routers/contentstack/base/query.ts | 314 +++++++++++++-------- server/routers/contentstack/base/utils.ts | 34 ++- types/components/footer/footer.ts | 12 + 5 files changed, 244 insertions(+), 133 deletions(-) create mode 100644 types/components/footer/footer.ts diff --git a/components/Footer/index.tsx b/components/Footer/index.tsx index 83c5aa7d7..ee11328ba 100644 --- a/components/Footer/index.tsx +++ b/components/Footer/index.tsx @@ -1,15 +1,11 @@ import { serverClient } from "@/lib/trpc/server" -import { getLang } from "@/i18n/serverContext" - import FooterDetails from "./Details" import FooterNavigation from "./Navigation" export default async function Footer() { - const footerData = await serverClient().contentstack.base.footer({ - lang: getLang(), - }) const languages = await serverClient().contentstack.languageSwitcher.get() + const footerData = await serverClient().contentstack.base.footer() if (!footerData || !languages) { return diff --git a/server/routers/contentstack/base/output.ts b/server/routers/contentstack/base/output.ts index a9ab60805..37c1a3ea1 100644 --- a/server/routers/contentstack/base/output.ts +++ b/server/routers/contentstack/base/output.ts @@ -291,14 +291,12 @@ const validateInternalLink = z }) .optional() -const validateLinkItem = z.object({ +export const validateLinkItem = z.object({ open_in_new_tab: z.boolean(), link: validateExternalLink, pageConnection: validateInternalLink, }) -export type FooterLinkItem = z.infer - export const validateFooterConfigSchema = z.object({ all_footer: z.object({ items: z.array( @@ -333,9 +331,6 @@ export const validateFooterConfigSchema = z.object({ }), }) -export type FooterDataRaw = z.infer -export type FooterData = FooterDataRaw["all_footer"]["items"][0] - const pageConnectionRefs = z.object({ edges: z.array( z.object({ @@ -350,7 +345,7 @@ const pageConnectionRefs = z.object({ ), }) -const validateFooterRefConfigSchema = z.object({ +export const validateFooterRefConfigSchema = z.object({ all_footer: z.object({ items: z.array( z.object({ @@ -382,8 +377,6 @@ const validateFooterRefConfigSchema = z.object({ }), }) -export type FooterRefDataRaw = z.infer - const linkConnectionNodeSchema = z .object({ edges: z diff --git a/server/routers/contentstack/base/query.ts b/server/routers/contentstack/base/query.ts index a3affedfc..30a262a42 100644 --- a/server/routers/contentstack/base/query.ts +++ b/server/routers/contentstack/base/query.ts @@ -29,17 +29,24 @@ import { CurrentHeaderData, CurrentHeaderDataRaw, CurrentHeaderRefDataRaw, - FooterDataRaw, - FooterRefDataRaw, getHeaderRefSchema, getHeaderSchema, validateContactConfigSchema, validateCurrentFooterConfigSchema, validateCurrentHeaderConfigSchema, validateFooterConfigSchema, + validateFooterRefConfigSchema, } from "./output" -import { getConnections, transformPageConnectionLinks } from "./utils" +import { + getConnections, + getFooterConnections, + transformPageConnectionLinks, +} from "./utils" +import type { + FooterDataRaw, + FooterRefDataRaw, +} from "@/types/components/footer/footer" import type { HeaderRefResponse, HeaderResponse } from "@/types/header" const meter = metrics.getMeter("trpc.contentstack.base") @@ -90,6 +97,27 @@ const getHeaderSuccessCounter = meter.createCounter( const getHeaderFailCounter = meter.createCounter( "trpc.contentstack.header.get-fail" ) + +// 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" +) + // OpenTelemetry metrics: Footer const getFooterRefCounter = meter.createCounter( "trpc.contentstack.footer.ref.get" @@ -403,9 +431,9 @@ export const baseQueryRouter = router({ currentFooter: contentstackBaseProcedure .input(langInput) .query(async ({ input }) => { - getFooterRefCounter.add(1, { lang: input.lang }) + getCurrentFooterRefCounter.add(1, { lang: input.lang }) console.info( - "contentstack.footer.ref start", + "contentstack.currentFooter.ref start", JSON.stringify({ query: { lang: input.lang } }) ) const responseRef = await request( @@ -421,9 +449,9 @@ export const baseQueryRouter = router({ } ) // There's currently no error handling/validation for the responseRef, should it be added? - getFooterCounter.add(1, { lang: input.lang }) + getCurrentFooterCounter.add(1, { lang: input.lang }) console.info( - "contentstack.footer start", + "contentstack.currentFooter start", JSON.stringify({ query: { lang: input.lang, @@ -447,13 +475,13 @@ export const baseQueryRouter = router({ if (!response.data) { const notFoundError = notFound(response) - getFooterFailCounter.add(1, { + getCurrentFooterFailCounter.add(1, { lang: input.lang, error_type: "not_found", error: JSON.stringify({ code: notFoundError.code }), }) console.error( - "contentstack.footer not found error", + "contentstack.currentFooter not found error", JSON.stringify({ query: { lang: input.lang, @@ -464,142 +492,194 @@ export const baseQueryRouter = router({ throw notFoundError } - const validatedFooterConfig = validateCurrentFooterConfigSchema.safeParse( - response.data - ) + const validatedCurrentFooterConfig = + validateCurrentFooterConfigSchema.safeParse(response.data) - if (!validatedFooterConfig.success) { + if (!validatedCurrentFooterConfig.success) { getFooterFailCounter.add(1, { lang: input.lang, error_type: "validation_error", - error: JSON.stringify(validatedFooterConfig.error), + error: JSON.stringify(validatedCurrentFooterConfig.error), }) console.error( - "contentstack.footer validation error", + "contentstack.currentFooter validation error", JSON.stringify({ query: { lang: input.lang }, - error: validatedFooterConfig.error, + error: validatedCurrentFooterConfig.error, }) ) return null } - getFooterSuccessCounter.add(1, { lang: input.lang }) + getCurrentFooterSuccessCounter.add(1, { lang: input.lang }) console.info( - "contentstack.footer success", + "contentstack.currentFooter success", JSON.stringify({ query: { lang: input.lang } }) ) - return validatedFooterConfig.data.all_current_footer.items[0] + return validatedCurrentFooterConfig.data.all_current_footer.items[0] }), - 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( - GetFooterRef, - { - locale: input.lang, + footer: contentstackBaseProcedure.query(async ({ ctx }) => { + const { lang } = ctx + getFooterRefCounter.add(1, { lang }) + console.info( + "contentstack.footer.ref start", + JSON.stringify({ query: { lang } }) + ) + const responseRef = await request( + GetFooterRef, + { + locale: lang, + }, + { + cache: "force-cache", + next: { + tags: [generateRefsResponseTag(lang, "footer")], }, - { - cache: "force-cache", - next: { - tags: [generateRefsResponseTag(input.lang, "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", + if (!responseRef.data) { + const notFoundError = notFound(responseRef) + getFooterRefFailCounter.add(1, { + lang, + error_type: "not_found", + error: JSON.stringify({ code: notFoundError.code }), + }) + console.error( + "contentstack.footer.refs not found error", JSON.stringify({ query: { - lang: input.lang, + lang, }, + error: { code: notFoundError.code }, }) ) - const footerUID = responseRef.data.all_footer.items[0].system.uid - const response = await request( - GetFooter, - { - locale: input.lang, + throw notFoundError + } + + const validatedFooterRefs = validateFooterRefConfigSchema.safeParse( + responseRef.data + ) + + if (!validatedFooterRefs.success) { + getFooterRefFailCounter.add(1, { + lang, + error_type: "validation_error", + error: JSON.stringify(validatedFooterRefs.error), + }) + console.error( + "contentstack.footer.refs validation error", + JSON.stringify({ + query: { + lang, + }, + error: validatedFooterRefs.error, + }) + ) + return null + } + + getFooterRefSuccessCounter.add(1, { lang }) + console.info( + "contentstack.footer.refs success", + JSON.stringify({ query: { lang } }) + ) + + const connections = getFooterConnections(validatedFooterRefs.data) + const footerUID = responseRef.data.all_footer.items[0].system.uid + + // There's currently no error handling/validation for the responseRef, should it be added? + getFooterCounter.add(1, { lang: lang }) + console.info( + "contentstack.footer start", + JSON.stringify({ + query: { + lang, }, - { - cache: "force-cache", - next: { - tags: [generateTag(input.lang, footerUID)], + }) + ) + const tags = [ + generateTags(lang, connections), + generateTag(lang, footerUID), + ].flat() + + const response = await request( + GetFooter, + { + locale: lang, + }, + { + cache: "force-cache", + next: { + tags, + }, + } + ) + + if (!response.data) { + const notFoundError = notFound(response) + getFooterFailCounter.add(1, { + lang, + error_type: "not_found", + error: JSON.stringify({ code: notFoundError.code }), + }) + console.error( + "contentstack.footer not found error", + JSON.stringify({ + query: { + lang, }, - } - ) - - 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 } }) - ) - const validatedFooterData = validatedFooterConfig.data.all_footer.items[0] - const mainLinks = transformPageConnectionLinks( - validatedFooterData.main_links - ) - - const secondaryLinks = validatedFooterData.secondary_links.map( - (section) => ({ - title: section.title, - links: transformPageConnectionLinks(section.links), + error: { code: notFoundError.code }, }) ) + throw notFoundError + } - const tertiaryLinks = transformPageConnectionLinks( - validatedFooterData.tertiary_links + const validatedFooterConfig = validateFooterConfigSchema.safeParse( + response.data + ) + + if (!validatedFooterConfig.success) { + getFooterFailCounter.add(1, { + lang, + error_type: "validation_error", + error: JSON.stringify(validatedFooterConfig.error), + }) + console.error( + "contentstack.footer validation error", + JSON.stringify({ + query: { lang: lang }, + error: validatedFooterConfig.error, + }) ) + return null + } + getFooterSuccessCounter.add(1, { lang }) + console.info( + "contentstack.footer success", + JSON.stringify({ query: { lang } }) + ) + const validatedFooterData = validatedFooterConfig.data.all_footer.items[0] + const mainLinks = transformPageConnectionLinks( + validatedFooterData.main_links + ) - return { - mainLinks: mainLinks, - appDownloads: validatedFooterData.app_downloads, - secondaryLinks: secondaryLinks, - socialMedia: validatedFooterData.social_media, - tertiaryLinks: tertiaryLinks, - } - }), + const secondaryLinks = validatedFooterData.secondary_links.map( + (section) => ({ + title: section.title, + links: transformPageConnectionLinks(section.links), + }) + ) + + const tertiaryLinks = transformPageConnectionLinks( + validatedFooterData.tertiary_links + ) + + return { + mainLinks: mainLinks, + appDownloads: validatedFooterData.app_downloads, + secondaryLinks: secondaryLinks, + socialMedia: validatedFooterData.social_media, + tertiaryLinks: tertiaryLinks, + } + }), }) diff --git a/server/routers/contentstack/base/utils.ts b/server/routers/contentstack/base/utils.ts index 2493da1f4..83347bdb6 100644 --- a/server/routers/contentstack/base/utils.ts +++ b/server/routers/contentstack/base/utils.ts @@ -1,7 +1,10 @@ -import { HeaderRefResponse } from "@/types/header" +import type { + FooterLinkItem, + FooterRefDataRaw, +} from "@/types/components/footer/footer" +import type { HeaderRefResponse } from "@/types/header" import { Edges } from "@/types/requests/utils/edges" import { NodeRefs } from "@/types/requests/utils/refs" -import type { FooterLinkItem } from "./output" export function getConnections(refs: HeaderRefResponse) { const connections: Edges[] = [] @@ -40,6 +43,33 @@ export function getConnections(refs: HeaderRefResponse) { return connections } +export function getFooterConnections(refs: FooterRefDataRaw) { + const connections: Edges[] = [] + const footerData = refs.all_footer.items[0] + const mainLinks = footerData.main_links + const secondaryLinks = footerData.secondary_links + const tertiaryLinks = footerData.tertiary_links + if (mainLinks) { + mainLinks.forEach(({ pageConnection }) => { + connections.push(pageConnection) + }) + } + secondaryLinks?.forEach(({ links }) => { + if (links) { + links.forEach(({ pageConnection }) => { + connections.push(pageConnection) + }) + } + }) + if (tertiaryLinks) { + tertiaryLinks.forEach(({ pageConnection }) => { + connections.push(pageConnection) + }) + } + + return connections +} + export function transformPageConnectionLinks(links: FooterLinkItem[]) { if (!links) return [] return links.flatMap((link) => { diff --git a/types/components/footer/footer.ts b/types/components/footer/footer.ts new file mode 100644 index 000000000..8df0eaf8a --- /dev/null +++ b/types/components/footer/footer.ts @@ -0,0 +1,12 @@ +import { z } from "zod" + +import { + validateFooterConfigSchema, + validateFooterRefConfigSchema, + validateLinkItem, +} from "@/server/routers/contentstack/base/output" + +export type FooterRefDataRaw = z.infer +export type FooterDataRaw = z.infer +export type FooterData = FooterDataRaw["all_footer"]["items"][0] +export type FooterLinkItem = z.infer