diff --git a/lib/ajv.js b/lib/ajv.js index 66c18ed6b..dab6cccde 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -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 diff --git a/lib/dot/definitions.def b/lib/dot/definitions.def index a915d2a78..e230bc050 100644 --- a/lib/dot/definitions.def +++ b/lib/dot/definitions.def @@ -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'" diff --git a/lib/dot/required.jst b/lib/dot/required.jst index f5e0ae5ea..49b30e70a 100644 --- a/lib/dot/required.jst +++ b/lib/dot/required.jst @@ -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' }} diff --git a/spec/errors.spec.js b/spec/errors.spec.js index 2060d2f33..6027e4c18 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -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 WITHOUT option errorDataPath="property"', function() { + testRequiredLargeSchema(); }); - it('required validation and errors for large data/schemas', function() { + 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() { diff --git a/spec/json-schema.spec.js b/spec/json-schema.spec.js index 53d6c27fd..9494cd9f8 100644 --- a/spec/json-schema.spec.js +++ b/spec/json-schema.spec.js @@ -12,7 +12,8 @@ var instances = getAjvInstances(fullTest ? { verbose: true, format: 'full', inlineRefs: false, - jsonPointers: true + jsonPointers: true, + errorDataPath: 'property' } : { allErrors: true }); var remoteRefs = {