Update JsonSchema (#1856)
* Fix issues with validating properties * Simplify multiple destructuring * Add an optiona progress callback * Add functions * Add more functions
This commit is contained in:
parent
b0596c8a3c
commit
a0fa67d57c
@ -28,6 +28,9 @@ class JsonSchema {
|
|||||||
this._refCache = null;
|
this._refCache = null;
|
||||||
this._valueStack = [];
|
this._valueStack = [];
|
||||||
this._schemaStack = [];
|
this._schemaStack = [];
|
||||||
|
this._progress = null;
|
||||||
|
this._progressCounter = 0;
|
||||||
|
this._progressInterval = 1;
|
||||||
|
|
||||||
this._schemaPush(null, null);
|
this._schemaPush(null, null);
|
||||||
this._valuePush(null, null);
|
this._valuePush(null, null);
|
||||||
@ -41,6 +44,22 @@ class JsonSchema {
|
|||||||
return this._rootSchema;
|
return this._rootSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get progress() {
|
||||||
|
return this._progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
set progress(value) {
|
||||||
|
this._progress = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get progressInterval() {
|
||||||
|
return this._progressInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
set progressInterval(value) {
|
||||||
|
this._progressInterval = value;
|
||||||
|
}
|
||||||
|
|
||||||
createProxy(value) {
|
createProxy(value) {
|
||||||
return (
|
return (
|
||||||
typeof value === 'object' && value !== null ?
|
typeof value === 'object' && value !== null ?
|
||||||
@ -100,6 +119,44 @@ class JsonSchema {
|
|||||||
return Array.isArray(required) && required.includes(property);
|
return Array.isArray(required) && required.includes(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal state functions for error construction and progress callback
|
||||||
|
|
||||||
|
getValueStack() {
|
||||||
|
const valueStack = [];
|
||||||
|
for (let i = 1, ii = this._valueStack.length; i < ii; ++i) {
|
||||||
|
const {value, path} = this._valueStack[i];
|
||||||
|
valueStack.push({value, path});
|
||||||
|
}
|
||||||
|
return valueStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaStack() {
|
||||||
|
const schemaStack = [];
|
||||||
|
for (let i = 1, ii = this._schemaStack.length; i < ii; ++i) {
|
||||||
|
const {schema, path} = this._schemaStack[i];
|
||||||
|
schemaStack.push({schema, path});
|
||||||
|
}
|
||||||
|
return schemaStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
getValueStackLength() {
|
||||||
|
return this._valueStack.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getValueStackItem(index) {
|
||||||
|
const {value, path} = this._valueStack[index + 1];
|
||||||
|
return {value, path};
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaStackLength() {
|
||||||
|
return this._schemaStack.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaStackItem(index) {
|
||||||
|
const {schema, path} = this._schemaStack[index + 1];
|
||||||
|
return {schema, path};
|
||||||
|
}
|
||||||
|
|
||||||
// Stack
|
// Stack
|
||||||
|
|
||||||
_valuePush(value, path) {
|
_valuePush(value, path) {
|
||||||
@ -123,21 +180,11 @@ class JsonSchema {
|
|||||||
// Private
|
// Private
|
||||||
|
|
||||||
_createError(message) {
|
_createError(message) {
|
||||||
const valueStack = [];
|
const valueStack = this.getValueStack();
|
||||||
for (let i = 1, ii = this._valueStack.length; i < ii; ++i) {
|
const schemaStack = this.getSchemaStack();
|
||||||
const {value, path} = this._valueStack[i];
|
|
||||||
valueStack.push({value, path});
|
|
||||||
}
|
|
||||||
|
|
||||||
const schemaStack = [];
|
|
||||||
for (let i = 1, ii = this._schemaStack.length; i < ii; ++i) {
|
|
||||||
const {schema, path} = this._schemaStack[i];
|
|
||||||
schemaStack.push({schema, path});
|
|
||||||
}
|
|
||||||
|
|
||||||
const error = new Error(message);
|
const error = new Error(message);
|
||||||
error.value = valueStack[valueStack.length - 1].value;
|
error.value = valueStack[valueStack.length - 1].value;
|
||||||
error.schema = this._schema;
|
error.schema = schemaStack[schemaStack.length - 1].schema;
|
||||||
error.valueStack = valueStack;
|
error.valueStack = valueStack;
|
||||||
error.schemaStack = schemaStack;
|
error.schemaStack = schemaStack;
|
||||||
return error;
|
return error;
|
||||||
@ -347,6 +394,12 @@ class JsonSchema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_validate(value) {
|
_validate(value) {
|
||||||
|
if (this._progress !== null) {
|
||||||
|
const counter = (this._progressCounter + 1) % this._progressInterval;
|
||||||
|
this._progressCounter = counter;
|
||||||
|
if (counter === 0) { this._progress(this); }
|
||||||
|
}
|
||||||
|
|
||||||
const ref = this._schema.$ref;
|
const ref = this._schema.$ref;
|
||||||
const schemaInfo = (typeof ref === 'string') ? this._getReference(ref) : null;
|
const schemaInfo = (typeof ref === 'string') ? this._getReference(ref) : null;
|
||||||
|
|
||||||
@ -501,18 +554,16 @@ class JsonSchema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_validateSingleSchema(value) {
|
_validateSingleSchema(value) {
|
||||||
|
const {type: schemaType, const: schemaConst, enum: schemaEnum} = this._schema;
|
||||||
const type = this._getValueType(value);
|
const type = this._getValueType(value);
|
||||||
const schemaType = this._schema.type;
|
|
||||||
if (!this._isValueTypeAny(value, type, schemaType)) {
|
if (!this._isValueTypeAny(value, type, schemaType)) {
|
||||||
throw this._createError(`Value type ${type} does not match schema type ${schemaType}`);
|
throw this._createError(`Value type ${type} does not match schema type ${schemaType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const schemaConst = this._schema.const;
|
|
||||||
if (typeof schemaConst !== 'undefined' && !this._valuesAreEqual(value, schemaConst)) {
|
if (typeof schemaConst !== 'undefined' && !this._valuesAreEqual(value, schemaConst)) {
|
||||||
throw this._createError('Invalid constant value');
|
throw this._createError('Invalid constant value');
|
||||||
}
|
}
|
||||||
|
|
||||||
const schemaEnum = this._schema.enum;
|
|
||||||
if (Array.isArray(schemaEnum) && !this._valuesAreEqualAny(value, schemaEnum)) {
|
if (Array.isArray(schemaEnum) && !this._valuesAreEqualAny(value, schemaEnum)) {
|
||||||
throw this._createError('Invalid enum value');
|
throw this._createError('Invalid enum value');
|
||||||
}
|
}
|
||||||
@ -534,44 +585,38 @@ class JsonSchema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_validateNumber(value) {
|
_validateNumber(value) {
|
||||||
const {multipleOf} = this._schema;
|
const {multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum} = this._schema;
|
||||||
if (typeof multipleOf === 'number' && Math.floor(value / multipleOf) * multipleOf !== value) {
|
if (typeof multipleOf === 'number' && Math.floor(value / multipleOf) * multipleOf !== value) {
|
||||||
throw this._createError(`Number is not a multiple of ${multipleOf}`);
|
throw this._createError(`Number is not a multiple of ${multipleOf}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {minimum} = this._schema;
|
|
||||||
if (typeof minimum === 'number' && value < minimum) {
|
if (typeof minimum === 'number' && value < minimum) {
|
||||||
throw this._createError(`Number is less than ${minimum}`);
|
throw this._createError(`Number is less than ${minimum}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {exclusiveMinimum} = this._schema;
|
|
||||||
if (typeof exclusiveMinimum === 'number' && value <= exclusiveMinimum) {
|
if (typeof exclusiveMinimum === 'number' && value <= exclusiveMinimum) {
|
||||||
throw this._createError(`Number is less than or equal to ${exclusiveMinimum}`);
|
throw this._createError(`Number is less than or equal to ${exclusiveMinimum}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {maximum} = this._schema;
|
|
||||||
if (typeof maximum === 'number' && value > maximum) {
|
if (typeof maximum === 'number' && value > maximum) {
|
||||||
throw this._createError(`Number is greater than ${maximum}`);
|
throw this._createError(`Number is greater than ${maximum}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {exclusiveMaximum} = this._schema;
|
|
||||||
if (typeof exclusiveMaximum === 'number' && value >= exclusiveMaximum) {
|
if (typeof exclusiveMaximum === 'number' && value >= exclusiveMaximum) {
|
||||||
throw this._createError(`Number is greater than or equal to ${exclusiveMaximum}`);
|
throw this._createError(`Number is greater than or equal to ${exclusiveMaximum}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_validateString(value) {
|
_validateString(value) {
|
||||||
const {minLength} = this._schema;
|
const {minLength, maxLength, pattern} = this._schema;
|
||||||
if (typeof minLength === 'number' && value.length < minLength) {
|
if (typeof minLength === 'number' && value.length < minLength) {
|
||||||
throw this._createError('String length too short');
|
throw this._createError('String length too short');
|
||||||
}
|
}
|
||||||
|
|
||||||
const {maxLength} = this._schema;
|
|
||||||
if (typeof maxLength === 'number' && value.length > maxLength) {
|
if (typeof maxLength === 'number' && value.length > maxLength) {
|
||||||
throw this._createError('String length too long');
|
throw this._createError('String length too long');
|
||||||
}
|
}
|
||||||
|
|
||||||
const {pattern} = this._schema;
|
|
||||||
if (typeof pattern === 'string') {
|
if (typeof pattern === 'string') {
|
||||||
let {patternFlags} = this._schema;
|
let {patternFlags} = this._schema;
|
||||||
if (typeof patternFlags !== 'string') { patternFlags = ''; }
|
if (typeof patternFlags !== 'string') { patternFlags = ''; }
|
||||||
@ -590,14 +635,13 @@ class JsonSchema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_validateArray(value) {
|
_validateArray(value) {
|
||||||
|
const {minItems, maxItems} = this._schema;
|
||||||
const {length} = value;
|
const {length} = value;
|
||||||
|
|
||||||
const {minItems} = this._schema;
|
|
||||||
if (typeof minItems === 'number' && length < minItems) {
|
if (typeof minItems === 'number' && length < minItems) {
|
||||||
throw this._createError('Array length too short');
|
throw this._createError('Array length too short');
|
||||||
}
|
}
|
||||||
|
|
||||||
const {maxItems} = this._schema;
|
|
||||||
if (typeof maxItems === 'number' && length > maxItems) {
|
if (typeof maxItems === 'number' && length > maxItems) {
|
||||||
throw this._createError('Array length too long');
|
throw this._createError('Array length too long');
|
||||||
}
|
}
|
||||||
@ -648,28 +692,28 @@ class JsonSchema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_validateObject(value) {
|
_validateObject(value) {
|
||||||
const properties = new Set(Object.getOwnPropertyNames(value));
|
const {required, minProperties, maxProperties} = this._schema;
|
||||||
|
const properties = Object.getOwnPropertyNames(value);
|
||||||
|
const {length} = properties;
|
||||||
|
|
||||||
const {required} = this._schema;
|
|
||||||
if (Array.isArray(required)) {
|
if (Array.isArray(required)) {
|
||||||
for (const property of required) {
|
for (const property of required) {
|
||||||
if (!properties.has(property)) {
|
if (!Object.prototype.hasOwnProperty.call(value, property)) {
|
||||||
throw this._createError(`Missing property ${property}`);
|
throw this._createError(`Missing property ${property}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {minProperties} = this._schema;
|
if (typeof minProperties === 'number' && length < minProperties) {
|
||||||
if (typeof minProperties === 'number' && properties.length < minProperties) {
|
|
||||||
throw this._createError('Not enough object properties');
|
throw this._createError('Not enough object properties');
|
||||||
}
|
}
|
||||||
|
|
||||||
const {maxProperties} = this._schema;
|
if (typeof maxProperties === 'number' && length > maxProperties) {
|
||||||
if (typeof maxProperties === 'number' && properties.length > maxProperties) {
|
|
||||||
throw this._createError('Too many object properties');
|
throw this._createError('Too many object properties');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const property of properties) {
|
for (let i = 0; i < length; ++i) {
|
||||||
|
const property = properties[i];
|
||||||
const schemaInfo = this._getObjectPropertySchemaInfo(property);
|
const schemaInfo = this._getObjectPropertySchemaInfo(property);
|
||||||
if (schemaInfo === null) {
|
if (schemaInfo === null) {
|
||||||
throw this._createError(`No schema found for ${property}`);
|
throw this._createError(`No schema found for ${property}`);
|
||||||
|
Loading…
Reference in New Issue
Block a user