Merge branch 'master' into 1.0.0

master
Evgeny Poberezkin 2015-07-31 20:06:43 +01:00
commit 94f51e1248
9 changed files with 186 additions and 117 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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; }}
}
}

View File

@ -29,7 +29,7 @@
{{??}}
{{? it.opts._debug }} console.log('validate dataPath:', dataPath); {{?}}
{{
{{
var $lvl = it.level
, $dataLvl = it.dataLevel
, $data = 'data' + ($dataLvl || '');

View File

@ -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) + ') { ';

View File

@ -1,4 +0,0 @@
{
"username": "epoberezkin",
"accessKey": "6ab61c89-e0d3-46bc-8719-aa44d58cea84"
}

69
spec/options.spec.js Normal file
View File

@ -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');
});
});
});