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).
|
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
|
## API
|
||||||
|
|
||||||
##### Ajv(Object options) -> Object
|
##### Ajv(Object options) -> Object
|
||||||
|
@ -197,6 +204,7 @@ Options can have properties `separator` (string used to separate errors, ", " by
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
- _allErrors_: check all rules collecting all errors. Default is to return after the first error.
|
- _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).
|
- _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.
|
- _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.
|
- _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
|
## 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
|
##### 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() {
|
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);
|
addSchema(require('./refs/json-schema-draft-04.json'), META_SCHEMA_ID, true);
|
||||||
|
self.opts.removeAdditional = currentRemoveAdditional;
|
||||||
|
}
|
||||||
|
|
||||||
var optsSchemas = self.opts.schemas;
|
var optsSchemas = self.opts.schemas;
|
||||||
if (!optsSchemas) return;
|
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
|
, $noAdditional = $aProperties === false
|
||||||
, $additionalIsSchema = typeof $aProperties == 'object'
|
, $additionalIsSchema = typeof $aProperties == 'object'
|
||||||
&& Object.keys($aProperties).length
|
&& 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 }}
|
{{? $checkAdditional }}
|
||||||
var propertiesSchema{{=$lvl}} = validate.schema{{=$schemaPath}} || {};
|
var propertiesSchema{{=$lvl}} = validate.schema{{=$schemaPath}} || {};
|
||||||
{{?}}
|
|
||||||
|
|
||||||
|
|
||||||
{{? $checkAdditional }}
|
|
||||||
for (var key{{=$lvl}} in {{=$data}}) {
|
for (var key{{=$lvl}} in {{=$data}}) {
|
||||||
var isAdditional{{=$lvl}} = propertiesSchema{{=$lvl}}[key{{=$lvl}}] === undefined;
|
var isAdditional{{=$lvl}} = propertiesSchema{{=$lvl}}[key{{=$lvl}}] === undefined;
|
||||||
|
|
||||||
|
@ -42,30 +40,51 @@ var valid{{=$it.level}} = true;
|
||||||
{{?}}
|
{{?}}
|
||||||
|
|
||||||
if (isAdditional{{=$lvl}}) {
|
if (isAdditional{{=$lvl}}) {
|
||||||
{{
|
{{? $removeAdditional == 'all' }}
|
||||||
var $currentErrorPath = it.errorPath;
|
delete {{=$data}}[key{{=$lvl}}];
|
||||||
it.errorPath = (it.errorPath + ' + "[\'" + key' + $lvl + ' + "\']"').replace('" + "', '');
|
|
||||||
}}
|
|
||||||
{{? $noAdditional }}
|
|
||||||
valid{{=$it.level}} = false;
|
|
||||||
|
|
||||||
{{# def.error:'additionalProperties' }}
|
|
||||||
{{? $breakOnError }} break; {{?}}
|
|
||||||
{{??}}
|
{{??}}
|
||||||
{{ /* additionalProperties is schema */
|
{{
|
||||||
$it.schema = $aProperties;
|
var $currentErrorPath = it.errorPath;
|
||||||
$it.schemaPath = it.schemaPath + '.additionalProperties';
|
it.errorPath = (it.errorPath + ' + "[\'" + key' + $lvl + ' + "\']"').replace('" + "', '');
|
||||||
$it.errorPath = it.errorPath;
|
|
||||||
$it.dataPath = it.dataPath + '[key' + $lvl + ']';
|
|
||||||
var $passData = $data + '[key' + $lvl + ']';
|
|
||||||
}}
|
}}
|
||||||
|
{{? $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); }}
|
{{ var $code = it.validate($it); }}
|
||||||
{{# def.optimizeValidate }}
|
{{# 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; }}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,11 @@ module.exports = function anonymous(it) {
|
||||||
$aProperties = it.schema.additionalProperties,
|
$aProperties = it.schema.additionalProperties,
|
||||||
$noAdditional = $aProperties === false,
|
$noAdditional = $aProperties === false,
|
||||||
$additionalIsSchema = typeof $aProperties == 'object' && Object.keys($aProperties).length,
|
$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;';
|
out += 'var ' + ($errs) + ' = errors;var valid' + ($it.level) + ' = true;';
|
||||||
if ($checkAdditional) {
|
if ($checkAdditional) {
|
||||||
out += ' var propertiesSchema' + ($lvl) + ' = validate.schema' + ($schemaPath) + ' || {};';
|
out += ' var propertiesSchema' + ($lvl) + ' = validate.schema' + ($schemaPath) + ' || {}; for (var key' + ($lvl) + ' in ' + ($data) + ') { var isAdditional' + ($lvl) + ' = propertiesSchema' + ($lvl) + '[key' + ($lvl) + '] === undefined; ';
|
||||||
}
|
|
||||||
if ($checkAdditional) {
|
|
||||||
out += ' for (var key' + ($lvl) + ' in ' + ($data) + ') { var isAdditional' + ($lvl) + ' = propertiesSchema' + ($lvl) + '[key' + ($lvl) + '] === undefined; ';
|
|
||||||
if ($pPropertyKeys.length) {
|
if ($pPropertyKeys.length) {
|
||||||
out += ' if (isAdditional' + ($lvl) + ') { ';
|
out += ' if (isAdditional' + ($lvl) + ') { ';
|
||||||
var arr1 = $pPropertyKeys;
|
var arr1 = $pPropertyKeys;
|
||||||
|
@ -46,43 +44,58 @@ module.exports = function anonymous(it) {
|
||||||
out += ' } ';
|
out += ' } ';
|
||||||
}
|
}
|
||||||
out += ' if (isAdditional' + ($lvl) + ') { ';
|
out += ' if (isAdditional' + ($lvl) + ') { ';
|
||||||
var $currentErrorPath = it.errorPath;
|
if ($removeAdditional == 'all') {
|
||||||
it.errorPath = (it.errorPath + ' + "[\'" + key' + $lvl + ' + "\']"').replace('" + "', '');
|
out += ' delete ' + ($data) + '[key' + ($lvl) + ']; ';
|
||||||
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; ';
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$it.schema = $aProperties;
|
var $currentErrorPath = it.errorPath;
|
||||||
$it.schemaPath = it.schemaPath + '.additionalProperties';
|
it.errorPath = (it.errorPath + ' + "[\'" + key' + $lvl + ' + "\']"').replace('" + "', '');
|
||||||
$it.errorPath = it.errorPath;
|
if ($noAdditional) {
|
||||||
$it.dataPath = it.dataPath + '[key' + $lvl + ']';
|
if ($removeAdditional) {
|
||||||
var $passData = $data + '[key' + $lvl + ']';
|
out += ' delete ' + ($data) + '[key' + ($lvl) + ']; ';
|
||||||
var $code = it.validate($it);
|
} else {
|
||||||
if (it.util.varOccurences($code, $nextData) < 2) {
|
out += ' valid' + ($it.level) + ' = false; ';
|
||||||
out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
|
if (it.wasTop && $breakOnError) {
|
||||||
} else {
|
out += ' validate.errors = [ { keyword: \'' + ('additionalProperties') + '\', dataPath: (dataPath || \'\') + ' + (it.errorPath) + ', message: \'additional properties NOT allowed\' ';
|
||||||
out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
|
if (it.opts.verbose) {
|
||||||
}
|
out += ', schema: false, data: ' + ($data);
|
||||||
if ($breakOnError) {
|
}
|
||||||
out += ' if (!valid' + ($it.level) + ') break; ';
|
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 += ' } } ';
|
out += ' } } ';
|
||||||
if ($breakOnError) {
|
if ($breakOnError) {
|
||||||
out += ' if (valid' + ($it.level) + ') { ';
|
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