default behaviour in >=2.0.0 (#69) reverts #18 and #55 - now dataPath points to the object that is validated and not to the missing property; old (<=1.4.10 ) error reporting of dataPath for "required" keyword is available with option errorDataPath == "property"

master
Evgeny Poberezkin 2015-11-19 23:52:08 +00:00
parent 771e79f36b
commit a46600e14a
5 changed files with 83 additions and 27 deletions

View File

@ -53,6 +53,9 @@ function Ajv(opts) {
addInitialSchemas();
if (this.opts.formats) addInitialFormats();
if (this.opts.errorDataPath == 'property')
this.opts._errorDataPathProperty = true;
/**
* Validate data using schema

View File

@ -161,7 +161,7 @@
not: "'should NOT be valid'",
oneOf: "'should match exactly one schema in oneOf'",
pattern: "'should match pattern \"{{=it.util.escapeQuotes($schema)}}\"'",
required: "'is a required property'",
required: "'{{? it.opts._errorDataPathProperty }}is a required property{{??}}should have required property {{=$missingProperty}}{{?}}'",
type: "'should be {{? $isArray }}{{= $typeSchema.join(\",\") }}{{??}}{{=$typeSchema}}{{?}}'",
uniqueItems: "'should NOT have duplicate items (items ## ' + j + ' and ' + i + ' are identical)'",
custom: "'should pass \"{{=$rule.keyword}}\" keyword validation'"

View File

@ -15,7 +15,9 @@
var $i = 'i' + $lvl
, $propertyPath = 'schema' + $lvl + '[' + $i + ']'
, $missingProperty = '\' + "\'" + ' + $propertyPath + ' + "\'" + \'';
it.errorPath = it.util.getPathExpr($currentErrorPath, $propertyPath, it.opts.jsonPointers);
if (it.opts._errorDataPathProperty) {
it.errorPath = it.util.getPathExpr($currentErrorPath, $propertyPath, it.opts.jsonPointers);
}
}}
#}}
@ -43,9 +45,11 @@
{{
var $propertyPath = 'missing' + $lvl
, $missingProperty = '\' + ' + $propertyPath + ' + \'';
it.errorPath = it.opts.jsonPointers
? it.util.getPathExpr($currentErrorPath, $propertyPath, true)
: $currentErrorPath + ' + ' + $propertyPath;
if (it.opts._errorDataPathProperty) {
it.errorPath = it.opts.jsonPointers
? it.util.getPathExpr($currentErrorPath, $propertyPath, true)
: $currentErrorPath + ' + ' + $propertyPath;
}
}}
{{# def.error:'required' }}
} else {
@ -66,7 +70,9 @@
{{
var $prop = it.util.getProperty($property)
, $missingProperty = it.util.escapeQuotes($prop);
it.errorPath = it.util.getPath($currentErrorPath, $property, it.opts.jsonPointers);
if (it.opts._errorDataPathProperty) {
it.errorPath = it.util.getPath($currentErrorPath, $property, it.opts.jsonPointers);
}
}}
if ({{=$data}}{{=$prop}} === undefined) {
{{# def.addError:'required' }}

View File

@ -9,11 +9,15 @@ describe('Validation errors', function () {
var ajv, ajvJP, fullAjv;
beforeEach(function() {
ajv = Ajv();
ajvJP = Ajv({ jsonPointers: true });
fullAjv = Ajv({ allErrors: true, jsonPointers: true });
createInstances();
});
function createInstances(errorDataPath) {
ajv = Ajv({ errorDataPath: errorDataPath });
ajvJP = Ajv({ errorDataPath: errorDataPath, jsonPointers: true });
fullAjv = Ajv({ errorDataPath: errorDataPath, allErrors: true, jsonPointers: true });
}
it('error should include dataPath', function() {
testSchema1({
properties: {
@ -71,7 +75,18 @@ describe('Validation errors', function () {
});
it('errors for required should include missing property in dataPath', function() {
it('with option errorDataPath="property" errors for required should include missing property in dataPath', function() {
createInstances('property');
testRequired('property');
});
it('without option errorDataPath errors for required should NOT include missing property in dataPath', function() {
testRequired();
});
function testRequired(errorDataPath) {
var schema = {
required: ['foo', 'bar', 'baz']
};
@ -83,28 +98,49 @@ describe('Validation errors', function () {
var validate = ajv.compile(schema);
shouldBeValid(validate, data);
shouldBeInvalid(validate, invalidData1);
shouldBeError(validate.errors[0], 'required', '.bar', 'is a required property');
shouldBeError(validate.errors[0], 'required', path('.bar'), msg('.bar'));
shouldBeInvalid(validate, invalidData2);
shouldBeError(validate.errors[0], 'required', '.foo', 'is a required property');
shouldBeError(validate.errors[0], 'required', path('.foo'), msg('.foo'));
var validateJP = ajvJP.compile(schema);
shouldBeValid(validateJP, data);
shouldBeInvalid(validateJP, invalidData1);
shouldBeError(validateJP.errors[0], 'required', '/bar', 'is a required property');
shouldBeError(validateJP.errors[0], 'required', path('/bar'), msg('bar'));
shouldBeInvalid(validateJP, invalidData2);
shouldBeError(validateJP.errors[0], 'required', '/foo', 'is a required property');
shouldBeError(validateJP.errors[0], 'required', path('/foo'), msg('foo'));
var fullValidate = fullAjv.compile(schema);
shouldBeValid(fullValidate, data);
shouldBeInvalid(fullValidate, invalidData1);
shouldBeError(fullValidate.errors[0], 'required', '/bar', 'is a required property');
shouldBeError(fullValidate.errors[0], 'required', path('/bar'), msg('.bar'));
shouldBeInvalid(fullValidate, invalidData2, 2);
shouldBeError(fullValidate.errors[0], 'required', '/foo', 'is a required property');
shouldBeError(fullValidate.errors[1], 'required', '/baz', 'is a required property');
shouldBeError(fullValidate.errors[0], 'required', path('/foo'), msg('.foo'));
shouldBeError(fullValidate.errors[1], 'required', path('/baz'), msg('.baz'));
function path(dataPath) {
return errorDataPath == 'property' ? dataPath : '';
}
function msg(prop) {
return errorDataPath == 'property'
? 'is a required property'
: 'should have required property ' + prop;
}
}
it('required validation and errors for large data/schemas with option errorDataPath="property"', function() {
createInstances('property');
testRequiredLargeSchema('property');
});
it('required validation and errors for large data/schemas', function() {
it('required validation and errors for large data/schemas WITHOUT option errorDataPath="property"', function() {
testRequiredLargeSchema();
});
function testRequiredLargeSchema(errorDataPath) {
var schema = { required: [] }
, data = {}
, invalidData1 = {}
@ -121,25 +157,35 @@ describe('Validation errors', function () {
var validate = ajv.compile(schema);
shouldBeValid(validate, data);
shouldBeInvalid(validate, invalidData1);
shouldBeError(validate.errors[0], 'required', "['1']", "is a required property");
shouldBeError(validate.errors[0], 'required', path("['1']"), msg("'1'"));
shouldBeInvalid(validate, invalidData2);
shouldBeError(validate.errors[0], 'required', "['2']", "is a required property");
shouldBeError(validate.errors[0], 'required', path("['2']"), msg("'2'"));
var validateJP = ajvJP.compile(schema);
shouldBeValid(validateJP, data);
shouldBeInvalid(validateJP, invalidData1);
shouldBeError(validateJP.errors[0], 'required', "/1", "is a required property");
shouldBeError(validateJP.errors[0], 'required', path("/1"), msg("'1'"));
shouldBeInvalid(validateJP, invalidData2);
shouldBeError(validateJP.errors[0], 'required', "/2", "is a required property");
shouldBeError(validateJP.errors[0], 'required', path("/2"), msg("'2'"));
var fullValidate = fullAjv.compile(schema);
shouldBeValid(fullValidate, data);
shouldBeInvalid(fullValidate, invalidData1);
shouldBeError(fullValidate.errors[0], 'required', '/1', "is a required property");
shouldBeError(fullValidate.errors[0], 'required', path('/1'), msg("'1'"));
shouldBeInvalid(fullValidate, invalidData2, 2);
shouldBeError(fullValidate.errors[0], 'required', '/2', "is a required property");
shouldBeError(fullValidate.errors[1], 'required', '/98', "is a required property");
});
shouldBeError(fullValidate.errors[0], 'required', path('/2'), msg("'2'"));
shouldBeError(fullValidate.errors[1], 'required', path('/98'), msg("'98'"));
function path(dataPath) {
return errorDataPath == 'property' ? dataPath : '';
}
function msg(prop) {
return errorDataPath == 'property'
? 'is a required property'
: 'should have required property ' + prop;
}
}
it('errors for items should include item index without quotes in dataPath (#48)', function() {

View File

@ -12,7 +12,8 @@ var instances = getAjvInstances(fullTest ? {
verbose: true,
format: 'full',
inlineRefs: false,
jsonPointers: true
jsonPointers: true,
errorDataPath: 'property'
} : { allErrors: true });
var remoteRefs = {