From aa59a8eb48d072dd10991fb213b203dd02e3efe3 Mon Sep 17 00:00:00 2001 From: Guillaume Robin <10772294+cesumilo@users.noreply.github.com> Date: Sun, 3 Mar 2024 07:16:43 +0100 Subject: [PATCH] Add support for verbose Ajv option (#1718) --- lib/model/AjvValidator.js | 20 ++++++--- tests/integration/misc/#1718.js | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 tests/integration/misc/#1718.js diff --git a/lib/model/AjvValidator.js b/lib/model/AjvValidator.js index 97b8f3af2..3083417f7 100644 --- a/lib/model/AjvValidator.js +++ b/lib/model/AjvValidator.js @@ -77,7 +77,7 @@ class AjvValidator extends Validator { } validator.call(model, json); - const error = parseValidationError(validator.errors, modelClass, options); + const error = parseValidationError(validator.errors, modelClass, options, this.ajvOptions); if (error) { throw error; @@ -143,7 +143,7 @@ class AjvValidator extends Validator { } } -function parseValidationError(errors, modelClass, options) { +function parseValidationError(errors, modelClass, options, ajvOptions) { if (!errors) { return null; } @@ -178,13 +178,21 @@ function parseValidationError(errors, modelClass, options) { // More than one error can occur for the same key in Ajv, merge them in the array: const array = errorHash[key] || (errorHash[key] = []); - // Use unshift instead of push so that the last error ends up at [0], - // preserving previous behavior where only the last error was stored. - array.unshift({ + // Prepare error object + const errorObj = { message: error.message, keyword: error.keyword, params: error.params, - }); + }; + + // Add data if verbose enabled + if (ajvOptions.verbose) { + errorObj.data = error.data; + } + + // Use unshift instead of push so that the last error ends up at [0], + // preserving previous behavior where only the last error was stored. + array.unshift(errorObj); ++numErrors; } diff --git a/tests/integration/misc/#1718.js b/tests/integration/misc/#1718.js new file mode 100644 index 000000000..b341adc50 --- /dev/null +++ b/tests/integration/misc/#1718.js @@ -0,0 +1,75 @@ +const _ = require('lodash'); +const expect = require('expect.js'); +const { Model } = require('../../../'); +const { AjvValidator } = require('../../../lib/model/AjvValidator'); +const { ValidationError } = require('../../../lib/model/ValidationError'); + +module.exports = (session) => { + describe('When Ajv verbose is enabled, pass through the data field in exception #1718', () => { + class A extends Model { + static get tableName() { + return 'a'; + } + + static createValidator() { + return new AjvValidator({ + onCreateAjv: (ajv) => { + // Here you can modify the `Ajv` instance. + }, + options: { + allErrors: true, + validateSchema: false, + ownProperties: true, + v5: true, + verbose: true, + }, + }); + } + + static get jsonSchema() { + return { + type: 'object', + required: ['id'], + properties: { + id: { type: 'integer' }, + }, + }; + } + } + + beforeEach(() => { + return session.knex.schema + .dropTableIfExists('a') + .createTable('a', (table) => { + table.integer('id').primary(); + }) + .then(() => { + return Promise.all([session.knex('a').insert({ id: 1 })]); + }); + }); + + afterEach(() => { + return session.knex.schema.dropTableIfExists('a'); + }); + + it('the test', () => { + return A.query(session.knex) + .insert({ id: '2' }) + .catch((err) => { + expect(err).to.be.an(ValidationError); + expect(err.data).to.be.an('object'); + expect(err.data.id).to.be.an('array'); + expect(err.data).to.eql({ + id: [ + { + message: 'must be integer', + keyword: 'type', + params: { type: 'integer' }, + data: '2', + }, + ], + }); + }); + }); + }); +};