Skip to content

Commit

Permalink
Fix issues with validating schemas with "$id"s in @rjsf/validator-ajv8 (
Browse files Browse the repository at this point in the history
#3232)

Fixes #2821
Fixes #3212
  • Loading branch information
nickgros committed Nov 11, 2022
1 parent 651bc02 commit 397740e
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 64 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -21,10 +21,13 @@ should change the heading of the (upcoming) version to include a major version b
- Fix Vite development server [#3228](https://github.com/rjsf-team/react-jsonschema-form/issues/3228)

## @rjsf/validator-ajv8
- BREAKING CHANGE: Disable form data validation for invalid JSON Schemas. Use @rjsf/validator-ajv6 if you need to validate against invalid schemas.
- Fix additionalProperties validation [#3213](https://github.com/rjsf-team/react-jsonschema-form/issues/3213)
- Report all schema errors thrown by Ajv. Previously, we would only report errors thrown for a missing meta-schema. This behavior is unchanged for @rjsf/validator-ajv6.
- Disable Ajv strict mode by default.
- Add RJSF-specific additional properties keywords to Ajv to prevent errors from being reported in strict mode.
- For JSON Schemas with `$id`s, use a pre-compiled Ajv validation function when available.
- No longer fail to validate inner schemas with `$id`s, fixing [#2821](https://github.com/rjsf-team/react-jsonschema-form/issues/2181).

# 5.0.0-beta.12

Expand Down
59 changes: 42 additions & 17 deletions packages/validator-ajv8/src/validator.ts
@@ -1,4 +1,4 @@
import Ajv, { ErrorObject } from "ajv8";
import Ajv, { ErrorObject, ValidateFunction } from "ajv8";
import toPath from "lodash/toPath";
import isObject from "lodash/isObject";
import clone from "lodash/clone";
Expand Down Expand Up @@ -245,22 +245,35 @@ export default class AJV8Validator<
schema: RJSFSchema,
formData?: T
): { errors?: Result[]; validationError?: Error } {
let validationError: Error | undefined = undefined;
let compilationError: Error | undefined = undefined;
let compiledValidator: ValidateFunction | undefined;
if (schema["$id"]) {
compiledValidator = this.ajv.getSchema(schema["$id"]);
}
try {
this.ajv.validate(schema, formData);
if (compiledValidator === undefined) {
compiledValidator = this.ajv.compile(schema);
}
compiledValidator(formData);
} catch (err) {
validationError = err as Error;
compilationError = err as Error;
}

if (typeof this.localizer === "function") {
this.localizer(this.ajv.errors);
}
const errors = this.ajv.errors || undefined;
let errors;
if (compiledValidator) {
if (typeof this.localizer === "function") {
this.localizer(compiledValidator.errors);
}
errors = compiledValidator.errors || undefined;

// Clear errors to prevent persistent errors, see #1104
this.ajv.errors = null;
// Clear errors to prevent persistent errors, see #1104
compiledValidator.errors = null;
}

return { errors: errors as unknown as Result[], validationError };
return {
errors: errors as unknown as Result[],
validationError: compilationError,
};
}

/** This function processes the `formData` with an optional user contributed `customValidate` function, which receives
Expand Down Expand Up @@ -366,25 +379,37 @@ export default class AJV8Validator<
* false otherwise. If the schema is invalid, then this function will return
* false.
*
* @param schema - The schema against which to validate the form data * @param schema
* @param formData- - The form data to validate
* @param schema - The schema against which to validate the form data
* @param formData - The form data to validate
* @param rootSchema - The root schema used to provide $ref resolutions
*/
isValid(schema: S, formData: T, rootSchema: S) {
const rootSchemaId = rootSchema["$id"] ?? ROOT_SCHEMA_PREFIX;
try {
// add the rootSchema ROOT_SCHEMA_PREFIX as id.
// then rewrite the schema ref's to point to the rootSchema
// this accounts for the case where schema have references to models
// that lives in the rootSchema but not in the schema in question.
const result = this.ajv
.addSchema(rootSchema, ROOT_SCHEMA_PREFIX)
.validate(this.withIdRefPrefix(schema), formData);
if (this.ajv.getSchema(rootSchemaId) === undefined) {
this.ajv.addSchema(rootSchema, rootSchemaId);
}
const schemaWithIdRefPrefix = this.withIdRefPrefix(schema) as S;
let compiledValidator: ValidateFunction | undefined;
if (schemaWithIdRefPrefix["$id"]) {
compiledValidator = this.ajv.getSchema(schemaWithIdRefPrefix["$id"]);
}
if (compiledValidator === undefined) {
compiledValidator = this.ajv.compile(schemaWithIdRefPrefix);
}
const result = compiledValidator(formData);
return result as boolean;
} catch (e) {
console.warn("Error encountered compiling schema:", e);
return false;
} finally {
// TODO: A function should be called if the root schema changes so we don't have to remove and recompile the schema every run.
// make sure we remove the rootSchema from the global ajv instance
this.ajv.removeSchema(ROOT_SCHEMA_PREFIX);
this.ajv.removeSchema(rootSchemaId);
}
}

Expand Down

0 comments on commit 397740e

Please sign in to comment.