Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Support raw validation in the playground #3217

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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