fix: handle caching of card grids on content page blocks
This commit is contained in:
@@ -1,11 +1,31 @@
|
|||||||
import "server-only"
|
import "server-only"
|
||||||
|
|
||||||
|
import deepmerge from "deepmerge"
|
||||||
|
|
||||||
import { request } from "./request"
|
import { request } from "./request"
|
||||||
|
|
||||||
import type { BatchRequestDocument } from "graphql-request"
|
import type { BatchRequestDocument } from "graphql-request"
|
||||||
|
|
||||||
import type { Data } from "@/types/request"
|
import type { Data } from "@/types/request"
|
||||||
|
|
||||||
|
function arrayMerge(
|
||||||
|
target: any[],
|
||||||
|
source: any[],
|
||||||
|
options: deepmerge.ArrayMergeOptions | undefined
|
||||||
|
) {
|
||||||
|
const destination = target.slice()
|
||||||
|
source.forEach((item, index) => {
|
||||||
|
if (typeof destination[index] === "undefined") {
|
||||||
|
destination[index] = options?.cloneUnlessOtherwiseSpecified(item, options)
|
||||||
|
} else if (options?.isMergeableObject(item)) {
|
||||||
|
destination[index] = deepmerge(target[index], item, options)
|
||||||
|
} else if (target.indexOf(item) === -1) {
|
||||||
|
destination.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return destination
|
||||||
|
}
|
||||||
|
|
||||||
export async function batchRequest<T>(
|
export async function batchRequest<T>(
|
||||||
queries: (BatchRequestDocument & { options?: RequestInit })[]
|
queries: (BatchRequestDocument & { options?: RequestInit })[]
|
||||||
): Promise<Data<T>> {
|
): Promise<Data<T>> {
|
||||||
@@ -17,15 +37,21 @@ export async function batchRequest<T>(
|
|||||||
)
|
)
|
||||||
|
|
||||||
let data = {} as T
|
let data = {} as T
|
||||||
const reasons = []
|
const reasons: PromiseRejectedResult["reason"][] = []
|
||||||
response.forEach((res) => {
|
response.forEach((res) => {
|
||||||
if (res.status === "fulfilled") {
|
if (res.status === "fulfilled") {
|
||||||
data = Object.assign({}, data, res.value.data)
|
data = deepmerge(data, res.value.data, { arrayMerge })
|
||||||
} else {
|
} else {
|
||||||
reasons.push(res.reason)
|
reasons.push(res.reason)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (reasons.length) {
|
||||||
|
reasons.forEach((reason) => {
|
||||||
|
console.error(`Batch request failed`, reason)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return { data }
|
return { data }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in batched graphql request")
|
console.error("Error in batched graphql request")
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Lang } from "@/constants/languages"
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { batchRequest } from "@/lib/graphql/batchRequest"
|
||||||
import {
|
import {
|
||||||
GetContentPage,
|
GetContentPage,
|
||||||
GetContentPageBlocksBatch1,
|
GetContentPageBlocksBatch1,
|
||||||
GetContentPageBlocksBatch2,
|
GetContentPageBlocksBatch2,
|
||||||
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { contentPageSchema } from "./output"
|
import { contentPageSchema } from "./output"
|
||||||
@@ -12,30 +12,20 @@ import {
|
|||||||
fetchContentPageRefs,
|
fetchContentPageRefs,
|
||||||
generatePageTags,
|
generatePageTags,
|
||||||
getContentPageCounter,
|
getContentPageCounter,
|
||||||
validateContentPageRefs,
|
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TrackingChannelEnum,
|
TrackingChannelEnum,
|
||||||
type TrackingSDKPageData,
|
type TrackingSDKPageData,
|
||||||
} from "@/types/components/tracking"
|
} from "@/types/components/tracking"
|
||||||
import { ContentPageEnum } from "@/types/enums/contentPage"
|
import type { GetContentPageSchema } from "@/types/trpc/routers/contentstack/contentPage"
|
||||||
import type {
|
|
||||||
GetBlock,
|
|
||||||
GetContentPageSchema,
|
|
||||||
} from "@/types/trpc/routers/contentstack/contentPage"
|
|
||||||
|
|
||||||
export const contentPageQueryRouter = router({
|
export const contentPageQueryRouter = router({
|
||||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||||
const { lang, uid } = ctx
|
const { lang, uid } = ctx
|
||||||
|
|
||||||
const contentPageRefsData = await fetchContentPageRefs(lang, uid)
|
const contentPageRefs = await fetchContentPageRefs(lang, uid)
|
||||||
|
|
||||||
const contentPageRefs = validateContentPageRefs(
|
|
||||||
contentPageRefsData,
|
|
||||||
lang,
|
|
||||||
uid
|
|
||||||
)
|
|
||||||
if (!contentPageRefs) {
|
if (!contentPageRefs) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -50,69 +40,42 @@ export const contentPageQueryRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const [mainResponse, blocksResponse1, blocksResponse2] = await Promise.all([
|
const contentPageRequest = await batchRequest<GetContentPageSchema>([
|
||||||
request<GetContentPageSchema>(
|
{
|
||||||
GetContentPage,
|
document: GetContentPage,
|
||||||
{ locale: lang, uid },
|
variables: { locale: lang, uid },
|
||||||
{
|
options: {
|
||||||
cache: "force-cache",
|
cache: "force-cache",
|
||||||
next: {
|
next: {
|
||||||
tags,
|
tags,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
),
|
},
|
||||||
request<GetContentPageSchema>(
|
|
||||||
GetContentPageBlocksBatch1,
|
{
|
||||||
{ locale: lang, uid },
|
document: GetContentPageBlocksBatch1,
|
||||||
{
|
variables: { locale: lang, uid },
|
||||||
|
options: {
|
||||||
cache: "force-cache",
|
cache: "force-cache",
|
||||||
next: {
|
next: {
|
||||||
tags,
|
tags,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
),
|
},
|
||||||
request<GetContentPageSchema>(
|
|
||||||
GetContentPageBlocksBatch2,
|
{
|
||||||
{ locale: lang, uid },
|
document: GetContentPageBlocksBatch2,
|
||||||
{
|
variables: { locale: lang, uid },
|
||||||
|
options: {
|
||||||
cache: "force-cache",
|
cache: "force-cache",
|
||||||
next: {
|
next: {
|
||||||
tags,
|
tags,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
),
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const blocksOrder = mainResponse.data.content_page.blocks?.map(
|
const contentPage = contentPageSchema.safeParse(contentPageRequest.data)
|
||||||
(block) => block.__typename
|
|
||||||
)
|
|
||||||
|
|
||||||
let sortedBlocks
|
|
||||||
if (blocksOrder) {
|
|
||||||
const blocks = [
|
|
||||||
blocksResponse1.data.content_page.blocks,
|
|
||||||
blocksResponse2.data.content_page.blocks,
|
|
||||||
]
|
|
||||||
.flat(2)
|
|
||||||
.filter((obj) => !(obj && Object.keys(obj).length < 2))
|
|
||||||
// Remove empty objects and objects with only typename
|
|
||||||
|
|
||||||
sortedBlocks = blocksOrder
|
|
||||||
.map((typename: ContentPageEnum.ContentStack.blocks) =>
|
|
||||||
blocks.find((block) => block?.__typename === typename)
|
|
||||||
)
|
|
||||||
.filter((block): block is GetBlock => !!block)
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = {
|
|
||||||
...mainResponse.data,
|
|
||||||
content_page: {
|
|
||||||
...mainResponse.data.content_page,
|
|
||||||
blocks: sortedBlocks,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentPage = contentPageSchema.safeParse(responseData)
|
|
||||||
if (!contentPage.success) {
|
if (!contentPage.success) {
|
||||||
console.error(
|
console.error(
|
||||||
`Failed to validate Contentpage Data - (lang: ${lang}, uid: ${uid})`
|
`Failed to validate Contentpage Data - (lang: ${lang}, uid: ${uid})`
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
import { metrics } from "@opentelemetry/api"
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { batchRequest } from "@/lib/graphql/batchRequest"
|
||||||
import {
|
import {
|
||||||
GetContentPageBlocksRefs,
|
GetContentPageBlocksRefs,
|
||||||
GetContentPageRefs,
|
GetContentPageRefs,
|
||||||
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
|
||||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||||
@@ -44,30 +44,30 @@ export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
|||||||
query: { lang, uid },
|
query: { lang, uid },
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
const [mainRefsResponse, blockRefsResponse] = await Promise.all([
|
const res = await batchRequest<GetContentPageRefsSchema>([
|
||||||
request<GetContentPageRefsSchema>(
|
{
|
||||||
GetContentPageRefs,
|
document: GetContentPageRefs,
|
||||||
{ locale: lang, uid },
|
variables: { locale: lang, uid },
|
||||||
{
|
options: {
|
||||||
cache: "force-cache",
|
cache: "force-cache",
|
||||||
next: {
|
next: {
|
||||||
tags: [generateTag(lang, uid)],
|
tags: [generateTag(lang, uid)],
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
),
|
},
|
||||||
request<GetContentPageRefsSchema>(
|
{
|
||||||
GetContentPageBlocksRefs,
|
document: GetContentPageBlocksRefs,
|
||||||
{ locale: lang, uid },
|
variables: { locale: lang, uid },
|
||||||
{
|
options: {
|
||||||
cache: "force-cache",
|
cache: "force-cache",
|
||||||
next: {
|
next: {
|
||||||
tags: [generateTag(lang, uid)],
|
tags: [generateTag(lang, uid + 1)],
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
),
|
},
|
||||||
])
|
])
|
||||||
if (!mainRefsResponse.data) {
|
if (!res.data) {
|
||||||
const notFoundError = notFound(mainRefsResponse)
|
const notFoundError = notFound(res)
|
||||||
getContentPageRefsFailCounter.add(1, {
|
getContentPageRefsFailCounter.add(1, {
|
||||||
lang,
|
lang,
|
||||||
uid,
|
uid,
|
||||||
@@ -88,22 +88,7 @@ export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
|||||||
)
|
)
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
const responseData = {
|
const validatedData = contentPageRefsSchema.safeParse(res.data)
|
||||||
...mainRefsResponse.data,
|
|
||||||
content_page: {
|
|
||||||
...mainRefsResponse.data.content_page,
|
|
||||||
blocks: blockRefsResponse.data.content_page.blocks,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return responseData
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateContentPageRefs(
|
|
||||||
data: GetContentPageRefsSchema,
|
|
||||||
lang: Lang,
|
|
||||||
uid: string
|
|
||||||
) {
|
|
||||||
const validatedData = contentPageRefsSchema.safeParse(data)
|
|
||||||
if (!validatedData.success) {
|
if (!validatedData.success) {
|
||||||
getContentPageRefsFailCounter.add(1, {
|
getContentPageRefsFailCounter.add(1, {
|
||||||
lang,
|
lang,
|
||||||
@@ -192,6 +177,14 @@ export function getConnections({ content_page }: ContentPageRefs) {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case ContentPageEnum.ContentStack.blocks.CardsGrid: {
|
||||||
|
if (block.cards_grid.length) {
|
||||||
|
block.cards_grid.forEach((card) => {
|
||||||
|
connections.push(card)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user