From 66db560ca55b7fab966dbaf54e6b8adc88d2a245 Mon Sep 17 00:00:00 2001 From: Richard Taylor Date: Thu, 5 Jan 2017 10:48:12 +0000 Subject: [PATCH] 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 --- lib/dot/definitions.def | 2 +- lib/keyword.js | 2 +- spec/custom.spec.js | 70 ++++++++++++------------- spec/custom_rules/range.jst | 4 +- spec/custom_rules/range_with_errors.jst | 8 +-- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/lib/dot/definitions.def b/lib/dot/definitions.def index 76150ce..bde38fb 100644 --- a/lib/dot/definitions.def +++ b/lib/dot/definitions.def @@ -3,7 +3,7 @@ var $lvl = it.level; var $dataLvl = it.dataLevel; var $schema = it.schema[$keyword]; - var $schemaPath = it.schemaPath + '.' + $keyword; + var $schemaPath = it.schemaPath + '[\'' + $keyword + '\']'; var $errSchemaPath = it.errSchemaPath + '/' + $keyword; var $breakOnError = !it.opts.allErrors; var $errorKeyword; diff --git a/lib/keyword.js b/lib/keyword.js index 954baca..a34774e 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -1,6 +1,6 @@ 'use strict'; -var IDENTIFIER = /^[a-z_$][a-z0-9_$]*$/i; +var IDENTIFIER = /^.*$/i; var customRuleCode = require('./dotjs/custom'); module.exports = { diff --git a/spec/custom.spec.js b/spec/custom.spec.js index 6150222..7a68650 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -37,7 +37,7 @@ describe('Custom keywords', function () { metaSchema: { "type": "boolean" } }); - shouldBeInvalidSchema({ "even": "not_boolean" }); + shouldBeInvalidSchema({ "x-even": "not_boolean" }); function validateEven(schema, data) { return data % 2 ? !schema : schema; @@ -69,9 +69,9 @@ describe('Custom keywords', function () { "additionalItems": false } }); - shouldBeInvalidSchema({ range: [ "1", 2 ] }); - shouldBeInvalidSchema({ range: {} }); - shouldBeInvalidSchema({ range: [ 1, 2, 3 ] }); + shouldBeInvalidSchema({ 'x-range': [ "1", 2 ] }); + shouldBeInvalidSchema({ 'x-range': {} }); + shouldBeInvalidSchema({ 'x-range': [ 1, 2, 3 ] }); function validateRange(schema, data, parentSchema) { return parentSchema.exclusiveRange === true @@ -94,7 +94,7 @@ describe('Custom keywords', function () { var valid = minOk && maxOk; if (!valid) { - var err = { keyword: 'range' }; + var err = { keyword: 'x-range' }; validateRange.errors = [err]; var comparison, limit; if (minOk) { @@ -121,7 +121,7 @@ describe('Custom keywords', function () { describe('rule with "compiled" keyword validation', function() { it('should add and validate rule', function() { testEvenKeyword({ type: 'number', compile: compileEven }); - shouldBeInvalidSchema({ "even": "not_boolean" }); + shouldBeInvalidSchema({ "x-even": "not_boolean" }); function compileEven(schema) { 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, metaSchema: { "type": "boolean" } }); - shouldBeInvalidSchema({ "even": "not_boolean" }); + shouldBeInvalidSchema({ "x-even": "not_boolean" }); function compileEven(schema) { return schema ? isEven : isOdd; @@ -565,7 +565,7 @@ describe('Custom keywords', function () { metaSchema: { "type": "boolean" } }); compileCalled .should.equal(true); - shouldBeInvalidSchema({ "even": "false" }); + shouldBeInvalidSchema({ "x-even-$data": "false" }); function validateEven(schema, data) { return data % 2 ? !schema : schema; @@ -613,7 +613,7 @@ describe('Custom keywords', function () { metaSchema: { "type": "boolean" } }, 2); macroCalled .should.equal(true); - shouldBeInvalidSchema({ "even": "false" }); + shouldBeInvalidSchema({ "x-even-$data": "false" }); function validateEven(schema, data) { return data % 2 ? !schema : schema; @@ -658,7 +658,7 @@ describe('Custom keywords', function () { metaSchema: { "type": "boolean" } }); inlineCalled .should.equal(true); - shouldBeInvalidSchema({ "even": "false" }); + shouldBeInvalidSchema({ "x-even-$data": "false" }); function validateEven(schema, data) { return data % 2 ? !schema : schema; @@ -685,8 +685,8 @@ describe('Custom keywords', function () { function testEvenKeyword(definition, numErrors) { instances.forEach(function (_ajv) { - _ajv.addKeyword('even', definition); - var schema = { "even": true }; + _ajv.addKeyword('x-even', definition); + var schema = { "x-even": true }; var validate = _ajv.compile(schema); shouldBeValid(validate, 2); @@ -698,9 +698,9 @@ describe('Custom keywords', function () { function testEvenKeyword$data(definition, numErrors) { 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); shouldBeValid(validate, 2); @@ -710,7 +710,7 @@ describe('Custom keywords', function () { schema = { "properties": { - "data": { "even": { "$data": "1/evenValue" } }, + "data": { "x-even-$data": { "$data": "1/evenValue" } }, "evenValue": {} } }; @@ -744,15 +744,15 @@ describe('Custom keywords', function () { function testMultipleConstantKeyword(definition, numErrors) { instances.forEach(function (_ajv) { - _ajv.addKeyword('constant', definition); + _ajv.addKeyword('x-constant', definition); var schema = { "properties": { - "a": { "constant": 1 }, - "b": { "constant": 1 } + "a": { "x-constant": 1 }, + "b": { "x-constant": 1 } }, - "additionalProperties": { "constant": { "foo": "bar" } }, - "items": { "constant": { "foo": "bar" } } + "additionalProperties": { "x-constant": { "foo": "bar" } }, + "items": { "x-constant": { "foo": "bar" } } }; var validate = _ajv.compile(schema); @@ -771,9 +771,9 @@ describe('Custom keywords', function () { function testRangeKeyword(definition, customErrors, numErrors) { 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); shouldBeValid(validate, 2); @@ -782,14 +782,14 @@ describe('Custom keywords', function () { shouldBeValid(validate, 'abc'); 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); - if (customErrors) shouldBeRangeError(validate.errors[0], '', '#/range','<=', 4); + if (customErrors) shouldBeRangeError(validate.errors[0], '', '#/x-range','<=', 4); schema = { "properties": { "foo": { - "range": [2, 4], + "x-range": [2, 4], "exclusiveRange": true } } @@ -801,23 +801,23 @@ describe('Custom keywords', function () { shouldBeValid(validate, { foo: 3.99 }); 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); - 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) { instances.forEach(function (_ajv) { - _ajv.addKeyword('range', definition); + _ajv.addKeyword('x-range', definition); var schema = { "properties": { - "a": { "range": [2, 4], "exclusiveRange": true }, - "b": { "range": [2, 4], "exclusiveRange": false } + "a": { "x-range": [2, 4], "exclusiveRange": true }, + "b": { "x-range": [2, 4], "exclusiveRange": false } }, - "additionalProperties": { "range": [5, 7] }, - "items": { "range": [5, 7] } + "additionalProperties": { "x-range": [5, 7] }, + "items": { "x-range": [5, 7] } }; var validate = _ajv.compile(schema); @@ -836,7 +836,7 @@ describe('Custom keywords', function () { delete error.schema; delete error.data; error .should.eql({ - keyword: 'range', + keyword: 'x-range', dataPath: dataPath, schemaPath: schemaPath, 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() { ajv.addKeyword('mykeyword', { validate: function() { return true; } }); }); - should.throw(function() { + should.not.throw(function() { ajv.addKeyword('my-keyword', { validate: function() { return true; } }); diff --git a/spec/custom_rules/range.jst b/spec/custom_rules/range.jst index 74f5686..adb481e 100644 --- a/spec/custom_rules/range.jst +++ b/spec/custom_rules/range.jst @@ -1,7 +1,7 @@ {{ var $data = 'data' + (it.dataLevel || '') - , $min = it.schema.range[0] - , $max = it.schema.range[1] + , $min = it.schema['x-range'][0] + , $max = it.schema['x-range'][1] , $gt = it.schema.exclusiveRange ? '>' : '>=' , $lt = it.schema.exclusiveRange ? '<' : '<='; }} diff --git a/spec/custom_rules/range_with_errors.jst b/spec/custom_rules/range_with_errors.jst index a9bb7da..6bfb6e5 100644 --- a/spec/custom_rules/range_with_errors.jst +++ b/spec/custom_rules/range_with_errors.jst @@ -1,7 +1,7 @@ {{ var $data = 'data' + (it.dataLevel || '') - , $min = it.schema.range[0] - , $max = it.schema.range[1] + , $min = it.schema['x-range'][0] + , $max = it.schema['x-range'][1] , $exclusive = !!it.schema.exclusiveRange , $gt = $exclusive ? '>' : '>=' , $lt = $exclusive ? '<' : '<=' @@ -16,7 +16,7 @@ if (!valid{{=$lvl}}) { var {{=$err}}; if (minOk{{=$lvl}}) { {{=$err}} = { - keyword: 'range', + keyword: 'x-range', message: 'should be {{=$lt}} {{=$max}}', params: { comparison: '{{=$lt}}', @@ -26,7 +26,7 @@ if (!valid{{=$lvl}}) { }; } else { {{=$err}} = { - keyword: 'range', + keyword: 'x-range', message: 'should be {{=$gt}} {{=$min}}', params: { comparison: '{{=$gt}}',