Skip to content

Commit

Permalink
Add throwFirst/throwAll options
Browse files Browse the repository at this point in the history
  • Loading branch information
awwright committed Oct 20, 2020
1 parent a0d4c70 commit 85836ef
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 11 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,14 @@ validator.validate('foo', {type: 'string', format: 'myFormat'}).valid; // false

### Results

The first error found will be thrown as an `Error` object if `options.throwError` is `true`. Otherwise all results will be appended to the `result.errors` array. Each item in this array is a `ValidationError` with the following properties:
By default, results will be returned in a `ValidatorResult` object with the following properties:

* `instance`: any.
* `schema`: Schema.
* `errors`: ValidationError[].
* `valid`: boolean.

Each item in `errors` is a `ValidationError` with the following properties:

* path: array. An array of property keys or array offsets, indicating where inside objects or arrays the instance was found.
* property: string. Describes the property path. Starts with `instance`, and is delimited with a dot (`.`).
Expand All @@ -154,6 +161,16 @@ The first error found will be thrown as an `Error` object if `options.throwError
* name: string. The keyword within the schema that failed.
* argument: any. Provides information about the keyword that failed.

The validator can be configured to throw in the event of a validation error:

* If the `throwFirst` option is set, the validator will terminate validation at the first encountered error and throw a `ValidatorResultError` object.

* If the `throwAll` option is set, the validator will throw a `ValidatorResultError` object after the entire instance has been validated.

* If the `throwError` option is set, it will throw at the first encountered validation error (like `throwFirst`), but the `ValidationError` object itself will be thrown. Note that, despite the name, this does not inherit from Error like `ValidatorResultError` does.

The `ValidatorResultError` object has the same properties as `ValidatorResult` and additionally inherits from Error.

#### "nestedErrors" option

When `oneOf` or `anyOf` validations fail, errors that caused any of the sub-schemas referenced therein to fail are normally suppressed, because it is not necessary to fix all of them. And in the case of `oneOf`, it would itself be an error to fix all of the listed errors.
Expand Down
3 changes: 3 additions & 0 deletions lib/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,12 @@ validators.type = function validateType (instance, schema, options, ctx) {

function testSchemaNoThrow(instance, options, ctx, callback, schema){
var throwError = options.throwError;
var throwAll = options.throwAll;
options.throwError = false;
options.throwAll = false;
var res = this.validateSchema(instance, schema, options, ctx);
options.throwError = throwError;
options.throwAll = throwAll;

if (!res.valid && callback instanceof Function) {
callback(res);
Expand Down
22 changes: 20 additions & 2 deletions lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ var ValidatorResult = exports.ValidatorResult = function ValidatorResult(instanc
this.propertyPath = ctx.propertyPath;
this.errors = [];
this.throwError = options && options.throwError;
this.throwFirst = options && options.throwFirst;
this.throwAll = options && options.throwAll;
this.disableFormat = options && options.disableFormat === true;
};

Expand All @@ -52,10 +54,12 @@ ValidatorResult.prototype.addError = function addError(detail) {
err = new ValidationError(detail.message, this.instance, this.schema, this.path, detail.name, detail.argument);
}

if (this.throwError) {
this.errors.push(err);
if (this.throwFirst) {
throw new ValidatorResultError(this);
}else if(this.throwError){
throw err;
}
this.errors.push(err);
return err;
};

Expand All @@ -78,6 +82,20 @@ Object.defineProperty(ValidatorResult.prototype, "valid", { get: function() {
return !this.errors.length;
} });

module.exports.ValidatorResultError = ValidatorResultError;
function ValidatorResultError(result) {
if(Error.captureStackTrace){
Error.captureStackTrace(this, ValidatorResultError);
}
this.instance = result.instance;
this.schema = result.schema;
this.options = result.options;
this.errors = result.errors;
}
ValidatorResultError.prototype = new Error();
ValidatorResultError.prototype.constructor = ValidatorResultError;
ValidatorResultError.prototype.name = "Validation Error";

/**
* Describes a problem with a Schema which prevents validation of an instance
* @name SchemaError
Expand Down
2 changes: 2 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ export interface Options {
rewrite?: RewriteFunction;
base?: string;
throwError?: boolean;
throwFirst?: boolean;
throwAll?: boolean;
nestedErrors?: boolean;
}

Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
var Validator = module.exports.Validator = require('./validator');

module.exports.ValidatorResult = require('./helpers').ValidatorResult;
module.exports.ValidatorResultError = require('./helpers').ValidatorResultError;
module.exports.ValidationError = require('./helpers').ValidationError;
module.exports.SchemaError = require('./helpers').SchemaError;
module.exports.SchemaScanResult = require('./scan').SchemaScanResult;
Expand Down
3 changes: 3 additions & 0 deletions lib/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var attribute = require('./attribute');
var helpers = require('./helpers');
var scanSchema = require('./scan').scan;
var ValidatorResult = helpers.ValidatorResult;
var ValidatorResultError = helpers.ValidatorResultError;
var SchemaError = helpers.SchemaError;
var SchemaContext = helpers.SchemaContext;
//var anonymousBase = 'vnd.jsonschema:///';
Expand Down Expand Up @@ -134,6 +135,8 @@ Validator.prototype.validate = function validate (instance, schema, options, ctx
var result = this.validateSchema(instance, schema, options, ctx);
if (!result) {
throw new Error('Result undefined');
}else if(options.throwAll && result.errors.length){
throw new ValidatorResultError(result);
}
return result;
};
Expand Down
82 changes: 74 additions & 8 deletions test/Validator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

var Validator = require('../lib/index.js').Validator;
var SchemaError = require('../lib/index.js').SchemaError;
var ValidationError = require('../lib/index.js').ValidationError;
var ValidatorResultError = require('../lib/index.js').ValidatorResultError;
var assert = require('assert');

describe('Validator', function () {
Expand Down Expand Up @@ -216,6 +218,70 @@ describe('Validator', function () {
var neg = validator.validate(null, true, {required: true});
assert(neg.valid);
});
it('options.throwError', function () {
var schema = {
properties: {
"a": {type: 'number'},
"b": {type: 'number'},
},
};
var valid = {a:0, b:0};
var invalid = {a:null, b:null};
var res = validator.validate(valid, schema, {});
assert(res.valid);
var neg = validator.validate(invalid, schema, {});
assert(!neg.valid);
assert.throws(function(){
validator.validate(invalid, schema, {throwError: true});
}, function(err){
assert(err instanceof ValidationError);
return true;
});
});
it('options.throwFirst', function () {
var schema = {
properties: {
"a": {type: 'number'},
"b": {type: 'number'},
},
};
var valid = {a:0, b:0};
var invalid = {a:null, b:null};
var res = validator.validate(valid, schema, {throwAll: true});
assert(res.valid);
var neg = validator.validate(invalid, schema, {});
assert(!neg.valid);
assert.throws(function(){
validator.validate(invalid, schema, {throwFirst: true});
}, function(err){
assert(err instanceof Error);
assert(err instanceof ValidatorResultError);
assert.strictEqual(err.errors.length, 1);
return true;
});
});
it('options.throwAll', function () {
var schema = {
properties: {
"a": {type: 'number'},
"b": {type: 'number'},
},
};
var valid = {a:0, b:0};
var invalid = {a:null, b:null};
var res = validator.validate(valid, schema, {throwAll: true});
assert(res.valid);
var neg = validator.validate(invalid, schema, {});
assert(!neg.valid);
assert.throws(function(){
validator.validate(invalid, schema, {throwAll: true});
}, function(err){
assert(err instanceof Error);
assert(err instanceof ValidatorResultError);
assert.strictEqual(err.errors.length, 2);
return true;
});
});
it('subschema references (named reference)', function () {
var schema = {
items: {$ref: '#items'},
Expand All @@ -228,8 +294,8 @@ describe('Validator', function () {
};
var res = validator.validate([[]], schema);
assert(res.valid);
var res = validator.validate([null], schema);
assert(!res.valid);
var neg = validator.validate([null], schema);
assert(!neg.valid);
});
it('subschema references (path reference)', function () {
var schema = {
Expand All @@ -242,8 +308,8 @@ describe('Validator', function () {
};
var res = validator.validate([[]], schema);
assert(res.valid);
var res = validator.validate([null], schema);
assert(!res.valid);
var neg = validator.validate([null], schema);
assert(!neg.valid);
});
it('recursive references (fragment reference)', function () {
var schema = {
Expand All @@ -253,8 +319,8 @@ describe('Validator', function () {
};
var res = validator.validate([[[[]]]], schema);
assert(res.valid);
var res = validator.validate([null], schema);
assert(!res.valid);
var neg = validator.validate([null], schema);
assert(!neg.valid);
});
it('recursive references (filename reference)', function () {
var schema = {
Expand All @@ -264,8 +330,8 @@ describe('Validator', function () {
};
var res = validator.validate([[[[]]]], schema);
assert(res.valid);
var res = validator.validate([null], schema);
assert(!res.valid);
var neg = validator.validate([null], schema);
assert(!neg.valid);
});
});
});

0 comments on commit 85836ef

Please sign in to comment.