feat: strictKeywords option to report unknown keywords, closes #781
parent
9a28689340
commit
e993bd6b4e
12
README.md
12
README.md
|
@ -1072,7 +1072,9 @@ Defaults:
|
||||||
removeAdditional: false,
|
removeAdditional: false,
|
||||||
useDefaults: false,
|
useDefaults: false,
|
||||||
coerceTypes: false,
|
coerceTypes: false,
|
||||||
|
// strict mode options
|
||||||
strictDefaults: false,
|
strictDefaults: false,
|
||||||
|
strictKeywords: false,
|
||||||
// asynchronous validation options:
|
// asynchronous validation options:
|
||||||
transpile: undefined, // requires ajv-async package
|
transpile: undefined, // requires ajv-async package
|
||||||
// advanced options:
|
// advanced options:
|
||||||
|
@ -1154,10 +1156,18 @@ Defaults:
|
||||||
- `false` (default) - no type coercion.
|
- `false` (default) - no type coercion.
|
||||||
- `true` - coerce scalar data types.
|
- `true` - coerce scalar data types.
|
||||||
- `"array"` - in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema).
|
- `"array"` - in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema).
|
||||||
- _strictDefaults_: specify behavior for ignored `default` keywords in schemas. Option values:
|
|
||||||
|
|
||||||
|
##### Strict mode options
|
||||||
|
|
||||||
|
- _strictDefaults_: report ignored `default` keywords in schemas. Option values:
|
||||||
- `false` (default) - ignored defaults are not reported
|
- `false` (default) - ignored defaults are not reported
|
||||||
- `true` - if an ignored default is present, throw an error
|
- `true` - if an ignored default is present, throw an error
|
||||||
- `"log"` - if an ignored default is present, log warning
|
- `"log"` - if an ignored default is present, log warning
|
||||||
|
- _strictKeywords_: report unknown keywords in schemas. Option values:
|
||||||
|
- `false` (default) - unknown keywords are not reported
|
||||||
|
- `true` - if an unknown keyword is present, throw an error
|
||||||
|
- `"log"` - if an unknown keyword is present, log warning
|
||||||
|
|
||||||
|
|
||||||
##### Asynchronous validation options
|
##### Asynchronous validation options
|
||||||
|
|
|
@ -20,7 +20,7 @@ module.exports = function rules() {
|
||||||
|
|
||||||
var ALL = [ 'type', '$comment' ];
|
var ALL = [ 'type', '$comment' ];
|
||||||
var KEYWORDS = [
|
var KEYWORDS = [
|
||||||
'$schema', '$id', 'id', '$data', 'title',
|
'$schema', '$id', 'id', '$data', '$async', 'title',
|
||||||
'description', 'default', 'definitions',
|
'description', 'default', 'definitions',
|
||||||
'examples', 'readOnly', 'writeOnly',
|
'examples', 'readOnly', 'writeOnly',
|
||||||
'contentMediaType', 'contentEncoding',
|
'contentMediaType', 'contentEncoding',
|
||||||
|
|
|
@ -17,6 +17,7 @@ module.exports = {
|
||||||
finalCleanUpCode: finalCleanUpCode,
|
finalCleanUpCode: finalCleanUpCode,
|
||||||
schemaHasRules: schemaHasRules,
|
schemaHasRules: schemaHasRules,
|
||||||
schemaHasRulesExcept: schemaHasRulesExcept,
|
schemaHasRulesExcept: schemaHasRulesExcept,
|
||||||
|
schemaUnknownRules: schemaUnknownRules,
|
||||||
toQuotedString: toQuotedString,
|
toQuotedString: toQuotedString,
|
||||||
getPathExpr: getPathExpr,
|
getPathExpr: getPathExpr,
|
||||||
getPath: getPath,
|
getPath: getPath,
|
||||||
|
@ -183,6 +184,12 @@ function schemaHasRulesExcept(schema, rules, exceptKeyword) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function schemaUnknownRules(schema, rules) {
|
||||||
|
if (typeof schema == 'boolean') return;
|
||||||
|
for (var key in schema) if (!rules[key]) return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function toQuotedString(str) {
|
function toQuotedString(str) {
|
||||||
return '\'' + escapeQuotes(str) + '\'';
|
return '\'' + escapeQuotes(str) + '\'';
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,17 @@
|
||||||
, $id = it.self._getId(it.schema);
|
, $id = it.self._getId(it.schema);
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
{{
|
||||||
|
if (it.opts.strictKeywords) {
|
||||||
|
var $unknownKwd = it.util.schemaUnknownRules(it.schema, it.RULES.keywords);
|
||||||
|
if ($unknownKwd) {
|
||||||
|
var $keywordsMsg = 'unknown keyword: ' + $unknownKwd;
|
||||||
|
if (it.opts.strictKeywords === 'log') it.logger.warn($keywordsMsg);
|
||||||
|
else throw new Error($keywordsMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
{{? it.isTop }}
|
{{? it.isTop }}
|
||||||
var validate = {{?$async}}{{it.async = true;}}async {{?}}function(data, dataPath, parentData, parentDataProperty, rootData) {
|
var validate = {{?$async}}{{it.async = true;}}async {{?}}function(data, dataPath, parentData, parentDataProperty, rootData) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
|
@ -415,6 +415,7 @@ describe('Custom keywords', function () {
|
||||||
it('should correctly expand macros in macro expansions', function() {
|
it('should correctly expand macros in macro expansions', function() {
|
||||||
instances.forEach(function (_ajv) {
|
instances.forEach(function (_ajv) {
|
||||||
_ajv.addKeyword('range', { type: 'number', macro: macroRange });
|
_ajv.addKeyword('range', { type: 'number', macro: macroRange });
|
||||||
|
_ajv.addKeyword('exclusiveRange', { metaSchema: {type: 'boolean'} });
|
||||||
_ajv.addKeyword('myContains', { type: 'array', macro: macroContains });
|
_ajv.addKeyword('myContains', { type: 'array', macro: macroContains });
|
||||||
|
|
||||||
var schema = {
|
var schema = {
|
||||||
|
@ -811,6 +812,7 @@ describe('Custom keywords', function () {
|
||||||
function testRangeKeyword(definition, customErrors, numErrors) {
|
function testRangeKeyword(definition, customErrors, numErrors) {
|
||||||
instances.forEach(function (_ajv) {
|
instances.forEach(function (_ajv) {
|
||||||
_ajv.addKeyword('x-range', definition);
|
_ajv.addKeyword('x-range', definition);
|
||||||
|
_ajv.addKeyword('exclusiveRange', {metaSchema: {type: 'boolean'}});
|
||||||
|
|
||||||
var schema = { "x-range": [2, 4] };
|
var schema = { "x-range": [2, 4] };
|
||||||
var validate = _ajv.compile(schema);
|
var validate = _ajv.compile(schema);
|
||||||
|
@ -849,6 +851,7 @@ describe('Custom keywords', function () {
|
||||||
function testMultipleRangeKeyword(definition, numErrors) {
|
function testMultipleRangeKeyword(definition, numErrors) {
|
||||||
instances.forEach(function (_ajv) {
|
instances.forEach(function (_ajv) {
|
||||||
_ajv.addKeyword('x-range', definition);
|
_ajv.addKeyword('x-range', definition);
|
||||||
|
_ajv.addKeyword('exclusiveRange', {metaSchema: {type: 'boolean'}});
|
||||||
|
|
||||||
var schema = {
|
var schema = {
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Ajv = require('../ajv');
|
||||||
|
var should = require('../chai').should();
|
||||||
|
|
||||||
|
|
||||||
|
describe('strictKeywords option', function() {
|
||||||
|
describe('strictKeywords = false', function() {
|
||||||
|
it('should NOT throw an error or log a warning given an unknown keyword', function() {
|
||||||
|
var output = {};
|
||||||
|
var ajv = new Ajv({
|
||||||
|
strictKeywords: false,
|
||||||
|
logger: getLogger(output)
|
||||||
|
});
|
||||||
|
var schema = {
|
||||||
|
properties: {},
|
||||||
|
unknownKeyword: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
ajv.compile(schema);
|
||||||
|
should.not.exist(output.warning);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('strictKeywords = true', function() {
|
||||||
|
it('should throw an error given an unknown keyword in the schema root when strictKeywords is true', function() {
|
||||||
|
var ajv = new Ajv({strictKeywords: true});
|
||||||
|
var schema = {
|
||||||
|
properties: {},
|
||||||
|
unknownKeyword: 1
|
||||||
|
};
|
||||||
|
should.throw(function() { ajv.compile(schema); });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('strictKeywords = "log"', function() {
|
||||||
|
it('should log a warning given an unknown keyword in the schema root when strictKeywords is "log"', function() {
|
||||||
|
var output = {};
|
||||||
|
var ajv = new Ajv({
|
||||||
|
strictKeywords: 'log',
|
||||||
|
logger: getLogger(output)
|
||||||
|
});
|
||||||
|
var schema = {
|
||||||
|
properties: {},
|
||||||
|
unknownKeyword: 1
|
||||||
|
};
|
||||||
|
ajv.compile(schema);
|
||||||
|
should.equal(output.warning, 'unknown keyword: unknownKeyword');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function getLogger(output) {
|
||||||
|
return {
|
||||||
|
log: function() {
|
||||||
|
throw new Error('log should not be called');
|
||||||
|
},
|
||||||
|
warn: function(warning) {
|
||||||
|
output.warning = warning;
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
throw new Error('error should not be called');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in New Issue