From fe701e46f8cee6b94aa6ee451c7761393adf537e Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Sun, 20 Aug 2023 14:42:57 +0300 Subject: [PATCH] feat: add validationErrors to schema mismatch errors --- index.js | 6 +++++- lib/validator.js | 15 ++++++++++++++- test/any.test.js | 14 ++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index e15bc0c5..2f10a6ba 100644 --- a/index.js +++ b/index.js @@ -122,6 +122,8 @@ function build (schema, options) { let contextFunctionCode + const resetValidatorErrors = (context.validatorSchemasIds.size > 0) ? 'validator.resetErrors();' : '' + // If we have only the invocation of the 'anonymous0' function, we would // basically just wrap the 'anonymous0' function in the 'main' function and // and the overhead of the intermediate variable 'json'. We can avoid the @@ -129,6 +131,7 @@ function build (schema, options) { // 'main' if (code === 'json += anonymous0(input)') { contextFunctionCode = ` + ${resetValidatorErrors} ${context.functions.join('\n')} const main = anonymous0 return main @@ -136,6 +139,7 @@ function build (schema, options) { } else { contextFunctionCode = ` function main (input) { + ${resetValidatorErrors} let json = '' ${code} return json @@ -896,7 +900,7 @@ function buildValue (context, location, input) { } code += ` - else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`) + else validator.throwAccumulatedErrors("${schemaRef}"); ` if (schema.type === 'object') { code += ` diff --git a/lib/validator.js b/lib/validator.js index 26c93f28..ce64763a 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -26,6 +26,7 @@ class Validator { } }) + this._errors = [] this._ajvSchemas = {} this._ajvOptions = ajvOptions || {} } @@ -47,8 +48,20 @@ class Validator { } } + resetErrors () { + this._errors = [] + } + validate (schemaRef, data) { - return this.ajv.validate(schemaRef, data) + const result = this.ajv.validate(schemaRef, data) + if (this.ajv.errors) this._errors.push(...this.ajv.errors) + return result + } + + throwAccumulatedErrors (schemaRef) { + const error = new TypeError(`The value of '${schemaRef}' does not match schema definition.`) + error.validationErrors = this._errors + throw error } // Ajv does not support js date format. In order to properly validate objects containing a date, diff --git a/test/any.test.js b/test/any.test.js index be136abb..c6c93b48 100644 --- a/test/any.test.js +++ b/test/any.test.js @@ -186,7 +186,12 @@ test('should throw a TypeError with the path to the key of the invalid value /1' const stringify = build(schema) - t.throws(() => stringify({ kind: 'Baz', value: 1 }), new TypeError('The value of \'#\' does not match schema definition.')) + t.throws(() => stringify({ kind: 'Baz', value: 1 }), Object.assign(new TypeError('The value of \'#\' does not match schema definition.'), { + validationErrors: [ + { message: 'must be equal to one of the allowed values', schemaPath: '#/properties/kind/enum', instancePath: '/kind' }, + { message: 'must be equal to one of the allowed values', schemaPath: '#/properties/kind/enum', instancePath: '/kind' } + ] + })) }) test('should throw a TypeError with the path to the key of the invalid value /2', (t) => { @@ -227,5 +232,10 @@ test('should throw a TypeError with the path to the key of the invalid value /2' const stringify = build(schema) - t.throws(() => stringify({ data: { kind: 'Baz', value: 1 } }), new TypeError('The value of \'#/properties/data\' does not match schema definition.')) + t.throws(() => stringify({ data: { kind: 'Baz', value: 1 } }), Object.assign(new TypeError('The value of \'#/properties/data\' does not match schema definition.'), { + validationErrors: [ + { message: 'must be equal to one of the allowed values', schemaPath: '#/properties/kind/enum', instancePath: '/kind' }, + { message: 'must be equal to one of the allowed values', schemaPath: '#/properties/kind/enum', instancePath: '/kind' } + ] + })) })