anyOf, enum, maxItems, minItems, maxLength, minLength, maxProperties, minProperties, multipleOf, oneOf, pattern rules

master
Evgeny Poberezkin 2015-05-28 22:27:16 +01:00
parent 008a93f447
commit 26abbd96d2
16 changed files with 292 additions and 43 deletions

View File

@ -1,7 +1,8 @@
'use strict';
var doT = require('dot')
, fs = require('fs');
, fs = require('fs')
, stableStringify = require('json-stable-stringify');
var RULES = require('./rules')
, _validateTemplateStr = fs.readFileSync(__dirname + '/_validate.dot.js')
@ -19,6 +20,8 @@ function compile(schema) {
RULES: RULES,
_validate: _validateTemplate,
copy: copy,
getDataType: getDataType,
stableStringify: stableStringify,
opts: this.opts
});
// console.log('\n\n\n *** \n', validateCode);

View File

@ -0,0 +1,23 @@
function (data, dataType, dataPath) {
'use strict';
var errors = [];
{{~ it.schema:$schema:$i }}
{{
var $it = it.copy(it);
$it.schema = $schema;
$it.schemaPath = it.schemaPath + '[' + $i + ']';
}}
var result = ({{= it._validate($it) }})(data, dataType, dataPath);
if (result.valid) {
return { valid: true, errors: [] };
} else {
errors.push.apply(errors, result.errors);
}
{{~}}
return { valid: false, errors: errors };
}

View File

@ -0,0 +1,49 @@
function (data, dataType, dataPath) {
'use strict';
var errors = [];
{{
var $simpleTypes = [ 'boolean', 'null', 'number', 'string' ]
, $itemsHash = { 'boolean': {}, 'null': {}, 'number': {}, 'string': {} };
var $onlySimpleTypes = it.schema.every(function ($item) {
var $itemType = typeof $item;
var $isSimpleType = $simpleTypes.indexOf($itemType) >= 0;
if ($isSimpleType) $itemsHash[$itemType][$item] = true;
return $isSimpleType;
});
if (!$onlySimpleTypes) {
var $itemsList = it.schema.map(function ($item) {
return {
type: it.getDataType($item),
valueStr: it.stableStringify($item)
}
});
}
}}
{{? $onlySimpleTypes }}
var itemsHash = {{= JSON.stringify($itemsHash) }};
var valid = itemsHash[dataType] && itemsHash[dataType][data];
{{??}}
var itemsList = {{= JSON.stringify($itemsList) }};
for (var i = 0; i < itemsList.length; i++) {
var item = itemsList[i];
var valid = dataType == item.type && stableStringify(data) == item.valueStr;
if (valid) break;
}
{{?}}
return {
valid: valid || false,
errors: valid ? [] : [{
keyword: 'enum',
schema: self.schema{{= it.schemaPath }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should be equal to one of values in the schema'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -5,10 +5,10 @@ var fs = require('fs')
var RULES = module.exports = {
type: { code: fs.readFileSync(__dirname + '/type.dot.js') },
// enum: { code: '' },
enum: { code: fs.readFileSync(__dirname + '/enum.dot.js') },
allOf: { code: fs.readFileSync(__dirname + '/allOf.dot.js') },
// anyOf: { code: '' },
// oneOf: { code: '' },
anyOf: { code: fs.readFileSync(__dirname + '/anyOf.dot.js') },
oneOf: { code: fs.readFileSync(__dirname + '/oneOf.dot.js') },
not: { code: fs.readFileSync(__dirname + '/not.dot.js') },
maximum: {
code: fs.readFileSync(__dirname + '/maximum.dot.js'),
@ -18,22 +18,22 @@ var RULES = module.exports = {
code: fs.readFileSync(__dirname + '/minimum.dot.js'),
type: 'number'
},
// multipleOf: {
// code: '',
// type: 'number'
// },
// maxLength: {
// code: '',
// type: 'string'
// },
// minLength: {
// code: '',
// type: 'string'
// },
// pattern: {
// code: '',
// type: 'string'
// },
multipleOf: {
code: fs.readFileSync(__dirname + '/multipleOf.dot.js'),
type: 'number'
},
maxLength: {
code: fs.readFileSync(__dirname + '/maxLength.dot.js'),
type: 'string'
},
minLength: {
code: fs.readFileSync(__dirname + '/minLength.dot.js'),
type: 'string'
},
pattern: {
code: fs.readFileSync(__dirname + '/pattern.dot.js'),
type: 'string'
},
// additionalItems: {
// code: '',
// type: 'array'
@ -42,26 +42,26 @@ var RULES = module.exports = {
// code: '',
// type: 'array'
// },
// maxItems: {
// code: '',
// type: 'array'
// },
// minItems: {
// code: '',
// type: 'array'
// },
maxItems: {
code: fs.readFileSync(__dirname + '/maxItems.dot.js'),
type: 'array'
},
minItems: {
code: fs.readFileSync(__dirname + '/minItems.dot.js'),
type: 'array'
},
// uniqueItems: {
// code: '',
// type: 'array'
// },
// maxProperties: {
// code: '',
// type: 'object'
// },
// minProperties: {
// code: '',
// type: 'object'
// },
maxProperties: {
code: fs.readFileSync(__dirname + '/maxProperties.dot.js'),
type: 'object'
},
minProperties: {
code: fs.readFileSync(__dirname + '/minProperties.dot.js'),
type: 'object'
},
required: {
code: fs.readFileSync(__dirname + '/required.dot.js'),
type: 'object'

View File

@ -0,0 +1,16 @@
function (data, dataType, dataPath) {
'use strict';
var valid = data.length <= {{= it.schema }};
return {
valid: valid,
errors: valid ? [] : [{
keyword: 'maxItems',
schema: {{= it.schema }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should NOT have more than {{= it.schema }} items'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -0,0 +1,16 @@
function (data, dataType, dataPath) {
'use strict';
var valid = data.length <= {{= it.schema }};
return {
valid: valid,
errors: valid ? [] : [{
keyword: 'maxLength',
schema: {{= it.schema }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should NOT be longer than {{= it.schema }} characters'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -0,0 +1,17 @@
function (data, dataType, dataPath) {
'use strict';
var propertiesNum = Object.keys(data).length;
var valid = propertiesNum <= {{= it.schema }};
return {
valid: valid,
errors: valid ? [] : [{
keyword: 'maxProperties',
schema: {{= it.schema }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should NOT have more than {{= it.schema }} properties'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -0,0 +1,16 @@
function (data, dataType, dataPath) {
'use strict';
var valid = data.length >= {{= it.schema }};
return {
valid: valid,
errors: valid ? [] : [{
keyword: 'minItems',
schema: {{= it.schema }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should NOT have less than {{= it.schema }} items'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -0,0 +1,16 @@
function (data, dataType, dataPath) {
'use strict';
var valid = data.length >= {{= it.schema }};
return {
valid: valid,
errors: valid ? [] : [{
keyword: 'minLength',
schema: {{= it.schema }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should NOT be shorter than {{= it.schema }} characters'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -0,0 +1,17 @@
function (data, dataType, dataPath) {
'use strict';
var propertiesNum = Object.keys(data).length;
var valid = propertiesNum >= {{= it.schema }};
return {
valid: valid,
errors: valid ? [] : [{
keyword: 'minProperties',
schema: {{= it.schema }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should NOT have less than {{= it.schema }} properties'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -0,0 +1,16 @@
function (data, dataType, dataPath) {
'use strict';
var division = data / {{= it.schema }};
var valid = division === parseInt(division);
return {
valid: valid,
errors: valid ? [] : [{
keyword: 'multipleOf',
schema: {{= it.schema }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should be multiple of {{= it.schema }}'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -0,0 +1,38 @@
function (data, dataType, dataPath) {
'use strict';
var errors = [], foundValid = false;
{{~ it.schema:$schema:$i }}
{{
var $it = it.copy(it);
$it.schema = $schema;
$it.schemaPath = it.schemaPath + '[' + $i + ']';
}}
var result = ({{= it._validate($it) }})(data, dataType, dataPath);
if (result.valid) {
if (foundValid) return { valid: false, errors: [{
keyword: 'oneOf',
schema: self.schema{{= it.schemaPath }},
dataPath: dataPath,
message: 'data' + dataPath + ' NOT valid, matches more that one schema in oneOf'
{{? it.opts.verbose }}, data: data{{?}}
}] };
foundValid = true;
} else
errors.push.apply(errors, result.errors);
{{~}}
if (foundValid) errors = [];
else errors.push({
keyword: 'oneOf',
schema: self.schema{{= it.schemaPath }},
dataPath: dataPath,
message: 'data' + dataPath + ' NOT valid, matches none of the schemas in oneOf'
{{? it.opts.verbose }}, data: data{{?}}
});
return { valid: foundValid, errors: errors };
}

View File

@ -0,0 +1,16 @@
function (data, dataType, dataPath) {
'use strict';
var valid = /{{= it.schema }}/.test(data);
return {
valid: valid,
errors: valid ? [] : [{
keyword: 'minimum',
schema: '{{= it.schema }}',
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, should match pattern "{{= it.schema }}"'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}

View File

@ -27,10 +27,10 @@ function (data, dataType, dataPath) {
valid: valid,
errors: valid ? [] : [{
keyword: 'required',
schema: self.schema,
schema: self.schema{{= it.schemaPath }},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid, properties ' + missing + ' are missing'
{{? it.opts.verbose }}, data: data{{?}}
}]
};
}
}

View File

@ -41,7 +41,7 @@ function (data, dataType, dataPath) {
valid: valid,
errors: valid ? [] : [{
keyword: 'type',
schema: {{? $isArray }}self.schema{{= it.schemaPath }}{{??}}"{{= $schema }}"{{?}},
schema: {{? $isArray }}self.schema{{= it.schemaPath }}{{??}}'{{= $schema }}'{{?}},
dataPath: dataPath,
message: 'data' + dataPath + ' is not valid. Expected {{? $isArray }}{{= $schema.join(",") }}{{??}}{{= $schema }}{{?}}'
{{? it.opts.verbose }}, data: data{{?}}

View File

@ -6,8 +6,10 @@ var fs = require('fs')
, TESTS_PATH = 'JSON-Schema-Test-Suite/tests/draft4/';
var ONLY_RULES;
// ONLY_RULES = ['properties'];
ONLY_RULES = ['type', 'not', 'maximum', 'minimum', 'allOf', 'properties', 'required'];
// ONLY_RULES = ['maxItems', 'minItems'];
// ONLY_RULES = ['type', 'not', 'maximum', 'minimum', 'allOf', 'anyOf', 'oneOf', 'enum',
// 'properties', 'required', 'multipleOf', 'maxProperties', 'minProperties',
// 'maxLength', 'minLength', 'pattern', 'maxItems', 'minItems'];
var Jv = require('../lib/jv')
@ -23,7 +25,7 @@ describe.only('JSON-Schema tests', function () {
describe(file.name, function() {
var testSets = require(file.path);
testSets.forEach(function (testSet) {
// if (testSet.description != 'not more complex schema') return;
// if (testSet.description != 'oneOf') return;
describe(testSet.description, function() {
// it(testSet.description, function() {
var validate, fullValidate;
@ -33,15 +35,19 @@ describe.only('JSON-Schema tests', function () {
});
testSet.tests.forEach(function (test) {
// if (test.description != 'both properties present and valid is valid') return;
// if (test.description != 'neither oneOf valid') return;
it(test.description, function() {
var result = validate(test.data);
// console.log('result', result);
assert.equal(result.valid, test.valid);
if (result.valid) assert(result.errors.length == 0);
else assert(result.errors.length > 0);
var result = fullValidate(test.data);
// console.log('full result', result);
assert.equal(result.valid, test.valid);
if (result.valid) assert(result.errors.length == 0);
else assert(result.errors.length > 0);
});
});
});