Merged in fix/book-453-linkconnection (pull request #2972)

fix(BOOK-453): avoid linkConnection invalid

* fix(BOOK-453): avoid linkConnection invalid

* test

* test

* test

* Merge master
This commit is contained in:
Linus Flood
2025-10-22 12:39:17 +00:00
parent 03f17acbbe
commit acdc3dcec1
17 changed files with 79 additions and 46 deletions

View File

@@ -11,7 +11,7 @@ export function getConnections({ account_page }: AccountPageRefs) {
switch (block.__typename) { switch (block.__typename) {
case AccountPageEnum.ContentStack.blocks.ShortCuts: { case AccountPageEnum.ContentStack.blocks.ShortCuts: {
if (block.shortcuts.shortcuts.length) { if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts) connections.push(...block.shortcuts.shortcuts.filter((c) => !!c))
} }
break break
} }

View File

@@ -19,6 +19,7 @@ import {
import { import {
linkRefsUnionSchema, linkRefsUnionSchema,
linkUnionSchema, linkUnionSchema,
rawLinkUnionSchema,
transformPageLink, transformPageLink,
transformPageLinkRef, transformPageLinkRef,
} from "../schemas/pageLinks" } from "../schemas/pageLinks"
@@ -410,7 +411,7 @@ const linkSchema = z
linkConnection: z.object({ linkConnection: z.object({
edges: z.array( edges: z.array(
z.object({ z.object({
node: discriminatedUnion(linkUnionSchema.options), node: discriminatedUnion(rawLinkUnionSchema.options),
}) })
), ),
}), }),

View File

@@ -1,3 +1,8 @@
import * as Sentry from "@sentry/nextjs"
import { z } from "zod"
import { logger } from "@scandic-hotels/common/logger"
import { getValueFromContactConfig } from "../../../utils/contactConfig" import { getValueFromContactConfig } from "../../../utils/contactConfig"
import type { Edges } from "../../../types/edges" import type { Edges } from "../../../types/edges"
@@ -95,7 +100,9 @@ export function getSiteConfigConnections(refs: GetSiteConfigRefData) {
} }
node.sidepeek_content.content.embedded_itemsConnection.edges.forEach( node.sidepeek_content.content.embedded_itemsConnection.edges.forEach(
({ node }) => { ({ node }) => {
connections.push(node.system) if (node.system) {
connections.push(node.system)
}
} }
) )
}) })
@@ -120,3 +127,14 @@ export function getAlertPhoneContactData(
} }
return null return null
} }
export const safeUnion = <T extends z.ZodTypeAny>(schema: T) =>
z.preprocess((val) => {
try {
return schema.parse(val)
} catch (err) {
Sentry.captureException(err)
logger.warn("Invalid node in safeUnion", err)
return null
}
}, schema)

View File

@@ -49,7 +49,9 @@ export function getConnections({
} }
case CampaignOverviewPageEnum.ContentStack.blocks.AllCampaigns: { case CampaignOverviewPageEnum.ContentStack.blocks.AllCampaigns: {
block.all_campaigns.campaignsConnection.edges.forEach(({ node }) => { block.all_campaigns.campaignsConnection.edges.forEach(({ node }) => {
connections.push(node.system) if (node.system) {
connections.push(node.system)
}
}) })
break break
} }

View File

@@ -57,7 +57,7 @@ export function getConnections({ campaign_page }: CampaignPageRefs) {
} }
case CampaignPageEnum.ContentStack.blocks.Accordion: { case CampaignPageEnum.ContentStack.blocks.Accordion: {
if (block.accordion.length) { if (block.accordion.length) {
connections.push(...block.accordion) connections.push(...block.accordion.filter((c) => !!c))
} }
break break
} }

View File

@@ -97,7 +97,7 @@ export function getConnections({ collection_page }: CollectionPageRefs) {
switch (block.__typename) { switch (block.__typename) {
case CollectionPageEnum.ContentStack.blocks.Shortcuts: { case CollectionPageEnum.ContentStack.blocks.Shortcuts: {
if (block.shortcuts.shortcuts.length) { if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts) connections.push(...block.shortcuts.shortcuts.filter((c) => !!c))
} }
break break
} }
@@ -109,7 +109,7 @@ export function getConnections({ collection_page }: CollectionPageRefs) {
} }
case CollectionPageEnum.ContentStack.blocks.UspGrid: { case CollectionPageEnum.ContentStack.blocks.UspGrid: {
if (block.usp_grid.length) { if (block.usp_grid.length) {
connections.push(...block.usp_grid) connections.push(...block.usp_grid.filter((c) => !!c))
} }
} }
} }

View File

@@ -87,7 +87,7 @@ export function getConnections({ content_page }: ContentPageRefs) {
switch (block.__typename) { switch (block.__typename) {
case ContentPageEnum.ContentStack.blocks.Accordion: { case ContentPageEnum.ContentStack.blocks.Accordion: {
if (block.accordion.length) { if (block.accordion.length) {
connections.push(...block.accordion) connections.push(...block.accordion.filter((c) => !!c))
} }
break break
} }
@@ -112,7 +112,7 @@ export function getConnections({ content_page }: ContentPageRefs) {
} }
case ContentPageEnum.ContentStack.blocks.Shortcuts: { case ContentPageEnum.ContentStack.blocks.Shortcuts: {
if (block.shortcuts.shortcuts.length) { if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts) connections.push(...block.shortcuts.shortcuts.filter((c) => !!c))
} }
break break
} }
@@ -124,7 +124,7 @@ export function getConnections({ content_page }: ContentPageRefs) {
} }
case ContentPageEnum.ContentStack.blocks.UspGrid: { case ContentPageEnum.ContentStack.blocks.UspGrid: {
if (block.usp_grid.length) { if (block.usp_grid.length) {
connections.push(...block.usp_grid) connections.push(...block.usp_grid.filter((c) => !!c))
} }
break break
} }
@@ -145,7 +145,7 @@ export function getConnections({ content_page }: ContentPageRefs) {
switch (block.__typename) { switch (block.__typename) {
case ContentPageEnum.ContentStack.sidebar.Content: case ContentPageEnum.ContentStack.sidebar.Content:
if (block.content.length) { if (block.content.length) {
connections.push(...block.content) connections.push(...block.content.filter((c) => !!c))
} }
break break
case ContentPageEnum.ContentStack.sidebar.JoinLoyaltyContact: case ContentPageEnum.ContentStack.sidebar.JoinLoyaltyContact:
@@ -165,7 +165,7 @@ export function getConnections({ content_page }: ContentPageRefs) {
break break
case ContentPageEnum.ContentStack.sidebar.QuickLinks: case ContentPageEnum.ContentStack.sidebar.QuickLinks:
if (block.shortcuts.shortcuts.length) { if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts) connections.push(...block.shortcuts.shortcuts.filter((c) => !!c))
} }
break break
default: default:

View File

@@ -41,7 +41,7 @@ export function getConnections({
switch (block.__typename) { switch (block.__typename) {
case DestinationCityPageEnum.ContentStack.blocks.Accordion: { case DestinationCityPageEnum.ContentStack.blocks.Accordion: {
if (block.accordion.length) { if (block.accordion.length) {
connections.push(...block.accordion) connections.push(...block.accordion.filter((c) => !!c))
} }
break break
} }
@@ -60,7 +60,9 @@ export function getConnections({
if (destination_city_page.sidepeek_content) { if (destination_city_page.sidepeek_content) {
destination_city_page.sidepeek_content?.content?.embedded_itemsConnection.edges.forEach( destination_city_page.sidepeek_content?.content?.embedded_itemsConnection.edges.forEach(
({ node }) => { ({ node }) => {
connections.push(node.system) if (node.system) {
connections.push(node.system)
}
} }
) )
} }

View File

@@ -39,7 +39,7 @@ export function getConnections({
switch (block.__typename) { switch (block.__typename) {
case DestinationCountryPageEnum.ContentStack.blocks.Accordion: { case DestinationCountryPageEnum.ContentStack.blocks.Accordion: {
if (block.accordion.length) { if (block.accordion.length) {
connections.push(...block.accordion) connections.push(...block.accordion.filter((c) => !!c))
} }
break break
} }
@@ -58,7 +58,9 @@ export function getConnections({
if (destination_country_page.sidepeek_content) { if (destination_country_page.sidepeek_content) {
destination_country_page.sidepeek_content?.content?.embedded_itemsConnection.edges.forEach( destination_country_page.sidepeek_content?.content?.embedded_itemsConnection.edges.forEach(
({ node }) => { ({ node }) => {
connections.push(node.system) if (node.system) {
connections.push(node.system)
}
} }
) )
} }

View File

@@ -28,7 +28,7 @@ export function getConnections({ loyalty_page }: LoyaltyPageRefs) {
break break
case LoyaltyPageEnum.ContentStack.blocks.Shortcuts: case LoyaltyPageEnum.ContentStack.blocks.Shortcuts:
if (block.shortcuts.shortcuts.length) { if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts) connections.push(...block.shortcuts.shortcuts.filter((c) => !!c))
} }
break break
default: default:

View File

@@ -27,7 +27,7 @@ export function getConnections({ promo_campaign_page }: PromoCampaignPageRefs) {
switch (block.__typename) { switch (block.__typename) {
case PromoCampaignPageEnum.ContentStack.blocks.Accordion: { case PromoCampaignPageEnum.ContentStack.blocks.Accordion: {
if (block.accordion.length) { if (block.accordion.length) {
connections.push(...block.accordion) connections.push(...block.accordion.filter((c) => !!c))
} }
break break
} }

View File

@@ -3,8 +3,8 @@ import { z } from "zod"
import { BlocksEnums } from "../../../../types/blocksEnum" import { BlocksEnums } from "../../../../types/blocksEnum"
import { ContentEnum } from "../../../../types/content" import { ContentEnum } from "../../../../types/content"
import { import {
linkRefsUnionSchema, rawLinkRefsUnionSchema,
linkUnionSchema, rawLinkUnionSchema,
transformPageLink, transformPageLink,
} from "../pageLinks" } from "../pageLinks"
import { import {
@@ -30,7 +30,7 @@ export const contentSchema = z.object({
.discriminatedUnion("__typename", [ .discriminatedUnion("__typename", [
imageContainerSchema, imageContainerSchema,
sysAssetSchema, sysAssetSchema,
...linkUnionSchema.options, ...rawLinkUnionSchema.options,
]) ])
.transform((data) => { .transform((data) => {
const link = transformPageLink(data) const link = transformPageLink(data)
@@ -62,7 +62,7 @@ export const contentRefsSchema = z.object({
node: z.discriminatedUnion("__typename", [ node: z.discriminatedUnion("__typename", [
sysAssetRefsSchema, sysAssetRefsSchema,
imageContainerRefsSchema, imageContainerRefsSchema,
...linkRefsUnionSchema.options, ...rawLinkRefsUnionSchema.options,
]), ]),
}) })
), ),

View File

@@ -3,12 +3,14 @@ import { z } from "zod"
import { BlocksEnums } from "../../../../types/blocksEnum" import { BlocksEnums } from "../../../../types/blocksEnum"
import { ContentEnum } from "../../../../types/content" import { ContentEnum } from "../../../../types/content"
import { import {
linkRefsUnionSchema, rawLinkRefsUnionSchema,
linkUnionSchema, rawLinkUnionSchema,
transformPageLink, transformPageLink,
} from "../pageLinks" } from "../pageLinks"
import { sysAssetRefsSchema, sysAssetSchema } from "./sysAsset" import { sysAssetRefsSchema, sysAssetSchema } from "./sysAsset"
import type { linkUnionSchema } from "../pageLinks"
export const textColsSchema = z.object({ export const textColsSchema = z.object({
typename: z typename: z
.literal(BlocksEnums.block.TextCols) .literal(BlocksEnums.block.TextCols)
@@ -26,7 +28,7 @@ export const textColsSchema = z.object({
node: z node: z
.discriminatedUnion("__typename", [ .discriminatedUnion("__typename", [
sysAssetSchema, sysAssetSchema,
...linkUnionSchema.options, ...rawLinkUnionSchema.options,
]) ])
.transform((data) => { .transform((data) => {
const link = transformPageLink(data) const link = transformPageLink(data)
@@ -59,7 +61,7 @@ export const textColsRefsSchema = z.object({
z.object({ z.object({
node: z.discriminatedUnion("__typename", [ node: z.discriminatedUnion("__typename", [
sysAssetRefsSchema, sysAssetRefsSchema,
...linkRefsUnionSchema.options, ...rawLinkRefsUnionSchema.options,
]), ]),
}) })
), ),

View File

@@ -17,5 +17,5 @@ export const linkConnectionRefsSchema = z
return null return null
} }
return data.linkConnection.edges[0].node.system return data.linkConnection.edges[0].node?.system
}) })

View File

@@ -4,6 +4,7 @@ import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator" import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
import { ContentEnum } from "../../../types/content" import { ContentEnum } from "../../../types/content"
import { safeUnion } from "../base/utils"
import { systemSchema } from "./system" import { systemSchema } from "./system"
export const pageLinkSchema = z.object({ export const pageLinkSchema = z.object({
@@ -20,7 +21,7 @@ export const accountPageSchema = z
export const accountPageRefSchema = z.object({ export const accountPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.AccountPage), __typename: z.literal(ContentEnum.blocks.AccountPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const extendedPageLinkSchema = pageLinkSchema.merge( export const extendedPageLinkSchema = pageLinkSchema.merge(
@@ -41,7 +42,7 @@ export const campaignOverviewPageSchema = z
export const campaignOverviewPageRefSchema = z.object({ export const campaignOverviewPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.CampaignOverviewPage), __typename: z.literal(ContentEnum.blocks.CampaignOverviewPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const collectionPageSchema = z export const collectionPageSchema = z
@@ -52,7 +53,7 @@ export const collectionPageSchema = z
export const collectionPageRefSchema = z.object({ export const collectionPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.CollectionPage), __typename: z.literal(ContentEnum.blocks.CollectionPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const contentPageSchema = z export const contentPageSchema = z
@@ -63,7 +64,7 @@ export const contentPageSchema = z
export const contentPageRefSchema = z.object({ export const contentPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.ContentPage), __typename: z.literal(ContentEnum.blocks.ContentPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const destinationCityPageSchema = z export const destinationCityPageSchema = z
@@ -74,7 +75,7 @@ export const destinationCityPageSchema = z
export const destinationCityPageRefSchema = z.object({ export const destinationCityPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.DestinationCityPage), __typename: z.literal(ContentEnum.blocks.DestinationCityPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const campaignPageSchema = z export const campaignPageSchema = z
@@ -85,7 +86,7 @@ export const campaignPageSchema = z
export const campaignPageRefSchema = z.object({ export const campaignPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.CampaignPage), __typename: z.literal(ContentEnum.blocks.CampaignPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const destinationCountryPageSchema = z export const destinationCountryPageSchema = z
@@ -96,7 +97,7 @@ export const destinationCountryPageSchema = z
export const destinationCountryPageRefSchema = z.object({ export const destinationCountryPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.DestinationCountryPage), __typename: z.literal(ContentEnum.blocks.DestinationCountryPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const destinationOverviewPageSchema = z export const destinationOverviewPageSchema = z
@@ -107,7 +108,7 @@ export const destinationOverviewPageSchema = z
export const destinationOverviewPageRefSchema = z.object({ export const destinationOverviewPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.DestinationOverviewPage), __typename: z.literal(ContentEnum.blocks.DestinationOverviewPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const hotelPageSchema = z export const hotelPageSchema = z
@@ -118,7 +119,7 @@ export const hotelPageSchema = z
export const hotelPageRefSchema = z.object({ export const hotelPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.HotelPage), __typename: z.literal(ContentEnum.blocks.HotelPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const loyaltyPageSchema = z export const loyaltyPageSchema = z
@@ -129,7 +130,7 @@ export const loyaltyPageSchema = z
export const loyaltyPageRefSchema = z.object({ export const loyaltyPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.LoyaltyPage), __typename: z.literal(ContentEnum.blocks.LoyaltyPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const startPageSchema = z export const startPageSchema = z
@@ -140,7 +141,7 @@ export const startPageSchema = z
export const startPageRefSchema = z.object({ export const startPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.StartPage), __typename: z.literal(ContentEnum.blocks.StartPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const promoCampaignPageSchema = z export const promoCampaignPageSchema = z
@@ -151,10 +152,10 @@ export const promoCampaignPageSchema = z
export const promoCampaignPageRefSchema = z.object({ export const promoCampaignPageRefSchema = z.object({
__typename: z.literal(ContentEnum.blocks.PromoCampaignPage), __typename: z.literal(ContentEnum.blocks.PromoCampaignPage),
system: systemSchema, system: systemSchema.nullable(),
}) })
export const linkUnionSchema = z.discriminatedUnion("__typename", [ export const rawLinkUnionSchema = z.discriminatedUnion("__typename", [
accountPageSchema, accountPageSchema,
campaignOverviewPageSchema, campaignOverviewPageSchema,
campaignPageSchema, campaignPageSchema,
@@ -169,6 +170,7 @@ export const linkUnionSchema = z.discriminatedUnion("__typename", [
promoCampaignPageSchema, promoCampaignPageSchema,
]) ])
export const linkUnionSchema = safeUnion(rawLinkUnionSchema)
type Data = type Data =
| z.output<typeof accountPageSchema> | z.output<typeof accountPageSchema>
| z.output<typeof campaignOverviewPageSchema> | z.output<typeof campaignOverviewPageSchema>
@@ -262,7 +264,7 @@ export const internalOrExternalLinkSchema = z
} }
) )
export const linkRefsUnionSchema = z.discriminatedUnion("__typename", [ export const rawLinkRefsUnionSchema = z.discriminatedUnion("__typename", [
accountPageRefSchema, accountPageRefSchema,
campaignOverviewPageRefSchema, campaignOverviewPageRefSchema,
campaignPageRefSchema, campaignPageRefSchema,
@@ -277,6 +279,8 @@ export const linkRefsUnionSchema = z.discriminatedUnion("__typename", [
promoCampaignPageRefSchema, promoCampaignPageRefSchema,
]) ])
export const linkRefsUnionSchema = safeUnion(rawLinkRefsUnionSchema)
type RefData = type RefData =
| z.output<typeof accountPageRefSchema> | z.output<typeof accountPageRefSchema>
| z.output<typeof campaignOverviewPageRefSchema> | z.output<typeof campaignOverviewPageRefSchema>

View File

@@ -8,8 +8,8 @@ import {
} from "../blocks/imageContainer" } from "../blocks/imageContainer"
import { sysAssetRefsSchema, sysAssetSchema } from "../blocks/sysAsset" import { sysAssetRefsSchema, sysAssetSchema } from "../blocks/sysAsset"
import { import {
linkRefsUnionSchema, rawLinkRefsUnionSchema,
linkUnionSchema, rawLinkUnionSchema,
transformPageLink, transformPageLink,
} from "../pageLinks" } from "../pageLinks"
@@ -29,7 +29,7 @@ export const contentSchema = z.object({
.discriminatedUnion("__typename", [ .discriminatedUnion("__typename", [
imageContainerSchema, imageContainerSchema,
sysAssetSchema, sysAssetSchema,
...linkUnionSchema.options, ...rawLinkUnionSchema.options,
]) ])
.transform((data) => { .transform((data) => {
const link = transformPageLink(data) const link = transformPageLink(data)
@@ -53,7 +53,7 @@ export const contentSchema = z.object({
const actualRefs = z.discriminatedUnion("__typename", [ const actualRefs = z.discriminatedUnion("__typename", [
imageContainerRefsSchema, imageContainerRefsSchema,
...linkRefsUnionSchema.options, ...rawLinkRefsUnionSchema.options,
]) ])
type Ref = typeof actualRefs._type type Ref = typeof actualRefs._type

View File

@@ -25,7 +25,9 @@ export function getConnections({ start_page }: StartPageRefs) {
case StartPageEnum.ContentStack.blocks.FullWidthCampaign: { case StartPageEnum.ContentStack.blocks.FullWidthCampaign: {
block.full_width_campaign.full_width_campaignConnection.edges.forEach( block.full_width_campaign.full_width_campaignConnection.edges.forEach(
({ node }) => { ({ node }) => {
connections.push(node.system) if (node.system) {
connections.push(node.system)
}
} }
) )
break break