feat: support numeric exclusiveMaximum/Minimum (no $data support yet), #367

master
Evgeny Poberezkin 2016-12-28 23:08:25 +00:00
parent 7f8a40af4b
commit 4a9d765e5a
9 changed files with 657 additions and 31 deletions

View File

@ -22,24 +22,26 @@ var KEYWORDS = [
'const'
];
module.exports = function (metaSchema, keywordsJsonPointer) {
metaSchema = JSON.parse(JSON.stringify(metaSchema));
var segments = keywordsJsonPointer.split('/');
var keywords = metaSchema;
var i;
for (i=1; i<segments.length; i++)
keywords = keywords[segments[i]];
module.exports = function (metaSchema, keywordsJsonPointers) {
for (var i=0; i<keywordsJsonPointers.length;i++) {
metaSchema = JSON.parse(JSON.stringify(metaSchema));
var segments = keywordsJsonPointers[i].split('/');
var keywords = metaSchema;
var j;
for (j=1; j<segments.length; j++)
keywords = keywords[segments[j]];
for (i=0; i<KEYWORDS.length; i++) {
var key = KEYWORDS[i];
var schema = keywords[key];
if (schema) {
keywords[key] = {
anyOf: [
schema,
{ $ref: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#' }
]
};
for (j=0; j<KEYWORDS.length; j++) {
var key = KEYWORDS[j];
var schema = keywords[key];
if (schema) {
keywords[key] = {
anyOf: [
schema,
{ $ref: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#' }
]
};
}
}
}

View File

@ -46,6 +46,11 @@ function SCHEMA_URI_FORMAT_FUNC(str) {
}
var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes' ];
var META_SUPPORT_DATA = [
'/properties',
'/dependencies/exclusiveMaximum/anyOf/0/properties',
'/dependencies/exclusiveMinimum/anyOf/0/properties'
];
/**
* Creates validator instance.
@ -403,7 +408,7 @@ function addDraft6MetaSchema(self) {
if (self._opts.$data) {
var $dataSchema = require('./refs/$data.json');
self.addMetaSchema($dataSchema, $dataSchema.id, true);
metaSchema = $dataMetaSchema(metaSchema, '/properties');
metaSchema = $dataMetaSchema(metaSchema, META_SUPPORT_DATA);
}
self.addMetaSchema(metaSchema, META_SCHEMA_ID, true);
self._refs['http://json-schema.org/schema'] = META_SCHEMA_ID;

View File

@ -6,7 +6,8 @@ var ruleModules = require('./_rules')
module.exports = function rules() {
var RULES = [
{ type: 'number',
rules: [ 'maximum', 'minimum', 'multipleOf'] },
rules: [ { 'maximum': ['exclusiveMaximum'] },
{ 'minimum': ['exclusiveMinimum'] }, 'multipleOf'] },
{ type: 'string',
rules: [ 'maxLength', 'minLength', 'pattern', 'format' ] },
{ type: 'array',
@ -19,8 +20,8 @@ module.exports = function rules() {
var ALL = [ 'type' ];
var KEYWORDS = [
'additionalItems', 'exclusiveMinimum', 'exclusiveMaximum',
'$schema', 'id', 'title', 'description', 'default', 'definitions'
'additionalItems', '$schema', 'id', 'title',
'description', 'default', 'definitions'
];
var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ];
RULES.all = toHash(ALL);

View File

@ -34,15 +34,40 @@
var op{{=$lvl}} = exclusive{{=$lvl}} ? '{{=$op}}' : '{{=$op}}=';
{{??}}
{{
var $exclusive = $schemaExcl === true
var $exclIsNumber = typeof $schemaExcl == 'number'
, $opStr = $op; /*used in error*/
if (!$exclusive) $opStr += '=';
var $opExpr = '\'' + $opStr + '\''; /*used in error*/
}}
if ({{# def.$dataNotType:'number' }}
{{=$data}} {{=$notOp}}{{?$exclusive}}={{?}} {{=$schemaValue}}
|| {{=$data}} !== {{=$data}}) {
{{? $exclIsNumber && $isData }}
{{ var $opExpr = '\'' + $opStr + '\''; /*used in error*/ }}
if ({{# def.$dataNotType:'number' }}
( {{=$schemaValue}} === undefined
|| {{=$schemaExcl}} {{=$op}}= {{=$schemaValue}}
? {{=$data}} {{=$notOp}}= {{=$schemaExcl}}
: {{=$data}} {{=$notOp}} {{=$schemaValue}} )
|| {{=$data}} !== {{=$data}}) {
{{??}}
{{
if ($exclIsNumber && $schema === undefined) {
$schemaValue = $schemaExcl;
$notOp += '=';
} else {
if ($exclIsNumber)
$schemaValue = Math[$isMax ? 'min' : 'max']($schemaExcl, $schema);
if ($schemaExcl === ($exclIsNumber ? $schemaValue : true))
$notOp += '=';
else
$opStr += '=';
}
var $opExpr = '\'' + $opStr + '\''; /*used in error*/
}}
if ({{# def.$dataNotType:'number' }}
{{=$data}} {{=$notOp}} {{=$schemaValue}}
|| {{=$data}} !== {{=$data}}) {
{{?}}
{{?}}
{{ var $errorKeyword = $keyword; }}
{{# def.error:'_limit' }}

View File

@ -61,14 +61,14 @@
"type": "number"
},
"exclusiveMaximum": {
"type": "boolean",
"type": ["number", "boolean"],
"default": false
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "boolean",
"type": ["number", "boolean"],
"default": false
},
"maxLength": { "$ref": "#/definitions/positiveInteger" },
@ -145,8 +145,18 @@
"not": { "$ref": "#" }
},
"dependencies": {
"exclusiveMaximum": [ "maximum" ],
"exclusiveMinimum": [ "minimum" ]
"exclusiveMaximum": {
"anyOf": [
{ "properties": { "exclusiveMaximum": { "type": "number" } } },
{ "required": ["maximum"] }
]
},
"exclusiveMinimum": {
"anyOf": [
{ "properties": { "exclusiveMinimum": { "type": "number" } } },
{ "required": ["minimum"] }
]
}
},
"default": {}
}

View File

@ -0,0 +1,198 @@
[
{
"description": "one property is exclusiveMaximum for another",
"schema": {
"properties": {
"larger": {},
"smaller": {
"exclusiveMaximum": { "$data": "1/larger" }
}
}
},
"tests": [
{
"skip": true,
"description": "below the exclusiveMaximum is valid",
"data": {
"larger": 3,
"smaller": 2
},
"valid": true
},
{
"description": "equal to the exclusiveMaximum is invalid",
"data": {
"larger": 3,
"smaller": 3
},
"valid": false
},
{
"description": "above the exclusiveMaximum is invalid",
"data": {
"larger": 3,
"smaller": 4
},
"valid": false
},
{
"description": "ignores non-numbers",
"data": {
"larger": 3,
"smaller": "4"
},
"valid": true
},
{
"description": "fails if value of exclusiveMaximum is not number",
"data": {
"larger": "3",
"smaller": 2
},
"valid": false
},
{
"description": "valid if value of exclusiveMaximum is undefined",
"data": {
"smaller": 2
},
"valid": true
}
]
},
{
"description": "exclusiveMaximum as number and maximum as $data, exclusiveMaximum > maximum",
"schema": {
"properties": {
"larger": {},
"smaller": {
"exclusiveMaximum": 3.5,
"maximum": { "$data": "1/larger" }
}
}
},
"tests": [
{
"description": "below the maximum is valid",
"data": {
"larger": 3,
"smaller": 2
},
"valid": true
},
{
"description": "equal to the maximum is valid",
"data": {
"larger": 3,
"smaller": 3
},
"valid": true
},
{
"description": "above the maximum is invalid",
"data": {
"larger": 3,
"smaller": 3.2
},
"valid": false
}
]
},
{
"description": "exclusiveMaximum as number and maximum as $data, exclusiveMaximum = maximum",
"schema": {
"properties": {
"larger": {},
"smaller": {
"exclusiveMaximum": 3,
"maximum": { "$data": "1/larger" }
}
}
},
"tests": [
{
"description": "below the maximum is valid",
"data": {
"larger": 3,
"smaller": 2
},
"valid": true
},
{
"description": "boundary point is invalid",
"data": {
"larger": 3,
"smaller": 3
},
"valid": false
},
{
"description": "above the maximum is invalid",
"data": {
"larger": 3,
"smaller": 4
},
"valid": false
}
]
},
{
"description": "exclusiveMaximum as number and maximum as $data, exclusiveMaximum < maximum",
"schema": {
"properties": {
"larger": {},
"smaller": {
"exclusiveMaximum": 2.5,
"maximum": { "$data": "1/larger" }
}
}
},
"tests": [
{
"description": "below the exclusiveMaximum is valid",
"data": {
"larger": 3,
"smaller": 2
},
"valid": true
},
{
"description": "boundary point is invalid",
"data": {
"larger": 3,
"smaller": 2.5
},
"valid": false
},
{
"description": "above the exclusiveMaximum is invalid",
"data": {
"larger": 3,
"smaller": 2.8
},
"valid": false
}
]
},
{
"description": "items in array are < than their indeces",
"schema": {
"items": {
"exclusiveMaximum": { "$data": "0#" }
}
},
"tests": [
{
"skip": true,
"description": "valid array",
"data": ["", 0, 1, 2, 3, 4],
"valid": true
},
{
"description": "invalid array (1=1)",
"data": ["", 1],
"valid": false
}
]
}
]

View File

@ -0,0 +1,191 @@
[
{
"description": "one property is exclusiveMinimum for another",
"schema": {
"properties": {
"smaller": {},
"larger": {
"exclusiveMinimum": { "$data": "1/smaller" }
}
}
},
"tests": [
{
"skip": true,
"description": "above the exclusiveMinimum is valid",
"data": {
"smaller": 3,
"larger": 4
},
"valid": true
},
{
"description": "equal to the exclusiveMinimum is invalid",
"data": {
"smaller": 3,
"larger": 3
},
"valid": false
},
{
"description": "below the exclusiveMinimum is invalid",
"data": {
"smaller": 3,
"larger": 2
},
"valid": false
},
{
"description": "ignores non-numbers",
"data": {
"smaller": 3,
"larger": "2"
},
"valid": true
},
{
"description": "fails if value of exclusiveMinimum is not number",
"data": {
"smaller": "3",
"larger": 4
},
"valid": false
}
]
},
{
"description": "exclusiveMinimum as number and minimum as $data, exclusiveMinimum < minimum",
"schema": {
"properties": {
"smaller": {},
"larger": {
"exclusiveMinimum": 2.5,
"minimum": { "$data": "1/smaller" }
}
}
},
"tests": [
{
"description": "above the minimum is valid",
"data": {
"smaller": 3,
"larger": 4
},
"valid": true
},
{
"description": "equal to the minimum is valid",
"data": {
"smaller": 3,
"larger": 3
},
"valid": true
},
{
"description": "below the minimum is invalid",
"data": {
"smaller": 3,
"larger": 2.8
},
"valid": false
}
]
},
{
"description": "exclusiveMinimum as number and minimum as $data, exclusiveMinimum = minimum",
"schema": {
"properties": {
"smaller": {},
"larger": {
"exclusiveMinimum": 3,
"minimum": { "$data": "1/smaller" }
}
}
},
"tests": [
{
"description": "above the minimum is valid",
"data": {
"smaller": 3,
"larger": 4
},
"valid": true
},
{
"description": "boundary point is invalid",
"data": {
"smaller": 3,
"larger": 3
},
"valid": false
},
{
"description": "below the minimum is invalid",
"data": {
"smaller": 3,
"larger": 2
},
"valid": false
}
]
},
{
"description": "exclusiveMinimum as number and minimum as $data, exclusiveMinimum > minimum",
"schema": {
"properties": {
"smaller": {},
"larger": {
"exclusiveMinimum": 3.5,
"minimum": { "$data": "1/smaller" }
}
}
},
"tests": [
{
"description": "above the exclusiveMinimum is valid",
"data": {
"smaller": 3,
"larger": 4
},
"valid": true
},
{
"description": "boundary point is invalid",
"data": {
"smaller": 3,
"larger": 3.5
},
"valid": false
},
{
"description": "below the exclusiveMinimum is invalid",
"data": {
"smaller": 3,
"larger": 3.3
},
"valid": false
}
]
},
{
"description": "items in array are > than their indeces",
"schema": {
"items": {
"exclusiveMinimum": { "$data": "0#" }
}
},
"tests": [
{
"skip": true,
"description": "valid array",
"data": [1, 2, 3, 4, 5, 6],
"valid": true
},
{
"description": "invalid array (1=1)",
"data": [2, 1],
"valid": false
}
]
}
]

View File

@ -0,0 +1,97 @@
[
{
"description": "exclusiveMaximum as number",
"schema": {
"exclusiveMaximum": 3.0
},
"tests": [
{
"description": "below the exclusiveMaximum is valid",
"data": 2.2,
"valid": true
},
{
"description": "boundary point is invalid",
"data": 3.0,
"valid": false
},
{
"description": "above the exclusiveMaximum is invalid",
"data": 3.2,
"valid": false
}
]
},
{
"description": "both exclusiveMaximum and maximum are numbers, exclusiveMaximum > maximum",
"schema": {
"exclusiveMaximum": 3.0,
"maximum": 2.0
},
"tests": [
{
"description": "below the maximum is valid",
"data": 1.2,
"valid": true
},
{
"description": "boundary point is valid",
"data": 2.0,
"valid": true
},
{
"description": "above maximum is invalid",
"data": 2.2,
"valid": false
}
]
},
{
"description": "both exclusiveMaximum and maximum are numbers, exclusiveMaximum = maximum",
"schema": {
"exclusiveMaximum": 3.0,
"maximum": 3.0
},
"tests": [
{
"description": "below the maximum is valid",
"data": 2.2,
"valid": true
},
{
"description": "boundary point is invalid",
"data": 3.0,
"valid": false
},
{
"description": "above maximum is invalid",
"data": 3.2,
"valid": false
}
]
},
{
"description": "both exclusiveMaximum and maximum are numbers, exclusiveMaximum < maximum",
"schema": {
"exclusiveMaximum": 2.0,
"maximum": 3.0
},
"tests": [
{
"description": "below the exclusiveMaximum is valid",
"data": 1.2,
"valid": true
},
{
"description": "boundary point is invalid",
"data": 2.0,
"valid": false
},
{
"description": "above exclusiveMaximum is invalid",
"data": 2.2,
"valid": false
}
]
}
]

View File

@ -0,0 +1,97 @@
[
{
"description": "exclusiveMinimum as number",
"schema": {
"exclusiveMinimum": 1.1
},
"tests": [
{
"description": "above the exclusiveMinimum is still valid",
"data": 1.2,
"valid": true
},
{
"description": "boundary point is invalid",
"data": 1.1,
"valid": false
},
{
"description": "below exclusiveMinimum is invalid",
"data": 1.0,
"valid": false
}
]
},
{
"description": "both exclusiveMinimum and minimum are numbers, exclusiveMinimum < minimum",
"schema": {
"exclusiveMinimum": 2.0,
"minimum": 3.0
},
"tests": [
{
"description": "above the minimum is valid",
"data": 3.2,
"valid": true
},
{
"description": "boundary point is valid",
"data": 3.0,
"valid": true
},
{
"description": "below minimum is invalid",
"data": 2.2,
"valid": false
}
]
},
{
"description": "both exclusiveMinimum and minimum are numbers, exclusiveMinimum = minimum",
"schema": {
"exclusiveMinimum": 3.0,
"minimum": 3.0
},
"tests": [
{
"description": "above the minimum is valid",
"data": 3.2,
"valid": true
},
{
"description": "boundary point is invalid",
"data": 3.0,
"valid": false
},
{
"description": "below minimum is invalid",
"data": 2.2,
"valid": false
}
]
},
{
"description": "both exclusiveMinimum and minimum are numbers, exclusiveMinimum > minimum",
"schema": {
"exclusiveMinimum": 3.0,
"minimum": 2.0
},
"tests": [
{
"description": "above the exclusiveMinimum is valid",
"data": 3.2,
"valid": true
},
{
"description": "boundary point is invalid",
"data": 3.0,
"valid": false
},
{
"description": "below exclusiveMinimum is invalid",
"data": 2.2,
"valid": false
}
]
}
]