Allow custom keywords that are not valid JS identifiers

Custom keywords were required to be valid JS identifiers due to the
way they were being used in the generated code.  This prevented the use
of keywords with the `x-` prefix that is required by [swagger](http://swagger.io].

This change fixes that by updating the only place that was using them
directly as an identifier to use //brackets// notation rather than
//dot// notation.  This brings the definition of `$schemaPath` in
`definitions.def` in line with the way the rule definition is accessed
from `RULES.custom` at the top of `custom.jst`.

The validation of the keyword name has been changed to allow any name
in this change, but this should be updated to either just remove the
test or update it to a new rule before this change is included in
the project.

Finally, this also updates a number of the tests in `custom.spec.js` to
use non-valid identifiers for the names of some of the custom keywords
being tested (primarily by adding `x-` to the front of the name).

Test Plan:
- Run `npm test` and ensure all tests run and pass
master
Richard Taylor 2017-01-05 10:48:12 +00:00
parent 7613986c29
commit 66db560ca5
5 changed files with 43 additions and 43 deletions

View File

@ -3,7 +3,7 @@
var $lvl = it.level; var $lvl = it.level;
var $dataLvl = it.dataLevel; var $dataLvl = it.dataLevel;
var $schema = it.schema[$keyword]; var $schema = it.schema[$keyword];
var $schemaPath = it.schemaPath + '.' + $keyword; var $schemaPath = it.schemaPath + '[\'' + $keyword + '\']';
var $errSchemaPath = it.errSchemaPath + '/' + $keyword; var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
var $breakOnError = !it.opts.allErrors; var $breakOnError = !it.opts.allErrors;
var $errorKeyword; var $errorKeyword;

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
var IDENTIFIER = /^[a-z_$][a-z0-9_$]*$/i; var IDENTIFIER = /^.*$/i;
var customRuleCode = require('./dotjs/custom'); var customRuleCode = require('./dotjs/custom');
module.exports = { module.exports = {

View File

@ -37,7 +37,7 @@ describe('Custom keywords', function () {
metaSchema: { "type": "boolean" } metaSchema: { "type": "boolean" }
}); });
shouldBeInvalidSchema({ "even": "not_boolean" }); shouldBeInvalidSchema({ "x-even": "not_boolean" });
function validateEven(schema, data) { function validateEven(schema, data) {
return data % 2 ? !schema : schema; return data % 2 ? !schema : schema;
@ -69,9 +69,9 @@ describe('Custom keywords', function () {
"additionalItems": false "additionalItems": false
} }
}); });
shouldBeInvalidSchema({ range: [ "1", 2 ] }); shouldBeInvalidSchema({ 'x-range': [ "1", 2 ] });
shouldBeInvalidSchema({ range: {} }); shouldBeInvalidSchema({ 'x-range': {} });
shouldBeInvalidSchema({ range: [ 1, 2, 3 ] }); shouldBeInvalidSchema({ 'x-range': [ 1, 2, 3 ] });
function validateRange(schema, data, parentSchema) { function validateRange(schema, data, parentSchema) {
return parentSchema.exclusiveRange === true return parentSchema.exclusiveRange === true
@ -94,7 +94,7 @@ describe('Custom keywords', function () {
var valid = minOk && maxOk; var valid = minOk && maxOk;
if (!valid) { if (!valid) {
var err = { keyword: 'range' }; var err = { keyword: 'x-range' };
validateRange.errors = [err]; validateRange.errors = [err];
var comparison, limit; var comparison, limit;
if (minOk) { if (minOk) {
@ -121,7 +121,7 @@ describe('Custom keywords', function () {
describe('rule with "compiled" keyword validation', function() { describe('rule with "compiled" keyword validation', function() {
it('should add and validate rule', function() { it('should add and validate rule', function() {
testEvenKeyword({ type: 'number', compile: compileEven }); testEvenKeyword({ type: 'number', compile: compileEven });
shouldBeInvalidSchema({ "even": "not_boolean" }); shouldBeInvalidSchema({ "x-even": "not_boolean" });
function compileEven(schema) { function compileEven(schema) {
if (typeof schema != 'boolean') throw new Error('The value of "even" keyword must be boolean'); if (typeof schema != 'boolean') throw new Error('The value of "even" keyword must be boolean');
@ -138,7 +138,7 @@ describe('Custom keywords', function () {
compile: compileEven, compile: compileEven,
metaSchema: { "type": "boolean" } metaSchema: { "type": "boolean" }
}); });
shouldBeInvalidSchema({ "even": "not_boolean" }); shouldBeInvalidSchema({ "x-even": "not_boolean" });
function compileEven(schema) { function compileEven(schema) {
return schema ? isEven : isOdd; return schema ? isEven : isOdd;
@ -565,7 +565,7 @@ describe('Custom keywords', function () {
metaSchema: { "type": "boolean" } metaSchema: { "type": "boolean" }
}); });
compileCalled .should.equal(true); compileCalled .should.equal(true);
shouldBeInvalidSchema({ "even": "false" }); shouldBeInvalidSchema({ "x-even-$data": "false" });
function validateEven(schema, data) { function validateEven(schema, data) {
return data % 2 ? !schema : schema; return data % 2 ? !schema : schema;
@ -613,7 +613,7 @@ describe('Custom keywords', function () {
metaSchema: { "type": "boolean" } metaSchema: { "type": "boolean" }
}, 2); }, 2);
macroCalled .should.equal(true); macroCalled .should.equal(true);
shouldBeInvalidSchema({ "even": "false" }); shouldBeInvalidSchema({ "x-even-$data": "false" });
function validateEven(schema, data) { function validateEven(schema, data) {
return data % 2 ? !schema : schema; return data % 2 ? !schema : schema;
@ -658,7 +658,7 @@ describe('Custom keywords', function () {
metaSchema: { "type": "boolean" } metaSchema: { "type": "boolean" }
}); });
inlineCalled .should.equal(true); inlineCalled .should.equal(true);
shouldBeInvalidSchema({ "even": "false" }); shouldBeInvalidSchema({ "x-even-$data": "false" });
function validateEven(schema, data) { function validateEven(schema, data) {
return data % 2 ? !schema : schema; return data % 2 ? !schema : schema;
@ -685,8 +685,8 @@ describe('Custom keywords', function () {
function testEvenKeyword(definition, numErrors) { function testEvenKeyword(definition, numErrors) {
instances.forEach(function (_ajv) { instances.forEach(function (_ajv) {
_ajv.addKeyword('even', definition); _ajv.addKeyword('x-even', definition);
var schema = { "even": true }; var schema = { "x-even": true };
var validate = _ajv.compile(schema); var validate = _ajv.compile(schema);
shouldBeValid(validate, 2); shouldBeValid(validate, 2);
@ -698,9 +698,9 @@ describe('Custom keywords', function () {
function testEvenKeyword$data(definition, numErrors) { function testEvenKeyword$data(definition, numErrors) {
instances.forEach(function (_ajv) { instances.forEach(function (_ajv) {
_ajv.addKeyword('even', definition); _ajv.addKeyword('x-even-$data', definition);
var schema = { "even": true }; var schema = { "x-even-$data": true };
var validate = _ajv.compile(schema); var validate = _ajv.compile(schema);
shouldBeValid(validate, 2); shouldBeValid(validate, 2);
@ -710,7 +710,7 @@ describe('Custom keywords', function () {
schema = { schema = {
"properties": { "properties": {
"data": { "even": { "$data": "1/evenValue" } }, "data": { "x-even-$data": { "$data": "1/evenValue" } },
"evenValue": {} "evenValue": {}
} }
}; };
@ -744,15 +744,15 @@ describe('Custom keywords', function () {
function testMultipleConstantKeyword(definition, numErrors) { function testMultipleConstantKeyword(definition, numErrors) {
instances.forEach(function (_ajv) { instances.forEach(function (_ajv) {
_ajv.addKeyword('constant', definition); _ajv.addKeyword('x-constant', definition);
var schema = { var schema = {
"properties": { "properties": {
"a": { "constant": 1 }, "a": { "x-constant": 1 },
"b": { "constant": 1 } "b": { "x-constant": 1 }
}, },
"additionalProperties": { "constant": { "foo": "bar" } }, "additionalProperties": { "x-constant": { "foo": "bar" } },
"items": { "constant": { "foo": "bar" } } "items": { "x-constant": { "foo": "bar" } }
}; };
var validate = _ajv.compile(schema); var validate = _ajv.compile(schema);
@ -771,9 +771,9 @@ describe('Custom keywords', function () {
function testRangeKeyword(definition, customErrors, numErrors) { function testRangeKeyword(definition, customErrors, numErrors) {
instances.forEach(function (_ajv) { instances.forEach(function (_ajv) {
_ajv.addKeyword('range', definition); _ajv.addKeyword('x-range', definition);
var schema = { "range": [2, 4] }; var schema = { "x-range": [2, 4] };
var validate = _ajv.compile(schema); var validate = _ajv.compile(schema);
shouldBeValid(validate, 2); shouldBeValid(validate, 2);
@ -782,14 +782,14 @@ describe('Custom keywords', function () {
shouldBeValid(validate, 'abc'); shouldBeValid(validate, 'abc');
shouldBeInvalid(validate, 1.99, numErrors); shouldBeInvalid(validate, 1.99, numErrors);
if (customErrors) shouldBeRangeError(validate.errors[0], '', '#/range', '>=', 2); if (customErrors) shouldBeRangeError(validate.errors[0], '', '#/x-range', '>=', 2);
shouldBeInvalid(validate, 4.01, numErrors); shouldBeInvalid(validate, 4.01, numErrors);
if (customErrors) shouldBeRangeError(validate.errors[0], '', '#/range','<=', 4); if (customErrors) shouldBeRangeError(validate.errors[0], '', '#/x-range','<=', 4);
schema = { schema = {
"properties": { "properties": {
"foo": { "foo": {
"range": [2, 4], "x-range": [2, 4],
"exclusiveRange": true "exclusiveRange": true
} }
} }
@ -801,23 +801,23 @@ describe('Custom keywords', function () {
shouldBeValid(validate, { foo: 3.99 }); shouldBeValid(validate, { foo: 3.99 });
shouldBeInvalid(validate, { foo: 2 }, numErrors); shouldBeInvalid(validate, { foo: 2 }, numErrors);
if (customErrors) shouldBeRangeError(validate.errors[0], '.foo', '#/properties/foo/range', '>', 2, true); if (customErrors) shouldBeRangeError(validate.errors[0], '.foo', '#/properties/foo/x-range', '>', 2, true);
shouldBeInvalid(validate, { foo: 4 }, numErrors); shouldBeInvalid(validate, { foo: 4 }, numErrors);
if (customErrors) shouldBeRangeError(validate.errors[0], '.foo', '#/properties/foo/range', '<', 4, true); if (customErrors) shouldBeRangeError(validate.errors[0], '.foo', '#/properties/foo/x-range', '<', 4, true);
}); });
} }
function testMultipleRangeKeyword(definition, numErrors) { function testMultipleRangeKeyword(definition, numErrors) {
instances.forEach(function (_ajv) { instances.forEach(function (_ajv) {
_ajv.addKeyword('range', definition); _ajv.addKeyword('x-range', definition);
var schema = { var schema = {
"properties": { "properties": {
"a": { "range": [2, 4], "exclusiveRange": true }, "a": { "x-range": [2, 4], "exclusiveRange": true },
"b": { "range": [2, 4], "exclusiveRange": false } "b": { "x-range": [2, 4], "exclusiveRange": false }
}, },
"additionalProperties": { "range": [5, 7] }, "additionalProperties": { "x-range": [5, 7] },
"items": { "range": [5, 7] } "items": { "x-range": [5, 7] }
}; };
var validate = _ajv.compile(schema); var validate = _ajv.compile(schema);
@ -836,7 +836,7 @@ describe('Custom keywords', function () {
delete error.schema; delete error.schema;
delete error.data; delete error.data;
error .should.eql({ error .should.eql({
keyword: 'range', keyword: 'x-range',
dataPath: dataPath, dataPath: dataPath,
schemaPath: schemaPath, schemaPath: schemaPath,
message: 'should be ' + comparison + ' ' + limit, message: 'should be ' + comparison + ' ' + limit,
@ -906,14 +906,14 @@ describe('Custom keywords', function () {
} }
}); });
it('should throw if keyword is not a valid identifier', function() { it('should not throw even if keyword is not a valid identifier', function() {
should.not.throw(function() { should.not.throw(function() {
ajv.addKeyword('mykeyword', { ajv.addKeyword('mykeyword', {
validate: function() { return true; } validate: function() { return true; }
}); });
}); });
should.throw(function() { should.not.throw(function() {
ajv.addKeyword('my-keyword', { ajv.addKeyword('my-keyword', {
validate: function() { return true; } validate: function() { return true; }
}); });

View File

@ -1,7 +1,7 @@
{{ {{
var $data = 'data' + (it.dataLevel || '') var $data = 'data' + (it.dataLevel || '')
, $min = it.schema.range[0] , $min = it.schema['x-range'][0]
, $max = it.schema.range[1] , $max = it.schema['x-range'][1]
, $gt = it.schema.exclusiveRange ? '>' : '>=' , $gt = it.schema.exclusiveRange ? '>' : '>='
, $lt = it.schema.exclusiveRange ? '<' : '<='; , $lt = it.schema.exclusiveRange ? '<' : '<=';
}} }}

View File

@ -1,7 +1,7 @@
{{ {{
var $data = 'data' + (it.dataLevel || '') var $data = 'data' + (it.dataLevel || '')
, $min = it.schema.range[0] , $min = it.schema['x-range'][0]
, $max = it.schema.range[1] , $max = it.schema['x-range'][1]
, $exclusive = !!it.schema.exclusiveRange , $exclusive = !!it.schema.exclusiveRange
, $gt = $exclusive ? '>' : '>=' , $gt = $exclusive ? '>' : '>='
, $lt = $exclusive ? '<' : '<=' , $lt = $exclusive ? '<' : '<='
@ -16,7 +16,7 @@ if (!valid{{=$lvl}}) {
var {{=$err}}; var {{=$err}};
if (minOk{{=$lvl}}) { if (minOk{{=$lvl}}) {
{{=$err}} = { {{=$err}} = {
keyword: 'range', keyword: 'x-range',
message: 'should be {{=$lt}} {{=$max}}', message: 'should be {{=$lt}} {{=$max}}',
params: { params: {
comparison: '{{=$lt}}', comparison: '{{=$lt}}',
@ -26,7 +26,7 @@ if (!valid{{=$lvl}}) {
}; };
} else { } else {
{{=$err}} = { {{=$err}} = {
keyword: 'range', keyword: 'x-range',
message: 'should be {{=$gt}} {{=$min}}', message: 'should be {{=$gt}} {{=$min}}',
params: { params: {
comparison: '{{=$gt}}', comparison: '{{=$gt}}',