From 197a9e8c32506cb099884d794511d033363088dc Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 22 Jul 2020 16:42:02 +0100 Subject: [PATCH 1/4] feat: remove support for booleam exclusiveMaximum/Minimum (draft-04) --- KEYWORDS.md | 12 +-- lib/compile/rules.js | 6 +- lib/dot/_limit.jst | 115 +++------------------------- lib/dot/errors.def | 8 +- lib/dotjs/index.js | 2 + spec/errors.spec.js | 4 +- spec/extras/$data/maximum.json | 132 ++++++++++++++++----------------- spec/extras/$data/minimum.json | 122 ++++++++++++++---------------- spec/json-schema.spec.js | 22 ------ spec/options/schemaId.spec.js | 2 - 10 files changed, 145 insertions(+), 280 deletions(-) diff --git a/KEYWORDS.md b/KEYWORDS.md index 56175a1baa..73c3658350 100644 --- a/KEYWORDS.md +++ b/KEYWORDS.md @@ -76,11 +76,9 @@ Most other keywords apply only to a particular type of data. If the data is of d The value of keyword `maximum` (`minimum`) should be a number. This value is the maximum (minimum) allowed value for the data to be valid. -Draft-04: The value of keyword `exclusiveMaximum` (`exclusiveMinimum`) should be a boolean value. These keyword cannot be used without `maximum` (`minimum`). If this keyword value is equal to `true`, the data should not be equal to the value in `maximum` (`minimum`) keyword to be valid. +The value of keyword `exclusiveMaximum` (`exclusiveMinimum`) should be a number. This value is the exclusive maximum (minimum) allowed value for the data to be valid (the data equal to this keyword value is invalid). -Draft-06/07: The value of keyword `exclusiveMaximum` (`exclusiveMinimum`) should be a number. This value is the exclusive maximum (minimum) allowed value for the data to be valid (the data equal to this keyword value is invalid). - -Ajv supports both draft-04 and draft-06/07 syntaxes. +**Please note**: Boolean value for keywords `exclusiveMaximum` (`exclusiveMinimum`) is no longer supported. **Examples** @@ -90,15 +88,13 @@ Ajv supports both draft-04 and draft-06/07 syntaxes. _invalid_: `6`, `7` -2) _schema_: `{ "minimum": 5 }` +2. _schema_: `{ "minimum": 5 }` _valid_: `5`, `6`, any non-number (`"abc"`, `[]`, `{}`, `null`, `true`) _invalid_: `4`, `4.5` -3. _schema_: - draft-04: `{ "minimum": 5, "exclusiveMinimum": true }` - draft-06/07: `{ "exclusiveMinimum": 5 }` +3. _schema_: `{ "exclusiveMinimum": 5 }` _valid_: `6`, `7`, any non-number (`"abc"`, `[]`, `{}`, `null`, `true`) diff --git a/lib/compile/rules.js b/lib/compile/rules.js index a99691b439..9890a7fa23 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -8,8 +8,10 @@ module.exports = function rules() { { type: "number", rules: [ - {maximum: ["exclusiveMaximum"]}, - {minimum: ["exclusiveMinimum"]}, + "maximum", + "minimum", + "exclusiveMaximum", + "exclusiveMinimum", "multipleOf", "format", ], diff --git a/lib/dot/_limit.jst b/lib/dot/_limit.jst index f152189222..125f5fd6f9 100644 --- a/lib/dot/_limit.jst +++ b/lib/dot/_limit.jst @@ -3,111 +3,20 @@ {{# def.setupKeyword }} {{# def.$data }} -{{## def.setExclusiveLimit: - $exclusive = true; - $errorKeyword = $exclusiveKeyword; - $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword; -#}} +{{# def.numberKeyword }} {{ - var $isMax = $keyword == 'maximum' - , $exclusiveKeyword = $isMax ? 'exclusiveMaximum' : 'exclusiveMinimum' - , $schemaExcl = it.schema[$exclusiveKeyword] - , $isDataExcl = it.opts.$data && $schemaExcl && $schemaExcl.$data - , $op = $isMax ? '<' : '>' - , $notOp = $isMax ? '>' : '<' - , $errorKeyword = undefined; - - if (!($isData || typeof $schema == 'number' || $schema === undefined)) { - throw new Error($keyword + ' must be number'); - } - if (!($isDataExcl || $schemaExcl === undefined - || typeof $schemaExcl == 'number' - || typeof $schemaExcl == 'boolean')) { - throw new Error($exclusiveKeyword + ' must be number or boolean'); + var $op /* used in errors */, $notOp; + switch ($keyword) { + case 'maximum': $op = '<='; $notOp = '>'; break; + case 'minimum': $op = '>='; $notOp = '<'; break; + case 'exclusiveMaximum': $op = '<'; $notOp = '>='; break; + case 'exclusiveMinimum': $op = '>'; $notOp = '<='; break; + default: throw Error('not _limit keyword ' + $keyword); } }} -{{? $isDataExcl }} - {{ - var $schemaValueExcl = it.util.getData($schemaExcl.$data, $dataLvl, it.dataPathArr) - , $exclusive = 'exclusive' + $lvl - , $exclType = 'exclType' + $lvl - , $exclIsNumber = 'exclIsNumber' + $lvl - , $opExpr = 'op' + $lvl - , $opStr = '\' + ' + $opExpr + ' + \''; - }} - var schemaExcl{{=$lvl}} = {{=$schemaValueExcl}}; - {{ $schemaValueExcl = 'schemaExcl' + $lvl; }} - - var {{=$exclusive}}; - var {{=$exclType}} = typeof {{=$schemaValueExcl}}; - if ({{=$exclType}} != 'boolean' && {{=$exclType}} != 'undefined' && {{=$exclType}} != 'number') { - {{ var $errorKeyword = $exclusiveKeyword; }} - {{# def.error:'_exclusiveLimit' }} - } else if ({{# def.$dataNotType:'number' }} - {{=$exclType}} == 'number' - ? ( - ({{=$exclusive}} = {{=$schemaValue}} === undefined || {{=$schemaValueExcl}} {{=$op}}= {{=$schemaValue}}) - ? {{=$data}} {{=$notOp}}= {{=$schemaValueExcl}} - : {{=$data}} {{=$notOp}} {{=$schemaValue}} - ) - : ( - ({{=$exclusive}} = {{=$schemaValueExcl}} === true) - ? {{=$data}} {{=$notOp}}= {{=$schemaValue}} - : {{=$data}} {{=$notOp}} {{=$schemaValue}} - ) - || {{=$data}} !== {{=$data}}) { - var op{{=$lvl}} = {{=$exclusive}} ? '{{=$op}}' : '{{=$op}}='; - {{ - if ($schema === undefined) { - $errorKeyword = $exclusiveKeyword; - $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword; - $schemaValue = $schemaValueExcl; - $isData = $isDataExcl; - } - }} -{{??}} - {{ - var $exclIsNumber = typeof $schemaExcl == 'number' - , $opStr = $op; /*used in error*/ - }} - - {{? $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) { - {{# def.setExclusiveLimit }} - $schemaValue = $schemaExcl; - $notOp += '='; - } else { - if ($exclIsNumber) - $schemaValue = Math[$isMax ? 'min' : 'max']($schemaExcl, $schema); - - if ($schemaExcl === ($exclIsNumber ? $schemaValue : true)) { - {{# def.setExclusiveLimit }} - $notOp += '='; - } else { - $exclusive = false; - $opStr += '='; - } - } - - var $opExpr = '\'' + $opStr + '\''; /*used in error*/ - }} - - if ({{# def.$dataNotType:'number' }} - {{=$data}} {{=$notOp}} {{=$schemaValue}} - || {{=$data}} !== {{=$data}}) { - {{?}} -{{?}} - {{ $errorKeyword = $errorKeyword || $keyword; }} - {{# def.error:'_limit' }} - } {{? $breakOnError }} else { {{?}} +if ({{# def.$dataNotType:'number' }} {{=$data}} {{=$notOp}} {{=$schemaValue}} || {{=$data}} !== {{=$data}}) { + {{ var $errorKeyword = $keyword; }} + {{# def.error:'_limit' }} +} {{? $breakOnError }} else { {{?}} diff --git a/lib/dot/errors.def b/lib/dot/errors.def index 5c5752cb04..d5658834d5 100644 --- a/lib/dot/errors.def +++ b/lib/dot/errors.def @@ -102,8 +102,8 @@ 'enum': "'should be equal to one of the allowed values'", format: "'should match format \"{{#def.concatSchemaEQ}}\"'", 'if': "'should match \"' + {{=$ifClause}} + '\" schema'", - _limit: "'should be {{=$opStr}} {{#def.appendSchema}}", - _exclusiveLimit: "'{{=$exclusiveKeyword}} should be boolean'", + _limit: "'should be {{=$op}} {{#def.appendSchema}}", + _exclusiveLimit: "'should be {{=$op}} {{#def.appendSchema}}", _limitItems: "'should NOT have {{?$keyword=='maxItems'}}more{{??}}fewer{{?}} than {{#def.concatSchema}} items'", _limitLength: "'should NOT be {{?$keyword=='maxLength'}}longer{{??}}shorter{{?}} than {{#def.concatSchema}} characters'", _limitProperties:"'should NOT have {{?$keyword=='maxProperties'}}more{{??}}fewer{{?}} than {{#def.concatSchema}} properties'", @@ -139,7 +139,6 @@ format: "{{#def.schemaRefOrQS}}", 'if': "validate.schema{{=$schemaPath}}", _limit: "{{#def.schemaRefOrVal}}", - _exclusiveLimit: "validate.schema{{=$schemaPath}}", _limitItems: "{{#def.schemaRefOrVal}}", _limitLength: "{{#def.schemaRefOrVal}}", _limitProperties:"{{#def.schemaRefOrVal}}", @@ -173,8 +172,7 @@ 'enum': "{ allowedValues: schema{{=$lvl}} }", format: "{ format: {{#def.schemaValueQS}} }", 'if': "{ failingKeyword: {{=$ifClause}} }", - _limit: "{ comparison: {{=$opExpr}}, limit: {{=$schemaValue}}, exclusive: {{=$exclusive}} }", - _exclusiveLimit: "{}", + _limit: "{ comparison: '{{=$op}}', limit: {{=$schemaValue}} }", _limitItems: "{ limit: {{=$schemaValue}} }", _limitLength: "{ limit: {{=$schemaValue}} }", _limitProperties:"{ limit: {{=$schemaValue}} }", diff --git a/lib/dotjs/index.js b/lib/dotjs/index.js index 8539441688..39eca6e7b0 100644 --- a/lib/dotjs/index.js +++ b/lib/dotjs/index.js @@ -10,6 +10,8 @@ module.exports = { contains: require("./contains"), dependencies: require("./dependencies"), enum: require("./enum"), + exclusiveMaximum: require("./_limit"), + exclusiveMinimum: require("./_limit"), format: require("./format"), if: require("./if"), items: require("./items"), diff --git a/spec/errors.spec.js b/spec/errors.spec.js index b318dbe32c..b408c58031 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -984,14 +984,12 @@ describe("Validation errors", function () { testError("exclusiveMinimum", "should be > 2", { comparison: ">", limit: 2, - exclusive: true, }) shouldBeInvalid(validate, 5) testError("exclusiveMaximum", "should be < 5", { comparison: "<", limit: 5, - exclusive: true, }) function testError(keyword, message, params) { @@ -1038,7 +1036,7 @@ describe("Validation errors", function () { "#/properties/smaller/exclusiveMaximum", _ajv._opts.jsonPointers ? "/smaller" : ".smaller", "should be < 4", - {comparison: "<", limit: 4, exclusive: true} + {comparison: "<", limit: 4} ) } }) diff --git a/spec/extras/$data/maximum.json b/spec/extras/$data/maximum.json index e9f9807c55..6469d19163 100644 --- a/spec/extras/$data/maximum.json +++ b/spec/extras/$data/maximum.json @@ -64,49 +64,57 @@ "schema": { "properties": { "number": { - "maximum": 3, - "exclusiveMaximum": {"$data": "1/maxIsExclusive"} + "maximum": 5, + "exclusiveMaximum": {"$data": "1/exclusiveMaximum"} }, - "maxIsExclusive": {} + "exclusiveMaximum": {} } }, "tests": [ { - "description": "below the maximum is valid when exclusiveMaximum is true", + "description": "exclusiveMaximum boolean no longer supported", "data": { - "number": 2, - "maxIsExclusive": true + "number": 4, + "exclusiveMaximum": true }, - "valid": true + "valid": false }, { - "description": "below the maximum is valid when exclusiveMaximum is false", + "description": "below the maximum is valid when exclusiveMaximum is strictly larger", "data": { - "number": 2, - "maxIsExclusive": false + "number": 4, + "exclusiveMaximum": 4.1 }, "valid": true }, + { + "description": "below the maximum is NOT valid when exclusiveMaximum is equal", + "data": { + "number": 4, + "exclusiveMaximum": 4 + }, + "valid": false + }, { "description": "below the maximum is valid when exclusiveMaximum is undefined", "data": { - "number": 2 + "number": 4 }, "valid": true }, { - "description": "boundary point is invalid when exclusiveMaximum is true", + "description": "boundary point is invalid when exclusiveMaximum is equal", "data": { "number": 3, - "maxIsExclusive": true + "exclusiveMaximum": 3 }, "valid": false }, { - "description": "boundary point is valid when exclusiveMaximum is false", + "description": "boundary point is valid when exclusiveMaximum is smaller", "data": { "number": 3, - "maxIsExclusive": false + "exclusiveMaximum": 3.1 }, "valid": true }, @@ -118,137 +126,121 @@ "valid": true }, { - "description": "above the maximum is invalid when exclusiveMaximum is true", + "description": "above the maximum is invalid", "data": { - "number": 4, - "maxIsExclusive": true + "number": 6 }, "valid": false }, { - "description": "above the maximum is invalid when exclusiveMaximum is false", + "description": "fails if value of exclusiveMaximum is not number", "data": { "number": 4, - "maxIsExclusive": false - }, - "valid": false - }, - { - "description": "above the maximum is invalid when exclusiveMaximum is undefined", - "data": { - "number": 4 - }, - "valid": false - }, - { - "description": "fails if value of exclusiveMaximum is not boolean", - "data": { - "number": 2, - "maxIsExclusive": "false" + "exclusiveMaximum": "5" }, "valid": false } ] }, { - "description": "one property is maximum for another and exclusiveMaximum is $data", + "description": "maximum and exclusiveMaximum are $data", "schema": { "properties": { "larger": {}, "smallerOrEqual": { "maximum": {"$data": "1/larger"}, - "exclusiveMaximum": {"$data": "1/maxIsExclusive"} + "exclusiveMaximum": {"$data": "1/exclusiveMaximum"} }, - "maxIsExclusive": {} + "exclusiveMaximum": {} } }, "tests": [ { - "description": "below the maximum is valid when exclusiveMaximum is true", + "description": "exclusiveMaximum boolean no longer supported", "data": { "larger": 3, "smallerOrEqual": 2, - "maxIsExclusive": true + "exclusiveMaximum": true }, - "valid": true + "valid": false }, { - "description": "below the maximum is valid when exclusiveMaximum is false", + "description": "below the maximum is valid when exclusiveMaximum is strictly larger", "data": { "larger": 3, "smallerOrEqual": 2, - "maxIsExclusive": false + "exclusiveMaximum": 2.1 }, "valid": true }, { - "description": "below the maximum is valid when exclusiveMaximum is undefined", + "description": "below the maximum is NOT valid when exclusiveMaximum is equal", "data": { "larger": 3, - "smallerOrEqual": 2 + "smallerOrEqual": 2, + "exclusiveMaximum": 2 }, - "valid": true + "valid": false }, { - "description": "boundary point is invalid when exclusiveMaximum is true", + "description": "below the maximum is valid when exclusiveMaximum is undefined", "data": { "larger": 3, - "smallerOrEqual": 3, - "maxIsExclusive": true + "smallerOrEqual": 2 }, - "valid": false + "valid": true }, { - "description": "boundary point is valid when exclusiveMaximum is false", + "description": "above the maximum is invalid", "data": { "larger": 3, - "smallerOrEqual": 3, - "maxIsExclusive": false + "smallerOrEqual": 4, + "exclusiveMaximum": 5 }, - "valid": true + "valid": false }, { - "description": "boundary point is valid when exclusiveMaximum is undefined", + "description": "above the maximum is invalid when exclusiveMaximum is undefined", "data": { "larger": 3, - "smallerOrEqual": 3 + "smallerOrEqual": 4 }, - "valid": true + "valid": false }, { - "description": "above the maximum is invalid when exclusiveMaximum is true", + "description": "fails if value of exclusiveMaximum is not number", "data": { "larger": 3, - "smallerOrEqual": 4, - "maxIsExclusive": true + "smallerOrEqual": 2, + "exclusiveMaximum": "5" }, "valid": false }, { - "description": "above the maximum is invalid when exclusiveMaximum is false", + "description": "boundary point is valid when exclusiveMaximum is strictly larger", "data": { "larger": 3, - "smallerOrEqual": 4, - "maxIsExclusive": false + "smallerOrEqual": 3, + "exclusiveMaximum": 3.1 }, - "valid": false + "valid": true }, { - "description": "above the maximum is invalid when exclusiveMaximum is undefined", + "description": "boundary point is invalid when exclusiveMaximum is equal", "data": { "larger": 3, - "smallerOrEqual": 4 + "smallerOrEqual": 3, + "exclusiveMaximum": 3 }, "valid": false }, { - "description": "fails if value of exclusiveMaximum is not boolean", + "description": "boundary point is valid when exclusiveMaximum is undefined", "data": { "larger": 3, - "smallerOrEqual": 2, - "maxIsExclusive": "false" + "smallerOrEqual": 3 }, - "valid": false + "valid": true } ] }, diff --git a/spec/extras/$data/minimum.json b/spec/extras/$data/minimum.json index 00ddc30a7c..861501ab18 100644 --- a/spec/extras/$data/minimum.json +++ b/spec/extras/$data/minimum.json @@ -58,28 +58,36 @@ "properties": { "number": { "minimum": 3, - "exclusiveMinimum": {"$data": "1/minIsExclusive"} + "exclusiveMinimum": {"$data": "1/exclusiveMinimum"} }, - "minIsExclusive": {} + "exclusiveMinimum": {} } }, "tests": [ { - "description": "above the minimum is valid when exclusiveMinimum is true", + "description": "exclusiveMinimum boolean no longer supported", "data": { "number": 4, - "minIsExclusive": true + "exclusiveMinimum": true }, - "valid": true + "valid": false }, { - "description": "above the minimum is valid when exclusiveMinimum is false", + "description": "above the minimum is valid when exclusiveMinimum is strictly smaller", "data": { "number": 4, - "minIsExclusive": false + "exclusiveMinimum": 3.9 }, "valid": true }, + { + "description": "above the minimum is NOT valid when exclusiveMinimum is equal", + "data": { + "number": 4, + "exclusiveMinimum": 4 + }, + "valid": false + }, { "description": "above the minimum is valid when exclusiveMinimum is undefined", "data": { @@ -88,18 +96,18 @@ "valid": true }, { - "description": "boundary point is invalid when exclusiveMinimum is true", + "description": "boundary point is invalid when exclusiveMinimum is equal", "data": { "number": 3, - "minIsExclusive": true + "exclusiveMinimum": 3 }, "valid": false }, { - "description": "boundary point is valid when exclusiveMinimum is false", + "description": "boundary point is valid when exclusiveMinimum is smaller", "data": { "number": 3, - "minIsExclusive": false + "exclusiveMinimum": 2.9 }, "valid": true }, @@ -111,137 +119,121 @@ "valid": true }, { - "description": "below the minimum is invalid when exclusiveMinimum is true", - "data": { - "number": 2, - "minIsExclusive": true - }, - "valid": false - }, - { - "description": "below the minimum is invalid when exclusiveMinimum is false", - "data": { - "number": 2, - "minIsExclusive": false - }, - "valid": false - }, - { - "description": "below the minimum is invalid when exclusiveMinimum is undefined", + "description": "below the minimum is invalid", "data": { "number": 2 }, "valid": false }, { - "description": "fails if value of exclusiveMinimum is not boolean", + "description": "fails if value of exclusiveMinimum is not number", "data": { "number": 4, - "minIsExclusive": "false" + "exclusiveMinimum": "3" }, "valid": false } ] }, { - "description": "one property is minimum for another and exclusiveMinimum is $data", + "description": "minimum and exclusiveMinimum are $data", "schema": { "properties": { "smaller": {}, "largerOrEqual": { "minimum": {"$data": "1/smaller"}, - "exclusiveMinimum": {"$data": "1/minIsExclusive"} + "exclusiveMinimum": {"$data": "1/exclusiveMinimum"} }, - "minIsExclusive": {} + "exclusiveMinimum": {} } }, "tests": [ { - "description": "above the minimum is valid when exclusiveMinimum is true", + "description": "exclusiveMinimum boolean no longer supported", "data": { "smaller": 3, "largerOrEqual": 4, - "minIsExclusive": true + "exclusiveMinimum": true }, - "valid": true + "valid": false }, { - "description": "above the minimum is valid when exclusiveMinimum is false", + "description": "above the minimum is valid when exclusiveMinimum is strictly smaller", "data": { "smaller": 3, "largerOrEqual": 4, - "minIsExclusive": false + "exclusiveMinimum": 3.9 }, "valid": true }, { - "description": "above the minimum is valid when exclusiveMinimum is undefined", + "description": "above the minimum is NOT valid when exclusiveMinimum is equal", "data": { "smaller": 3, - "largerOrEqual": 4 + "largerOrEqual": 4, + "exclusiveMinimum": 4 }, - "valid": true + "valid": false }, { - "description": "boundary point is invalid when exclusiveMinimum is true", + "description": "above the minimum is valid when exclusiveMinimum is undefined", "data": { "smaller": 3, - "largerOrEqual": 3, - "minIsExclusive": true + "largerOrEqual": 4 }, - "valid": false + "valid": true }, { - "description": "boundary point is valid when exclusiveMinimum is false", + "description": "below the minimum is invalid", "data": { "smaller": 3, - "largerOrEqual": 3, - "minIsExclusive": false + "largerOrEqual": 2, + "exclusiveMinimum": 1.5 }, - "valid": true + "valid": false }, { - "description": "boundary point is valid when exclusiveMinimum is undefined", + "description": "below the minimum is invalid when exclusiveMinimum is undefined", "data": { "smaller": 3, - "largerOrEqual": 3 + "largerOrEqual": 2 }, - "valid": true + "valid": false }, { - "description": "below the minimum is invalid when exclusiveMinimum is true", + "description": "fails if value of exclusiveMinimum is not number", "data": { "smaller": 3, - "largerOrEqual": 2, - "minIsExclusive": true + "largerOrEqual": 4, + "exclusiveMinimum": "3" }, "valid": false }, { - "description": "below the minimum is invalid when exclusiveMinimum is false", + "description": "boundary point is valid when exclusiveMinimum is strictly smaller", "data": { "smaller": 3, - "largerOrEqual": 2, - "minIsExclusive": false + "largerOrEqual": 3, + "exclusiveMinimum": 2.9 }, - "valid": false + "valid": true }, { - "description": "below the minimum is invalid when exclusiveMinimum is undefined", + "description": "boundary point is invalid when exclusiveMinimum is equal", "data": { "smaller": 3, - "largerOrEqual": 2 + "largerOrEqual": 3, + "exclusiveMinimum": 3 }, "valid": false }, { - "description": "fails if value of exclusiveMinimum is not boolean", + "description": "boundary point is valid when exclusiveMinimum is undefined", "data": { "smaller": 3, - "largerOrEqual": 4, - "minIsExclusive": "false" + "largerOrEqual": 3 }, - "valid": false + "valid": true } ] }, diff --git a/spec/json-schema.spec.js b/spec/json-schema.spec.js index a8ff0683f6..5d60c46ef7 100644 --- a/spec/json-schema.spec.js +++ b/spec/json-schema.spec.js @@ -14,12 +14,6 @@ var remoteRefs = { } var SKIP = { - 4: [ - "optional/zeroTerminatedFloats", - "optional/ecmascript-regex", // TODO only format needs to be skipped, too much is skipped here - "optional/format", - "format", - ], 6: [ "optional/ecmascript-regex", // TODO only format needs to be skipped, too much is skipped here "optional/format", @@ -48,18 +42,6 @@ var SKIP = { ], } -runTest( - getAjvInstances(options, {meta: false, schemaId: "id"}), - 4, - typeof window == "object" - ? suite( - require("./JSON-Schema-Test-Suite/tests/draft4/{**/,}*.json", { - mode: "list", - }) - ) - : "./JSON-Schema-Test-Suite/tests/draft4/{**/,}*.json" -) - runTest( getAjvInstances(options, {meta: false}), 6, @@ -87,10 +69,6 @@ runTest( function runTest(instances, draft, tests) { instances.forEach(function (ajv) { switch (draft) { - case 4: - ajv.addMetaSchema(require("../lib/refs/json-schema-draft-04.json")) - ajv._opts.defaultMeta = "http://json-schema.org/draft-04/schema#" - break case 6: ajv.addMetaSchema(require("../lib/refs/json-schema-draft-06.json")) ajv._opts.defaultMeta = "http://json-schema.org/draft-06/schema#" diff --git a/spec/options/schemaId.spec.js b/spec/options/schemaId.spec.js index 7600f24fcd..bc40da2745 100644 --- a/spec/options/schemaId.spec.js +++ b/spec/options/schemaId.spec.js @@ -24,8 +24,6 @@ describe("schemaId option", function () { describe('= "id"', function () { it("should use id and ignore $id", function () { var ajv = new Ajv({schemaId: "id", meta: false}) - ajv.addMetaSchema(require("../../lib/refs/json-schema-draft-04.json")) - ajv._opts.defaultMeta = "http://json-schema.org/draft-04/schema#" ajv.addSchema({id: "mySchema1", type: "string"}) var validate = ajv.getSchema("mySchema1") From b1bb27e28026c023d8f786b421c01b18f99b6de2 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 22 Jul 2020 17:19:59 +0100 Subject: [PATCH 2/4] feat: remove schemaId option (and support for draft-04 "id" in schemas) --- README.md | 18 +---- lib/ajv.d.ts | 1 - lib/ajv.js | 40 ++--------- lib/compile/resolve.js | 12 ++-- lib/dot/validate.jst | 4 +- .../521_wrong_warning_id_property.spec.js | 2 +- spec/options/schemaId.spec.js | 67 +++++-------------- 7 files changed, 33 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index 4c266ba595..4e4c604098 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Ajv: Another JSON Schema Validator -The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/07. +The fastest JSON Schema validator for Node.js and browser. Supports draft-06/07 (draft-04 is supported in v6). [![Build Status](https://travis-ci.org/ajv-validator/ajv.svg?branch=master)](https://travis-ci.org/ajv-validator/ajv) [![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) @@ -63,14 +63,7 @@ Thank you ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json")) ``` -To use Ajv with draft-04 schemas in addition to explicitly adding meta-schema you also need to use option schemaId: - -```javascript -var ajv = new Ajv({schemaId: "id"}) -// If you want to use both draft-04 and draft-06/07 schemas: -// var ajv = new Ajv({schemaId: 'auto'}); -ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-04.json")) -``` +**Please note**: use Ajv v6 if you need draft-04 support - v7 does NOT support it. ## Contents @@ -128,7 +121,7 @@ Performance of different validators by [json-schema-benchmark](https://github.co ## Features -- Ajv implements full JSON Schema [draft-06/07](http://json-schema.org/) and draft-04 standards: +- Ajv implements full JSON Schema [draft-06/07](http://json-schema.org/) standards (draft-04 is supported in v6): - all validation keywords (see [JSON Schema validation keywords](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md)) - full support of remote refs (remote schemas have to be added with `addSchema` or compiled to be available) - support of circular references between schemas @@ -1122,7 +1115,6 @@ Defaults: schemas: {}, logger: undefined, // referenced schema options: - schemaId: '$id', missingRefs: true, extendRefs: 'ignore', // recommended 'fail' loadSchema: undefined, // function(uri: string): Promise {} @@ -1183,10 +1175,6 @@ Defaults: ##### Referenced schema options -- _schemaId_: this option defines which keywords are used as schema URI. Option value: - - `"$id"` (default) - only use `$id` keyword as schema URI (as specified in JSON Schema draft-06/07), ignore `id` keyword (if it is present a warning will be logged). - - `"id"` - only use `id` keyword as schema URI (as specified in JSON Schema draft-04), ignore `$id` keyword (if it is present a warning will be logged). - - `"auto"` - use both `$id` and `id` keywords as schema URI. If both are present (in the same schema object) and different the exception will be thrown during schema compilation. - _missingRefs_: handling of missing referenced schemas. Option values: - `true` (default) - if the reference cannot be resolved during compilation the exception is thrown. The thrown error has properties `missingRef` (with hash fragment) and `missingSchema` (without it). Both properties are resolved relative to the current base id (usually schema id, unless it was substituted). - `"ignore"` - to log error during compilation and always pass validation. diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 57ac813893..14d47aebcb 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -184,7 +184,6 @@ declare namespace ajv { keywords?: object unknownFormats?: true | string[] | "ignore" schemas?: Array | object - schemaId?: "$id" | "id" | "auto" missingRefs?: true | "ignore" | "fail" extendRefs?: true | "ignore" | "fail" loadSchema?: ( diff --git a/lib/ajv.js b/lib/ajv.js index 02541fc798..bfdd44dcb7 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -67,7 +67,8 @@ function Ajv(opts) { this._loadingSchemas = {} this._compilations = [] this.RULES = rules() - this._getId = chooseGetId(opts) + if (opts.schemaId !== undefined && opts.schemaId !== "$id") + throw new Error("option schemaId is not supported from v7") opts.loopRequired = opts.loopRequired || Infinity if (opts.errorDataPath == "property") opts._errorDataPathProperty = true @@ -134,7 +135,7 @@ function addSchema(schema, key, _skipValidation, _meta) { this.addSchema(schema[i], undefined, _skipValidation, _meta) return this } - var id = this._getId(schema) + var id = schema.$id if (id !== undefined && typeof id != "string") throw new Error("schema id must be string") key = resolve.normalizeId(key || id) @@ -187,7 +188,7 @@ function defaultMeta(self) { var meta = self._opts.meta self._opts.defaultMeta = typeof meta == "object" - ? self._getId(meta) || meta + ? meta.$id || meta : self.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined @@ -267,7 +268,7 @@ function removeSchema(schemaKeyRef) { var serialize = this._opts.serialize var cacheKey = serialize ? serialize(schemaKeyRef) : schemaKeyRef this._cache.del(cacheKey) - var id = this._getId(schemaKeyRef) + var id = schemaKeyRef.$id if (id) { id = resolve.normalizeId(id) delete this._schemas[id] @@ -298,7 +299,7 @@ function _addSchema(schema, skipValidation, meta, shouldAddSchema) { shouldAddSchema = shouldAddSchema || this._opts.addUsedSchema !== false - var id = resolve.normalizeId(this._getId(schema)) + var id = resolve.normalizeId(schema.$id) if (id && shouldAddSchema) checkUnique(this, id) var willValidate = this._opts.validateSchema !== false && !skipValidation @@ -372,35 +373,6 @@ function _compile(schemaObj, root) { } } -function chooseGetId(opts) { - switch (opts.schemaId) { - case "auto": - return _get$IdOrId - case "id": - return _getId - default: - return _get$Id - } -} - -/* @this Ajv */ -function _getId(schema) { - if (schema.$id) this.logger.warn("schema $id ignored", schema.$id) - return schema.id -} - -/* @this Ajv */ -function _get$Id(schema) { - if (schema.id) this.logger.warn("schema id ignored", schema.id) - return schema.$id -} - -function _get$IdOrId(schema) { - if (schema.$id && schema.id && schema.$id != schema.id) - throw new Error("schema $id is different from id") - return schema.$id || schema.id -} - /** * Convert array of error message objects to string * @this Ajv diff --git a/lib/compile/resolve.js b/lib/compile/resolve.js index bc47364820..73891d270b 100644 --- a/lib/compile/resolve.js +++ b/lib/compile/resolve.js @@ -70,7 +70,7 @@ function resolveSchema(root, ref) { /* jshint validthis: true */ var p = URI.parse(ref), refPath = _getFullPath(p), - baseId = getFullPath(this._getId(root.schema)) + baseId = getFullPath(root.schema.$id) if (Object.keys(root.schema).length === 0 || refPath !== baseId) { var id = normalizeId(refPath) var refVal = this._refs[id] @@ -91,7 +91,7 @@ function resolveSchema(root, ref) { } } if (!root.schema) return - baseId = getFullPath(this._getId(root.schema)) + baseId = getFullPath(root.schema.$id) } return getJsonPointer.call(this, p, baseId, root.schema, root) } @@ -104,7 +104,7 @@ function resolveRecursive(root, ref, parsedRef) { var schema = res.schema var baseId = res.baseId root = res.root - var id = this._getId(schema) + var id = schema.$id if (id) baseId = resolveUrl(baseId, id) return getJsonPointer.call(this, parsedRef, baseId, schema, root) } @@ -132,7 +132,7 @@ function getJsonPointer(parsedRef, baseId, schema, root) { if (schema === undefined) break var id if (!PREVENT_SCOPE_CHANGE[part]) { - id = this._getId(schema) + id = schema.$id if (id) baseId = resolveUrl(baseId, id) if (schema.$ref) { var $ref = resolveUrl(baseId, schema.$ref) @@ -236,7 +236,7 @@ function resolveUrl(baseId, id) { /* @this Ajv */ function resolveIds(schema) { - var schemaId = normalizeId(this._getId(schema)) + var schemaId = normalizeId(schema.$id) var baseIds = {"": schemaId} var fullPaths = {"": getFullPath(schemaId, false)} var localRefs = {} @@ -252,7 +252,7 @@ function resolveIds(schema) { keyIndex ) { if (jsonPtr === "") return - var id = self._getId(sch) + var id = sch.$id var baseId = baseIds[parentJsonPtr] var fullPath = fullPaths[parentJsonPtr] + "/" + parentKeyword if (keyIndex !== undefined) { diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index fd833a535c..a671490682 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -17,7 +17,7 @@ {{ var $async = it.schema.$async === true , $refKeywords = it.util.schemaHasRulesExcept(it.schema, it.RULES.all, '$ref') - , $id = it.self._getId(it.schema); + , $id = it.schema.$id; }} {{ @@ -77,7 +77,7 @@ , $lvl = it.level = 0 , $dataLvl = it.dataLevel = 0 , $data = 'data'; - it.rootId = it.resolve.fullPath(it.self._getId(it.root.schema)); + it.rootId = it.resolve.fullPath(it.root.schema.$id); it.baseId = it.baseId || it.rootId; delete it.isTop; diff --git a/spec/issues/521_wrong_warning_id_property.spec.js b/spec/issues/521_wrong_warning_id_property.spec.js index 308ebe662b..57c5bbb7fb 100644 --- a/spec/issues/521_wrong_warning_id_property.spec.js +++ b/spec/issues/521_wrong_warning_id_property.spec.js @@ -5,7 +5,7 @@ require("../chai").should() describe('issue #521, incorrect warning with "id" property', function () { it("should not log warning", function () { - var ajv = new Ajv({schemaId: "$id"}) + var ajv = new Ajv() var consoleWarn = console.warn console.warn = function () { throw new Error("should not log warning") diff --git a/spec/options/schemaId.spec.js b/spec/options/schemaId.spec.js index bc40da2745..7473cfb4c4 100644 --- a/spec/options/schemaId.spec.js +++ b/spec/options/schemaId.spec.js @@ -3,67 +3,30 @@ var Ajv = require("../ajv") var should = require("../chai").should() -describe("schemaId option", function () { - describe('= "$id" (default)', function () { - it("should use $id and ignore id", function () { - test(new Ajv()) - test(new Ajv({schemaId: "$id"})) - - function test(ajv) { - ajv.addSchema({$id: "mySchema1", type: "string"}) - var validate = ajv.getSchema("mySchema1") - validate("foo").should.equal(true) - validate(1).should.equal(false) - - validate = ajv.compile({id: "mySchema2", type: "string"}) - should.not.exist(ajv.getSchema("mySchema2")) - } +describe("removed schemaId option", function () { + it('should throw error if schemaId option is used and it is not equal to "$id"', function () { + new Ajv() + new Ajv({schemaId: "$id"}) + should.throw(function () { + new Ajv({schemaId: "id"}) }) - }) - - describe('= "id"', function () { - it("should use id and ignore $id", function () { - var ajv = new Ajv({schemaId: "id", meta: false}) - - ajv.addSchema({id: "mySchema1", type: "string"}) - var validate = ajv.getSchema("mySchema1") - validate("foo").should.equal(true) - validate(1).should.equal(false) - - validate = ajv.compile({$id: "mySchema2", type: "string"}) - should.not.exist(ajv.getSchema("mySchema2")) + should.throw(function () { + new Ajv({schemaId: "auto"}) }) }) - describe('= "auto"', function () { - it("should use both id and $id", function () { - var ajv = new Ajv({schemaId: "auto"}) + it("should use $id and ignore id", function () { + test(new Ajv()) + test(new Ajv({schemaId: "$id"})) + function test(ajv) { ajv.addSchema({$id: "mySchema1", type: "string"}) var validate = ajv.getSchema("mySchema1") validate("foo").should.equal(true) validate(1).should.equal(false) - ajv.addSchema({id: "mySchema2", type: "string"}) - validate = ajv.getSchema("mySchema2") - validate("foo").should.equal(true) - validate(1).should.equal(false) - }) - - it("should throw if both id and $id are available and different", function () { - var ajv = new Ajv({schemaId: "auto"}) - - ajv.compile({ - id: "mySchema", - $id: "mySchema", - }) - - should.throw(function () { - ajv.compile({ - id: "mySchema1", - $id: "mySchema2", - }) - }) - }) + validate = ajv.compile({id: "mySchema2", type: "string"}) + should.not.exist(ajv.getSchema("mySchema2")) + } }) }) From 39055fb191f1773a9274948319f9808054000a40 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 22 Jul 2020 19:45:29 +0100 Subject: [PATCH 3/4] remove draft-04 meta-schema --- lib/refs/json-schema-draft-04.json | 145 ----------------------------- 1 file changed, 145 deletions(-) delete mode 100644 lib/refs/json-schema-draft-04.json diff --git a/lib/refs/json-schema-draft-04.json b/lib/refs/json-schema-draft-04.json deleted file mode 100644 index 04c4d3b9fd..0000000000 --- a/lib/refs/json-schema-draft-04.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "id": "http://json-schema.org/draft-04/schema#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Core schema meta-schema", - "definitions": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": {"$ref": "#"} - }, - "positiveInteger": { - "type": "integer", - "minimum": 0 - }, - "positiveIntegerDefault0": { - "allOf": [{"$ref": "#/definitions/positiveInteger"}, {"default": 0}] - }, - "simpleTypes": { - "enum": [ - "array", - "boolean", - "integer", - "null", - "number", - "object", - "string" - ] - }, - "stringArray": { - "type": "array", - "items": {"type": "string"}, - "minItems": 1, - "uniqueItems": true - } - }, - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "$schema": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": {}, - "multipleOf": { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "boolean", - "default": false - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "boolean", - "default": false - }, - "maxLength": {"$ref": "#/definitions/positiveInteger"}, - "minLength": {"$ref": "#/definitions/positiveIntegerDefault0"}, - "pattern": { - "type": "string", - "format": "regex" - }, - "additionalItems": { - "anyOf": [{"type": "boolean"}, {"$ref": "#"}], - "default": {} - }, - "items": { - "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/schemaArray"}], - "default": {} - }, - "maxItems": {"$ref": "#/definitions/positiveInteger"}, - "minItems": {"$ref": "#/definitions/positiveIntegerDefault0"}, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "maxProperties": {"$ref": "#/definitions/positiveInteger"}, - "minProperties": {"$ref": "#/definitions/positiveIntegerDefault0"}, - "required": {"$ref": "#/definitions/stringArray"}, - "additionalProperties": { - "anyOf": [{"type": "boolean"}, {"$ref": "#"}], - "default": {} - }, - "definitions": { - "type": "object", - "additionalProperties": {"$ref": "#"}, - "default": {} - }, - "properties": { - "type": "object", - "additionalProperties": {"$ref": "#"}, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": {"$ref": "#"}, - "default": {} - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "anyOf": [{"$ref": "#"}, {"$ref": "#/definitions/stringArray"}] - } - }, - "enum": { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "type": { - "anyOf": [ - {"$ref": "#/definitions/simpleTypes"}, - { - "type": "array", - "items": {"$ref": "#/definitions/simpleTypes"}, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "format": {"type": "string"}, - "allOf": {"$ref": "#/definitions/schemaArray"}, - "anyOf": {"$ref": "#/definitions/schemaArray"}, - "oneOf": {"$ref": "#/definitions/schemaArray"}, - "not": {"$ref": "#"} - }, - "dependencies": { - "exclusiveMaximum": ["maximum"], - "exclusiveMinimum": ["minimum"] - }, - "default": {} -} From 078ffa781c7798295d8ec9e0773d520d67a4b8c6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 26 Jul 2020 17:08:34 +0100 Subject: [PATCH 4/4] use dot v2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cde5510b03..579529ef54 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "chai": "^4.0.1", "coveralls": "^3.0.1", "del-cli": "^3.0.0", - "dot": "^1.0.3", + "dot": "^2.0.0-beta", "eslint": "^7.3.1", "gh-pages-generator": "^0.2.3", "glob": "^7.0.0",