Sindbad~EG File Manager
"use strict";
var FormatValidators = require("./FormatValidators"),
Report = require("./Report"),
Utils = require("./Utils");
var shouldSkipValidate = function (options, errors) {
return options &&
Array.isArray(options.includeErrors) &&
options.includeErrors.length > 0 &&
!errors.some(function (err) { return options.includeErrors.includes(err);});
};
var JsonValidators = {
multipleOf: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.1.2
if (shouldSkipValidate(this.validateOptions, ["MULTIPLE_OF"])) {
return;
}
if (typeof json !== "number") {
return;
}
var stringMultipleOf = String(schema.multipleOf);
var scale = Math.pow(10, stringMultipleOf.length - stringMultipleOf.indexOf(".") - 1);
if (Utils.whatIs((json * scale) / (schema.multipleOf * scale)) !== "integer") {
report.addError("MULTIPLE_OF", [json, schema.multipleOf], null, schema);
}
},
maximum: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.2.2
if (shouldSkipValidate(this.validateOptions, ["MAXIMUM", "MAXIMUM_EXCLUSIVE"])) {
return;
}
if (typeof json !== "number") {
return;
}
if (schema.exclusiveMaximum !== true) {
if (json > schema.maximum) {
report.addError("MAXIMUM", [json, schema.maximum], null, schema);
}
} else {
if (json >= schema.maximum) {
report.addError("MAXIMUM_EXCLUSIVE", [json, schema.maximum], null, schema);
}
}
},
exclusiveMaximum: function () {
// covered in maximum
},
minimum: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.3.2
if (shouldSkipValidate(this.validateOptions, ["MINIMUM", "MINIMUM_EXCLUSIVE"])) {
return;
}
if (typeof json !== "number") {
return;
}
if (schema.exclusiveMinimum !== true) {
if (json < schema.minimum) {
report.addError("MINIMUM", [json, schema.minimum], null, schema);
}
} else {
if (json <= schema.minimum) {
report.addError("MINIMUM_EXCLUSIVE", [json, schema.minimum], null, schema);
}
}
},
exclusiveMinimum: function () {
// covered in minimum
},
maxLength: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.1.2
if (shouldSkipValidate(this.validateOptions, ["MAX_LENGTH"])) {
return;
}
if (typeof json !== "string") {
return;
}
if (Utils.ucs2decode(json).length > schema.maxLength) {
report.addError("MAX_LENGTH", [json.length, schema.maxLength], null, schema);
}
},
minLength: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.2.2
if (shouldSkipValidate(this.validateOptions, ["MIN_LENGTH"])) {
return;
}
if (typeof json !== "string") {
return;
}
if (Utils.ucs2decode(json).length < schema.minLength) {
report.addError("MIN_LENGTH", [json.length, schema.minLength], null, schema);
}
},
pattern: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.3.2
if (shouldSkipValidate(this.validateOptions, ["PATTERN"])) {
return;
}
if (typeof json !== "string") {
return;
}
if (RegExp(schema.pattern).test(json) === false) {
report.addError("PATTERN", [schema.pattern, json], null, schema);
}
},
additionalItems: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.1.2
if (shouldSkipValidate(this.validateOptions, ["ARRAY_ADDITIONAL_ITEMS"])) {
return;
}
if (!Array.isArray(json)) {
return;
}
// if the value of "additionalItems" is boolean value false and the value of "items" is an array,
// the json is valid if its size is less than, or equal to, the size of "items".
if (schema.additionalItems === false && Array.isArray(schema.items)) {
if (json.length > schema.items.length) {
report.addError("ARRAY_ADDITIONAL_ITEMS", null, null, schema);
}
}
},
items: function () { /*report, schema, json*/
// covered in additionalItems
},
maxItems: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.2.2
if (shouldSkipValidate(this.validateOptions, ["ARRAY_LENGTH_LONG"])) {
return;
}
if (!Array.isArray(json)) {
return;
}
if (json.length > schema.maxItems) {
report.addError("ARRAY_LENGTH_LONG", [json.length, schema.maxItems], null, schema);
}
},
minItems: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.3.2
if (shouldSkipValidate(this.validateOptions, ["ARRAY_LENGTH_SHORT"])) {
return;
}
if (!Array.isArray(json)) {
return;
}
if (json.length < schema.minItems) {
report.addError("ARRAY_LENGTH_SHORT", [json.length, schema.minItems], null, schema);
}
},
uniqueItems: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.4.2
if (shouldSkipValidate(this.validateOptions, ["ARRAY_UNIQUE"])) {
return;
}
if (!Array.isArray(json)) {
return;
}
if (schema.uniqueItems === true) {
var matches = [];
if (Utils.isUniqueArray(json, matches) === false) {
report.addError("ARRAY_UNIQUE", matches, null, schema);
}
}
},
maxProperties: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.1.2
if (shouldSkipValidate(this.validateOptions, ["OBJECT_PROPERTIES_MAXIMUM"])) {
return;
}
if (Utils.whatIs(json) !== "object") {
return;
}
var keysCount = Object.keys(json).length;
if (keysCount > schema.maxProperties) {
report.addError("OBJECT_PROPERTIES_MAXIMUM", [keysCount, schema.maxProperties], null, schema);
}
},
minProperties: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.2.2
if (shouldSkipValidate(this.validateOptions, ["OBJECT_PROPERTIES_MINIMUM"])) {
return;
}
if (Utils.whatIs(json) !== "object") {
return;
}
var keysCount = Object.keys(json).length;
if (keysCount < schema.minProperties) {
report.addError("OBJECT_PROPERTIES_MINIMUM", [keysCount, schema.minProperties], null, schema);
}
},
required: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.3.2
if (shouldSkipValidate(this.validateOptions, ["OBJECT_MISSING_REQUIRED_PROPERTY"])) {
return;
}
if (Utils.whatIs(json) !== "object") {
return;
}
var idx = schema.required.length;
while (idx--) {
var requiredPropertyName = schema.required[idx];
if (json[requiredPropertyName] === undefined) {
report.addError("OBJECT_MISSING_REQUIRED_PROPERTY", [requiredPropertyName], null, schema);
}
}
},
additionalProperties: function (report, schema, json) {
// covered in properties and patternProperties
if (schema.properties === undefined && schema.patternProperties === undefined) {
return JsonValidators.properties.call(this, report, schema, json);
}
},
patternProperties: function (report, schema, json) {
// covered in properties
if (schema.properties === undefined) {
return JsonValidators.properties.call(this, report, schema, json);
}
},
properties: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.4.2
if (shouldSkipValidate(this.validateOptions, ["OBJECT_ADDITIONAL_PROPERTIES"])) {
return;
}
if (Utils.whatIs(json) !== "object") {
return;
}
var properties = schema.properties !== undefined ? schema.properties : {};
var patternProperties = schema.patternProperties !== undefined ? schema.patternProperties : {};
if (schema.additionalProperties === false) {
// The property set of the json to validate.
var s = Object.keys(json);
// The property set from "properties".
var p = Object.keys(properties);
// The property set from "patternProperties".
var pp = Object.keys(patternProperties);
// remove from "s" all elements of "p", if any;
s = Utils.difference(s, p);
// for each regex in "pp", remove all elements of "s" which this regex matches.
var idx = pp.length;
while (idx--) {
var regExp = RegExp(pp[idx]),
idx2 = s.length;
while (idx2--) {
if (regExp.test(s[idx2]) === true) {
s.splice(idx2, 1);
}
}
}
// Validation of the json succeeds if, after these two steps, set "s" is empty.
if (s.length > 0) {
// assumeAdditional can be an array of allowed properties
var idx3 = this.options.assumeAdditional.length;
if (idx3) {
while (idx3--) {
var io = s.indexOf(this.options.assumeAdditional[idx3]);
if (io !== -1) {
s.splice(io, 1);
}
}
}
var idx4 = s.length;
if (idx4) {
while (idx4--) {
report.addError("OBJECT_ADDITIONAL_PROPERTIES", [s[idx4]], null, schema);
}
}
}
}
},
dependencies: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.5.2
if (shouldSkipValidate(this.validateOptions, ["OBJECT_DEPENDENCY_KEY"])) {
return;
}
if (Utils.whatIs(json) !== "object") {
return;
}
var keys = Object.keys(schema.dependencies),
idx = keys.length;
while (idx--) {
// iterate all dependencies
var dependencyName = keys[idx];
if (json[dependencyName]) {
var dependencyDefinition = schema.dependencies[dependencyName];
if (Utils.whatIs(dependencyDefinition) === "object") {
// if dependency is a schema, validate against this schema
exports.validate.call(this, report, dependencyDefinition, json);
} else { // Array
// if dependency is an array, object needs to have all properties in this array
var idx2 = dependencyDefinition.length;
while (idx2--) {
var requiredPropertyName = dependencyDefinition[idx2];
if (json[requiredPropertyName] === undefined) {
report.addError("OBJECT_DEPENDENCY_KEY", [requiredPropertyName, dependencyName], null, schema);
}
}
}
}
}
},
enum: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.1.2
if (shouldSkipValidate(this.validateOptions, ["ENUM_CASE_MISMATCH", "ENUM_MISMATCH"])) {
return;
}
var match = false,
caseInsensitiveMatch = false,
idx = schema.enum.length;
while (idx--) {
if (Utils.areEqual(json, schema.enum[idx])) {
match = true;
break;
} else if (Utils.areEqual(json, schema.enum[idx]), { caseInsensitiveComparison: true }) {
caseInsensitiveMatch = true;
}
}
if (match === false) {
var error = caseInsensitiveMatch && this.options.enumCaseInsensitiveComparison ? "ENUM_CASE_MISMATCH" : "ENUM_MISMATCH";
report.addError(error, [json], null, schema);
}
},
type: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.2.2
if (shouldSkipValidate(this.validateOptions, ["INVALID_TYPE"])) {
return;
}
var jsonType = Utils.whatIs(json);
if (typeof schema.type === "string") {
if (jsonType !== schema.type && (jsonType !== "integer" || schema.type !== "number")) {
report.addError("INVALID_TYPE", [schema.type, jsonType], null, schema);
}
} else {
if (schema.type.indexOf(jsonType) === -1 && (jsonType !== "integer" || schema.type.indexOf("number") === -1)) {
report.addError("INVALID_TYPE", [schema.type, jsonType], null, schema);
}
}
},
allOf: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.3.2
var idx = schema.allOf.length;
while (idx--) {
var validateResult = exports.validate.call(this, report, schema.allOf[idx], json);
if (this.options.breakOnFirstError && validateResult === false) {
break;
}
}
},
anyOf: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.4.2
var subReports = [],
passed = false,
idx = schema.anyOf.length;
while (idx-- && passed === false) {
var subReport = new Report(report);
subReports.push(subReport);
passed = exports.validate.call(this, subReport, schema.anyOf[idx], json);
}
if (passed === false) {
report.addError("ANY_OF_MISSING", undefined, subReports, schema);
}
},
oneOf: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.5.2
var passes = 0,
subReports = [],
idx = schema.oneOf.length;
while (idx--) {
var subReport = new Report(report, { maxErrors: 1 });
subReports.push(subReport);
if (exports.validate.call(this, subReport, schema.oneOf[idx], json) === true) {
passes++;
}
}
if (passes === 0) {
report.addError("ONE_OF_MISSING", undefined, subReports, schema);
} else if (passes > 1) {
report.addError("ONE_OF_MULTIPLE", null, null, schema);
}
},
not: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.6.2
var subReport = new Report(report);
if (exports.validate.call(this, subReport, schema.not, json) === true) {
report.addError("NOT_PASSED", null, null, schema);
}
},
definitions: function () { /*report, schema, json*/
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.7.2
// nothing to do here
},
format: function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.2
var formatValidatorFn = FormatValidators[schema.format];
if (typeof formatValidatorFn === "function") {
if (shouldSkipValidate(this.validateOptions, ["INVALID_FORMAT"])) {
return;
}
if (formatValidatorFn.length === 2) {
// async - need to clone the path here, because it will change by the time async function reports back
var pathBeforeAsync = Utils.clone(report.path);
report.addAsyncTask(formatValidatorFn, [json], function (result) {
if (result !== true) {
var backup = report.path;
report.path = pathBeforeAsync;
report.addError("INVALID_FORMAT", [schema.format, json], null, schema);
report.path = backup;
}
});
} else {
// sync
if (formatValidatorFn.call(this, json) !== true) {
report.addError("INVALID_FORMAT", [schema.format, json], null, schema);
}
}
} else if (this.options.ignoreUnknownFormats !== true) {
report.addError("UNKNOWN_FORMAT", [schema.format], null, schema);
}
}
};
var recurseArray = function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.2
var idx = json.length;
// If "items" is an array, this situation, the schema depends on the index:
// if the index is less than, or equal to, the size of "items",
// the child instance must be valid against the corresponding schema in the "items" array;
// otherwise, it must be valid against the schema defined by "additionalItems".
if (Array.isArray(schema.items)) {
while (idx--) {
// equal to doesn't make sense here
if (idx < schema.items.length) {
report.path.push(idx);
exports.validate.call(this, report, schema.items[idx], json[idx]);
report.path.pop();
} else {
// might be boolean, so check that it's an object
if (typeof schema.additionalItems === "object") {
report.path.push(idx);
exports.validate.call(this, report, schema.additionalItems, json[idx]);
report.path.pop();
}
}
}
} else if (typeof schema.items === "object") {
// If items is a schema, then the child instance must be valid against this schema,
// regardless of its index, and regardless of the value of "additionalItems".
while (idx--) {
report.path.push(idx);
exports.validate.call(this, report, schema.items, json[idx]);
report.path.pop();
}
}
};
var recurseObject = function (report, schema, json) {
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.3
// If "additionalProperties" is absent, it is considered present with an empty schema as a value.
// In addition, boolean value true is considered equivalent to an empty schema.
var additionalProperties = schema.additionalProperties;
if (additionalProperties === true || additionalProperties === undefined) {
additionalProperties = {};
}
// p - The property set from "properties".
var p = schema.properties ? Object.keys(schema.properties) : [];
// pp - The property set from "patternProperties". Elements of this set will be called regexes for convenience.
var pp = schema.patternProperties ? Object.keys(schema.patternProperties) : [];
// m - The property name of the child.
var keys = Object.keys(json),
idx = keys.length;
while (idx--) {
var m = keys[idx],
propertyValue = json[m];
// s - The set of schemas for the child instance.
var s = [];
// 1. If set "p" contains value "m", then the corresponding schema in "properties" is added to "s".
if (p.indexOf(m) !== -1) {
s.push(schema.properties[m]);
}
// 2. For each regex in "pp", if it matches "m" successfully, the corresponding schema in "patternProperties" is added to "s".
var idx2 = pp.length;
while (idx2--) {
var regexString = pp[idx2];
if (RegExp(regexString).test(m) === true) {
s.push(schema.patternProperties[regexString]);
}
}
// 3. The schema defined by "additionalProperties" is added to "s" if and only if, at this stage, "s" is empty.
if (s.length === 0 && additionalProperties !== false) {
s.push(additionalProperties);
}
// we are passing tests even without this assert because this is covered by properties check
// if s is empty in this stage, no additionalProperties are allowed
// report.expect(s.length !== 0, 'E001', m);
// Instance property value must pass all schemas from s
idx2 = s.length;
while (idx2--) {
report.path.push(m);
exports.validate.call(this, report, s[idx2], propertyValue);
report.path.pop();
}
}
};
exports.JsonValidators = JsonValidators;
/**
*
* @param {Report} report
* @param {*} schema
* @param {*} json
*/
exports.validate = function (report, schema, json) {
report.commonErrorMessage = "JSON_OBJECT_VALIDATION_FAILED";
// check if schema is an object
var to = Utils.whatIs(schema);
if (to !== "object") {
report.addError("SCHEMA_NOT_AN_OBJECT", [to], null, schema);
return false;
}
// check if schema is empty, everything is valid against empty schema
var keys = Object.keys(schema);
if (keys.length === 0) {
return true;
}
// this method can be called recursively, so we need to remember our root
var isRoot = false;
if (!report.rootSchema) {
report.rootSchema = schema;
isRoot = true;
}
// follow schema.$ref keys
if (schema.$ref !== undefined) {
// avoid infinite loop with maxRefs
var maxRefs = 99;
while (schema.$ref && maxRefs > 0) {
if (!schema.__$refResolved) {
report.addError("REF_UNRESOLVED", [schema.$ref], null, schema);
break;
} else if (schema.__$refResolved === schema) {
break;
} else {
schema = schema.__$refResolved;
keys = Object.keys(schema);
}
maxRefs--;
}
if (maxRefs === 0) {
throw new Error("Circular dependency by $ref references!");
}
}
// type checking first
var jsonType = Utils.whatIs(json);
if (schema.type) {
keys.splice(keys.indexOf("type"), 1);
JsonValidators.type.call(this, report, schema, json);
if (report.errors.length && this.options.breakOnFirstError) {
return false;
}
}
// now iterate all the keys in schema and execute validation methods
var idx = keys.length;
while (idx--) {
if (JsonValidators[keys[idx]]) {
JsonValidators[keys[idx]].call(this, report, schema, json);
if (report.errors.length && this.options.breakOnFirstError) { break; }
}
}
if (report.errors.length === 0 || this.options.breakOnFirstError === false) {
if (jsonType === "array") {
recurseArray.call(this, report, schema, json);
} else if (jsonType === "object") {
recurseObject.call(this, report, schema, json);
}
}
if (typeof this.options.customValidator === "function") {
this.options.customValidator.call(this, report, schema, json);
}
// we don't need the root pointer anymore
if (isRoot) {
report.rootSchema = undefined;
}
// return valid just to be able to break at some code points
return report.errors.length === 0;
};
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists