From 3fe279da682fc9091377c8f374ac924faf24cc5e Mon Sep 17 00:00:00 2001 From: Marcis Bergmanis Date: Tue, 5 Oct 2021 10:09:20 +0300 Subject: [PATCH] Adds `all-of` schema rule --- src/validate/all-of.js | 45 ++++++++++++++++++++++++++++ src/validate/schema.js | 12 ++++++++ tests/all-of.test.js | 67 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 src/validate/all-of.js create mode 100644 tests/all-of.test.js diff --git a/src/validate/all-of.js b/src/validate/all-of.js new file mode 100644 index 0000000..734f120 --- /dev/null +++ b/src/validate/all-of.js @@ -0,0 +1,45 @@ +/** + * @this {import('src').Context} + * @param {*} value + * @param {*[]} currentSchema + * @param {(string | number)[]} position + */ +function validateAllOf(value, currentSchema, position) { + let errorDepth = position.length + 1; + let possibleError; + + const passSize = currentSchema.length; + let passedValues = 0; + + for (const key in currentSchema) { + try { + // Run shallow validation + this.validateSchema.call( + {...this, shallow: true}, + value, + position, + currentSchema[key], + ); + + passedValues += 1; + } catch (e) { + if (e.position && e.position.length > errorDepth) { + possibleError = key; + errorDepth = e.position.length; + } + } + } + + if (passedValues >= passSize) { + return; + } + + // Guessing the error based on error depth + if (possibleError) { + return possibleError; + } + + this.error("Value doesn't match all of criteria", "allOf", position); +} + +module.exports = validateAllOf; diff --git a/src/validate/schema.js b/src/validate/schema.js index be8f96e..ccd70ad 100644 --- a/src/validate/schema.js +++ b/src/validate/schema.js @@ -1,6 +1,7 @@ const GenericError = require("../diagnostics/generic-error"); const validateEnum = require("./enum"); +const validateAllOf = require("./all-of"); const validateAnyOf = require("./any-of"); const validateString = require("./type/string"); const validateObject = require("./type/object"); @@ -67,6 +68,17 @@ function validateSchema(json, position, currentSchema = this.schema) { return; } + if (!this.shallow && currentSchema.allOf) { + const key = validateAllOf.call(this, json, currentSchema.allOf, position); + + if (typeof key === "undefined") { + return; + } + + validateSchema.call(this, json, position, currentSchema.allOf[key]); + return; + } + if (!this.shallow && currentSchema.anyOf) { const key = validateAnyOf.call(this, json, currentSchema.anyOf, position); diff --git a/tests/all-of.test.js b/tests/all-of.test.js new file mode 100644 index 0000000..19118f9 --- /dev/null +++ b/tests/all-of.test.js @@ -0,0 +1,67 @@ +const {test} = require("uvu"); +const assert = require("uvu/assert"); + +const ValidationError = require("../src/diagnostics/validation-error"); +const output = require("../src/output"); + +const userConfig = {}; + +test("allOf with one empty schema", () => { + const schema = { + "allOf": [{}], + }; + const errors = output("1", schema, userConfig); + + assert.equal(errors, []); +}); + +test("allOf with two empty schemas", () => { + const schema = { + "allOf": [{}, {}], + }; + const errors = output("1", schema, userConfig); + + assert.equal(errors, []); +}); + +test("allOf with two schemas, the first is empty - number is valid", () => { + const schema = { + "allOf": [{}, { "type": "number" }], + }; + const errors = output("1", schema, userConfig); + + assert.equal(errors, []); +}); + +test("allOf with two schemas, the first is empty - string is invalid", () => { + const schema = { + "allOf": [{}, { "type": "number" }], + }; + const errors = output("\"foo\"", schema, userConfig); + + assert.equal(errors, [ + new ValidationError("Value doesn't match all of criteria", "allOf", []) + ]); +}); + +test("allOf with two schemas, the second is empty - number is valid", () => { + const schema = { + "allOf": [{ "type": "number" }, {}], + }; + const errors = output("1", schema, userConfig); + + assert.equal(errors, []); +}); + +test("allOf with two schemas, the second is empty - string is invalid", () => { + const schema = { + "allOf": [{ "type": "number" }, {}], + }; + const errors = output("\"foo\"", schema, userConfig); + + assert.equal(errors, [ + new ValidationError("Value doesn't match all of criteria", "allOf", []) + ]); +}); + +test.run();