From 7e88e0be6b7b641945a630eb36ee589156adc455 Mon Sep 17 00:00:00 2001 From: Heath C <51679588+heath-freenome@users.noreply.github.com> Date: Mon, 31 Oct 2022 06:55:21 -0700 Subject: [PATCH] feature: Support raw validation in the playground (#3217) --- CHANGELOG.md | 4 ++ packages/playground/src/app.jsx | 8 +-- packages/utils/src/types.ts | 12 ++++- .../utils/test/testUtils/getTestValidator.ts | 1 + packages/validator-ajv6/src/validator.ts | 52 ++++++++++++------- .../test/utilsTests/getTestValidator.ts | 6 +++ packages/validator-ajv8/src/validator.ts | 52 ++++++++++++------- .../test/utilsTests/getTestValidator.ts | 6 +++ 8 files changed, 97 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 888e43635a..89f72c5790 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,20 +50,24 @@ should change the heading of the (upcoming) version to include a major version b - `StrictRJSFSchema` was added as the alias to `JSON7Schema` and `RJSFSchema` was modified to be `StrictRJSFSchema & GenericObjectType` - This new generic was added BEFORE the newly added `F = any` generic because it is assumed that more people will want to change the schema than the formContext types - This provides future support for the newer draft versions of the schema +- Updated the `ValidatorType` interface to add a new `rawValidation()` method for use by the playground ## @rjsf/validator-ajv6 - Fixed a few type casts given the new expanded definition of the `RJSFSchema` type change - Deprecated this library in favor of the `@rjsf/validator-ajv8` +- Refactored out the `rawValidation()` function for use by the playground ## @rjsf/validator-ajv8 - Updated the typing to add the new `S extends StrictRJSFSchema = RJSFSchema` generic and fixed up type casts - Added the `AjvClass` prop to the `CustomValidatorOptionsType` to support using the `Ajv2019` or `Ajv2020` class implementation instead of the default `Ajv` class; fixing [#3189](https://github.com/rjsf-team/react-jsonschema-form/issues/3189) +- Refactored out the `rawValidation()` function for use by the playground ## Dev / docs / playground - Updated the `5.x upgrade guide` and `utility-functions.md` to document the new `StrictRJSFSchema` and `S` generic - Updated the `validation` guide to document the new `AjvClass` prop on `CustomValidatorOptionsType` and mentioning the deprecation of `@rjsf/validator-ajv6` - Updated the playground to add support for using the AJV 8 validator with the `draft-2019-09` and `draft-2020-12` schemas and to make the `AJV8` validator the default validator, marking `AJV6` as deprecated - Updated all the documentation to switch to Typescript notation where missing along with switching to using the `@rjsf/validator-ajv8` validator as the default +- Added a way of doing a raw Ajv validation in the playground to determine whether an issue is with RJSF or Ajv # 5.0.0-beta.11 diff --git a/packages/playground/src/app.jsx b/packages/playground/src/app.jsx index 04993b9320..6ca58dd1eb 100644 --- a/packages/playground/src/app.jsx +++ b/packages/playground/src/app.jsx @@ -285,17 +285,17 @@ class CopyLink extends Component { } } -const EMPTY_RAW_VALIDATION = { errors: undefined, validationError: undefined }; - function RawValidatorTest({ validator, schema, formData }) { const [rawValidation, setRawValidation] = React.useState(); - const { errors, validationError } = rawValidation || EMPTY_RAW_VALIDATION; const handleClearClick = () => setRawValidation(undefined); const handleRawClick = () => setRawValidation(validator.rawValidation(schema, formData)); let displayErrors; if (rawValidation) { - displayErrors = errors ? JSON.stringify(errors, null, 2) : "No AJV errors encountered"; + displayErrors = + rawValidation.errors || rawValidation.validationError ? + JSON.stringify(rawValidation, null, 2) : + "No AJV errors encountered"; } return (
diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 3d63b63394..398cb80492 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -902,10 +902,20 @@ export interface ValidatorType< * false. * * @param schema - The schema against which to validate the form data * @param schema - * @param formData- - The form data to validate + * @param formData - The form data to validate * @param rootSchema - The root schema used to provide $ref resolutions */ isValid(schema: S, formData: T, rootSchema: S): boolean; + /** Runs the pure validation of the `schema` and `formData` without any of the RJSF functionality. Provided for use + * by the playground. Returns the `errors` from the validation + * + * @param schema - The schema against which to validate the form data * @param schema + * @param formData - The form data to validate + */ + rawValidation( + schema: S, + formData?: T + ): { errors?: Result[]; validationError?: Error }; } /** The `SchemaUtilsType` interface provides a wrapper around the publicly exported APIs in the `@rjsf/utils/schema` diff --git a/packages/utils/test/testUtils/getTestValidator.ts b/packages/utils/test/testUtils/getTestValidator.ts index 18b6299c2b..04f956efee 100644 --- a/packages/utils/test/testUtils/getTestValidator.ts +++ b/packages/utils/test/testUtils/getTestValidator.ts @@ -45,6 +45,7 @@ export default function getTestValidator({ } return []; }), + rawValidation: jest.fn().mockImplementation(() => {}), setReturnValues({ isValid, data, errorList }: TestValidatorParams) { if (isValid !== undefined) { testValidator._isValid = isValid; diff --git a/packages/validator-ajv6/src/validator.ts b/packages/validator-ajv6/src/validator.ts index dc031804c7..0970379ed1 100644 --- a/packages/validator-ajv6/src/validator.ts +++ b/packages/validator-ajv6/src/validator.ts @@ -27,7 +27,9 @@ const ROOT_SCHEMA_PREFIX = "__rjsf_rootSchema"; * * @deprecated in favor of the `@rjsf/validator-ajv8 */ -export default class AJV6Validator implements ValidatorType { +export default class AJV6Validator + implements ValidatorType +{ /** The AJV instance to use for all validations * * @private @@ -198,12 +200,8 @@ export default class AJV6Validator implements ValidatorType { * @private */ private transformRJSFValidationErrors( - errors: Ajv["errors"] = [] + errors: ErrorObject[] = [] ): RJSFValidationError[] { - if (errors === null) { - return []; - } - return errors.map((e: ErrorObject) => { const { dataPath, keyword, message, params, schemaPath } = e; const property = `${dataPath}`; @@ -220,6 +218,31 @@ export default class AJV6Validator implements ValidatorType { }); } + /** Runs the pure validation of the `schema` and `formData` without any of the RJSF functionality. Provided for use + * by the playground. Returns the `errors` from the validation + * + * @param schema - The schema against which to validate the form data * @param schema + * @param formData - The form data to validate + */ + rawValidation( + schema: RJSFSchema, + formData?: T + ): { errors?: Result[]; validationError?: Error } { + let validationError: Error | undefined = undefined; + try { + this.ajv.validate(schema, formData); + } catch (err) { + validationError = err as Error; + } + + const errors = this.ajv.errors || undefined; + + // Clear errors to prevent persistent errors, see #1104 + this.ajv.errors = null; + + return { errors: errors as unknown as Result[], validationError }; + } + /** This function processes the `formData` with an optional user contributed `customValidate` function, which receives * the form data and a `errorHandler` function that will be used to add custom validation errors for each field. Also * supports a `transformErrors` function that will take the raw AJV validation errors, prior to custom validation and @@ -238,7 +261,7 @@ export default class AJV6Validator implements ValidatorType { ): ValidationData { // Include form data with undefined values, which is required for validation. const rootSchema = schema; - const newFormData = getDefaultFormState( + const newFormData = getDefaultFormState( this, schema, formData, @@ -246,22 +269,13 @@ export default class AJV6Validator implements ValidatorType { true ) as T; - let validationError: Error | null = null; - try { - this.ajv.validate(schema, newFormData); - } catch (err) { - validationError = err as Error; - } - - let errors = this.transformRJSFValidationErrors(this.ajv.errors); - // Clear errors to prevent persistent errors, see #1104 - - this.ajv.errors = null; + const rawErrors = this.rawValidation(schema, newFormData); + const { validationError } = rawErrors; + let errors = this.transformRJSFValidationErrors(rawErrors.errors); const noProperMetaSchema = validationError && validationError.message && - typeof validationError.message === "string" && validationError.message.includes("no schema with key or ref "); if (noProperMetaSchema) { diff --git a/packages/validator-ajv6/test/utilsTests/getTestValidator.ts b/packages/validator-ajv6/test/utilsTests/getTestValidator.ts index 4a3ba70822..e6c9b28522 100644 --- a/packages/validator-ajv6/test/utilsTests/getTestValidator.ts +++ b/packages/validator-ajv6/test/utilsTests/getTestValidator.ts @@ -37,6 +37,12 @@ export default function getTestValidator( isValid(schema: RJSFSchema, formData: T, rootSchema: RJSFSchema): boolean { return validator.isValid(schema, formData, rootSchema); }, + rawValidation( + schema: RJSFSchema, + formData?: T + ): { errors?: Result[]; validationError?: Error } { + return validator.rawValidation(schema, formData); + }, // This is intentionally a no-op as we are using the real validator here setReturnValues() {}, }; diff --git a/packages/validator-ajv8/src/validator.ts b/packages/validator-ajv8/src/validator.ts index c601d0a6ac..4988577ed8 100644 --- a/packages/validator-ajv8/src/validator.ts +++ b/packages/validator-ajv8/src/validator.ts @@ -217,12 +217,8 @@ export default class AJV8Validator< * @private */ private transformRJSFValidationErrors( - errors: Ajv["errors"] = [] + errors: ErrorObject[] = [] ): RJSFValidationError[] { - if (errors === null) { - return []; - } - return errors.map((e: ErrorObject) => { const { instancePath, keyword, message, params, schemaPath } = e; const property = instancePath.replace(/\//g, "."); @@ -239,6 +235,34 @@ export default class AJV8Validator< }); } + /** Runs the pure validation of the `schema` and `formData` without any of the RJSF functionality. Provided for use + * by the playground. Returns the `errors` from the validation + * + * @param schema - The schema against which to validate the form data * @param schema + * @param formData - The form data to validate + */ + rawValidation( + schema: RJSFSchema, + formData?: T + ): { errors?: Result[]; validationError?: Error } { + let validationError: Error | undefined = undefined; + try { + this.ajv.validate(schema, formData); + } catch (err) { + validationError = err as Error; + } + + if (typeof this.localizer === "function") { + this.localizer(this.ajv.errors); + } + const errors = this.ajv.errors || undefined; + + // Clear errors to prevent persistent errors, see #1104 + this.ajv.errors = null; + + return { errors: errors as unknown as Result[], validationError }; + } + /** This function processes the `formData` with an optional user contributed `customValidate` function, which receives * the form data and a `errorHandler` function that will be used to add custom validation errors for each field. Also * supports a `transformErrors` function that will take the raw AJV validation errors, prior to custom validation and @@ -265,25 +289,13 @@ export default class AJV8Validator< true ) as T; - let validationError: Error | null = null; - try { - this.ajv.validate(schema, newFormData); - } catch (err) { - validationError = err as Error; - } - - if (typeof this.localizer === "function") { - this.localizer(this.ajv.errors); - } - let errors = this.transformRJSFValidationErrors(this.ajv.errors); - // Clear errors to prevent persistent errors, see #1104 - - this.ajv.errors = null; + const rawErrors = this.rawValidation(schema, newFormData); + const { validationError } = rawErrors; + let errors = this.transformRJSFValidationErrors(rawErrors.errors); const noProperMetaSchema = validationError && validationError.message && - typeof validationError.message === "string" && validationError.message.includes("no schema with key or ref "); if (noProperMetaSchema) { diff --git a/packages/validator-ajv8/test/utilsTests/getTestValidator.ts b/packages/validator-ajv8/test/utilsTests/getTestValidator.ts index 4a3ba70822..e6c9b28522 100644 --- a/packages/validator-ajv8/test/utilsTests/getTestValidator.ts +++ b/packages/validator-ajv8/test/utilsTests/getTestValidator.ts @@ -37,6 +37,12 @@ export default function getTestValidator( isValid(schema: RJSFSchema, formData: T, rootSchema: RJSFSchema): boolean { return validator.isValid(schema, formData, rootSchema); }, + rawValidation( + schema: RJSFSchema, + formData?: T + ): { errors?: Result[]; validationError?: Error } { + return validator.rawValidation(schema, formData); + }, // This is intentionally a no-op as we are using the real validator here setReturnValues() {}, };