feat: add revlidation for loyalty page
This commit is contained in:
@@ -3,6 +3,11 @@
|
||||
#import "../Fragments/PageLink/ContentPageLink.graphql"
|
||||
#import "../Fragments/PageLink/LoyaltyPageLink.graphql"
|
||||
|
||||
#import "../Fragments/Refs/AccountPage.graphql"
|
||||
#import "../Fragments/Refs/ContentPage.graphql"
|
||||
#import "../Fragments/Refs/LoyaltyPage.graphql"
|
||||
#import "../Fragments/Refs/System.graphql"
|
||||
|
||||
query GetLoyaltyPage($locale: String!, $url: String!) {
|
||||
all_loyalty_page(where: { url: $url }, locale: $locale) {
|
||||
items {
|
||||
@@ -145,3 +150,119 @@ query GetLoyaltyPage($locale: String!, $url: String!) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query GetLoyaltyPageRefs($locale: String!, $url: String!) {
|
||||
all_loyalty_page(where: { url: $url }, locale: $locale) {
|
||||
items {
|
||||
blocks {
|
||||
... on LoyaltyPageBlocksShortcuts {
|
||||
__typename
|
||||
shortcuts {
|
||||
shortcuts {
|
||||
linkConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...AccountPageRef
|
||||
...ContentPageRef
|
||||
...LoyaltyPageRef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on LoyaltyPageBlocksDynamicContent {
|
||||
__typename
|
||||
dynamic_content {
|
||||
link {
|
||||
pageConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...ContentPageRef
|
||||
...LoyaltyPageRef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on LoyaltyPageBlocksCardGrid {
|
||||
__typename
|
||||
card_grid {
|
||||
cards {
|
||||
referenceConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...AccountPageRef
|
||||
...ContentPageRef
|
||||
...LoyaltyPageRef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on LoyaltyPageBlocksContent {
|
||||
__typename
|
||||
content {
|
||||
content {
|
||||
embedded_itemsConnection {
|
||||
edges {
|
||||
node {
|
||||
# No fragments used since we want to include __typename for each type to avoid fetching SystemAsset
|
||||
... on ContentPage {
|
||||
__typename
|
||||
system {
|
||||
...System
|
||||
}
|
||||
}
|
||||
... on LoyaltyPage {
|
||||
__typename
|
||||
system {
|
||||
...System
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sidebar {
|
||||
... on LoyaltyPageSidebarContent {
|
||||
__typename
|
||||
content {
|
||||
content {
|
||||
embedded_itemsConnection {
|
||||
edges {
|
||||
node {
|
||||
# No fragments used since we want to include __typename for each type to avoid fetching SystemAsset
|
||||
... on ContentPage {
|
||||
__typename
|
||||
system {
|
||||
...System
|
||||
}
|
||||
}
|
||||
... on LoyaltyPage {
|
||||
__typename
|
||||
system {
|
||||
...System
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
system {
|
||||
...System
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SidebarTypenameEnum,
|
||||
} from "@/types/components/loyalty/enums"
|
||||
import { Embeds } from "@/types/requests/embeds"
|
||||
import { PageLinkEnum } from "@/types/requests/pageLinks"
|
||||
import { EdgesWithTotalCount } from "@/types/requests/utils/edges"
|
||||
import { RTEDocument } from "@/types/rte/node"
|
||||
|
||||
@@ -262,3 +263,99 @@ export type LoyaltyPage = Omit<LoyaltyPageRaw, "blocks" | "sidebar"> & {
|
||||
blocks: Block[]
|
||||
sidebar: Sidebar[]
|
||||
}
|
||||
|
||||
// Refs types
|
||||
const pageConnectionRefs = z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.object({
|
||||
__typename: z.nativeEnum(PageLinkEnum),
|
||||
system: z.object({
|
||||
content_type_uid: z.string(),
|
||||
uid: z.string(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
const loyaltyPageBlockCardGridRefs = z.object({
|
||||
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid),
|
||||
card_grid: z.object({
|
||||
cards: z.array(
|
||||
z.object({
|
||||
referenceConnection: pageConnectionRefs,
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyPageDynamicContentRefs = z.object({
|
||||
__typename: z.literal(
|
||||
LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent
|
||||
),
|
||||
dynamic_content: z.object({
|
||||
link: z.object({
|
||||
pageConnection: pageConnectionRefs,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyPageShortcutsRefs = z.object({
|
||||
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts),
|
||||
shortcuts: z.object({
|
||||
shortcuts: z.array(
|
||||
z.object({
|
||||
linkConnection: pageConnectionRefs,
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyPageBlockTextContentRefs = z.object({
|
||||
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent),
|
||||
content: z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: pageConnectionRefs,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyPageBlocRefsItem = z.discriminatedUnion("__typename", [
|
||||
loyaltyPageBlockCardGridRefs,
|
||||
loyaltyPageDynamicContentRefs,
|
||||
loyaltyPageBlockTextContentRefs,
|
||||
loyaltyPageShortcutsRefs,
|
||||
])
|
||||
|
||||
const loyaltyPageSidebarTextContentRef = z.object({
|
||||
__typename: z.literal(SidebarTypenameEnum.LoyaltyPageSidebarContent),
|
||||
content: z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: pageConnectionRefs,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyPageSidebarRefsItem = z.discriminatedUnion("__typename", [
|
||||
loyaltyPageSidebarTextContentRef,
|
||||
])
|
||||
|
||||
export const validateLoyaltyPageRefsSchema = z.object({
|
||||
all_loyalty_page: z.object({
|
||||
items: z.array(
|
||||
z.object({
|
||||
blocks: z.array(loyaltyPageBlocRefsItem).nullable(),
|
||||
sidebar: z.array(loyaltyPageSidebarRefsItem).nullable(),
|
||||
system: z.object({
|
||||
content_type_uid: z.string(),
|
||||
uid: z.string(),
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
export type LoyaltyPageRefsDataRaw = z.infer<
|
||||
typeof validateLoyaltyPageRefsSchema
|
||||
>
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
import GetLoyaltyPage from "@/lib/graphql/Query/LoyaltyPage.graphql"
|
||||
import {
|
||||
GetLoyaltyPage,
|
||||
GetLoyaltyPageRefs,
|
||||
} from "@/lib/graphql/Query/LoyaltyPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { _ } from "@/lib/translation"
|
||||
import { badRequestError } from "@/server/errors/trpc"
|
||||
import { badRequestError, internalServerError } from "@/server/errors/trpc"
|
||||
import { publicProcedure, router } from "@/server/trpc"
|
||||
|
||||
import { removeEmptyObjects } from "@/utils/contentType"
|
||||
import {
|
||||
generateRefsResponseTag,
|
||||
generateTag,
|
||||
generateTags,
|
||||
} from "@/utils/generateTag"
|
||||
|
||||
import { getLoyaltyPageInput } from "./input"
|
||||
import {
|
||||
type LoyaltyPage,
|
||||
type LoyaltyPageDataRaw,
|
||||
type LoyaltyPageRefsDataRaw,
|
||||
validateLoyaltyPageRefsSchema,
|
||||
validateLoyaltyPageSchema,
|
||||
} from "./output"
|
||||
import { getConnections } from "./utils"
|
||||
|
||||
import {
|
||||
LoyaltyBlocksTypenameEnum,
|
||||
@@ -22,10 +35,56 @@ import { RTEDocument } from "@/types/rte/node"
|
||||
export const loyaltyPageQueryRouter = router({
|
||||
get: publicProcedure.input(getLoyaltyPageInput).query(async ({ input }) => {
|
||||
try {
|
||||
const loyaltyPageRes = await request<LoyaltyPageDataRaw>(GetLoyaltyPage, {
|
||||
locale: input.locale,
|
||||
url: input.href,
|
||||
})
|
||||
const { locale } = input
|
||||
|
||||
const refsResponse = await request<LoyaltyPageRefsDataRaw>(
|
||||
GetLoyaltyPageRefs,
|
||||
{
|
||||
locale,
|
||||
url: input.href,
|
||||
},
|
||||
{
|
||||
next: {
|
||||
tags: [generateRefsResponseTag(locale, "loyalty_page")],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!refsResponse.data) {
|
||||
console.error("Bad response for `GetNavigationMyPagesRefs`")
|
||||
console.error({ refsResponse })
|
||||
throw internalServerError()
|
||||
}
|
||||
|
||||
const cleanedData = removeEmptyObjects(refsResponse.data)
|
||||
|
||||
const validatedLoyaltyPageRefs =
|
||||
validateLoyaltyPageRefsSchema.safeParse(cleanedData)
|
||||
if (!validatedLoyaltyPageRefs.success) {
|
||||
console.error("Bad validation for `GetLoyaltyPageRefs`")
|
||||
console.error(validatedLoyaltyPageRefs.error)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
const connections = getConnections(validatedLoyaltyPageRefs.data)
|
||||
|
||||
const tags = generateTags(locale, connections)
|
||||
|
||||
tags.push(
|
||||
generateTag(
|
||||
locale,
|
||||
validatedLoyaltyPageRefs.data.all_loyalty_page.items[0].system.uid
|
||||
)
|
||||
)
|
||||
|
||||
const loyaltyPageRes = await request<LoyaltyPageDataRaw>(
|
||||
GetLoyaltyPage,
|
||||
{
|
||||
locale,
|
||||
url: input.href,
|
||||
},
|
||||
{ next: { tags } }
|
||||
)
|
||||
|
||||
if (!loyaltyPageRes.data) {
|
||||
throw badRequestError()
|
||||
|
||||
54
server/routers/contentstack/loyaltyPage/utils.ts
Normal file
54
server/routers/contentstack/loyaltyPage/utils.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { LoyaltyPageRefsDataRaw } from "./output"
|
||||
|
||||
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
|
||||
import type { Edges } from "@/types/requests/utils/edges"
|
||||
import type { NodeRefs } from "@/types/requests/utils/refs"
|
||||
|
||||
export function getConnections(refs: LoyaltyPageRefsDataRaw) {
|
||||
const connections: Edges<NodeRefs>[] = []
|
||||
refs.all_loyalty_page.items.forEach((ref) => {
|
||||
if (ref.blocks) {
|
||||
ref.blocks.forEach((item) => {
|
||||
switch (item.__typename) {
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: {
|
||||
if (item.content.content.embedded_itemsConnection.edges.length) {
|
||||
connections.push(item.content.content.embedded_itemsConnection)
|
||||
}
|
||||
break
|
||||
}
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: {
|
||||
item.card_grid.cards.forEach((card) => {
|
||||
if (card.referenceConnection.edges.length) {
|
||||
connections.push(card.referenceConnection)
|
||||
}
|
||||
})
|
||||
break
|
||||
}
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: {
|
||||
item.shortcuts.shortcuts.forEach((shortcut) => {
|
||||
if (shortcut.linkConnection.edges.length) {
|
||||
connections.push(shortcut.linkConnection)
|
||||
}
|
||||
})
|
||||
break
|
||||
}
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: {
|
||||
if (item.dynamic_content.link.pageConnection.edges.length) {
|
||||
connections.push(item.dynamic_content.link.pageConnection)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (ref.sidebar) {
|
||||
ref.sidebar?.forEach((item) => {
|
||||
if (item.content.content.embedded_itemsConnection.edges.length) {
|
||||
connections.push(item.content.content.embedded_itemsConnection)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return connections
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { z } from "zod"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
|
||||
import { PageLinkEnum } from "@/types/requests/myPages/navigation"
|
||||
import { PageLinkEnum } from "@/types/requests/pageLinks"
|
||||
|
||||
const pageConnection = z.object({
|
||||
edges: z.array(
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { PageLinkEnum } from "../pageLinks"
|
||||
|
||||
import type { Lang } from "@/constants/languages"
|
||||
import type { System } from "../system"
|
||||
import type { AllRequestResponse } from "../utils/all"
|
||||
import type { Edges } from "../utils/edges"
|
||||
import type { TypenameInterface } from "../utils/typename"
|
||||
|
||||
export enum PageLinkEnum {
|
||||
AccountPage = "AccountPage",
|
||||
ContentPage = "ContentPage",
|
||||
LoyaltyPage = "LoyaltyPage",
|
||||
}
|
||||
|
||||
export type MenuItem = {
|
||||
lang: Lang
|
||||
linkText: string
|
||||
|
||||
5
types/requests/pageLinks.ts
Normal file
5
types/requests/pageLinks.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum PageLinkEnum {
|
||||
AccountPage = "AccountPage",
|
||||
ContentPage = "ContentPage",
|
||||
LoyaltyPage = "LoyaltyPage",
|
||||
}
|
||||
@@ -53,3 +53,26 @@ export async function getContentTypeByPathName(
|
||||
return PageTypeEnum.CurrentBlocksPage
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Function to remove empty objects from a fetched content type.
|
||||
* Used since Contentstack returns empty objects for all non
|
||||
* queried in modular blocks.
|
||||
*/
|
||||
export function removeEmptyObjects<T>(obj: T): T {
|
||||
const copy = obj as any
|
||||
|
||||
for (let key in copy) {
|
||||
if (typeof copy[key] === "object" && copy[key] !== null) {
|
||||
copy[key] = removeEmptyObjects(copy[key])
|
||||
if (Object.keys(copy[key]).length === 0 && !Array.isArray(copy[key])) {
|
||||
delete copy[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(copy)) {
|
||||
return copy.filter((item) => item != null).map(removeEmptyObjects) as T
|
||||
}
|
||||
|
||||
return copy as T
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user