Merged in fix/nullable-boolean (pull request #197)

fix: only zod validate output from trpc

Approved-by: Michael Zetterberg
This commit is contained in:
Christel Westerberg
2024-05-23 12:30:39 +00:00
committed by Michael Zetterberg
3 changed files with 64 additions and 184 deletions

View File

@@ -4,6 +4,7 @@ fragment CardBlock on Card {
background_image background_image
scripted_top_title scripted_top_title
title title
has_secondary_button
secondary_button { secondary_button {
is_contentstack_link is_contentstack_link
cta_text cta_text
@@ -23,6 +24,7 @@ fragment CardBlock on Card {
} }
} }
} }
has_primary_button
primary_button { primary_button {
is_contentstack_link is_contentstack_link
cta_text cta_text

View File

@@ -13,28 +13,6 @@ import { PageLinkEnum } from "@/types/requests/pageLinks"
import { EdgesWithTotalCount } from "@/types/requests/utils/edges" import { EdgesWithTotalCount } from "@/types/requests/utils/edges"
import { RTEDocument } from "@/types/rte/node" import { RTEDocument } from "@/types/rte/node"
const pageLink = z.object({
edges: z.array(
z.object({
node: z.object({
system: z.object({
uid: z.string(),
locale: z.nativeEnum(Lang),
}),
web: z
.object({
original_url: z.string().nullable(),
})
.nullable()
.optional(),
url: z.string(),
title: z.string(),
__typename: z.string().optional(),
}),
})
),
})
const loyaltyPageDynamicContent = z.object({ const loyaltyPageDynamicContent = z.object({
__typename: z.literal( __typename: z.literal(
LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent
@@ -43,10 +21,13 @@ const loyaltyPageDynamicContent = z.object({
title: z.string().nullable(), title: z.string().nullable(),
subtitle: z.string().nullable(), subtitle: z.string().nullable(),
component: z.nativeEnum(LoyaltyComponentEnum), component: z.nativeEnum(LoyaltyComponentEnum),
link: z.object({ link: z
text: z.string().nullable(), .object({
pageConnection: pageLink, text: z.string().nullable(),
}), href: z.string(),
title: z.string(),
})
.optional(),
}), }),
}) })
@@ -57,9 +38,10 @@ const loyaltyPageShortcuts = z.object({
preamble: z.string().nullable(), preamble: z.string().nullable(),
shortcuts: z.array( shortcuts: z.array(
z.object({ z.object({
linkConnection: pageLink, text: z.string().optional(),
text: z.string().nullable(), openInNewTab: z.boolean(),
open_in_new_tab: z.boolean(), url: z.string(),
title: z.string(),
}) })
), ),
}), }),
@@ -70,31 +52,22 @@ const cardBlock = z.object({
body_text: z.string().nullable(), body_text: z.string().nullable(),
background_image: z.any(), background_image: z.any(),
scripted_top_title: z.string().nullable(), scripted_top_title: z.string().nullable(),
primary_button: z primaryButton: z
.object({ .object({
is_contentstack_link: z.boolean(), openInNewTab: z.boolean(),
cta_text: z.string().nullable(), title: z.string(),
open_in_new_tab: z.boolean(), href: z.string(),
external_link: z.object({ isExternal: z.boolean(),
title: z.string().nullable(),
href: z.string().nullable(),
}),
linkConnection: pageLink,
}) })
.nullable(), .optional(),
secondary_button: z secondaryButton: z
.object({ .object({
is_contentstack_link: z.boolean(), openInNewTab: z.boolean(),
cta_text: z.string().nullable(), title: z.string(),
open_in_new_tab: z.boolean().nullable(), href: z.string(),
external_link: z.object({ isExternal: z.boolean(),
title: z.string().nullable(),
href: z.string().nullable(),
}),
linkConnection: pageLink,
}) })
.nullable(), .optional(),
system: z.object({ system: z.object({
locale: z.nativeEnum(Lang), locale: z.nativeEnum(Lang),
uid: z.string(), uid: z.string(),
@@ -108,13 +81,7 @@ const loyaltyPageCards = z.object({
preamble: z.string().nullable(), preamble: z.string().nullable(),
layout: z.enum(["twoColumnGrid", "threeColumnGrid", "twoPlusOne"]), layout: z.enum(["twoColumnGrid", "threeColumnGrid", "twoPlusOne"]),
theme: z.enum(["one", "two", "three"]).nullable(), theme: z.enum(["one", "two", "three"]).nullable(),
cardConnection: z.object({ cards: z.array(cardBlock),
edges: z.array(
z.object({
node: cardBlock,
})
),
}),
}), }),
}) })
@@ -178,31 +145,17 @@ const loyaltyPageSidebarItem = z.discriminatedUnion("__typename", [
]) ])
export const validateLoyaltyPageSchema = z.object({ export const validateLoyaltyPageSchema = z.object({
loyalty_page: z.object({ heading: z.string().nullable(),
title: z.string(), blocks: z.array(loyaltyPageBlockItem).nullable(),
heading: z.string().nullable(), sidebar: z.array(loyaltyPageSidebarItem).nullable(),
blocks: z.array(loyaltyPageBlockItem).nullable(), system: z.object({ uid: z.string() }),
sidebar: z.array(loyaltyPageSidebarItem).nullable(),
system: z.object({ uid: z.string() }),
}),
}) })
// Block types // Block types
type DynamicContentRaw = z.infer<typeof loyaltyPageDynamicContent>
export type DynamicContent = Omit<DynamicContentRaw, "dynamic_content"> & { export type DynamicContent = z.infer<typeof loyaltyPageDynamicContent>
dynamic_content: Omit<DynamicContentRaw["dynamic_content"], "link"> & {
link:
| {
href: string
title: string
text?: string
}
| undefined
}
}
type BlockContentRaw = z.infer<typeof loyaltyPageBlockTextContent> type BlockContentRaw = z.infer<typeof loyaltyPageBlockTextContent>
export interface RteBlockContent extends BlockContentRaw { export interface RteBlockContent extends BlockContentRaw {
content: { content: {
content: { content: {
@@ -214,44 +167,11 @@ export interface RteBlockContent extends BlockContentRaw {
type CardsGridRaw = z.infer<typeof loyaltyPageCards> type CardsGridRaw = z.infer<typeof loyaltyPageCards>
export type CardsRaw = export type CardsRaw = CardsGridRaw["cards_grid"]["cards"][number]
CardsGridRaw["cards_grid"]["cardConnection"]["edges"][number]["node"]
export type CardsGrid = Omit<CardsGridRaw, "cards_grid"> & { export type CardsGrid = z.infer<typeof loyaltyPageCards>
cards_grid: Omit<CardsGridRaw["cards_grid"], "cardConnection"> & {
cards: (Omit<CardsRaw, "primaryButton" | "secondaryButton"> & {
primaryButton:
| {
openInNewTab: boolean
title: string
href: string
isExternal: boolean
}
| undefined
secondaryButton:
| {
openInNewTab: boolean
title: string
href: string
isExternal: boolean
}
| undefined
})[]
}
}
type ShortcutsRaw = z.infer<typeof loyaltyPageShortcuts> export type Shortcuts = z.infer<typeof loyaltyPageShortcuts>
export type Shortcuts = Omit<ShortcutsRaw, "shortcuts"> & {
shortcuts: Omit<ShortcutsRaw["shortcuts"], "shortcuts"> & {
shortcuts: {
text?: string
openInNewTab: boolean
url: string
title: string
}[]
}
}
export type Block = RteBlockContent | DynamicContent | Shortcuts | CardsGrid export type Block = RteBlockContent | DynamicContent | Shortcuts | CardsGrid
@@ -268,12 +188,9 @@ export type RteSidebarContent = Omit<SidebarContentRaw, "content"> & {
} }
export type JoinLoyaltyContact = z.infer<typeof loyaltyPageJoinLoyaltyContact> export type JoinLoyaltyContact = z.infer<typeof loyaltyPageJoinLoyaltyContact>
export type Sidebar = JoinLoyaltyContact | RteSidebarContent export type Sidebar = JoinLoyaltyContact | RteSidebarContent
type LoyaltyPageDataRaw = z.infer<typeof validateLoyaltyPageSchema>
export type LoyaltyPageDataRaw = z.infer<typeof validateLoyaltyPageSchema> export type LoyaltyPage = Omit<LoyaltyPageDataRaw, "blocks" | "sidebar"> & {
type LoyaltyPageRaw = LoyaltyPageDataRaw["loyalty_page"]
export type LoyaltyPage = Omit<LoyaltyPageRaw, "blocks" | "sidebar"> & {
blocks: Block[] blocks: Block[]
sidebar: Sidebar[] sidebar: Sidebar[]
} }

View File

@@ -15,27 +15,16 @@ import {
import { removeEmptyObjects } from "../../utils" import { removeEmptyObjects } from "../../utils"
import { import {
CardsRaw, LoyaltyPage,
type LoyaltyPage,
type LoyaltyPageDataRaw,
type LoyaltyPageRefsDataRaw, type LoyaltyPageRefsDataRaw,
validateLoyaltyPageRefsSchema, validateLoyaltyPageRefsSchema,
validateLoyaltyPageSchema, validateLoyaltyPageSchema,
} from "./output" } from "./output"
import { getConnections } from "./utils" import { getConnections } from "./utils"
import { import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
LoyaltyBlocksTypenameEnum,
SidebarTypenameEnum,
} from "@/types/components/loyalty/enums"
import { Embeds } from "@/types/requests/embeds"
import { Edges } from "@/types/requests/utils/edges"
import { RTEDocument } from "@/types/rte/node"
function makeButtonObject( function makeButtonObject(button: any) {
button: CardsRaw["primary_button" | "secondary_button"]
) {
if (!button) return undefined
return { return {
openInNewTab: button.open_in_new_tab, openInNewTab: button.open_in_new_tab,
title: title:
@@ -93,7 +82,7 @@ export const loyaltyPageQueryRouter = router({
generateTag(lang, validatedLoyaltyPageRefs.data.loyalty_page.system.uid), generateTag(lang, validatedLoyaltyPageRefs.data.loyalty_page.system.uid),
].flat() ].flat()
const response = await request<LoyaltyPageDataRaw>( const response = await request<any>(
GetLoyaltyPage, GetLoyaltyPage,
{ {
locale: lang, locale: lang,
@@ -106,36 +95,8 @@ export const loyaltyPageQueryRouter = router({
throw notFound(response) throw notFound(response)
} }
const validatedLoyaltyPage = validateLoyaltyPageSchema.safeParse( const blocks = response.data.loyalty_page.blocks
response.data ? response.data.loyalty_page.blocks.map((block: any) => {
)
if (!validatedLoyaltyPage.success) {
throw internalServerError(validatedLoyaltyPage.error)
}
const sidebar = validatedLoyaltyPage.data.loyalty_page.sidebar
? validatedLoyaltyPage.data.loyalty_page.sidebar.map((block) => {
if (
block.__typename == SidebarTypenameEnum.LoyaltyPageSidebarContent
) {
return {
...block,
content: {
content: {
json: block.content.content.json as RTEDocument,
embedded_itemsConnection: block.content.content
.embedded_itemsConnection as Edges<Embeds>,
},
},
}
} else {
return block
}
})
: null
const blocks = validatedLoyaltyPage.data.loyalty_page.blocks
? validatedLoyaltyPage.data.loyalty_page.blocks.map((block) => {
switch (block.__typename) { switch (block.__typename) {
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent:
return { return {
@@ -153,23 +114,12 @@ export const loyaltyPageQueryRouter = router({
: undefined, : undefined,
}, },
} }
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
return {
...block,
content: {
content: {
json: block.content.content.json as RTEDocument,
embedded_itemsConnection: block.content.content
.embedded_itemsConnection as Edges<Embeds>,
},
},
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
return { return {
...block, ...block,
shortcuts: { shortcuts: {
...block.shortcuts, ...block.shortcuts,
shortcuts: block.shortcuts.shortcuts.map((shortcut) => ({ shortcuts: block.shortcuts.shortcuts.map((shortcut: any) => ({
text: shortcut.text, text: shortcut.text,
openInNewTab: shortcut.open_in_new_tab, openInNewTab: shortcut.open_in_new_tab,
...shortcut.linkConnection.edges[0].node, ...shortcut.linkConnection.edges[0].node,
@@ -185,13 +135,15 @@ export const loyaltyPageQueryRouter = router({
cards_grid: { cards_grid: {
...block.cards_grid, ...block.cards_grid,
cards: block.cards_grid.cardConnection.edges.map( cards: block.cards_grid.cardConnection.edges.map(
({ node: card }) => { ({ node: card }: { node: any }) => {
return { return {
...card, ...card,
primaryButton: makeButtonObject(card.primary_button), primaryButton: card.has_primary_button
secondaryButton: makeButtonObject( ? makeButtonObject(card.primary_button)
card.secondary_button : undefined,
), secondaryButton: card.has_secondary_button
? makeButtonObject(card.secondary_button)
: undefined,
} }
} }
), ),
@@ -204,11 +156,20 @@ export const loyaltyPageQueryRouter = router({
: null : null
const loyaltyPage = { const loyaltyPage = {
...validatedLoyaltyPage.data.loyalty_page, heading: response.data.loyalty_page.heading,
system: response.data.loyalty_page.system,
blocks, blocks,
sidebar, sidebar: response.data.loyalty_page.sidebar,
} as LoyaltyPage }
return loyaltyPage const validatedLoyaltyPage =
validateLoyaltyPageSchema.safeParse(loyaltyPage)
if (!validatedLoyaltyPage.success) {
throw internalServerError(validatedLoyaltyPage.error)
}
// Assert LoyaltyPage type to get correct typings for RTE fields
return validatedLoyaltyPage.data as LoyaltyPage
}), }),
}) })