Skip to content

Commit

Permalink
feature: Support raw validation in the playground (#3217)
Browse files Browse the repository at this point in the history
  • Loading branch information
heath-freenome committed Oct 31, 2022
1 parent 04dc047 commit 7e88e0b
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 44 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions packages/playground/src/app.jsx
Expand Up @@ -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 (
<div>
Expand Down
12 changes: 11 additions & 1 deletion packages/utils/src/types.ts
Expand Up @@ -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<Result = any>(
schema: S,
formData?: T
): { errors?: Result[]; validationError?: Error };
}

/** The `SchemaUtilsType` interface provides a wrapper around the publicly exported APIs in the `@rjsf/utils/schema`
Expand Down
1 change: 1 addition & 0 deletions packages/utils/test/testUtils/getTestValidator.ts
Expand Up @@ -45,6 +45,7 @@ export default function getTestValidator<T = any>({
}
return [];
}),
rawValidation: jest.fn().mockImplementation(() => {}),
setReturnValues({ isValid, data, errorList }: TestValidatorParams) {
if (isValid !== undefined) {
testValidator._isValid = isValid;
Expand Down
52 changes: 33 additions & 19 deletions packages/validator-ajv6/src/validator.ts
Expand Up @@ -27,7 +27,9 @@ const ROOT_SCHEMA_PREFIX = "__rjsf_rootSchema";
*
* @deprecated in favor of the `@rjsf/validator-ajv8
*/
export default class AJV6Validator<T = any> implements ValidatorType<T> {
export default class AJV6Validator<T = any>
implements ValidatorType<T, RJSFSchema>
{
/** The AJV instance to use for all validations
*
* @private
Expand Down Expand Up @@ -198,12 +200,8 @@ export default class AJV6Validator<T = any> implements ValidatorType<T> {
* @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}`;
Expand All @@ -220,6 +218,31 @@ export default class AJV6Validator<T = any> implements ValidatorType<T> {
});
}

/** 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<Result = any>(
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
Expand All @@ -238,30 +261,21 @@ export default class AJV6Validator<T = any> implements ValidatorType<T> {
): ValidationData<T> {
// Include form data with undefined values, which is required for validation.
const rootSchema = schema;
const newFormData = getDefaultFormState<T>(
const newFormData = getDefaultFormState<T, RJSFSchema>(
this,
schema,
formData,
rootSchema,
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<ErrorObject>(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) {
Expand Down
6 changes: 6 additions & 0 deletions packages/validator-ajv6/test/utilsTests/getTestValidator.ts
Expand Up @@ -37,6 +37,12 @@ export default function getTestValidator<T = any>(
isValid(schema: RJSFSchema, formData: T, rootSchema: RJSFSchema): boolean {
return validator.isValid(schema, formData, rootSchema);
},
rawValidation<Result = any>(
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() {},
};
Expand Down
52 changes: 32 additions & 20 deletions packages/validator-ajv8/src/validator.ts
Expand Up @@ -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, ".");
Expand All @@ -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<Result = any>(
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
Expand All @@ -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<ErrorObject>(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) {
Expand Down
6 changes: 6 additions & 0 deletions packages/validator-ajv8/test/utilsTests/getTestValidator.ts
Expand Up @@ -37,6 +37,12 @@ export default function getTestValidator<T = any>(
isValid(schema: RJSFSchema, formData: T, rootSchema: RJSFSchema): boolean {
return validator.isValid(schema, formData, rootSchema);
},
rawValidation<Result = any>(
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() {},
};
Expand Down

0 comments on commit 7e88e0b

Please sign in to comment.