| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- "use strict";
- require("./Polyfills");
- var get = require("lodash.get");
- var Report = require("./Report");
- var FormatValidators = require("./FormatValidators");
- var JsonValidation = require("./JsonValidation");
- var SchemaCache = require("./SchemaCache");
- var SchemaCompilation = require("./SchemaCompilation");
- var SchemaValidation = require("./SchemaValidation");
- var Utils = require("./Utils");
- var Draft4Schema = require("./schemas/schema.json");
- var Draft4HyperSchema = require("./schemas/hyper-schema.json");
- /**
- * default options
- */
- var defaultOptions = {
- // default timeout for all async tasks
- asyncTimeout: 2000,
- // force additionalProperties and additionalItems to be defined on "object" and "array" types
- forceAdditional: false,
- // assume additionalProperties and additionalItems are defined as "false" where appropriate
- assumeAdditional: false,
- // do case insensitive comparison for enums
- enumCaseInsensitiveComparison: false,
- // force items to be defined on "array" types
- forceItems: false,
- // force minItems to be defined on "array" types
- forceMinItems: false,
- // force maxItems to be defined on "array" types
- forceMaxItems: false,
- // force minLength to be defined on "string" types
- forceMinLength: false,
- // force maxLength to be defined on "string" types
- forceMaxLength: false,
- // force properties or patternProperties to be defined on "object" types
- forceProperties: false,
- // ignore references that cannot be resolved (remote schemas) // TODO: make sure this is only for remote schemas, not local ones
- ignoreUnresolvableReferences: false,
- // disallow usage of keywords that this validator can't handle
- noExtraKeywords: false,
- // disallow usage of schema's without "type" defined
- noTypeless: false,
- // disallow zero length strings in validated objects
- noEmptyStrings: false,
- // disallow zero length arrays in validated objects
- noEmptyArrays: false,
- // forces "uri" format to be in fully rfc3986 compliant
- strictUris: false,
- // turn on some of the above
- strictMode: false,
- // report error paths as an array of path segments to get to the offending node
- reportPathAsArray: false,
- // stop validation as soon as an error is found
- breakOnFirstError: false,
- // check if schema follows best practices and common sense
- pedanticCheck: false,
- // ignore unknown formats (do not report them as an error)
- ignoreUnknownFormats: false,
- // function to be called on every schema
- customValidator: null
- };
- function normalizeOptions(options) {
- var normalized;
- // options
- if (typeof options === "object") {
- var keys = Object.keys(options),
- idx = keys.length,
- key;
- // check that the options are correctly configured
- while (idx--) {
- key = keys[idx];
- if (defaultOptions[key] === undefined) {
- throw new Error("Unexpected option passed to constructor: " + key);
- }
- }
- // copy the default options into passed options
- keys = Object.keys(defaultOptions);
- idx = keys.length;
- while (idx--) {
- key = keys[idx];
- if (options[key] === undefined) {
- options[key] = Utils.clone(defaultOptions[key]);
- }
- }
- normalized = options;
- } else {
- normalized = Utils.clone(defaultOptions);
- }
- if (normalized.strictMode === true) {
- normalized.forceAdditional = true;
- normalized.forceItems = true;
- normalized.forceMaxLength = true;
- normalized.forceProperties = true;
- normalized.noExtraKeywords = true;
- normalized.noTypeless = true;
- normalized.noEmptyStrings = true;
- normalized.noEmptyArrays = true;
- }
- return normalized;
- }
- /**
- * @class
- *
- * @param {*} [options]
- */
- function ZSchema(options) {
- this.cache = {};
- this.referenceCache = [];
- this.validateOptions = {};
- this.options = normalizeOptions(options);
- // Disable strict validation for the built-in schemas
- var metaschemaOptions = normalizeOptions({ });
- this.setRemoteReference("http://json-schema.org/draft-04/schema", Draft4Schema, metaschemaOptions);
- this.setRemoteReference("http://json-schema.org/draft-04/hyper-schema", Draft4HyperSchema, metaschemaOptions);
- }
- /**
- * instance methods
- *
- * @param {*} schema
- *
- * @returns {boolean}
- */
- ZSchema.prototype.compileSchema = function (schema) {
- var report = new Report(this.options);
- schema = SchemaCache.getSchema.call(this, report, schema);
- SchemaCompilation.compileSchema.call(this, report, schema);
- this.lastReport = report;
- return report.isValid();
- };
- /**
- *
- * @param {*} schema
- *
- * @returns {boolean}
- */
- ZSchema.prototype.validateSchema = function (schema) {
- if (Array.isArray(schema) && schema.length === 0) {
- throw new Error(".validateSchema was called with an empty array");
- }
- var report = new Report(this.options);
- schema = SchemaCache.getSchema.call(this, report, schema);
- var compiled = SchemaCompilation.compileSchema.call(this, report, schema);
- if (compiled) { SchemaValidation.validateSchema.call(this, report, schema); }
- this.lastReport = report;
- return report.isValid();
- };
- /**
- *
- * @param {*} json
- * @param {*} schema
- * @param {*} [options]
- * @param {function(*, *)} [callback]
- *
- * @returns {boolean}
- */
- ZSchema.prototype.validate = function (json, schema, options, callback) {
- if (Utils.whatIs(options) === "function") {
- callback = options;
- options = {};
- }
- if (!options) { options = {}; }
- this.validateOptions = options;
- var whatIs = Utils.whatIs(schema);
- if (whatIs !== "string" && whatIs !== "object") {
- var e = new Error("Invalid .validate call - schema must be a string or object but " + whatIs + " was passed!");
- if (callback) {
- process.nextTick(function () {
- callback(e, false);
- });
- return;
- }
- throw e;
- }
- var foundError = false;
- var report = new Report(this.options);
- report.json = json;
- if (typeof schema === "string") {
- var schemaName = schema;
- schema = SchemaCache.getSchema.call(this, report, schemaName);
- if (!schema) {
- throw new Error("Schema with id '" + schemaName + "' wasn't found in the validator cache!");
- }
- } else {
- schema = SchemaCache.getSchema.call(this, report, schema);
- }
- var compiled = false;
- if (!foundError) {
- compiled = SchemaCompilation.compileSchema.call(this, report, schema);
- }
- if (!compiled) {
- this.lastReport = report;
- foundError = true;
- }
- var validated = false;
- if (!foundError) {
- validated = SchemaValidation.validateSchema.call(this, report, schema);
- }
- if (!validated) {
- this.lastReport = report;
- foundError = true;
- }
- if (options.schemaPath) {
- report.rootSchema = schema;
- schema = get(schema, options.schemaPath);
- if (!schema) {
- throw new Error("Schema path '" + options.schemaPath + "' wasn't found in the schema!");
- }
- }
- if (!foundError) {
- JsonValidation.validate.call(this, report, schema, json);
- }
- if (callback) {
- report.processAsyncTasks(this.options.asyncTimeout, callback);
- return;
- } else if (report.asyncTasks.length > 0) {
- throw new Error("This validation has async tasks and cannot be done in sync mode, please provide callback argument.");
- }
- // assign lastReport so errors are retrievable in sync mode
- this.lastReport = report;
- return report.isValid();
- };
- ZSchema.prototype.getLastError = function () {
- if (this.lastReport.errors.length === 0) {
- return null;
- }
- var e = new Error();
- e.name = "z-schema validation error";
- e.message = this.lastReport.commonErrorMessage;
- e.details = this.lastReport.errors;
- return e;
- };
- ZSchema.prototype.getLastErrors = function () {
- return this.lastReport && this.lastReport.errors.length > 0 ? this.lastReport.errors : null;
- };
- ZSchema.prototype.getMissingReferences = function (arr) {
- arr = arr || this.lastReport.errors;
- var res = [],
- idx = arr.length;
- while (idx--) {
- var error = arr[idx];
- if (error.code === "UNRESOLVABLE_REFERENCE") {
- var reference = error.params[0];
- if (res.indexOf(reference) === -1) {
- res.push(reference);
- }
- }
- if (error.inner) {
- res = res.concat(this.getMissingReferences(error.inner));
- }
- }
- return res;
- };
- ZSchema.prototype.getMissingRemoteReferences = function () {
- var missingReferences = this.getMissingReferences(),
- missingRemoteReferences = [],
- idx = missingReferences.length;
- while (idx--) {
- var remoteReference = SchemaCache.getRemotePath(missingReferences[idx]);
- if (remoteReference && missingRemoteReferences.indexOf(remoteReference) === -1) {
- missingRemoteReferences.push(remoteReference);
- }
- }
- return missingRemoteReferences;
- };
- ZSchema.prototype.setRemoteReference = function (uri, schema, validationOptions) {
- if (typeof schema === "string") {
- schema = JSON.parse(schema);
- } else {
- schema = Utils.cloneDeep(schema);
- }
- if (validationOptions) {
- schema.__$validationOptions = normalizeOptions(validationOptions);
- }
- SchemaCache.cacheSchemaByUri.call(this, uri, schema);
- };
- ZSchema.prototype.getResolvedSchema = function (schema) {
- var report = new Report(this.options);
- schema = SchemaCache.getSchema.call(this, report, schema);
- // clone before making any modifications
- schema = Utils.cloneDeep(schema);
- var visited = [];
- // clean-up the schema and resolve references
- var cleanup = function (schema) {
- var key,
- typeOf = Utils.whatIs(schema);
- if (typeOf !== "object" && typeOf !== "array") {
- return;
- }
- if (schema.___$visited) {
- return;
- }
- schema.___$visited = true;
- visited.push(schema);
- if (schema.$ref && schema.__$refResolved) {
- var from = schema.__$refResolved;
- var to = schema;
- delete schema.$ref;
- delete schema.__$refResolved;
- for (key in from) {
- if (from.hasOwnProperty(key)) {
- to[key] = from[key];
- }
- }
- }
- for (key in schema) {
- if (schema.hasOwnProperty(key)) {
- if (key.indexOf("__$") === 0) {
- delete schema[key];
- } else {
- cleanup(schema[key]);
- }
- }
- }
- };
- cleanup(schema);
- visited.forEach(function (s) {
- delete s.___$visited;
- });
- this.lastReport = report;
- if (report.isValid()) {
- return schema;
- } else {
- throw this.getLastError();
- }
- };
- /**
- *
- * @param {*} schemaReader
- *
- * @returns {void}
- */
- ZSchema.prototype.setSchemaReader = function (schemaReader) {
- return ZSchema.setSchemaReader(schemaReader);
- };
- ZSchema.prototype.getSchemaReader = function () {
- return ZSchema.schemaReader;
- };
- ZSchema.schemaReader = undefined;
- /*
- static methods
- */
- ZSchema.setSchemaReader = function (schemaReader) {
- ZSchema.schemaReader = schemaReader;
- };
- ZSchema.registerFormat = function (formatName, validatorFunction) {
- FormatValidators[formatName] = validatorFunction;
- };
- ZSchema.unregisterFormat = function (formatName) {
- delete FormatValidators[formatName];
- };
- ZSchema.getRegisteredFormats = function () {
- return Object.keys(FormatValidators);
- };
- ZSchema.getDefaultOptions = function () {
- return Utils.cloneDeep(defaultOptions);
- };
- ZSchema.schemaSymbol = Utils.schemaSymbol;
- ZSchema.jsonSymbol = Utils.jsonSymbol;
- module.exports = ZSchema;
|