feat: support for required dependencies of custom keyword (keywords that must be present in the same schema)
parent
f080c91e9a
commit
fdfbd4402a
|
@ -92,6 +92,8 @@ In some cases it is the best approach to define keywords, but it has the perform
|
||||||
|
|
||||||
All custom keywords types can have an optional `metaSchema` property in their definitions. It is a schema against which the value of keyword will be validated during schema compilation.
|
All custom keywords types can have an optional `metaSchema` property in their definitions. It is a schema against which the value of keyword will be validated during schema compilation.
|
||||||
|
|
||||||
|
Custom keyword can also have an optional `dependencies` property in their definitions - it is a list of required keywords in a containing (parent) schema.
|
||||||
|
|
||||||
Example. `range` and `exclusiveRange` keywords using compiled schema:
|
Example. `range` and `exclusiveRange` keywords using compiled schema:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
|
@ -1005,6 +1005,7 @@ Keyword definition is an object with the following properties:
|
||||||
- _inline_: compiling function that returns code (as string)
|
- _inline_: compiling function that returns code (as string)
|
||||||
- _schema_: an optional `false` value used with "validate" keyword to not pass schema
|
- _schema_: an optional `false` value used with "validate" keyword to not pass schema
|
||||||
- _metaSchema_: an optional meta-schema for keyword schema
|
- _metaSchema_: an optional meta-schema for keyword schema
|
||||||
|
- _dependencies_: an optional list of properties that must be present in the parent schema - it will be checked during schema compilation
|
||||||
- _modifying_: `true` MUST be passed if keyword modifies data
|
- _modifying_: `true` MUST be passed if keyword modifies data
|
||||||
- _valid_: pass `true`/`false` to pre-define validation result, the result returned from validation function will be ignored. This option cannot be used with macro keywords.
|
- _valid_: pass `true`/`false` to pre-define validation result, the result returned from validation function will be ignored. This option cannot be used with macro keywords.
|
||||||
- _$data_: an optional `true` value to support [$data reference](#data-reference) as the value of custom keyword. The reference will be resolved at validation time. If the keyword has meta-schema it would be extended to allow $data and it will be used to validate the resolved value. Supporting $data reference requires that keyword has validating function (as the only option or in addition to compile, macro or inline function).
|
- _$data_: an optional `true` value to support [$data reference](#data-reference) as the value of custom keyword. The reference will be resolved at validation time. If the keyword has meta-schema it would be extended to allow $data and it will be used to validate the resolved value. Supporting $data reference requires that keyword has validating function (as the only option or in addition to compile, macro or inline function).
|
||||||
|
|
|
@ -255,8 +255,16 @@ function compile(schema, root, localRefs, baseId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function useCustomRule(rule, schema, parentSchema, it) {
|
function useCustomRule(rule, schema, parentSchema, it) {
|
||||||
|
if (self._opts.validateSchema !== false) {
|
||||||
|
var deps = rule.definition.dependencies;
|
||||||
|
if (deps && !deps.every(function(keyword) {
|
||||||
|
return Object.prototype.hasOwnProperty.call(parentSchema, keyword);
|
||||||
|
})) {
|
||||||
|
throw new Error('parent schema must have all required keywords: ' + deps.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
var validateSchema = rule.definition.validateSchema;
|
var validateSchema = rule.definition.validateSchema;
|
||||||
if (validateSchema && self._opts.validateSchema !== false) {
|
if (validateSchema) {
|
||||||
var valid = validateSchema(schema);
|
var valid = validateSchema(schema);
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);
|
var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);
|
||||||
|
@ -264,6 +272,7 @@ function compile(schema, root, localRefs, baseId) {
|
||||||
else throw new Error(message);
|
else throw new Error(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var compile = rule.definition.compile
|
var compile = rule.definition.compile
|
||||||
, inline = rule.definition.inline
|
, inline = rule.definition.inline
|
||||||
|
|
|
@ -24,7 +24,7 @@ function addKeyword(keyword, definition) {
|
||||||
if (RULES.keywords[keyword])
|
if (RULES.keywords[keyword])
|
||||||
throw new Error('Keyword ' + keyword + ' is already defined');
|
throw new Error('Keyword ' + keyword + ' is already defined');
|
||||||
|
|
||||||
if (!IDENTIFIER.test(keyword))
|
if (!isIdentifier(keyword))
|
||||||
throw new Error('Keyword ' + keyword + ' is not a valid identifier');
|
throw new Error('Keyword ' + keyword + ' is not a valid identifier');
|
||||||
|
|
||||||
if (definition) {
|
if (definition) {
|
||||||
|
@ -45,6 +45,10 @@ function addKeyword(keyword, definition) {
|
||||||
if ($data && !definition.validate)
|
if ($data && !definition.validate)
|
||||||
throw new Error('$data support: "validate" function is not defined');
|
throw new Error('$data support: "validate" function is not defined');
|
||||||
|
|
||||||
|
var deps = definition.dependencies;
|
||||||
|
if (deps && !(Array.isArray(deps) && deps.every(isIdentifier)))
|
||||||
|
throw new Error('"dependencies" option should be a list of valid identifiers');
|
||||||
|
|
||||||
var metaSchema = definition.metaSchema;
|
var metaSchema = definition.metaSchema;
|
||||||
if (metaSchema) {
|
if (metaSchema) {
|
||||||
if ($data) {
|
if ($data) {
|
||||||
|
@ -93,6 +97,10 @@ function addKeyword(keyword, definition) {
|
||||||
if (!RULES.types[dataType]) throw new Error('Unknown type ' + dataType);
|
if (!RULES.types[dataType]) throw new Error('Unknown type ' + dataType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isIdentifier(name) {
|
||||||
|
return typeof name == 'string' && IDENTIFIER.test(name);
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1182,4 +1182,50 @@ describe('Custom keywords', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('"dependencies" in keyword definition', function() {
|
||||||
|
it("should require properties in the parent schema", function() {
|
||||||
|
ajv.addKeyword('allRequired', {
|
||||||
|
macro: function(schema, parentSchema) {
|
||||||
|
return schema ? {required: Object.keys(parentSchema.properties)} : true;
|
||||||
|
},
|
||||||
|
metaSchema: {type: 'boolean'},
|
||||||
|
dependencies: ['properties']
|
||||||
|
});
|
||||||
|
|
||||||
|
var invalidSchema = {
|
||||||
|
allRequired: true
|
||||||
|
};
|
||||||
|
|
||||||
|
should.throw(function () {
|
||||||
|
ajv.compile(invalidSchema);
|
||||||
|
});
|
||||||
|
|
||||||
|
var schema = {
|
||||||
|
properties: {
|
||||||
|
foo: true
|
||||||
|
},
|
||||||
|
allRequired: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var v = ajv.compile(schema);
|
||||||
|
v({foo: 1}) .should.equal(true);
|
||||||
|
v({}) .should.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'dependencies'should be array of valid strings", function() {
|
||||||
|
ajv.addKeyword('newKeyword1', {
|
||||||
|
metaSchema: {type: 'boolean'},
|
||||||
|
dependencies: ['dep1', 'dep-2']
|
||||||
|
});
|
||||||
|
|
||||||
|
should.throw(function () {
|
||||||
|
ajv.addKeyword('newKeyword2', {
|
||||||
|
metaSchema: {type: 'boolean'},
|
||||||
|
dependencies: ['dep.1']
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue