Skip to content
86 changes: 56 additions & 30 deletions lib/revalidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@
uniqueItems: "must hold a unique set of values",
format: "is not a valid %{expected}",
conform: "must conform to given constraint",
type: "must be of %{expected} type"
type: "must be of %{expected} type",
additionalProperties: "extra properties are not allowed"
};
validate.messages['enum'] = "must be present in given enumerator";

Expand Down Expand Up @@ -158,7 +159,7 @@
return obj;
};

function validateObject(object, schema, options, errors) {
function validateObject(object, schema, options, errors, path) {
var props, allProps = Object.keys(object),
visitedProps = [];

Expand All @@ -168,7 +169,7 @@
for (var p in props) {
if (props.hasOwnProperty(p)) {
visitedProps.push(p);
validateProperty(object, object[p], p, props[p], options, errors);
validateProperty(object, object[p], p, props[p], options, errors, path);
}
}
}
Expand All @@ -185,7 +186,7 @@
if (object.hasOwnProperty(k)) {
visitedProps.push(k);
if (re.exec(k) !== null) {
validateProperty(object, object[k], p, props[p], options, errors);
validateProperty(object, object[k], p, props[p], options, errors, path);
}
}
}
Expand All @@ -204,51 +205,65 @@
// Prevent additional properties; each unvisited property is therefore an error
if (schema.additionalProperties === false && unvisitedProps.length > 0) {
for (i = 0, l = unvisitedProps.length; i < l; i++) {
error("additionalProperties", unvisitedProps[i], object[unvisitedProps[i]], false, errors);
error("additionalProperties", unvisitedProps[i], object[unvisitedProps[i]], false, errors, path);
}
}
// additionalProperties is a schema and validate unvisited properties against that schema
else if (typeof schema.additionalProperties == "object" && unvisitedProps.length > 0) {
for (i = 0, l = unvisitedProps.length; i < l; i++) {
validateProperty(object, object[unvisitedProps[i]], unvisitedProps[i], schema.unvisitedProperties, options, errors);
validateProperty(object, object[unvisitedProps[i]], unvisitedProps[i], schema.unvisitedProperties, options, errors, path);
}
}
}

};

function validateProperty(object, value, property, schema, options, errors) {
function validateProperty(object, value, property, schema, options, errors, path) {
var format,
valid,
spec,
type;

function constrain(name, value, assert) {
if (schema[name] !== undefined && !assert(value, schema[name])) {
error(name, property, value, schema, errors);
error(name, property, value, schema, errors, path);
}
}

if (value === undefined) {
if (schema.required && schema.type !== 'any') {
return error('required', property, undefined, schema, errors);
error('required', property, undefined, schema, errors, path);

if (schema.properties || schema.patternProperties || schema.additionalProperties) {
validateObject({}, schema, options, errors, (!path ? property : path) + ".");
}
else if(schema.items && schema.items.properties){
checkType([], schema.type, function(err, type){
if(!err && type=='array'){
validateObject({}, schema.items, options, errors, (!path ? property : path) + "[0].");
}
});
}
return;
} else {
return;
}
}

if (options.cast) {
if (('integer' === schema.type || 'number' === schema.type) && value == +value) {
value = +value;
}

if ('boolean' === schema.type) {
if ('true' === value || '1' === value || 1 === value) {
value = true;
if(schema.type !== undefined && schema.type !== null){
if((schema.type.indexOf('integer') > -1 || schema.type.indexOf('number') > -1) && value == +value){
value = +value;
}

if ('false' === value || '0' === value || 0 === value) {
value = false;
if (schema.type.indexOf('boolean') > -1){
if ('true' === value || '1' === value || 1 === value) {
value = true;
}

if ('false' === value || '0' === value || 0 === value) {
value = false;
}
}
}
}
Expand All @@ -260,30 +275,40 @@
if (!spec) { spec = validate.formats[format] }
if (!spec) {
if (options.validateFormatsStrict) {
return error('format', property, value, schema, errors);
return error('format', property, value, schema, errors, path);
}
}
else {
if (!spec.test(value)) {
return error('format', property, value, schema, errors);
return error('format', property, value, schema, errors, path);
}
}
}

if (schema['enum'] && schema['enum'].indexOf(value) === -1) {
error('enum', property, value, schema, errors);
if (schema['enum']){
if(schema['type']=='array' && isArray(value)){
for(var i=0; i<value.length; i++){
if(schema['enum'].indexOf(value[i]) === -1){
error('enum', property, value, schema, errors, path);
break;
}
}
}
else if(schema['enum'].indexOf(value) === -1){
error('enum', property, value, schema, errors, path);
}
}

// Dependencies (see 5.8)
if (typeof schema.dependencies === 'string' &&
object[schema.dependencies] === undefined) {
error('dependencies', property, null, schema, errors);
error('dependencies', property, null, schema, errors, path);
}

if (isArray(schema.dependencies)) {
for (var i = 0, l = schema.dependencies.length; i < l; i++) {
if (object[schema.dependencies[i]] === undefined) {
error('dependencies', property, null, schema, errors);
error('dependencies', property, null, schema, errors, path);
}
}
}
Expand All @@ -293,9 +318,9 @@
}

checkType(value, schema.type, function(err, type) {
if (err) return error('type', property, typeof value, schema, errors);
if (err) return error('type', property, typeof value, schema, errors, path);

constrain('conform', value, function (a, e) { return e(a) });
constrain('conform', value, function (a, e) { return e(a, object) });

switch (type || (isArray(value) ? 'array' : typeof value)) {
case 'string':
Expand Down Expand Up @@ -323,7 +348,7 @@
case 'array':
constrain('items', value, function (a, e) {
for (var i = 0, l = a.length; i < l; i++) {
validateProperty(object, a[i], property, e, options, errors);
validateProperty(object, a[i], property, e, options, errors, (!path ? property : path) + "[" + i + "]");
}
return true;
});
Expand All @@ -344,7 +369,7 @@
case 'object':
// Recursive validation
if (schema.properties || schema.patternProperties || schema.additionalProperties) {
validateObject(value, schema, options, errors);
validateObject(value, schema, options, errors, (!path ? property : path) + ".");
}
break;
}
Expand Down Expand Up @@ -378,7 +403,7 @@
callback(true);
};

function error(attribute, property, actual, schema, errors) {
function error(attribute, property, actual, schema, errors, path) {
var lookup = { expected: schema[attribute], attribute: attribute, property: property };
var message = schema.messages && schema.messages[attribute] || schema.message || validate.messages[attribute] || "no default message";
message = message.replace(/%\{([a-z]+)\}/ig, function (_, match) { return lookup[match.toLowerCase()] || ''; });
Expand All @@ -387,7 +412,8 @@
property: property,
expected: schema[attribute],
actual: actual,
message: message
message: message,
path: path ? path + property : undefined
});
};

Expand Down