diff --git a/README.md b/README.md index 6cf6db0d9..06e75d727 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ Options can have properties `separator` (string used to separate errors, ", " by ## Options - _allErrors_: check all rules collecting all errors. Default is to return after the first error. -- _removeAdditional_: remove additional properties. Default is not to remove. If the option is 'all', then all additional properties are removed, regardless of `additionalProperties` keyword in schema (and no validation is made for them). If the option is `true` (or truthy), only additional properties with `additionalProperties` keyword equal to `false` are removed. +- _removeAdditional_: remove additional properties. Default is not to remove. If the option is 'all', then all additional properties are removed, regardless of `additionalProperties` keyword in schema (and no validation is made for them). If the option is `true` (or truthy), only additional properties with `additionalProperties` keyword equal to `false` are removed. If the option is 'failing', then additional properties that fail schema validation will be removed too (where `additionalProperties` keyword is schema). - _verbose_: include the reference to the part of the schema and validated data in errors (false by default). - _format_: formats validation mode ('fast' by default). Pass 'full' for more correct and slow validation or `false` not to validate formats at all. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode. - _formats_: an object with custom formats. Keys and values will be passed to `addFormat` method. @@ -241,6 +241,11 @@ All validation functions are generated using doT templates in dot folder. Templa ## Changes history +##### 0.6.11 + +Improved/fixed data filtering with `removeAdditional` option. + + ##### 0.6.10 `removeAdditional` option allowing to remove additional properties. diff --git a/lib/ajv.js b/lib/ajv.js index c6452a324..6f5976b5c 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -189,8 +189,12 @@ function Ajv(opts) { function addInitialSchemas() { - if (self.opts.meta !== false) + if (self.opts.meta !== false) { + var currentRemoveAdditional = self.opts.removeAdditional; + self.opts.removeAdditional = false; addSchema(require('./refs/json-schema-draft-04.json'), META_SCHEMA_ID, true); + self.opts.removeAdditional = currentRemoveAdditional; + } var optsSchemas = self.opts.schemas; if (!optsSchemas) return; diff --git a/lib/dot/PERFORMANCE.md b/lib/dot/PERFORMANCE.md deleted file mode 100644 index a365ceb1e..000000000 --- a/lib/dot/PERFORMANCE.md +++ /dev/null @@ -1,24 +0,0 @@ -- [ ] $ref -- [ ] allOf -- [ ] anyOf -- [ ] dependencies -- [ ] enum -- [ ] format -- [ ] index -- [ ] items (+ additionalItems) -- [ ] maxItems -- [ ] maxLength -- [ ] maxProperties -- [ ] maximum (+ exclusiveMaximum) -- [ ] minItems -- [ ] minLength -- [ ] minProperties -- [ ] minimum (+ exclusiveMinimum) -- [ ] multipleOf -- [ ] not -- [ ] oneOf -- [ ] pattern -- [ ] properties (+ patternProperties, additionalProperties) -- [ ] required -- [ ] type -- [x] uniqueItems diff --git a/lib/dot/properties.jst b/lib/dot/properties.jst index 4ee09bd79..4eefb582c 100644 --- a/lib/dot/properties.jst +++ b/lib/dot/properties.jst @@ -13,8 +13,8 @@ , $noAdditional = $aProperties === false , $additionalIsSchema = typeof $aProperties == 'object' && Object.keys($aProperties).length - , $checkAdditional = $noAdditional || $additionalIsSchema - , $removeAdditional = it.opts.removeAdditional; + , $removeAdditional = it.opts.removeAdditional + , $checkAdditional = $noAdditional || $additionalIsSchema || $removeAdditional; }} @@ -23,10 +23,7 @@ var valid{{=$it.level}} = true; {{? $checkAdditional }} var propertiesSchema{{=$lvl}} = validate.schema{{=$schemaPath}} || {}; -{{?}} - -{{? $checkAdditional }} for (var key{{=$lvl}} in {{=$data}}) { var isAdditional{{=$lvl}} = propertiesSchema{{=$lvl}}[key{{=$lvl}}] === undefined; @@ -58,7 +55,10 @@ var valid{{=$it.level}} = true; {{# def.error:'additionalProperties' }} {{? $breakOnError }} break; {{?}} {{?}} - {{??}} + {{?? $additionalIsSchema }} + {{? $removeAdditional == 'failing' }} + var {{=$errs}} = errors; + {{?}} {{ /* additionalProperties is schema */ $it.schema = $aProperties; $it.schemaPath = it.schemaPath + '.additionalProperties'; @@ -70,7 +70,18 @@ var valid{{=$it.level}} = true; {{ var $code = it.validate($it); }} {{# def.optimizeValidate }} - {{? $breakOnError }} if (!valid{{=$it.level}}) break; {{?}} + {{? $removeAdditional == 'failing' }} + if (!valid{{=$it.level}}) { + errors = {{=$errs}}; + if (validate.errors !== null) { + if (errors) validate.errors.length = errors; + else validate.errors = null; + } + delete {{=$data}}[key{{=$lvl}}]; + } + {{??}} + {{? $breakOnError }} if (!valid{{=$it.level}}) break; {{?}} + {{?}} {{?}} {{ it.errorPath = $currentErrorPath; }} {{?}} diff --git a/lib/dotjs/properties.js b/lib/dotjs/properties.js index 352668ab2..5ca18e188 100644 --- a/lib/dotjs/properties.js +++ b/lib/dotjs/properties.js @@ -22,14 +22,11 @@ module.exports = function anonymous(it) { $aProperties = it.schema.additionalProperties, $noAdditional = $aProperties === false, $additionalIsSchema = typeof $aProperties == 'object' && Object.keys($aProperties).length, - $checkAdditional = $noAdditional || $additionalIsSchema, - $removeAdditional = it.opts.removeAdditional; + $removeAdditional = it.opts.removeAdditional, + $checkAdditional = $noAdditional || $additionalIsSchema || $removeAdditional; out += 'var ' + ($errs) + ' = errors;var valid' + ($it.level) + ' = true;'; if ($checkAdditional) { - out += ' var propertiesSchema' + ($lvl) + ' = validate.schema' + ($schemaPath) + ' || {};'; - } - if ($checkAdditional) { - out += ' for (var key' + ($lvl) + ' in ' + ($data) + ') { var isAdditional' + ($lvl) + ' = propertiesSchema' + ($lvl) + '[key' + ($lvl) + '] === undefined; '; + out += ' var propertiesSchema' + ($lvl) + ' = validate.schema' + ($schemaPath) + ' || {}; for (var key' + ($lvl) + ' in ' + ($data) + ') { var isAdditional' + ($lvl) + ' = propertiesSchema' + ($lvl) + '[key' + ($lvl) + '] === undefined; '; if ($pPropertyKeys.length) { out += ' if (isAdditional' + ($lvl) + ') { '; var arr1 = $pPropertyKeys; @@ -74,7 +71,10 @@ module.exports = function anonymous(it) { out += ' break; '; } } - } else { + } else if ($additionalIsSchema) { + if ($removeAdditional == 'failing') { + out += ' var ' + ($errs) + ' = errors; '; + } $it.schema = $aProperties; $it.schemaPath = it.schemaPath + '.additionalProperties'; $it.errorPath = it.errorPath; @@ -86,8 +86,12 @@ module.exports = function anonymous(it) { } else { out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; } - if ($breakOnError) { - out += ' if (!valid' + ($it.level) + ') break; '; + if ($removeAdditional == 'failing') { + out += ' if (!valid' + ($it.level) + ') { errors = ' + ($errs) + '; if (validate.errors !== null) { if (errors) validate.errors.length = errors; else validate.errors = null; } delete ' + ($data) + '[key' + ($lvl) + ']; } '; + } else { + if ($breakOnError) { + out += ' if (!valid' + ($it.level) + ') break; '; + } } } it.errorPath = $currentErrorPath; diff --git a/package.json b/package.json index eccd619c8..10beb2705 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "0.6.10", + "version": "0.6.11", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "scripts": { diff --git a/spec/options.spec.js b/spec/options.spec.js index 31bfecb8c..76c17b948 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -7,10 +7,29 @@ var Ajv = require(typeof window == 'object' ? 'ajv' : '../lib/ajv') describe('Ajv Options', function () { describe('removeAdditional', function() { + it('should remove all additional properties', function() { + var ajv = Ajv({ removeAdditional: 'all' }); + + ajv.addSchema({ + id: '//test/fooBar', + properties: { foo: { type: 'string' }, bar: { type: 'string' } } + }); + + var object = { + foo: 'foo', bar: 'bar', baz: 'baz-to-be-removed' + }; + + ajv.validate('//test/fooBar', object).should.equal(true); + object.should.have.property('foo'); + object.should.have.property('bar'); + object.should.not.have.property('baz'); + }); + + it('should remove properties that would error when `additionalProperties = false`', function() { var ajv = Ajv({ removeAdditional: true }); - ajv.compile({ + ajv.addSchema({ id: '//test/fooBar', properties: { foo: { type: 'string' }, bar: { type: 'string' } }, additionalProperties: false @@ -27,10 +46,10 @@ describe('Ajv Options', function () { }); - it.skip('should remove properties that would error when `additionalProperties` is a schema', function() { - var ajv = Ajv({ removeAdditional: true }); + it('should remove properties that would error when `additionalProperties` is a schema', function() { + var ajv = Ajv({ removeAdditional: 'failing' }); - ajv.compile({ + ajv.addSchema({ id: '//test/fooBar', properties: { foo: { type: 'string' }, bar: { type: 'string' } }, additionalProperties: { type: 'string' }