Skip to content

Commit

Permalink
feat: $data support in custom keywords, #146
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Jul 19, 2016
1 parent ab4f658 commit 7f5fe70
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 15 deletions.
1 change: 1 addition & 0 deletions lib/compile/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = function rules() {
RULES.keywords = util.toHash(RULES.all.concat(RULES.keywords));
RULES.all = util.toHash(RULES.all);
RULES.types = util.toHash(RULES.types);
RULES.custom = {};

return RULES;
};
47 changes: 34 additions & 13 deletions lib/dot/custom.jst
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}
{{# def.$data }}

{{
var $rule = this
, $ruleValidate = it.useCustomRule($rule, $schema, it.schema, it)
, $ruleErrs = $ruleValidate.code + '.errors'
, $rDef = $rule.definition
, $validate = $rDef.validate
, $compile = $rDef.compile
, $inline
, $macro
, $ruleValidate
, $validateCode;
}}

{{? $isData && $rDef.$data }}
{{ $validateCode = 'keywordValidate' + $lvl; }}
var {{=$validateCode}} = RULES.custom['{{=$keyword}}'].definition
{{? $validate }}.validate{{??}}.compile({{=$schemaValue}}, validate.schema{{=it.schemaPath}}){{?}};
{{??}}
{{
$ruleValidate = it.useCustomRule($rule, $schema, it.schema, it);
$schemaValue = 'validate.schema' + $schemaPath;
$validateCode = $ruleValidate.code;
$inline = $rDef.inline;
$macro = $rDef.macro;
}}
{{?}}

{{
var $ruleErrs = $validateCode + '.errors'
, $i = 'i' + $lvl
, $ruleErr = 'ruleErr' + $lvl
, $rDef = $rule.definition
, $asyncKeyword = $rDef.async
, $inline = $rDef.inline
, $macro = $rDef.macro;
, $asyncKeyword = $rDef.async;

if ($asyncKeyword && !it.async)
throw new Error('async keyword in sync schema');
Expand All @@ -23,12 +44,12 @@ var {{=$errs}} = errors;
var valid{{=$lvl}};

{{## def.callRuleValidate:
{{=$ruleValidate.code}}.call(
{{=$validateCode}}.call(
{{? it.opts.passContext }}this{{??}}self{{?}}
{{? $rDef.compile || $rDef.schema === false }}
{{? $compile || $rDef.schema === false }}
, {{=$data}}
{{??}}
, validate.schema{{=$schemaPath}}
, {{=$schemaValue}}
, {{=$data}}
, validate.schema{{=it.schemaPath}}
{{?}}
Expand Down Expand Up @@ -69,7 +90,7 @@ var valid{{=$lvl}};
{{=$ruleErr}}.schemaPath = "{{=$errSchemaPath}}";
{{# _inline ? '}' : '' }}
{{? it.opts.verbose }}
{{=$ruleErr}}.schema = validate.schema{{=$schemaPath}};
{{=$ruleErr}}.schema = {{=$schemaValue}};
{{=$ruleErr}}.data = {{=$data}};
{{?}}
}
Expand All @@ -84,10 +105,10 @@ var valid{{=$lvl}};
$it.schemaPath = '';
}}
{{# def.setCompositeRule }}
{{ var $code = it.validate($it).replace(/validate\.schema/g, $ruleValidate.code); }}
{{ var $code = it.validate($it).replace(/validate\.schema/g, $validateCode); }}
{{# def.resetCompositeRule }}
{{= $code }}
{{?? $rDef.compile || $rDef.validate }}
{{?? $compile || $validate }}
{{# def.beginDefOut}}
{{# def.callRuleValidate }}
{{# def.storeDefOut:def_callRuleValidate }}
Expand All @@ -104,7 +125,7 @@ var valid{{=$lvl}};
else throw e;
}
{{??}}
{{=$ruleValidate.code}}.errors = null;
{{=$validateCode}}.errors = null;
{{?}}
{{?}}
{{?}}
Expand Down
25 changes: 23 additions & 2 deletions lib/keyword.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,28 @@ module.exports = function addKeyword(keyword, definition) {
_addRule(keyword, dataType, definition);
}

if (definition.metaSchema)
definition.validateSchema = self.compile(definition.metaSchema, true);
var $data = definition.$data === true && this._opts.v5;
if ($data) {
if (!definition.validate) {
if (definition.compile)
console.warn('$data support: it is recommended to define "validate" function');
else
throw new Error('$data support: neither "validate" nor "compile" functions are defined');
}
}

var metaSchema = definition.metaSchema;
if (metaSchema) {
if ($data) {
metaSchema = {
anyOf: [
metaSchema,
{ '$ref': 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#/definitions/$data' }
]
};
}
definition.validateSchema = self.compile(metaSchema, true);
}
}

this.RULES.keywords[keyword] = true;
Expand Down Expand Up @@ -59,6 +79,7 @@ module.exports = function addKeyword(keyword, definition) {
code: customRuleCode
};
ruleGroup.rules.push(rule);
self.RULES.custom[keyword] = rule;
}


Expand Down
90 changes: 90 additions & 0 deletions spec/custom.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,72 @@ describe('Custom keywords', function () {
});


describe('$data reference support with custom keywords (v5 only)', function() {
beforeEach(function() {
instances = getAjvInstances({
allErrors: true,
verbose: true,
inlineRefs: false
}, { v5: true });
ajv = instances[0];
});

it('should validate "interpreted" rule', function() {
testEvenKeyword$data({
type: 'number',
$data: true,
validate: validateEven
});

function validateEven(schema, data) {
if (typeof schema != 'boolean') return false;
return data % 2 ? !schema : schema;
}
});

it('should validate "compiled" rule', function() {
testEvenKeyword$data({
type: 'number',
$data: true,
compile: compileEven
});
shouldBeInvalidSchema({ "even": "false" });

function compileEven(schema) {
if (typeof schema != 'boolean') throw new Error('The value of "even" keyword must be boolean');
return schema ? isEven : isOdd;
}

function isEven(data) { return data % 2 === 0; }
function isOdd(data) { return data % 2 !== 0; }
});

it('should validate "interpreted" rule with meta-schema', function() {
testEvenKeyword$data({
type: 'number',
$data: true,
validate: validateEven,
metaSchema: { "type": "boolean" }
});
shouldBeInvalidSchema({ "even": "false" });

function validateEven(schema, data) {
return data % 2 ? !schema : schema;
}
});

it('should fail if keyword definition has "$data" but no "validate" or "compile"', function() {
should.throw(function() {
ajv.addKeyword('even', {
type: 'number',
$data: true,
macro: function() { return {}; }
});
});
});
});


function testEvenKeyword(definition, numErrors) {
instances.forEach(function (ajv) {
ajv.addKeyword('even', definition);
Expand All @@ -520,6 +586,30 @@ describe('Custom keywords', function () {
});
}

function testEvenKeyword$data(definition, numErrors) {
instances.forEach(function (ajv) {
var schema = {
"properties": {
"data": { "even": { "$data": "1/evenValue" } },
"evenValue": {}
}
};
ajv.addKeyword('even', definition);
var validate = ajv.compile(schema);

shouldBeValid(validate, { data: 2, evenValue: true });
shouldBeInvalid(validate, { data: 2, evenValue: false });
shouldBeValid(validate, { data: 'abc', evenValue: true });
shouldBeValid(validate, { data: 'abc', evenValue: false });
shouldBeInvalid(validate, { data: 2.5, evenValue: true }, numErrors);
shouldBeValid(validate, { data: 2.5, evenValue: false });
shouldBeInvalid(validate, { data: 3, evenValue: true }, numErrors);
shouldBeValid(validate, { data: 3, evenValue: false });

// shouldBeInvalid(validate, { data: 2, evenValue: "true" });
});
}

function testConstantKeyword(definition, numErrors) {
instances.forEach(function (ajv) {
ajv.addKeyword('constant', definition);
Expand Down

0 comments on commit 7f5fe70

Please sign in to comment.