From d672e948baa27868f244425bd0165bf697529a0b Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:34:32 +0100 Subject: [PATCH] [open-formulieren/open-forms#3597] Switch to a zod validation for jsonLogic --- .../builder/values/items-expression.tsx | 16 +++------------- src/registry/radio/edit-validation.ts | 11 ++++++----- src/registry/selectboxes/edit-validation.ts | 10 ++++++---- src/registry/validation.ts | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/components/builder/values/items-expression.tsx b/src/components/builder/values/items-expression.tsx index 53a16e3c..6d456827 100644 --- a/src/components/builder/values/items-expression.tsx +++ b/src/components/builder/values/items-expression.tsx @@ -1,11 +1,9 @@ -import {type JSONObject, type JSONValue} from '@open-formulieren/types/lib/types'; -import {Field, useFormikContext} from 'formik'; -import {useContext} from 'react'; +import type {JSONObject} from '@open-formulieren/types/lib/types'; +import {useFormikContext} from 'formik'; import {FormattedMessage} from 'react-intl'; import JSONEdit from '@/components/JSONEdit'; import {Component, Description} from '@/components/formio'; -import {BuilderContext} from '@/context'; const NAME = 'openForms.itemsExpression'; @@ -19,8 +17,6 @@ export const ItemsExpression: React.FC = () => { const {getFieldProps} = useFormikContext(); const {value = ''} = getFieldProps(NAME); - const {validateLogic} = useContext(BuilderContext); - const htmlId = `editform-${NAME}`; return ( { } >
- validateLogic(logic, [['', '']])} - > - {() => } - +
+const buildValuesSchema = (intl: IntlShape, builderContext: BuilderContextType) => z.object({ values: optionSchema(intl).array().min(1).optional(), openForms: z.object({ dataSrc: z.union([z.literal('manual'), z.literal('variable')]), - // TODO: wire up infernologic type checking - itemsExpression: jsonSchema.optional(), + itemsExpression: itemsExpressionSchema(builderContext).optional(), }), }); -const schema = (intl: IntlShape) => buildCommonSchema(intl).and(buildValuesSchema(intl)); +const schema = (intl: IntlShape, builderContext: BuilderContextType) => + buildCommonSchema(intl).and(buildValuesSchema(intl, builderContext)); export default schema; diff --git a/src/registry/selectboxes/edit-validation.ts b/src/registry/selectboxes/edit-validation.ts index 79355c37..78ffcc2b 100644 --- a/src/registry/selectboxes/edit-validation.ts +++ b/src/registry/selectboxes/edit-validation.ts @@ -1,23 +1,25 @@ import {IntlShape} from 'react-intl'; import {z} from 'zod'; -import {buildCommonSchema, jsonSchema, optionSchema} from '@/registry/validation'; +import {BuilderContextType} from '@/context'; +import {buildCommonSchema, itemsExpressionSchema, optionSchema} from '@/registry/validation'; // z.object(...).or(z.object(...)) based on openForms.dataSrc doesn't seem to work, // looks like the union validation only works if the discriminator is in the top level // object :( // so we mark each aspect as optional so that *when* it is provided, we can run the // validation -const buildValuesSchema = (intl: IntlShape) => +const buildValuesSchema = (intl: IntlShape, builderContext: BuilderContextType) => z.object({ values: optionSchema(intl).array().min(1).optional(), openForms: z.object({ dataSrc: z.union([z.literal('manual'), z.literal('variable')]), // TODO: wire up infernologic type checking - itemsExpression: jsonSchema.optional(), + itemsExpression: itemsExpressionSchema(builderContext).optional(), }), }); -const schema = (intl: IntlShape) => buildCommonSchema(intl).and(buildValuesSchema(intl)); +const schema = (intl: IntlShape, builderContext: BuilderContextType) => + buildCommonSchema(intl).and(buildValuesSchema(intl, builderContext)); export default schema; diff --git a/src/registry/validation.ts b/src/registry/validation.ts index 4cd5c5b3..bc12d494 100644 --- a/src/registry/validation.ts +++ b/src/registry/validation.ts @@ -4,9 +4,12 @@ * TODO: check the zodErrorMap implementation & patterns in the SDK for a default error * map. */ +import type {JSONValue} from '@open-formulieren/types/lib/types'; import {IntlShape, defineMessages} from 'react-intl'; import {z} from 'zod'; +import type {BuilderContextType} from '@/context'; + /* Validation message definitions. */ @@ -136,3 +139,19 @@ export const getErrorMap = (builder: ErrorBuilder): z.ZodErrorMap => { }; return errorMap; }; + +/* +Related to jsonLogic + */ + +export const itemsExpressionSchema = (builderContext: BuilderContextType) => + jsonSchema.superRefine((val, ctx) => { + const result = builderContext.validateLogic(val, [['', '']]); + if (result !== '') { + // TODO adapt once the InferNoLogic API uses exceptions + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: result, + }); + } + });