Commit 0ff97d42 authored by Boris Kocherov's avatar Boris Kocherov

erp5_json_form: update version from https://lab.nexedi.com/bk/rjs_json_form

parent c67dad31
...@@ -5,6 +5,12 @@ ...@@ -5,6 +5,12 @@
"use strict"; "use strict";
var expandSchema; var expandSchema;
function arrayIntersect(x, y) {
return x.filter(function (value) {
return y.indexOf(value) >= 0;
});
}
function URLwithJio(url, base_url) { function URLwithJio(url, base_url) {
var urn_prefix, var urn_prefix,
pathname, pathname,
...@@ -65,19 +71,40 @@ ...@@ -65,19 +71,40 @@
return target; return target;
} }
function checkCircular(g, path, url) { function checkCircular(urls, path, url) {
var required_stack, var stack,
idx, idx,
prev_field_path = getMaxPathInDict(g.props.schema_required_urls, path); prev_field_path = getMaxPathInDict(urls, path);
required_stack = g.props.schema_required_urls[prev_field_path] || []; stack = urls[prev_field_path] || [];
idx = required_stack.indexOf(url); idx = stack.indexOf(url);
if (idx >= 0) { if (idx >= 0) {
if (path === prev_field_path && idx === 0) { if (path === prev_field_path && idx === 0) {
return; return;
} }
throw new Error("Circular reference detected"); return true;
}
// copy and add url as first element
urls[path] = [url].concat(stack);
}
function checkHardCircular(g, path, url) {
return checkCircular(g.props.schema_required_urls, path, url);
}
function checkAndMarkSoftCircular(g, schema_arr, path, url) {
var ret = true;
// if schema_arr.length > 1 selection rendered in any case
// so we not need checkCircular and have small optimisation
if (schema_arr.length === 1) {
ret = checkCircular(g.props.schema_urls, path, url);
schema_arr[0].circular = ret;
}
if (ret) {
// if schema_arr.length > 1 selection rendered and loop break
// if circular found selection rendered and loop break
// so we can begin from start
g.props.schema_urls[path] = [];
} }
g.props.schema_required_urls[path] = [url].concat(required_stack);
} }
function convertToRealWorldSchemaPath(g, path) { function convertToRealWorldSchemaPath(g, path) {
...@@ -255,7 +282,9 @@ ...@@ -255,7 +282,9 @@
} }
queue queue
.push(function (json) { .push(function (json) {
checkCircular(g, path, url); if (checkHardCircular(g, path, url)) {
throw new Error("Circular reference detected");
}
return resolveLocalReference(json, hash); return resolveLocalReference(json, hash);
}); });
} }
...@@ -299,17 +328,146 @@ ...@@ -299,17 +328,146 @@
return expandSchema(g, schema_part, path, $ref); return expandSchema(g, schema_part, path, $ref);
}) })
.push(function (schema_arr) { .push(function (schema_arr) {
// if length array > 1 form rendered on demand already checkAndMarkSoftCircular(g, schema_arr, path, url);
// so not needed circular detection
if (schema_arr.length === 1) {
// XXX need smart circular detection in this place
schema_arr[0].circular = true;
}
return schema_arr; return schema_arr;
}); });
} }
function allOf(g, schema_array, schema_path) { function mergeSchemas(x, y, doesntcopy) {
if (x === true && y === true) {
return true;
}
if (x === false || y === false) {
return false;
}
var key,
p;
if (x === true) {
x = {};
} else if (!doesntcopy) {
x = JSON.parse(JSON.stringify(x));
}
if (y === true) {
y = {};
}
for (key in y) {
if (y.hasOwnProperty(key)) {
if (x.hasOwnProperty(key)) {
switch (key) {
case "maxProperties":
case "maxLength":
case "maxItems":
case "maximum":
case "exclusiveMaximum":
if (y[key] < x[key]) {
x[key] = y[key];
}
break;
case "minProperties":
case "minItems":
case "minLength":
case "minimum":
case "exclusiveMinimum":
if (x[key] < y[key]) {
x[key] = y[key];
}
break;
case "additionalProperties":
case "additionalItems":
case "contains":
case "propertyNames":
mergeSchemas(x[key], y[key], true);
break;
case "items":
// XXX items can be array
mergeSchemas(x[key], y[key], true);
break;
case "contentEncoding":
case "contentMediaType":
if (x[key] !== y[key]) {
return false;
}
break;
case "multipleOf":
x[key] = x[key] * y[key];
break;
case "type":
if (typeof x.type === "string") {
if (typeof y.type === "string") {
if (x.type !== y.type) {
return false;
}
} else if (y.type.indexOf(x.type) === -1) {
return false;
}
} else {
if (typeof y.type === "string") {
if (x.type.indexOf(y.type) === -1) {
return false;
}
} else {
x.type = arrayIntersect(x.type, y.type);
if (x.type.length === 0) {
return false;
}
}
}
break;
case "properties":
case "patternProperties":
for (p in y[key]) {
if (y[key].hasOwnProperty(p)) {
if (x[key].hasOwnProperty(p)) {
mergeSchemas(x[key][p], y[key][p], true);
} else {
x[key][p] = y[key][p];
}
}
}
break;
case "pattern":
// XXX regex string merge
case "dependencies":
// XXX find solution how merge
x[key] = y[key];
break;
case "required":
for (p = 0; p < y.required.length; p += 1) {
if (x.required.indexOf(y.required[p]) < 0) {
x.required.push(y.required[p]);
}
}
break;
case "uniqueItems":
x[key] = y[key];
break;
case "allOf":
case "anyOf":
case "$ref":
case "id":
case "$id":
// XXX
break;
default:
// XXX
x[key] = y[key];
}
} else {
switch (key) {
case "allOf":
case "anyOf":
case "$ref":
break;
default:
x[key] = y[key];
}
}
}
}
return x;
}
function allOf(g, schema_array, schema_path, base_schema) {
return RSVP.Queue() return RSVP.Queue()
.push(function () { .push(function () {
var i, var i,
...@@ -323,10 +481,8 @@ ...@@ -323,10 +481,8 @@
var i, var i,
x, x,
y, y,
key,
next_schema, next_schema,
schema, schema,
schema_item,
summ_arr; summ_arr;
for (i = 0; i < arr.length - 1; i += 1) { for (i = 0; i < arr.length - 1; i += 1) {
summ_arr = []; summ_arr = [];
...@@ -334,56 +490,31 @@ ...@@ -334,56 +490,31 @@
for (y = 0; y < arr[i + 1].length; y += 1) { for (y = 0; y < arr[i + 1].length; y += 1) {
schema = arr[i][x].schema; schema = arr[i][x].schema;
next_schema = arr[i + 1][y].schema; next_schema = arr[i + 1][y].schema;
if (schema === true && next_schema === true) { schema = mergeSchemas(schema, next_schema);
schema_item = { summ_arr.push({
schema: true, schema: schema,
schema_path: arr[i][x].schema_path // XXX we loss path arr[i + 1][y].schema_path
}; schema_path: arr[i][x].schema_path
} else if (schema === false || next_schema === false) { });
schema_item = {
schema: false,
schema_path: arr[i][x].schema_path
};
} else {
if (schema === true) {
schema = {};
}
if (next_schema === true) {
next_schema = {};
}
// copy before change
schema = JSON.parse(JSON.stringify(schema));
for (key in next_schema) {
if (next_schema.hasOwnProperty(key)) {
if (schema.hasOwnProperty(key)) {
// XXX need use many many rules for merging
schema[key] = next_schema[key];
} else {
schema[key] = next_schema[key];
}
}
}
schema_item = {
schema: schema,
schema_path: arr[i][x].schema_path
};
}
summ_arr.push(schema_item);
} }
} }
arr[i + 1] = summ_arr; arr[i + 1] = summ_arr;
} }
return arr[arr.length - 1]; for (x = 0; x < summ_arr.length; x += 1) {
summ_arr[x].schema = mergeSchemas(summ_arr[x].schema, base_schema);
}
return summ_arr;
}); });
} }
function anyOf(g, schema_array, schema_path) { function anyOf(g, schema, schema_path) {
var schema_array = schema.anyOf;
return RSVP.Queue() return RSVP.Queue()
.push(function () { .push(function () {
var i, var i,
arr = []; arr = [];
for (i = 0; i < schema_array.length; i += 1) { for (i = 0; i < schema_array.length; i += 1) {
arr.push(expandSchema(g, schema_array[i], schema_path + '/anyOf/' + i.toString())); arr.push(expandSchema(g, mergeSchemas(schema_array[i], schema), schema_path + '/anyOf/' + i.toString()));
} }
return RSVP.all(arr); return RSVP.all(arr);
}) })
...@@ -413,10 +544,10 @@ ...@@ -413,10 +544,10 @@
schema = true; schema = true;
} }
if (schema.anyOf !== undefined) { if (schema.anyOf !== undefined) {
return anyOf(g, schema.anyOf, schema_path); return anyOf(g, schema, schema_path);
} }
if (schema.allOf !== undefined) { if (schema.allOf !== undefined) {
return allOf(g, schema.allOf, schema_path); return allOf(g, schema.allOf, schema_path, schema);
} }
if (schema.$ref) { if (schema.$ref) {
return loadJSONSchema(g, schema.$ref, schema_path); return loadJSONSchema(g, schema.$ref, schema_path);
...@@ -627,11 +758,19 @@ ...@@ -627,11 +758,19 @@
"http://json-schema.org/draft-07/schema": "json-schema/schema7.json", "http://json-schema.org/draft-07/schema": "json-schema/schema7.json",
"http://json-schema.org/schema": "json-schema/schema7.json" "http://json-schema.org/schema": "json-schema/schema7.json"
}; };
// schema_urls[path] = [
// stack urls
// "url1",
// "url2"
// ]
// used for break soft circular relation of schemas
g.props.schema_urls = {};
// schema_required_urls[path] = [ // schema_required_urls[path] = [
// stack required urls, on every unrequired field stack begining from [] // stack required urls, on every unrequired field stack begining from []
// "url1", // "url1",
// "url2" // "url2"
// ] // ]
// used for break hard circular relation of schemas
g.props.schema_required_urls = {}; g.props.schema_required_urls = {};
// schema_resolve_errors[schema_url] = { // schema_resolve_errors[schema_url] = {
// schemaPath: local_schema_path, // schemaPath: local_schema_path,
......
...@@ -199,12 +199,15 @@ ...@@ -199,12 +199,15 @@
return input; return input;
} }
function render_const(schema, json_document) { function render_const(g, schema, json_document) {
var input = document.createElement("input"), var input = document.createElement("input"),
ser_doc = JSON.stringify(json_document), ser_doc = JSON.stringify(json_document),
ser_const = JSON.stringify(schema.const); ser_const = JSON.stringify(schema.const);
input.setAttribute('readonly', true); input.setAttribute('readonly', true);
if (json_document === undefined || deepEqual(json_document, schema.const)) { if (json_document === undefined || deepEqual(json_document, schema.const)) {
if (json_document === undefined) {
g.props.changed = true;
}
input.setAttribute('data-origin-value', ser_const); input.setAttribute('data-origin-value', ser_const);
input.value = ser_const; input.value = ser_const;
} else { } else {
...@@ -636,6 +639,13 @@ ...@@ -636,6 +639,13 @@
} }
} }
if (json_document === undefined) {
if (schema.hasOwnProperty('default')) {
json_document = schema.default;
gadget.props.changed = true;
}
}
// XXX add failback rendering if json_document not array // XXX add failback rendering if json_document not array
// input = render_textarea(schema, default_value, "array"); // input = render_textarea(schema, default_value, "array");
return RSVP.Queue() return RSVP.Queue()
...@@ -880,7 +890,7 @@ ...@@ -880,7 +890,7 @@
div_input.setAttribute("class", "input"); div_input.setAttribute("class", "input");
if (json_field.const !== undefined) { if (json_field.const !== undefined) {
input = render_const(json_field, default_value); input = render_const(gadget, json_field, default_value);
} else if (json_field.enum !== undefined) { } else if (json_field.enum !== undefined) {
input = render_enum(gadget, json_field, default_value); input = render_enum(gadget, json_field, default_value);
// XXX take in account existing type with enum // XXX take in account existing type with enum
...@@ -888,7 +898,7 @@ ...@@ -888,7 +898,7 @@
} }
if (!input && type === "null") { if (!input && type === "null") {
input = render_const({const: null}, default_value); input = render_const(gadget, {const: null}, default_value);
} }
if (!input && type === "boolean") { if (!input && type === "boolean") {
...@@ -1208,7 +1218,12 @@ ...@@ -1208,7 +1218,12 @@
} }
if (default_dict === undefined) { if (default_dict === undefined) {
default_dict = {}; if (json_field.hasOwnProperty('default')) {
default_dict = json_field.default;
g.props.changed = true;
} else {
default_dict = {};
}
} }
return expandProperties(g, json_field.properties, schema_path + '/properties/', required) return expandProperties(g, json_field.properties, schema_path + '/properties/', required)
...@@ -1222,6 +1237,7 @@ ...@@ -1222,6 +1237,7 @@
if (properties.hasOwnProperty(key)) { if (properties.hasOwnProperty(key)) {
schema_arr = properties[key]; schema_arr = properties[key];
s_o = schemaArrFilteredByDocument(schema_arr, default_dict[key]); s_o = schemaArrFilteredByDocument(schema_arr, default_dict[key]);
// XXX need schema merge with patternProperties passed key
if (checkSchemaArrOneChoise(schema_arr)) { if (checkSchemaArrOneChoise(schema_arr)) {
if (required.indexOf(key) >= 0) { if (required.indexOf(key) >= 0) {
used_properties[key] = false; used_properties[key] = false;
...@@ -1312,30 +1328,44 @@ ...@@ -1312,30 +1328,44 @@
}) })
.push(function () { .push(function () {
var queue = RSVP.Queue(), var queue = RSVP.Queue(),
key,
additionalProperties; additionalProperties;
// XXX for pattern properties needs schemas merge for
// all passed patterns
if (json_field.patternProperties !== undefined) { if (json_field.patternProperties !== undefined) {
// XXX need loop on any pattern properties for (key in json_field.patternProperties) {
if (json_field.patternProperties['.*'] !== undefined) { if (json_field.patternProperties.hasOwnProperty(key)) {
queue if (key === ".*" ||
.push(render_object_additionalProperty.bind(g, key === "^.*$" ||
key === ".*$" ||
key === "^.*"
) {
// additionalProperties nether used in this case
additionalProperties = false;
}
queue
.push(render_object_additionalProperty.bind(g,
g, g,
".* property", key + " property",
default_dict, default_dict,
path, path,
json_field.patternProperties['.*'], json_field.patternProperties[key],
schema_path + '/patternProperties/.*', schema_path + '/patternProperties/' + key,
used_properties, used_properties,
element_append element_append
)) ))
.push(root_append); .push(root_append);
}
} }
} }
if (json_field.additionalProperties === undefined) { if (additionalProperties === undefined) {
additionalProperties = true; if (json_field.additionalProperties === undefined) {
} else { additionalProperties = true;
additionalProperties = json_field.additionalProperties; } else {
additionalProperties = json_field.additionalProperties;
}
} }
if (additionalProperties !== false) { if (additionalProperties !== false) {
queue queue
......
...@@ -6,7 +6,11 @@ This code is released into the "public domain" by its author(s). Anybody may us ...@@ -6,7 +6,11 @@ This code is released into the "public domain" by its author(s). Anybody may us
If you find a bug or make an improvement, it would be courteous to let the author know, but it is not compulsory. If you find a bug or make an improvement, it would be courteous to let the author know, but it is not compulsory.
*/ */
/*global module, define*/
/*jslint indent: 2, white: true*/
/*jshint -W014: true, -W089: true, -W084: true, -W069: true*/
(function (global, factory) { (function (global, factory) {
"use strict";
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module. // AMD. Register as an anonymous module.
define([], factory); define([], factory);
...@@ -18,6 +22,7 @@ If you find a bug or make an improvement, it would be courteous to let the autho ...@@ -18,6 +22,7 @@ If you find a bug or make an improvement, it would be courteous to let the autho
global.tv4 = factory(); global.tv4 = factory();
} }
}(this, function () { }(this, function () {
"use strict";
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fkeys // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fkeys
if (!Object.keys) { if (!Object.keys) {
...@@ -82,6 +87,7 @@ if(!Array.isArray) { ...@@ -82,6 +87,7 @@ if(!Array.isArray) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FindexOf // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FindexOf
if (!Array.prototype.indexOf) { if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
/*jslint bitwise: true */
if (this === null) { if (this === null) {
throw new TypeError(); throw new TypeError();
} }
...@@ -262,7 +268,7 @@ function uriTemplateSubstitution(spec) { ...@@ -262,7 +268,7 @@ function uriTemplateSubstitution(spec) {
result += "="; result += "=";
} }
} }
if (varSpec.truncate != null) { if (varSpec.truncate !== null) {
value = value.substring(0, varSpec.truncate); value = value.substring(0, varSpec.truncate);
} }
result += shouldEscape ? encodeURIComponent(value).replace(/!/g, "%21"): notReallyPercentEncode(value); result += shouldEscape ? encodeURIComponent(value).replace(/!/g, "%21"): notReallyPercentEncode(value);
...@@ -533,9 +539,22 @@ ValidatorContext.prototype.reset = function () { ...@@ -533,9 +539,22 @@ ValidatorContext.prototype.reset = function () {
this.errors = []; this.errors = [];
}; };
ValidatorContext.prototype.validateAllValidators = function validateAllValidators(data, schema, dataPointerPath) {
return this.validateBasic(data, schema, dataPointerPath)
|| this.validateNumeric(data, schema, dataPointerPath)
|| this.validateString(data, schema, dataPointerPath)
|| this.validateArray(data, schema, dataPointerPath)
|| this.validateObject(data, schema, dataPointerPath)
|| this.validateCombinations(data, schema, dataPointerPath)
|| this.validateHypermedia(data, schema, dataPointerPath)
|| this.validateFormat(data, schema, dataPointerPath)
|| this.validateDefinedKeywords(data, schema, dataPointerPath)
|| null;
};
ValidatorContext.prototype.validateAll = function (data, schema, dataPathParts, schemaPathParts, dataPointerPath) { ValidatorContext.prototype.validateAll = function (data, schema, dataPathParts, schemaPathParts, dataPointerPath) {
var topLevel; var topLevel;
if (!schema) { if (schema === undefined || schema === true) {
return null; return null;
} else if (schema instanceof ValidationError) { } else if (schema instanceof ValidationError) {
this.errors.push(schema); this.errors.push(schema);
...@@ -597,16 +616,7 @@ ValidatorContext.prototype.validateAll = function (data, schema, dataPathParts, ...@@ -597,16 +616,7 @@ ValidatorContext.prototype.validateAll = function (data, schema, dataPathParts,
} }
var errorCount = this.errors.length; var errorCount = this.errors.length;
var error = this.validateBasic(data, schema, dataPointerPath) var error = this.validateAllValidators(data, schema, dataPointerPath);
|| this.validateNumeric(data, schema, dataPointerPath)
|| this.validateString(data, schema, dataPointerPath)
|| this.validateArray(data, schema, dataPointerPath)
|| this.validateObject(data, schema, dataPointerPath)
|| this.validateCombinations(data, schema, dataPointerPath)
|| this.validateHypermedia(data, schema, dataPointerPath)
|| this.validateFormat(data, schema, dataPointerPath)
|| this.validateDefinedKeywords(data, schema, dataPointerPath)
|| null;
if (topLevel) { if (topLevel) {
while (this.scanned.length) { while (this.scanned.length) {
...@@ -718,10 +728,16 @@ function recursiveCompare(A, B) { ...@@ -718,10 +728,16 @@ function recursiveCompare(A, B) {
} }
ValidatorContext.prototype.validateBasic = function validateBasic(data, schema, dataPointerPath) { ValidatorContext.prototype.validateBasic = function validateBasic(data, schema, dataPointerPath) {
if (schema === false && data !== undefined) {
return this.createError(ErrorCodes.BOOLEAN_SCHEMA_FALSE, {}, '', '', null, data, schema);
}
var error; var error;
if (error = this.validateType(data, schema, dataPointerPath)) { if (error = this.validateType(data, schema, dataPointerPath)) {
return error.prefixWith(null, "type"); return error.prefixWith(null, "type");
} }
if (error = this.validateConst(data, schema, dataPointerPath)) {
return error.prefixWith(null, "const");
}
if (error = this.validateEnum(data, schema, dataPointerPath)) { if (error = this.validateEnum(data, schema, dataPointerPath)) {
return error.prefixWith(null, "type"); return error.prefixWith(null, "type");
} }
...@@ -752,6 +768,14 @@ ValidatorContext.prototype.validateType = function validateType(data, schema) { ...@@ -752,6 +768,14 @@ ValidatorContext.prototype.validateType = function validateType(data, schema) {
return this.createError(ErrorCodes.INVALID_TYPE, {type: dataType, expected: allowedTypes.join("/")}, '', '', null, data, schema); return this.createError(ErrorCodes.INVALID_TYPE, {type: dataType, expected: allowedTypes.join("/")}, '', '', null, data, schema);
}; };
ValidatorContext.prototype.validateConst = function validateConst(data, schema) {
if (schema.const === undefined ||
recursiveCompare(data, schema.const)) {
return null;
}
return this.createError(ErrorCodes.CONST_NOT_EQUAL, {}, '', '', null, data, schema);
};
ValidatorContext.prototype.validateEnum = function validateEnum(data, schema) { ValidatorContext.prototype.validateEnum = function validateEnum(data, schema) {
if (schema["enum"] === undefined) { if (schema["enum"] === undefined) {
return null; return null;
...@@ -796,18 +820,24 @@ ValidatorContext.prototype.validateMinMax = function validateMinMax(data, schema ...@@ -796,18 +820,24 @@ ValidatorContext.prototype.validateMinMax = function validateMinMax(data, schema
if (data < schema.minimum) { if (data < schema.minimum) {
return this.createError(ErrorCodes.NUMBER_MINIMUM, {value: data, minimum: schema.minimum}, '', '/minimum', null, data, schema); return this.createError(ErrorCodes.NUMBER_MINIMUM, {value: data, minimum: schema.minimum}, '', '/minimum', null, data, schema);
} }
if (schema.exclusiveMinimum && data === schema.minimum) { if (schema.exclusiveMinimum === true && data === schema.minimum) {
return this.createError(ErrorCodes.NUMBER_MINIMUM_EXCLUSIVE, {value: data, minimum: schema.minimum}, '', '/exclusiveMinimum', null, data, schema); return this.createError(ErrorCodes.NUMBER_MINIMUM_EXCLUSIVE, {value: data, minimum: schema.minimum}, '', '/exclusiveMinimum', null, data, schema);
} }
} }
if ((typeof schema.exclusiveMinimum === "number") && data <= schema.exclusiveMinimum) {
return this.createError(ErrorCodes.NUMBER_MINIMUM_EXCLUSIVE, {value: data, minimum: schema.exclusiveMinimum}, '', '/exclusiveMinimum', null, data, schema);
}
if (schema.maximum !== undefined) { if (schema.maximum !== undefined) {
if (data > schema.maximum) { if (data > schema.maximum) {
return this.createError(ErrorCodes.NUMBER_MAXIMUM, {value: data, maximum: schema.maximum}, '', '/maximum', null, data, schema); return this.createError(ErrorCodes.NUMBER_MAXIMUM, {value: data, maximum: schema.maximum}, '', '/maximum', null, data, schema);
} }
if (schema.exclusiveMaximum && data === schema.maximum) { if (schema.exclusiveMaximum === true && data === schema.maximum) {
return this.createError(ErrorCodes.NUMBER_MAXIMUM_EXCLUSIVE, {value: data, maximum: schema.maximum}, '', '/exclusiveMaximum', null, data, schema); return this.createError(ErrorCodes.NUMBER_MAXIMUM_EXCLUSIVE, {value: data, maximum: schema.maximum}, '', '/exclusiveMaximum', null, data, schema);
} }
} }
if ((typeof schema.exclusiveMaximum === "number") && data >= schema.exclusiveMaximum) {
return this.createError(ErrorCodes.NUMBER_MAXIMUM_EXCLUSIVE, {value: data, maximum: schema.exclusiveMaximum}, '', '/exclusiveMaximum', null, data, schema);
}
return null; return null;
}; };
...@@ -878,6 +908,7 @@ ValidatorContext.prototype.validateArray = function validateArray(data, schema, ...@@ -878,6 +908,7 @@ ValidatorContext.prototype.validateArray = function validateArray(data, schema,
} }
return this.validateArrayLength(data, schema, dataPointerPath) return this.validateArrayLength(data, schema, dataPointerPath)
|| this.validateArrayUniqueItems(data, schema, dataPointerPath) || this.validateArrayUniqueItems(data, schema, dataPointerPath)
|| this.validateArrayContains(data, schema, dataPointerPath)
|| this.validateArrayItems(data, schema, dataPointerPath) || this.validateArrayItems(data, schema, dataPointerPath)
|| null; || null;
}; };
...@@ -903,6 +934,28 @@ ValidatorContext.prototype.validateArrayLength = function validateArrayLength(da ...@@ -903,6 +934,28 @@ ValidatorContext.prototype.validateArrayLength = function validateArrayLength(da
return null; return null;
}; };
ValidatorContext.prototype.validateArrayContains = function validateArrayContains(data, schema, dataPointerPath) {
if ((schema.contains === true && data.length > 0) || schema.contains === undefined) {
return null;
}
var error;
if (data.length === 0 || schema.contains === false) {
error = true;
} else {
for (var i = 0; i < data.length; i++) {
error = this.validateAllValidators(data[i], schema.contains, dataPointerPath + "/" + i);
if (!error) {
return null;
}
}
error = true;
}
if (error) {
return this.createError(ErrorCodes.ARRAY_CONTAINS, {}, '', '/contains', null, data, schema);
}
return null;
};
ValidatorContext.prototype.validateArrayUniqueItems = function validateArrayUniqueItems(data, schema) { ValidatorContext.prototype.validateArrayUniqueItems = function validateArrayUniqueItems(data, schema) {
if (schema.uniqueItems) { if (schema.uniqueItems) {
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
...@@ -1006,6 +1059,11 @@ ValidatorContext.prototype.validateObjectProperties = function validateObjectPro ...@@ -1006,6 +1059,11 @@ ValidatorContext.prototype.validateObjectProperties = function validateObjectPro
for (var key in data) { for (var key in data) {
var keyPointerPath = dataPointerPath + "/" + key.replace(/~/g, '~0').replace(/\//g, '~1'); var keyPointerPath = dataPointerPath + "/" + key.replace(/~/g, '~0').replace(/\//g, '~1');
var foundMatch = false; var foundMatch = false;
if (schema.propertyNames !== undefined && schema.propertyNames !== true) {
if (error = this.validateAllValidators(key, schema.propertyNames, keyPointerPath)) {
return this.createError(ErrorCodes.OBJECT_PROPERTY_NAMES, {key: key, error: error.message}, '', '/propertyNames', null, data, schema).prefixWith(key, null);
}
}
if (schema.properties !== undefined && schema.properties[key] !== undefined) { if (schema.properties !== undefined && schema.properties[key] !== undefined) {
foundMatch = true; foundMatch = true;
if (error = this.validateAll(data[key], schema.properties[key], [key], ["properties", key], keyPointerPath)) { if (error = this.validateAll(data[key], schema.properties[key], [key], ["properties", key], keyPointerPath)) {
...@@ -1375,6 +1433,8 @@ var ErrorCodes = { ...@@ -1375,6 +1433,8 @@ var ErrorCodes = {
ONE_OF_MISSING: 11, ONE_OF_MISSING: 11,
ONE_OF_MULTIPLE: 12, ONE_OF_MULTIPLE: 12,
NOT_PASSED: 13, NOT_PASSED: 13,
BOOLEAN_SCHEMA_FALSE: 14,
CONST_NOT_EQUAL: 15,
// Numeric errors // Numeric errors
NUMBER_MULTIPLE_OF: 100, NUMBER_MULTIPLE_OF: 100,
NUMBER_MINIMUM: 101, NUMBER_MINIMUM: 101,
...@@ -1392,11 +1452,13 @@ var ErrorCodes = { ...@@ -1392,11 +1452,13 @@ var ErrorCodes = {
OBJECT_REQUIRED: 302, OBJECT_REQUIRED: 302,
OBJECT_ADDITIONAL_PROPERTIES: 303, OBJECT_ADDITIONAL_PROPERTIES: 303,
OBJECT_DEPENDENCY_KEY: 304, OBJECT_DEPENDENCY_KEY: 304,
OBJECT_PROPERTY_NAMES: 305,
// Array errors // Array errors
ARRAY_LENGTH_SHORT: 400, ARRAY_LENGTH_SHORT: 400,
ARRAY_LENGTH_LONG: 401, ARRAY_LENGTH_LONG: 401,
ARRAY_UNIQUE: 402, ARRAY_UNIQUE: 402,
ARRAY_ADDITIONAL_ITEMS: 403, ARRAY_ADDITIONAL_ITEMS: 403,
ARRAY_CONTAINS: 404,
// Custom/user-defined errors // Custom/user-defined errors
FORMAT_CUSTOM: 500, FORMAT_CUSTOM: 500,
KEYWORD_CUSTOM: 501, KEYWORD_CUSTOM: 501,
...@@ -1416,6 +1478,8 @@ var ErrorMessagesDefault = { ...@@ -1416,6 +1478,8 @@ var ErrorMessagesDefault = {
ONE_OF_MISSING: "Data does not match any schemas from \"oneOf\"", ONE_OF_MISSING: "Data does not match any schemas from \"oneOf\"",
ONE_OF_MULTIPLE: "Data is valid against more than one schema from \"oneOf\": indices {index1} and {index2}", ONE_OF_MULTIPLE: "Data is valid against more than one schema from \"oneOf\": indices {index1} and {index2}",
NOT_PASSED: "Data matches schema from \"not\"", NOT_PASSED: "Data matches schema from \"not\"",
BOOLEAN_SCHEMA_FALSE: "Schema does not allow any data",
CONST_NOT_EQUAL: "Data does not match schema.const",
// Numeric errors // Numeric errors
NUMBER_MULTIPLE_OF: "Value {value} is not a multiple of {multipleOf}", NUMBER_MULTIPLE_OF: "Value {value} is not a multiple of {multipleOf}",
NUMBER_MINIMUM: "Value {value} is less than minimum {minimum}", NUMBER_MINIMUM: "Value {value} is less than minimum {minimum}",
...@@ -1433,11 +1497,13 @@ var ErrorMessagesDefault = { ...@@ -1433,11 +1497,13 @@ var ErrorMessagesDefault = {
OBJECT_REQUIRED: "Missing required property: {key}", OBJECT_REQUIRED: "Missing required property: {key}",
OBJECT_ADDITIONAL_PROPERTIES: "Additional properties not allowed", OBJECT_ADDITIONAL_PROPERTIES: "Additional properties not allowed",
OBJECT_DEPENDENCY_KEY: "Dependency failed - key must exist: {missing} (due to key: {key})", OBJECT_DEPENDENCY_KEY: "Dependency failed - key must exist: {missing} (due to key: {key})",
OBJECT_PROPERTY_NAMES: "Property name \"{key}\" does not match schema with error: {error}",
// Array errors // Array errors
ARRAY_LENGTH_SHORT: "Array is too short ({length}), minimum {minimum}", ARRAY_LENGTH_SHORT: "Array is too short ({length}), minimum {minimum}",
ARRAY_LENGTH_LONG: "Array is too long ({length}), maximum {maximum}", ARRAY_LENGTH_LONG: "Array is too long ({length}), maximum {maximum}",
ARRAY_UNIQUE: "Array items are not unique (indices {match1} and {match2})", ARRAY_UNIQUE: "Array items are not unique (indices {match1} and {match2})",
ARRAY_ADDITIONAL_ITEMS: "Additional items not allowed", ARRAY_ADDITIONAL_ITEMS: "Additional items not allowed",
ARRAY_CONTAINS: "Array are not contain item matching schema.contains",
// Format errors // Format errors
FORMAT_CUSTOM: "Format validation failed ({message})", FORMAT_CUSTOM: "Format validation failed ({message})",
KEYWORD_CUSTOM: "Keyword failed: {key} ({message})", KEYWORD_CUSTOM: "Keyword failed: {key} ({message})",
...@@ -1462,6 +1528,7 @@ function ValidationError(code, params, dataPath, schemaPath, subErrors) { ...@@ -1462,6 +1528,7 @@ function ValidationError(code, params, dataPath, schemaPath, subErrors) {
var err = new Error(this.message); var err = new Error(this.message);
this.stack = err.stack || err.stacktrace; this.stack = err.stack || err.stacktrace;
if (!this.stack) { if (!this.stack) {
/*jshint -W002: true*/
try { try {
throw err; throw err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment