Merge branch 'master' into 1.0.0
commit
94f51e1248
17
README.md
17
README.md
|
@ -108,6 +108,13 @@ You can add additional formats and replace any of the formats above using [addFo
|
|||
You can find patterns used for format validation and the sources that were used in [formats.js](https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js).
|
||||
|
||||
|
||||
## Filtering data
|
||||
|
||||
With [option `removeAdditional`](#options) (added by [andyscott](https://github.com/andyscott)) you can filter data during the validation.
|
||||
|
||||
This option modifies original object.
|
||||
|
||||
|
||||
## API
|
||||
|
||||
##### Ajv(Object options) -> Object
|
||||
|
@ -197,6 +204,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. 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.
|
||||
|
@ -232,6 +240,15 @@ 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.
|
||||
|
||||
|
||||
##### 1.0.0
|
||||
|
||||
|
|
25
circle.yml
25
circle.yml
|
@ -1,25 +0,0 @@
|
|||
machine:
|
||||
environment:
|
||||
SAUCE_USERNAME: epoberezkin
|
||||
SAUCE_ACCESS_KEY: 6ab61c89-e0d3-46bc-8719-aa44d58cea84
|
||||
checkout:
|
||||
post:
|
||||
- git submodule update --init
|
||||
dependencies:
|
||||
post:
|
||||
- wget https://saucelabs.com/downloads/sc-4.3-linux.tar.gz
|
||||
- tar -xzf sc-4.3-linux.tar.gz
|
||||
- npm install -g karma
|
||||
- npm install -g browserify
|
||||
test:
|
||||
override:
|
||||
- npm test
|
||||
- ./bin/prepare-tests
|
||||
- echo $SAUCE_USERNAME $SAUCE_ACCESS_KEY
|
||||
- ./sc-4.3-linux/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY -f ~/sc_ready:
|
||||
background: true
|
||||
pwd: sc-4.3-linux
|
||||
# Wait for tunnel to be ready
|
||||
- sleep 10
|
||||
# while [ ! -e ~/sc_ready ]; do sleep 1; done
|
||||
- karma start karma.sauce.js
|
|
@ -230,8 +230,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;
|
||||
|
|
|
@ -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
|
|
@ -13,7 +13,8 @@
|
|||
, $noAdditional = $aProperties === false
|
||||
, $additionalIsSchema = typeof $aProperties == 'object'
|
||||
&& Object.keys($aProperties).length
|
||||
, $checkAdditional = $noAdditional || $additionalIsSchema;
|
||||
, $removeAdditional = it.opts.removeAdditional
|
||||
, $checkAdditional = $noAdditional || $additionalIsSchema || $removeAdditional;
|
||||
}}
|
||||
|
||||
|
||||
|
@ -22,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;
|
||||
|
||||
|
@ -42,30 +40,51 @@ var valid{{=$it.level}} = true;
|
|||
{{?}}
|
||||
|
||||
if (isAdditional{{=$lvl}}) {
|
||||
{{
|
||||
var $currentErrorPath = it.errorPath;
|
||||
it.errorPath = (it.errorPath + ' + "[\'" + key' + $lvl + ' + "\']"').replace('" + "', '');
|
||||
}}
|
||||
{{? $noAdditional }}
|
||||
valid{{=$it.level}} = false;
|
||||
|
||||
{{# def.error:'additionalProperties' }}
|
||||
{{? $breakOnError }} break; {{?}}
|
||||
{{? $removeAdditional == 'all' }}
|
||||
delete {{=$data}}[key{{=$lvl}}];
|
||||
{{??}}
|
||||
{{ /* additionalProperties is schema */
|
||||
$it.schema = $aProperties;
|
||||
$it.schemaPath = it.schemaPath + '.additionalProperties';
|
||||
$it.errorPath = it.errorPath;
|
||||
$it.dataPath = it.dataPath + '[key' + $lvl + ']';
|
||||
var $passData = $data + '[key' + $lvl + ']';
|
||||
{{
|
||||
var $currentErrorPath = it.errorPath;
|
||||
it.errorPath = (it.errorPath + ' + "[\'" + key' + $lvl + ' + "\']"').replace('" + "', '');
|
||||
}}
|
||||
{{? $noAdditional }}
|
||||
{{? $removeAdditional }}
|
||||
delete {{=$data}}[key{{=$lvl}}];
|
||||
{{??}}
|
||||
valid{{=$it.level}} = false;
|
||||
{{# def.error:'additionalProperties' }}
|
||||
{{? $breakOnError }} break; {{?}}
|
||||
{{?}}
|
||||
{{?? $additionalIsSchema }}
|
||||
{{? $removeAdditional == 'failing' }}
|
||||
var {{=$errs}} = errors;
|
||||
{{?}}
|
||||
{{ /* additionalProperties is schema */
|
||||
$it.schema = $aProperties;
|
||||
$it.schemaPath = it.schemaPath + '.additionalProperties';
|
||||
$it.errorPath = it.errorPath;
|
||||
$it.dataPath = it.dataPath + '[key' + $lvl + ']';
|
||||
var $passData = $data + '[key' + $lvl + ']';
|
||||
}}
|
||||
|
||||
{{ var $code = it.validate($it); }}
|
||||
{{# def.optimizeValidate }}
|
||||
{{ 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; }}
|
||||
{{?}}
|
||||
{{ it.errorPath = $currentErrorPath; }}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
{{??}}
|
||||
{{? it.opts._debug }} console.log('validate dataPath:', dataPath); {{?}}
|
||||
|
||||
{{
|
||||
{{
|
||||
var $lvl = it.level
|
||||
, $dataLvl = it.dataLevel
|
||||
, $data = 'data' + ($dataLvl || '');
|
||||
|
|
|
@ -22,13 +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,
|
||||
$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;
|
||||
|
@ -46,43 +44,58 @@ module.exports = function anonymous(it) {
|
|||
out += ' } ';
|
||||
}
|
||||
out += ' if (isAdditional' + ($lvl) + ') { ';
|
||||
var $currentErrorPath = it.errorPath;
|
||||
it.errorPath = (it.errorPath + ' + "[\'" + key' + $lvl + ' + "\']"').replace('" + "', '');
|
||||
if ($noAdditional) {
|
||||
out += ' valid' + ($it.level) + ' = false; ';
|
||||
if (it.wasTop && $breakOnError) {
|
||||
out += ' validate.errors = [ { keyword: \'' + ('additionalProperties') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'additional properties NOT allowed\' ';
|
||||
if (it.opts.verbose) {
|
||||
out += ', schema: false, data: ' + ($data);
|
||||
}
|
||||
out += ' }]; return false; ';
|
||||
} else {
|
||||
out += ' var err = { keyword: \'' + ('additionalProperties') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'additional properties NOT allowed\' ';
|
||||
if (it.opts.verbose) {
|
||||
out += ', schema: false, data: ' + ($data);
|
||||
}
|
||||
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
|
||||
}
|
||||
if ($breakOnError) {
|
||||
out += ' break; ';
|
||||
}
|
||||
if ($removeAdditional == 'all') {
|
||||
out += ' delete ' + ($data) + '[key' + ($lvl) + ']; ';
|
||||
} else {
|
||||
$it.schema = $aProperties;
|
||||
$it.schemaPath = it.schemaPath + '.additionalProperties';
|
||||
$it.errorPath = it.errorPath;
|
||||
$it.dataPath = it.dataPath + '[key' + $lvl + ']';
|
||||
var $passData = $data + '[key' + $lvl + ']';
|
||||
var $code = it.validate($it);
|
||||
if (it.util.varOccurences($code, $nextData) < 2) {
|
||||
out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
|
||||
} else {
|
||||
out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
|
||||
}
|
||||
if ($breakOnError) {
|
||||
out += ' if (!valid' + ($it.level) + ') break; ';
|
||||
var $currentErrorPath = it.errorPath;
|
||||
it.errorPath = (it.errorPath + ' + "[\'" + key' + $lvl + ' + "\']"').replace('" + "', '');
|
||||
if ($noAdditional) {
|
||||
if ($removeAdditional) {
|
||||
out += ' delete ' + ($data) + '[key' + ($lvl) + ']; ';
|
||||
} else {
|
||||
out += ' valid' + ($it.level) + ' = false; ';
|
||||
if (it.wasTop && $breakOnError) {
|
||||
out += ' validate.errors = [ { keyword: \'' + ('additionalProperties') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'additional properties NOT allowed\' ';
|
||||
if (it.opts.verbose) {
|
||||
out += ', schema: false, data: ' + ($data);
|
||||
}
|
||||
out += ' }]; return false; ';
|
||||
} else {
|
||||
out += ' var err = { keyword: \'' + ('additionalProperties') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'additional properties NOT allowed\' ';
|
||||
if (it.opts.verbose) {
|
||||
out += ', schema: false, data: ' + ($data);
|
||||
}
|
||||
out += ' }; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
|
||||
}
|
||||
if ($breakOnError) {
|
||||
out += ' break; ';
|
||||
}
|
||||
}
|
||||
} else if ($additionalIsSchema) {
|
||||
if ($removeAdditional == 'failing') {
|
||||
out += ' var ' + ($errs) + ' = errors; ';
|
||||
}
|
||||
$it.schema = $aProperties;
|
||||
$it.schemaPath = it.schemaPath + '.additionalProperties';
|
||||
$it.errorPath = it.errorPath;
|
||||
$it.dataPath = it.dataPath + '[key' + $lvl + ']';
|
||||
var $passData = $data + '[key' + $lvl + ']';
|
||||
var $code = it.validate($it);
|
||||
if (it.util.varOccurences($code, $nextData) < 2) {
|
||||
out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
|
||||
} else {
|
||||
out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
|
||||
}
|
||||
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;
|
||||
}
|
||||
it.errorPath = $currentErrorPath;
|
||||
out += ' } } ';
|
||||
if ($breakOnError) {
|
||||
out += ' if (valid' + ($it.level) + ') { ';
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"username": "epoberezkin",
|
||||
"accessKey": "6ab61c89-e0d3-46bc-8719-aa44d58cea84"
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
var Ajv = require(typeof window == 'object' ? 'ajv' : '../lib/ajv')
|
||||
, should = require('chai').should();
|
||||
|
||||
|
||||
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.addSchema({
|
||||
id: '//test/fooBar',
|
||||
properties: { foo: { type: 'string' }, bar: { type: 'string' } },
|
||||
additionalProperties: false
|
||||
});
|
||||
|
||||
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` is a schema', function() {
|
||||
var ajv = Ajv({ removeAdditional: 'failing' });
|
||||
|
||||
ajv.addSchema({
|
||||
id: '//test/fooBar',
|
||||
properties: { foo: { type: 'string' }, bar: { type: 'string' } },
|
||||
additionalProperties: { type: 'string' }
|
||||
});
|
||||
|
||||
var object = {
|
||||
foo: 'foo', bar: 'bar', baz: 'baz-to-be-kept', fizz: 1000
|
||||
};
|
||||
|
||||
ajv.validate('//test/fooBar', object).should.equal(true);
|
||||
object.should.have.property('foo');
|
||||
object.should.have.property('bar');
|
||||
object.should.have.property('baz');
|
||||
object.should.not.have.property('fizz');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue