client/src/types/PayloadAndType.ts (93 lines of code) (raw):

import * as Sentry from "@sentry/react"; export const sources = ["grid", "mam"] as const; export const sourceTypes = ["crop", "original", "search", "video"] as const; export type Source = (typeof sources)[number]; export const isSource = (source: unknown): source is Source => sources.includes(source as Source); export type SourceType = (typeof sourceTypes)[number]; export const isSourceType = (sourceType: unknown): sourceType is SourceType => sourceTypes.includes(sourceType as SourceType); export type PayloadType = `${Source}-${SourceType}`; // TODO improve this type as it enumerates all the combinations, e.g. mam-original which is not valid export const isPayloadType = ( payloadType: string ): payloadType is PayloadType => { const parts = payloadType.split("-"); return parts.length === 2 && isSource(parts[0]) && isSourceType(parts[1]); }; interface PayloadCommon { embeddableUrl: string; } export interface PayloadWithThumbnail extends PayloadCommon { thumbnail: string; aspectRatio?: string; cropType?: string; } export interface PayloadWithExternalUrl extends PayloadWithThumbnail { externalUrl: string; } export interface PayloadWithApiUrl extends PayloadCommon { apiUrl: string; } export type Payload = | PayloadWithThumbnail | PayloadWithApiUrl | PayloadWithExternalUrl; export const isPayload = (maybePayload: unknown): maybePayload is Payload => { return ( typeof maybePayload === "object" && maybePayload !== null && "embeddableUrl" in maybePayload && ("thumbnail" in maybePayload || "apiUrl" in maybePayload) ); }; export type StaticGridPayload = { type: "grid-original" | "grid-crop"; payload: PayloadWithThumbnail; }; export type DynamicGridPayload = { type: "grid-search"; payload: PayloadWithApiUrl; }; export type MamVideoPayload = { type: "mam-video"; payload: PayloadWithExternalUrl; }; export type PayloadAndType = | StaticGridPayload | DynamicGridPayload | MamVideoPayload; export const buildPayloadAndType = ( type: string, payload: unknown ): PayloadAndType | undefined => { if (!(isPayloadType(type) && isPayload(payload))) return; if (type === "grid-search" && "apiUrl" in payload) { return { type, payload }; } else if ( (type === "grid-crop" || type === "grid-original") && "thumbnail" in payload ) { return { type, payload }; } else if ( type === "mam-video" && "thumbnail" in payload && "externalUrl" in payload ) { return { type, payload }; } }; export const maybeConstructPayloadAndType = ( type: string, payload: string | null | undefined ): PayloadAndType | undefined => { if (!isPayloadType(type) || !payload) { return; } const payloadAndType = buildPayloadAndType(type, JSON.parse(payload)); if (!payloadAndType) { Sentry.captureException( new Error(`Failed to parse payload with type=${type}, payload=${payload}`) ); } return payloadAndType; };