You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
820 lines
25 KiB
820 lines
25 KiB
'use strict'; |
|
|
|
var helpers = require('./helpers'); |
|
|
|
/** @type ValidatorResult */ |
|
var ValidatorResult = helpers.ValidatorResult; |
|
/** @type SchemaError */ |
|
var SchemaError = helpers.SchemaError; |
|
|
|
var attribute = {}; |
|
|
|
attribute.ignoreProperties = { |
|
// informative properties |
|
'id': true, |
|
'default': true, |
|
'description': true, |
|
'title': true, |
|
// arguments to other properties |
|
'exclusiveMinimum': true, |
|
'exclusiveMaximum': true, |
|
'additionalItems': true, |
|
// special-handled properties |
|
'$schema': true, |
|
'$ref': true, |
|
'extends': true |
|
}; |
|
|
|
/** |
|
* @name validators |
|
*/ |
|
var validators = attribute.validators = {}; |
|
|
|
/** |
|
* Validates whether the instance if of a certain type |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {ValidatorResult|null} |
|
*/ |
|
validators.type = function validateType (instance, schema, options, ctx) { |
|
// Ignore undefined instances |
|
if (instance === undefined) { |
|
return null; |
|
} |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var types = Array.isArray(schema.type) ? schema.type : [schema.type]; |
|
if (!types.some(this.testType.bind(this, instance, schema, options, ctx))) { |
|
var list = types.map(function (v) { |
|
return v.id && ('<' + v.id + '>') || (v+''); |
|
}); |
|
result.addError({ |
|
name: 'type', |
|
argument: list, |
|
message: "is not of a type(s) " + list, |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
function testSchemaNoThrow(instance, options, ctx, callback, schema){ |
|
var throwError = options.throwError; |
|
options.throwError = false; |
|
var res = this.validateSchema(instance, schema, options, ctx); |
|
options.throwError = throwError; |
|
|
|
if (! res.valid && callback instanceof Function) { |
|
callback(res); |
|
} |
|
return res.valid; |
|
} |
|
|
|
/** |
|
* Validates whether the instance matches some of the given schemas |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {ValidatorResult|null} |
|
*/ |
|
validators.anyOf = function validateAnyOf (instance, schema, options, ctx) { |
|
// Ignore undefined instances |
|
if (instance === undefined) { |
|
return null; |
|
} |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var inner = new ValidatorResult(instance, schema, options, ctx); |
|
if (!Array.isArray(schema.anyOf)){ |
|
throw new SchemaError("anyOf must be an array"); |
|
} |
|
if (!schema.anyOf.some( |
|
testSchemaNoThrow.bind( |
|
this, instance, options, ctx, function(res){inner.importErrors(res);} |
|
))) { |
|
var list = schema.anyOf.map(function (v, i) { |
|
return (v.id && ('<' + v.id + '>')) || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; |
|
}); |
|
if (options.nestedErrors) { |
|
result.importErrors(inner); |
|
} |
|
result.addError({ |
|
name: 'anyOf', |
|
argument: list, |
|
message: "is not any of " + list.join(','), |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance matches every given schema |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {String|null} |
|
*/ |
|
validators.allOf = function validateAllOf (instance, schema, options, ctx) { |
|
// Ignore undefined instances |
|
if (instance === undefined) { |
|
return null; |
|
} |
|
if (!Array.isArray(schema.allOf)){ |
|
throw new SchemaError("allOf must be an array"); |
|
} |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var self = this; |
|
schema.allOf.forEach(function(v, i){ |
|
var valid = self.validateSchema(instance, v, options, ctx); |
|
if(!valid.valid){ |
|
var msg = (v.id && ('<' + v.id + '>')) || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; |
|
result.addError({ |
|
name: 'allOf', |
|
argument: { id: msg, length: valid.errors.length, valid: valid }, |
|
message: 'does not match allOf schema ' + msg + ' with ' + valid.errors.length + ' error[s]:', |
|
}); |
|
result.importErrors(valid); |
|
} |
|
}); |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance matches exactly one of the given schemas |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {String|null} |
|
*/ |
|
validators.oneOf = function validateOneOf (instance, schema, options, ctx) { |
|
// Ignore undefined instances |
|
if (instance === undefined) { |
|
return null; |
|
} |
|
if (!Array.isArray(schema.oneOf)){ |
|
throw new SchemaError("oneOf must be an array"); |
|
} |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var inner = new ValidatorResult(instance, schema, options, ctx); |
|
var count = schema.oneOf.filter( |
|
testSchemaNoThrow.bind( |
|
this, instance, options, ctx, function(res) {inner.importErrors(res);} |
|
) ).length; |
|
var list = schema.oneOf.map(function (v, i) { |
|
return (v.id && ('<' + v.id + '>')) || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; |
|
}); |
|
if (count!==1) { |
|
if (options.nestedErrors) { |
|
result.importErrors(inner); |
|
} |
|
result.addError({ |
|
name: 'oneOf', |
|
argument: list, |
|
message: "is not exactly one from " + list.join(','), |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates properties |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {String|null|ValidatorResult} |
|
*/ |
|
validators.properties = function validateProperties (instance, schema, options, ctx) { |
|
if(!this.types.object(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var properties = schema.properties || {}; |
|
for (var property in properties) { |
|
if (typeof options.preValidateProperty == 'function') { |
|
options.preValidateProperty(instance, property, properties[property], options, ctx); |
|
} |
|
|
|
var prop = Object.hasOwnProperty.call(instance, property) ? instance[property] : undefined; |
|
var res = this.validateSchema(prop, properties[property], options, ctx.makeChild(properties[property], property)); |
|
if(res.instance !== result.instance[property]) result.instance[property] = res.instance; |
|
result.importErrors(res); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Test a specific property within in instance against the additionalProperties schema attribute |
|
* This ignores properties with definitions in the properties schema attribute, but no other attributes. |
|
* If too many more types of property-existance tests pop up they may need their own class of tests (like `type` has) |
|
* @private |
|
* @return {boolean} |
|
*/ |
|
function testAdditionalProperty (instance, schema, options, ctx, property, result) { |
|
if(!this.types.object(instance)) return; |
|
if (schema.properties && schema.properties[property] !== undefined) { |
|
return; |
|
} |
|
if (schema.additionalProperties === false) { |
|
result.addError({ |
|
name: 'additionalProperties', |
|
argument: property, |
|
message: "additionalProperty " + JSON.stringify(property) + " exists in instance when not allowed", |
|
}); |
|
} else { |
|
var additionalProperties = schema.additionalProperties || {}; |
|
|
|
if (typeof options.preValidateProperty == 'function') { |
|
options.preValidateProperty(instance, property, additionalProperties, options, ctx); |
|
} |
|
|
|
var res = this.validateSchema(instance[property], additionalProperties, options, ctx.makeChild(additionalProperties, property)); |
|
if(res.instance !== result.instance[property]) result.instance[property] = res.instance; |
|
result.importErrors(res); |
|
} |
|
} |
|
|
|
/** |
|
* Validates patternProperties |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {String|null|ValidatorResult} |
|
*/ |
|
validators.patternProperties = function validatePatternProperties (instance, schema, options, ctx) { |
|
if(!this.types.object(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var patternProperties = schema.patternProperties || {}; |
|
|
|
for (var property in instance) { |
|
var test = true; |
|
for (var pattern in patternProperties) { |
|
var expr = new RegExp(pattern); |
|
if (!expr.test(property)) { |
|
continue; |
|
} |
|
test = false; |
|
|
|
if (typeof options.preValidateProperty == 'function') { |
|
options.preValidateProperty(instance, property, patternProperties[pattern], options, ctx); |
|
} |
|
|
|
var res = this.validateSchema(instance[property], patternProperties[pattern], options, ctx.makeChild(patternProperties[pattern], property)); |
|
if(res.instance !== result.instance[property]) result.instance[property] = res.instance; |
|
result.importErrors(res); |
|
} |
|
if (test) { |
|
testAdditionalProperty.call(this, instance, schema, options, ctx, property, result); |
|
} |
|
} |
|
|
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates additionalProperties |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {String|null|ValidatorResult} |
|
*/ |
|
validators.additionalProperties = function validateAdditionalProperties (instance, schema, options, ctx) { |
|
if(!this.types.object(instance)) return; |
|
// if patternProperties is defined then we'll test when that one is called instead |
|
if (schema.patternProperties) { |
|
return null; |
|
} |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
for (var property in instance) { |
|
testAdditionalProperty.call(this, instance, schema, options, ctx, property, result); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance value is at least of a certain length, when the instance value is a string. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.minProperties = function validateMinProperties (instance, schema, options, ctx) { |
|
if (!this.types.object(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var keys = Object.keys(instance); |
|
if (!(keys.length >= schema.minProperties)) { |
|
result.addError({ |
|
name: 'minProperties', |
|
argument: schema.minProperties, |
|
message: "does not meet minimum property length of " + schema.minProperties, |
|
}) |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance value is at most of a certain length, when the instance value is a string. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.maxProperties = function validateMaxProperties (instance, schema, options, ctx) { |
|
if (!this.types.object(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var keys = Object.keys(instance); |
|
if (!(keys.length <= schema.maxProperties)) { |
|
result.addError({ |
|
name: 'maxProperties', |
|
argument: schema.maxProperties, |
|
message: "does not meet maximum property length of " + schema.maxProperties, |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates items when instance is an array |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {String|null|ValidatorResult} |
|
*/ |
|
validators.items = function validateItems (instance, schema, options, ctx) { |
|
var self = this; |
|
if (!this.types.array(instance)) return; |
|
if (!schema.items) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
instance.every(function (value, i) { |
|
var items = Array.isArray(schema.items) ? (schema.items[i] || schema.additionalItems) : schema.items; |
|
if (items === undefined) { |
|
return true; |
|
} |
|
if (items === false) { |
|
result.addError({ |
|
name: 'items', |
|
message: "additionalItems not permitted", |
|
}); |
|
return false; |
|
} |
|
var res = self.validateSchema(value, items, options, ctx.makeChild(items, i)); |
|
if(res.instance !== result.instance[i]) result.instance[i] = res.instance; |
|
result.importErrors(res); |
|
return true; |
|
}); |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates minimum and exclusiveMinimum when the type of the instance value is a number. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.minimum = function validateMinimum (instance, schema, options, ctx) { |
|
if (!this.types.number(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var valid = true; |
|
if (schema.exclusiveMinimum && schema.exclusiveMinimum === true) { |
|
valid = instance > schema.minimum; |
|
} else { |
|
valid = instance >= schema.minimum; |
|
} |
|
if (!valid) { |
|
result.addError({ |
|
name: 'minimum', |
|
argument: schema.minimum, |
|
message: "must have a minimum value of " + schema.minimum, |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates maximum and exclusiveMaximum when the type of the instance value is a number. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.maximum = function validateMaximum (instance, schema, options, ctx) { |
|
if (!this.types.number(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var valid; |
|
if (schema.exclusiveMaximum && schema.exclusiveMaximum === true) { |
|
valid = instance < schema.maximum; |
|
} else { |
|
valid = instance <= schema.maximum; |
|
} |
|
if (!valid) { |
|
result.addError({ |
|
name: 'maximum', |
|
argument: schema.maximum, |
|
message: "must have a maximum value of " + schema.maximum, |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Perform validation for multipleOf and divisibleBy, which are essentially the same. |
|
* @param instance |
|
* @param schema |
|
* @param validationType |
|
* @param errorMessage |
|
* @returns {String|null} |
|
*/ |
|
var validateMultipleOfOrDivisbleBy = function validateMultipleOfOrDivisbleBy (instance, schema, options, ctx, validationType, errorMessage) { |
|
if (!this.types.number(instance)) return; |
|
|
|
var validationArgument = schema[validationType]; |
|
if (validationArgument == 0) { |
|
throw new SchemaError(validationType + " cannot be zero"); |
|
} |
|
|
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
|
|
var instanceDecimals = helpers.getDecimalPlaces(instance); |
|
var divisorDecimals = helpers.getDecimalPlaces(validationArgument); |
|
|
|
var maxDecimals = Math.max(instanceDecimals , divisorDecimals); |
|
var multiplier = Math.pow(10, maxDecimals); |
|
|
|
if (Math.round(instance * multiplier) % Math.round(validationArgument * multiplier) !== 0) { |
|
result.addError({ |
|
name: validationType, |
|
argument: validationArgument, |
|
message: errorMessage + JSON.stringify(validationArgument) |
|
}); |
|
} |
|
|
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates divisibleBy when the type of the instance value is a number. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.multipleOf = function validateMultipleOf (instance, schema, options, ctx) { |
|
return validateMultipleOfOrDivisbleBy.call(this, instance, schema, options, ctx, "multipleOf", "is not a multiple of (divisible by) "); |
|
}; |
|
|
|
/** |
|
* Validates multipleOf when the type of the instance value is a number. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.divisibleBy = function validateDivisibleBy (instance, schema, options, ctx) { |
|
return validateMultipleOfOrDivisbleBy.call(this, instance, schema, options, ctx, "divisibleBy", "is not divisible by (multiple of) "); |
|
}; |
|
|
|
/** |
|
* Validates whether the instance value is present. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.required = function validateRequired (instance, schema, options, ctx) { |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
if (instance === undefined && schema.required === true) { |
|
// A boolean form is implemented for reverse-compatability with schemas written against older drafts |
|
result.addError({ |
|
name: 'required', |
|
message: "is required" |
|
}); |
|
} else if (this.types.object(instance) && Array.isArray(schema.required)) { |
|
schema.required.forEach(function(n){ |
|
if(instance[n]===undefined){ |
|
result.addError({ |
|
name: 'required', |
|
argument: n, |
|
message: "requires property " + JSON.stringify(n), |
|
}); |
|
} |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance value matches the regular expression, when the instance value is a string. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.pattern = function validatePattern (instance, schema, options, ctx) { |
|
if (!this.types.string(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
if (!instance.match(schema.pattern)) { |
|
result.addError({ |
|
name: 'pattern', |
|
argument: schema.pattern, |
|
message: "does not match pattern " + JSON.stringify(schema.pattern), |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance value is of a certain defined format or a custom |
|
* format. |
|
* The following formats are supported for string types: |
|
* - date-time |
|
* - date |
|
* - time |
|
* - ip-address |
|
* - ipv6 |
|
* - uri |
|
* - color |
|
* - host-name |
|
* - alpha |
|
* - alpha-numeric |
|
* - utc-millisec |
|
* @param instance |
|
* @param schema |
|
* @param [options] |
|
* @param [ctx] |
|
* @return {String|null} |
|
*/ |
|
validators.format = function validateFormat (instance, schema, options, ctx) { |
|
if (instance===undefined) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
if (!result.disableFormat && !helpers.isFormat(instance, schema.format, this)) { |
|
result.addError({ |
|
name: 'format', |
|
argument: schema.format, |
|
message: "does not conform to the " + JSON.stringify(schema.format) + " format", |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance value is at least of a certain length, when the instance value is a string. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.minLength = function validateMinLength (instance, schema, options, ctx) { |
|
if (!this.types.string(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var hsp = instance.match(/[\uDC00-\uDFFF]/g); |
|
var length = instance.length - (hsp ? hsp.length : 0); |
|
if (!(length >= schema.minLength)) { |
|
result.addError({ |
|
name: 'minLength', |
|
argument: schema.minLength, |
|
message: "does not meet minimum length of " + schema.minLength, |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance value is at most of a certain length, when the instance value is a string. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.maxLength = function validateMaxLength (instance, schema, options, ctx) { |
|
if (!this.types.string(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
// TODO if this was already computed in "minLength", use that value instead of re-computing |
|
var hsp = instance.match(/[\uDC00-\uDFFF]/g); |
|
var length = instance.length - (hsp ? hsp.length : 0); |
|
if (!(length <= schema.maxLength)) { |
|
result.addError({ |
|
name: 'maxLength', |
|
argument: schema.maxLength, |
|
message: "does not meet maximum length of " + schema.maxLength, |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether instance contains at least a minimum number of items, when the instance is an Array. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.minItems = function validateMinItems (instance, schema, options, ctx) { |
|
if (!this.types.array(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
if (!(instance.length >= schema.minItems)) { |
|
result.addError({ |
|
name: 'minItems', |
|
argument: schema.minItems, |
|
message: "does not meet minimum length of " + schema.minItems, |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether instance contains no more than a maximum number of items, when the instance is an Array. |
|
* @param instance |
|
* @param schema |
|
* @return {String|null} |
|
*/ |
|
validators.maxItems = function validateMaxItems (instance, schema, options, ctx) { |
|
if (!this.types.array(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
if (!(instance.length <= schema.maxItems)) { |
|
result.addError({ |
|
name: 'maxItems', |
|
argument: schema.maxItems, |
|
message: "does not meet maximum length of " + schema.maxItems, |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates that every item in an instance array is unique, when instance is an array |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {String|null|ValidatorResult} |
|
*/ |
|
validators.uniqueItems = function validateUniqueItems (instance, schema, options, ctx) { |
|
if (!this.types.array(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
function testArrays (v, i, a) { |
|
for (var j = i + 1; j < a.length; j++) if (helpers.deepCompareStrict(v, a[j])) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
if (!instance.every(testArrays)) { |
|
result.addError({ |
|
name: 'uniqueItems', |
|
message: "contains duplicate item", |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Deep compares arrays for duplicates |
|
* @param v |
|
* @param i |
|
* @param a |
|
* @private |
|
* @return {boolean} |
|
*/ |
|
function testArrays (v, i, a) { |
|
var j, len = a.length; |
|
for (j = i + 1, len; j < len; j++) { |
|
if (helpers.deepCompareStrict(v, a[j])) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
/** |
|
* Validates whether there are no duplicates, when the instance is an Array. |
|
* @param instance |
|
* @return {String|null} |
|
*/ |
|
validators.uniqueItems = function validateUniqueItems (instance, schema, options, ctx) { |
|
if (!this.types.array(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
if (!instance.every(testArrays)) { |
|
result.addError({ |
|
name: 'uniqueItems', |
|
message: "contains duplicate item", |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validate for the presence of dependency properties, if the instance is an object. |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {null|ValidatorResult} |
|
*/ |
|
validators.dependencies = function validateDependencies (instance, schema, options, ctx) { |
|
if (!this.types.object(instance)) return; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
for (var property in schema.dependencies) { |
|
if (instance[property] === undefined) { |
|
continue; |
|
} |
|
var dep = schema.dependencies[property]; |
|
var childContext = ctx.makeChild(dep, property); |
|
if (typeof dep == 'string') { |
|
dep = [dep]; |
|
} |
|
if (Array.isArray(dep)) { |
|
dep.forEach(function (prop) { |
|
if (instance[prop] === undefined) { |
|
result.addError({ |
|
// FIXME there's two different "dependencies" errors here with slightly different outputs |
|
// Can we make these the same? Or should we create different error types? |
|
name: 'dependencies', |
|
argument: childContext.propertyPath, |
|
message: "property " + prop + " not found, required by " + childContext.propertyPath, |
|
}); |
|
} |
|
}); |
|
} else { |
|
var res = this.validateSchema(instance, dep, options, childContext); |
|
if(result.instance !== res.instance) result.instance = res.instance; |
|
if (res && res.errors.length) { |
|
result.addError({ |
|
name: 'dependencies', |
|
argument: childContext.propertyPath, |
|
message: "does not meet dependency required by " + childContext.propertyPath, |
|
}); |
|
result.importErrors(res); |
|
} |
|
} |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance value is one of the enumerated values. |
|
* |
|
* @param instance |
|
* @param schema |
|
* @return {ValidatorResult|null} |
|
*/ |
|
validators['enum'] = function validateEnum (instance, schema, options, ctx) { |
|
if (instance === undefined) { |
|
return null; |
|
} |
|
if (!Array.isArray(schema['enum'])) { |
|
throw new SchemaError("enum expects an array", schema); |
|
} |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
if (!schema['enum'].some(helpers.deepCompareStrict.bind(null, instance))) { |
|
result.addError({ |
|
name: 'enum', |
|
argument: schema['enum'], |
|
message: "is not one of enum values: " + schema['enum'].map(String).join(','), |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance exactly matches a given value |
|
* |
|
* @param instance |
|
* @param schema |
|
* @return {ValidatorResult|null} |
|
*/ |
|
validators['const'] = function validateEnum (instance, schema, options, ctx) { |
|
if (instance === undefined) { |
|
return null; |
|
} |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
if (!helpers.deepCompareStrict(schema['const'], instance)) { |
|
result.addError({ |
|
name: 'const', |
|
argument: schema['const'], |
|
message: "does not exactly match expected constant: " + schema['const'], |
|
}); |
|
} |
|
return result; |
|
}; |
|
|
|
/** |
|
* Validates whether the instance if of a prohibited type. |
|
* @param instance |
|
* @param schema |
|
* @param options |
|
* @param ctx |
|
* @return {null|ValidatorResult} |
|
*/ |
|
validators.not = validators.disallow = function validateNot (instance, schema, options, ctx) { |
|
var self = this; |
|
if(instance===undefined) return null; |
|
var result = new ValidatorResult(instance, schema, options, ctx); |
|
var notTypes = schema.not || schema.disallow; |
|
if(!notTypes) return null; |
|
if(!Array.isArray(notTypes)) notTypes=[notTypes]; |
|
notTypes.forEach(function (type) { |
|
if (self.testType(instance, schema, options, ctx, type)) { |
|
var schemaId = type && type.id && ('<' + type.id + '>') || type; |
|
result.addError({ |
|
name: 'not', |
|
argument: schemaId, |
|
message: "is of prohibited type " + schemaId, |
|
}); |
|
} |
|
}); |
|
return result; |
|
}; |
|
|
|
module.exports = attribute;
|
|
|