From 4fa6e6447eec4fa12a8dd4f317320fb998f5c654 Mon Sep 17 00:00:00 2001 From: Nils Kolvenbach Date: Thu, 13 Jun 2024 22:47:29 +0200 Subject: [PATCH] Made references to Assets and Entries translateble by using a record of language to array of objects --- src/baseSchema.ts | 4 + src/entrySchema.ts | 15 +-- src/fileSchema.ts | 5 - src/valueSchema.ts | 72 +++++-------- test/valueSchema.test.ts | 227 ++++++++++++++++----------------------- 5 files changed, 130 insertions(+), 193 deletions(-) diff --git a/src/baseSchema.ts b/src/baseSchema.ts index a1a7c9d..86f652a 100644 --- a/src/baseSchema.ts +++ b/src/baseSchema.ts @@ -145,3 +145,7 @@ export const translatableBooleanSchema = z.record( z.boolean({ required_error: 'shared.translatableBooleanRequired' }) ); export type TranslatableBoolean = z.infer; + +export function translatableArrayOf(schema: T) { + return z.record(supportedLanguageSchema, z.array(schema)); +} diff --git a/src/entrySchema.ts b/src/entrySchema.ts index 2c9ac80..ea5a74b 100644 --- a/src/entrySchema.ts +++ b/src/entrySchema.ts @@ -1,11 +1,7 @@ import z from 'zod'; import { objectTypeSchema, uuidSchema } from './baseSchema.js'; import { baseFileSchema } from './fileSchema.js'; -import { - resolvedValueSchema, - valueSchema, - type ResolvedValue, -} from './valueSchema.js'; +import { resolvedValueSchema, valueSchema } from './valueSchema.js'; export const entryFileSchema = baseFileSchema.extend({ objectType: z.literal(objectTypeSchema.Enum.entry).readonly(), @@ -13,13 +9,10 @@ export const entryFileSchema = baseFileSchema.extend({ }); export type EntryFile = z.infer; -// @see https://github.com/colinhacks/zod?tab=readme-ov-file#recursive-types -export type Entry = z.infer & { - values: ResolvedValue[]; -}; export const entrySchema = entryFileSchema.extend({ - values: z.lazy(() => resolvedValueSchema.array()), -}) satisfies z.ZodType; + values: z.array(resolvedValueSchema), +}); +export type Entry = z.infer; export const entryExportSchema = entrySchema.extend({}); export type EntryExport = z.infer; diff --git a/src/fileSchema.ts b/src/fileSchema.ts index 76ac117..a8279c2 100644 --- a/src/fileSchema.ts +++ b/src/fileSchema.ts @@ -1,6 +1,5 @@ import z from 'zod'; import { - objectTypeSchema, supportedAssetExtensionSchema, supportedLanguageSchema, uuidSchema, @@ -16,10 +15,6 @@ export const baseFileSchema = z.object({ * The ID is part of the files name. */ id: uuidSchema.readonly(), - /** - * The type of the file is used to identify the content structure of it - */ - objectType: objectTypeSchema.readonly(), /** * The timestamp of the file being created is set by the service of "objectType" while creating it */ diff --git a/src/valueSchema.ts b/src/valueSchema.ts index bb278c0..22ca1a9 100644 --- a/src/valueSchema.ts +++ b/src/valueSchema.ts @@ -4,6 +4,7 @@ import { objectTypeSchema, supportedAssetMimeTypeSchema, supportedLanguageSchema, + translatableArrayOf, translatableBooleanSchema, translatableNumberSchema, translatableStringSchema, @@ -262,35 +263,33 @@ export const valueDefinitionSchema = z.union([ ]); export type ValueDefinition = z.infer; -export const valueContentReferenceToAssetSchema = z.object({ - referenceObjectType: z.literal(objectTypeSchema.Enum.asset), - references: z.array( - z.object({ - id: uuidSchema, - language: supportedLanguageSchema, - }) - ), +export const valueContentReferenceBase = z.object({ + id: uuidSchema, }); + +export const valueContentReferenceWithLanguageBase = + valueContentReferenceBase.extend({ + language: supportedLanguageSchema, + }); + +export const valueContentReferenceToAssetSchema = + valueContentReferenceWithLanguageBase.extend({ + objectType: z.literal(objectTypeSchema.Enum.asset), + }); export type ValueContentReferenceToAsset = z.infer< typeof valueContentReferenceToAssetSchema >; export const resolvedValueContentReferenceToAssetSchema = - valueContentReferenceToAssetSchema.extend({ - references: z.array(assetSchema), - }); + valueContentReferenceToAssetSchema.merge(assetSchema); export type ResolvedValueContentReferenceToAsset = z.infer< typeof resolvedValueContentReferenceToAssetSchema >; -export const valueContentReferenceToEntrySchema = z.object({ - referenceObjectType: z.literal(objectTypeSchema.Enum.entry), - references: z.array( - z.object({ - id: uuidSchema, - }) - ), -}); +export const valueContentReferenceToEntrySchema = + valueContentReferenceBase.extend({ + objectType: z.literal(objectTypeSchema.Enum.entry), + }); export type ValueContentReferenceToEntry = z.infer< typeof valueContentReferenceToEntrySchema >; @@ -298,13 +297,10 @@ export type ValueContentReferenceToEntry = z.infer< // @see https://github.com/colinhacks/zod?tab=readme-ov-file#recursive-types export type ResolvedValueContentReferenceToEntry = z.infer< typeof valueContentReferenceToEntrySchema -> & { - references: Entry[]; -}; +> & + z.ZodType; export const resolvedValueContentReferenceToEntrySchema: z.ZodType = - valueContentReferenceToEntrySchema.extend({ - references: z.array(z.lazy(() => entrySchema)), - }); + valueContentReferenceToEntrySchema.merge(z.lazy(() => entrySchema)); // export const valueContentReferenceToSharedValueSchema = z.object({ // referenceObjectType: z.literal(objectTypeSchema.Enum.sharedValue), @@ -402,7 +398,7 @@ export const referencedValueSchema = z.object({ objectType: z.literal(objectTypeSchema.Enum.value).readonly(), definitionId: uuidSchema.readonly(), valueType: z.literal(ValueTypeSchema.Enum.reference).readonly(), - content: valueContentReferenceSchema, + content: translatableArrayOf(valueContentReferenceSchema), }); export type ReferencedValue = z.infer; @@ -410,7 +406,7 @@ export const valueSchema = z.union([directValueSchema, referencedValueSchema]); export type Value = z.infer; export const resolvedReferencedValueSchema = referencedValueSchema.extend({ - content: resolvedValueContentReferenceSchema, + content: translatableArrayOf(resolvedValueContentReferenceSchema), }); export type ResolvedReferencedValue = z.infer< typeof resolvedReferencedValueSchema @@ -524,12 +520,12 @@ function getReferenceValueContentSchema( switch (definition.inputType) { case ValueInputTypeSchema.Enum.asset: { - schema = valueContentReferenceToAssetSchema.extend({}); // Deep copy to not overwrite the base schema + schema = z.array(valueContentReferenceToAssetSchema); } break; case ValueInputTypeSchema.Enum.entry: { - schema = valueContentReferenceToEntrySchema.extend({}); // Deep copy to not overwrite the base schema + schema = z.array(valueContentReferenceToEntrySchema); } break; // case ValueInputTypeSchema.Enum.sharedValue: { @@ -550,27 +546,15 @@ function getReferenceValueContentSchema( } if (definition.isRequired) { - const requiredReferences = schema.shape.references.min( - 1, - 'shared.referenceRequired' - ); - schema = schema.extend({ - references: requiredReferences, - }); + schema = schema.min(1, 'shared.referenceRequired'); } if (definition.min) { - const minReferences = schema.shape.references.min(definition.min); - schema = schema.extend({ - references: minReferences, - }); + schema = schema.min(definition.min); } if (definition.max) { - const maxReferences = schema.shape.references.max(definition.max); - schema = schema.extend({ - references: maxReferences, - }); + schema = schema.max(definition.max); } return schema; diff --git a/test/valueSchema.test.ts b/test/valueSchema.test.ts index 56d5ebe..c2c9506 100644 --- a/test/valueSchema.test.ts +++ b/test/valueSchema.test.ts @@ -380,39 +380,28 @@ describe('Dynamic zod schema', () => { }); expect( - requiredAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [ - { - id: uuid(), - language: 'en', - }, - ], - }).success + requiredAssetValueschema.safeParse([ + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + ]).success ).toBe(true); + expect(requiredAssetValueschema.safeParse([]).success).toBe(false); expect( - requiredAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [], - }).success - ).toBe(false); - expect( - requiredAssetValueschema.safeParse({ - referenceObjectType: 'entry', - references: [ - { - id: uuid(), - language: 'en', - }, - ], - }).success + requiredAssetValueschema.safeParse([ + { + objectType: 'entry', + id: uuid(), + }, + ]).success ).toBe(false); expect(requiredAssetValueschema.safeParse('').success).toBe(false); expect(requiredAssetValueschema.safeParse(undefined).success).toBe(false); expect(requiredAssetValueschema.safeParse(null).success).toBe(false); expect(requiredAssetValueschema.safeParse(0).success).toBe(false); - expect(requiredAssetValueschema.safeParse([]).success).toBe(false); expect(requiredAssetValueschema.safeParse({}).success).toBe(false); }); @@ -433,28 +422,20 @@ describe('Dynamic zod schema', () => { }); expect( - optionalAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [ - { - id: uuid(), - language: 'en', - }, - ], - }).success - ).toBe(true); - expect( - optionalAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [], - }).success + optionalAssetValueschema.safeParse([ + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + ]).success ).toBe(true); + expect(optionalAssetValueschema.safeParse([]).success).toBe(true); expect(optionalAssetValueschema.safeParse('').success).toBe(false); expect(optionalAssetValueschema.safeParse(undefined).success).toBe(false); expect(optionalAssetValueschema.safeParse(null).success).toBe(false); expect(optionalAssetValueschema.safeParse(0).success).toBe(false); - expect(optionalAssetValueschema.safeParse([]).success).toBe(false); expect(optionalAssetValueschema.safeParse({}).success).toBe(false); }); @@ -477,85 +458,77 @@ describe('Dynamic zod schema', () => { }); expect( - requiredAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [ - { - id: uuid(), - language: 'en', - }, - { - id: uuid(), - language: 'en', - }, - ], - }).success + requiredAssetValueschema.safeParse([ + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + ]).success ).toBe(true); expect( - requiredAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [ - { - id: uuid(), - language: 'en', - }, - { - id: uuid(), - language: 'en', - }, - { - id: uuid(), - language: 'en', - }, - ], - }).success + requiredAssetValueschema.safeParse([ + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + ]).success ).toBe(true); + expect(requiredAssetValueschema.safeParse([]).success).toBe(false); expect( - requiredAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [], - }).success - ).toBe(false); - expect( - requiredAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [ - { - id: uuid(), - language: 'en', - }, - ], - }).success + requiredAssetValueschema.safeParse([ + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + ]).success ).toBe(false); expect( - requiredAssetValueschema.safeParse({ - referenceObjectType: 'asset', - references: [ - { - id: uuid(), - language: 'en', - }, - { - id: uuid(), - language: 'en', - }, - { - id: uuid(), - language: 'en', - }, - { - id: uuid(), - language: 'en', - }, - ], - }).success + requiredAssetValueschema.safeParse([ + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + { + objectType: 'asset', + id: uuid(), + language: 'en', + }, + ]).success ).toBe(false); expect(requiredAssetValueschema.safeParse('').success).toBe(false); expect(requiredAssetValueschema.safeParse(undefined).success).toBe(false); expect(requiredAssetValueschema.safeParse(null).success).toBe(false); expect(requiredAssetValueschema.safeParse(0).success).toBe(false); - expect(requiredAssetValueschema.safeParse([]).success).toBe(false); expect(requiredAssetValueschema.safeParse({}).success).toBe(false); }); @@ -577,39 +550,27 @@ describe('Dynamic zod schema', () => { }); expect( - requiredEntryValueschema.safeParse({ - referenceObjectType: 'entry', - references: [ - { - id: uuid(), - language: 'en', - }, - ], - }).success + requiredEntryValueschema.safeParse([ + { + objectType: 'entry', + id: uuid(), + }, + ]).success ).toBe(true); + expect(requiredEntryValueschema.safeParse([]).success).toBe(false); expect( - requiredEntryValueschema.safeParse({ - referenceObjectType: 'entry', - references: [], - }).success - ).toBe(false); - expect( - requiredEntryValueschema.safeParse({ - referenceObjectType: 'asset', - references: [ - { - id: uuid(), - language: 'en', - }, - ], - }).success + requiredEntryValueschema.safeParse([ + { + objectType: 'asset', + id: uuid(), + }, + ]).success ).toBe(false); expect(requiredEntryValueschema.safeParse('').success).toBe(false); expect(requiredEntryValueschema.safeParse(undefined).success).toBe(false); expect(requiredEntryValueschema.safeParse(null).success).toBe(false); expect(requiredEntryValueschema.safeParse(0).success).toBe(false); - expect(requiredEntryValueschema.safeParse([]).success).toBe(false); expect(requiredEntryValueschema.safeParse({}).success).toBe(false); }); });