diff --git a/src/error-handlers/draft-04/maximum.js b/src/error-handlers/draft-04/maximum.js deleted file mode 100644 index ad3b5cc..0000000 --- a/src/error-handlers/draft-04/maximum.js +++ /dev/null @@ -1,56 +0,0 @@ -import { getSchema } from "@hyperjump/json-schema/experimental"; -import * as Schema from "@hyperjump/browser"; -import * as Instance from "@hyperjump/json-schema/instance/experimental"; - -/** - * @import { ErrorHandler, ErrorObject } from "../../index.d.ts" - */ - -/** @type ErrorHandler */ -const maximumErrorHandler = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; - - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/maximum"]) { - if (normalizedErrors["https://json-schema.org/keyword/draft-04/maximum"][schemaLocation]) { - continue; - } - - const schemaLocations = [schemaLocation]; - const parentLocation = pointerPop(schemaLocation); - - let exclusive = false; - for (const exclusiveLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/exclusiveMaximum"]) { - if (pointerPop(exclusiveLocation) === parentLocation) { - schemaLocations.push(exclusiveLocation); - const exclusiveNode = await getSchema(exclusiveLocation); - exclusive = /** @type boolean */ (Schema.value(exclusiveNode)); - break; - } - } - - const keywordNode = await getSchema(schemaLocation); - const maximum = /** @type number */ (Schema.value(keywordNode)); - - if (exclusive) { - errors.push({ - message: localization.getExclusiveMaximumErrorMessage(maximum), - instanceLocation: Instance.uri(instance), - schemaLocations: schemaLocations - }); - } else { - errors.push({ - message: localization.getMaximumErrorMessage(maximum), - instanceLocation: Instance.uri(instance), - schemaLocations: schemaLocations - }); - } - } - - return errors; -}; - -/** @type (pointer: string) => string */ -const pointerPop = (pointer) => pointer.replace(/\/[^/]+$/, ""); - -export default maximumErrorHandler; diff --git a/src/error-handlers/draft-04/minimum.js b/src/error-handlers/draft-04/minimum.js deleted file mode 100644 index aa89758..0000000 --- a/src/error-handlers/draft-04/minimum.js +++ /dev/null @@ -1,56 +0,0 @@ -import { getSchema } from "@hyperjump/json-schema/experimental"; -import * as Schema from "@hyperjump/browser"; -import * as Instance from "@hyperjump/json-schema/instance/experimental"; - -/** - * @import { ErrorHandler, ErrorObject } from "../../index.d.ts" - */ - -/** @type ErrorHandler */ -const minimumErrorHandler = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; - - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/minimum"]) { - if (normalizedErrors["https://json-schema.org/keyword/draft-04/minimum"][schemaLocation]) { - continue; - } - - const schemaLocations = [schemaLocation]; - const parentLocation = pointerPop(schemaLocation); - - let exclusive = false; - for (const exclusiveLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/exclusiveMinimum"]) { - if (pointerPop(exclusiveLocation) === parentLocation) { - schemaLocations.push(exclusiveLocation); - const exclusiveNode = await getSchema(exclusiveLocation); - exclusive = /** @type boolean */ (Schema.value(exclusiveNode)); - break; - } - } - - const keywordNode = await getSchema(schemaLocation); - const minimum = /** @type number */ (Schema.value(keywordNode)); - - if (exclusive) { - errors.push({ - message: localization.getExclusiveMinimumErrorMessage(minimum), - instanceLocation: Instance.uri(instance), - schemaLocations: schemaLocations - }); - } else { - errors.push({ - message: localization.getMinimumErrorMessage(minimum), - instanceLocation: Instance.uri(instance), - schemaLocations: schemaLocations - }); - } - } - - return errors; -}; - -/** @type (pointer: string) => string */ -const pointerPop = (pointer) => pointer.replace(/\/[^/]+$/, ""); - -export default minimumErrorHandler; diff --git a/src/error-handlers/exclusiveMaximum.js b/src/error-handlers/exclusiveMaximum.js deleted file mode 100644 index 9b550d3..0000000 --- a/src/error-handlers/exclusiveMaximum.js +++ /dev/null @@ -1,32 +0,0 @@ -import { getSchema } from "@hyperjump/json-schema/experimental"; -import * as Schema from "@hyperjump/browser"; -import * as Instance from "@hyperjump/json-schema/instance/experimental"; - -/** - * @import { ErrorHandler, ErrorObject } from "../index.d.ts" - */ - -/** @type ErrorHandler */ -const exclusiveMaximumErrorHandler = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; - - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/exclusiveMaximum"]) { - if (normalizedErrors["https://json-schema.org/keyword/exclusiveMaximum"][schemaLocation]) { - continue; - } - - const keyword = await getSchema(schemaLocation); - const exclusiveMaximum = /** @type number */ (Schema.value(keyword)); - - errors.push({ - message: localization.getExclusiveMaximumErrorMessage(exclusiveMaximum), - instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] - }); - } - - return errors; -}; - -export default exclusiveMaximumErrorHandler; diff --git a/src/error-handlers/exclusiveMinimum.js b/src/error-handlers/exclusiveMinimum.js deleted file mode 100644 index 46d934d..0000000 --- a/src/error-handlers/exclusiveMinimum.js +++ /dev/null @@ -1,32 +0,0 @@ -import { getSchema } from "@hyperjump/json-schema/experimental"; -import * as Schema from "@hyperjump/browser"; -import * as Instance from "@hyperjump/json-schema/instance/experimental"; - -/** - * @import { ErrorHandler, ErrorObject } from "../index.d.ts" - */ - -/** @type ErrorHandler */ -const exclusiveMinimumErrorHandler = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; - - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/exclusiveMinimum"]) { - if (normalizedErrors["https://json-schema.org/keyword/exclusiveMinimum"][schemaLocation]) { - continue; - } - - const keyword = await getSchema(schemaLocation); - const exclusiveMinimum = /** @type number */ (Schema.value(keyword)); - - errors.push({ - message: localization.getExclusiveMinimumErrorMessage(exclusiveMinimum), - instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] - }); - } - - return errors; -}; - -export default exclusiveMinimumErrorHandler; diff --git a/src/error-handlers/maximum.js b/src/error-handlers/maximum.js index 67cb57d..ee8b62d 100644 --- a/src/error-handlers/maximum.js +++ b/src/error-handlers/maximum.js @@ -3,13 +3,16 @@ import * as Schema from "@hyperjump/browser"; import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** - * @import { ErrorHandler, ErrorObject } from "../index.d.ts" + * @import { ErrorHandler } from "../index.d.ts" */ /** @type ErrorHandler */ const maximumErrorHandler = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; + let lowestMaximum = Infinity; + let isExclusive = false; + + /** @type string[] */ + let schemaLocations = []; for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/maximum"]) { if (normalizedErrors["https://json-schema.org/keyword/maximum"][schemaLocation]) { @@ -18,15 +21,73 @@ const maximumErrorHandler = async (normalizedErrors, instance, localization) => const keyword = await getSchema(schemaLocation); const maximum = /** @type number */ (Schema.value(keyword)); + if (maximum < lowestMaximum) { + lowestMaximum = maximum; + schemaLocations = [schemaLocation]; + } + } - errors.push({ - message: localization.getMaximumErrorMessage(maximum), - instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] - }); + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/exclusiveMaximum"]) { + if (normalizedErrors["https://json-schema.org/keyword/exclusiveMaximum"][schemaLocation]) { + continue; + } + + const keyword = await getSchema(schemaLocation); + const exclusiveMaximum = /** @type number */ (Schema.value(keyword)); + if (exclusiveMaximum < lowestMaximum) { + lowestMaximum = exclusiveMaximum; + isExclusive = true; + schemaLocations = [schemaLocation]; + } + } + + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/maximum"]) { + if (normalizedErrors["https://json-schema.org/keyword/draft-04/maximum"][schemaLocation]) { + continue; + } + + const parentLocation = pointerPop(schemaLocation); + /** @type string */ + let exclusiveLocation = ""; + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/exclusiveMaximum"]) { + const exclusiveParentLocation = pointerPop(schemaLocation); + if (exclusiveParentLocation === parentLocation) { + const exclusiveNode = await getSchema(schemaLocation); + if (Schema.value(exclusiveNode)) { + exclusiveLocation = schemaLocation; + } + break; + } + } + + const keywordNode = await getSchema(schemaLocation); + const maximum = /** @type number */ (Schema.value(keywordNode)); + + if (maximum < lowestMaximum) { + lowestMaximum = maximum; + isExclusive = !!exclusiveLocation; + schemaLocations = exclusiveLocation ? [schemaLocation, exclusiveLocation] : [schemaLocation]; + } } - return errors; + if (lowestMaximum === Infinity) { + return []; + } else if (isExclusive) { + return [{ + message: localization.getExclusiveMaximumErrorMessage(lowestMaximum), + instanceLocation: Instance.uri(instance), + schemaLocations: schemaLocations + }]; + } else { + return [{ + message: localization.getMaximumErrorMessage(lowestMaximum), + instanceLocation: Instance.uri(instance), + schemaLocations: schemaLocations + }]; + } }; +/** @type (pointer: string) => string */ +const pointerPop = (pointer) => pointer.replace(/\/[^/]+$/, ""); + export default maximumErrorHandler; diff --git a/src/error-handlers/minimum.js b/src/error-handlers/minimum.js index 5d4fbf0..467836c 100644 --- a/src/error-handlers/minimum.js +++ b/src/error-handlers/minimum.js @@ -3,13 +3,15 @@ import * as Schema from "@hyperjump/browser"; import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** - * @import { ErrorHandler, ErrorObject } from "../index.d.ts" + * @import { ErrorHandler } from "../index.d.ts" */ /** @type ErrorHandler */ const minimumErrorHandler = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; + let highestMinimum = -Infinity; + let isExclusive = false; + /** @type string[] */ + let schemaLocations = []; for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/minimum"]) { if (normalizedErrors["https://json-schema.org/keyword/minimum"][schemaLocation]) { @@ -19,14 +21,73 @@ const minimumErrorHandler = async (normalizedErrors, instance, localization) => const keyword = await getSchema(schemaLocation); const minimum = /** @type number */ (Schema.value(keyword)); - errors.push({ - message: localization.getMinimumErrorMessage(minimum), - instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] - }); + if (minimum > highestMinimum) { + highestMinimum = minimum; + schemaLocations = [schemaLocation]; + } + } + + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/exclusiveMinimum"]) { + if ( + normalizedErrors["https://json-schema.org/keyword/exclusiveMinimum"][schemaLocation]) { + continue; + } + + const keyword = await getSchema(schemaLocation); + const exclusiveMinimum = /** @type number */ (Schema.value(keyword)); + + if (exclusiveMinimum > highestMinimum) { + highestMinimum = exclusiveMinimum; + isExclusive = true; + schemaLocations = [schemaLocation]; + } + } + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/minimum"]) { + if (normalizedErrors["https://json-schema.org/keyword/draft-04/minimum"][schemaLocation]) { + continue; + } + + const parentLocation = pointerPop(schemaLocation); + + let exclusiveLocation = ""; + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/exclusiveMinimum"]) { + const exclusiveParentLocation = pointerPop(schemaLocation); + if (exclusiveParentLocation === parentLocation) { + const exclusiveNode = await getSchema(schemaLocation); + if (Schema.value(exclusiveNode)) { + exclusiveLocation = schemaLocation; + } + break; + } + } + + const keywordNode = await getSchema(schemaLocation); + const minimum = /** @type number */ (Schema.value(keywordNode)); + if (minimum > highestMinimum) { + highestMinimum = minimum; + isExclusive = !!exclusiveLocation; + schemaLocations = exclusiveLocation ? [schemaLocation, exclusiveLocation] : [schemaLocation]; + } } - return errors; + if (highestMinimum === -Infinity) { + return []; + } else if (isExclusive) { + return [{ + message: localization.getExclusiveMinimumErrorMessage(highestMinimum), + instanceLocation: Instance.uri(instance), + schemaLocations: schemaLocations + }]; + } else { + return [{ + message: localization.getMinimumErrorMessage(highestMinimum), + instanceLocation: Instance.uri(instance), + schemaLocations: schemaLocations + }]; + } }; +/** @type (pointer: string) => string */ +const pointerPop = (pointer) => pointer.replace(/\/[^/]+$/, ""); + export default minimumErrorHandler; diff --git a/src/index.js b/src/index.js index c617f46..fc97319 100644 --- a/src/index.js +++ b/src/index.js @@ -58,15 +58,11 @@ import booleanSchemaErrorHandler from "./error-handlers/boolean-schema.js"; import constEnumErrorHandler from "./error-handlers/constEnum.js"; import containsErrorHandler from "./error-handlers/contains.js"; import dependenciesErrorHandler from "./error-handlers/draft-04/dependencies.js"; -import exclusiveMaximumErrorHandler from "./error-handlers/exclusiveMaximum.js"; -import exclusiveMinimumErrorHandler from "./error-handlers/exclusiveMinimum.js"; import formatErrorHandler from "./error-handlers/format.js"; -import maximumDraft04ErrorHandler from "./error-handlers/draft-04/maximum.js"; import maximumErrorHandler from "./error-handlers/maximum.js"; import maxItemsErrorHandler from "./error-handlers/maxItems.js"; import maxLengthErrorHandler from "./error-handlers/maxLength.js"; import maxPropertiesErrorHandler from "./error-handlers/maxProperties.js"; -import minimumDraft04ErrorHandler from "./error-handlers/draft-04/minimum.js"; import minimumErrorHandler from "./error-handlers/minimum.js"; import minItemsErrorHandler from "./error-handlers/minItems.js"; import minLengthErrorHandler from "./error-handlers/minLength.js"; @@ -142,15 +138,11 @@ addErrorHandler(booleanSchemaErrorHandler); addErrorHandler(constEnumErrorHandler); addErrorHandler(containsErrorHandler); addErrorHandler(dependenciesErrorHandler); -addErrorHandler(exclusiveMaximumErrorHandler); -addErrorHandler(exclusiveMinimumErrorHandler); addErrorHandler(formatErrorHandler); -addErrorHandler(maximumDraft04ErrorHandler); addErrorHandler(maximumErrorHandler); addErrorHandler(maxItemsErrorHandler); addErrorHandler(maxLengthErrorHandler); addErrorHandler(maxPropertiesErrorHandler); -addErrorHandler(minimumDraft04ErrorHandler); addErrorHandler(minimumErrorHandler); addErrorHandler(minItemsErrorHandler); addErrorHandler(minLengthErrorHandler); diff --git a/src/test-suite/tests/maximum.json b/src/test-suite/tests/maximum.json index 201f8a6..be20462 100644 --- a/src/test-suite/tests/maximum.json +++ b/src/test-suite/tests/maximum.json @@ -86,6 +86,81 @@ }, "instance": 2, "errors": [] + }, + { + "description": "maximum with multiple constraints (lowest value considered)", + "schema": { + "allOf": [{ "maximum": 3 }, { "maximum": 2 }] + }, + "instance": 6, + "errors": [ + { + "messageId": "maximum-message", + "messageParams": { + "maximum": "2" + }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/1/maximum"] + } + ] + }, + { + "description": "exclusiveMaximum with multiple constraints (lowest value considered)", + "compatibility": "6", + "schema": { + "allOf": [{ "exclusiveMaximum": 3 }, { "exclusiveMaximum": 2 }] + }, + "instance": 6, + "errors": [ + { + "messageId": "exclusiveMaximum-message", + "messageParams": { + "exclusiveMaximum": "2" + }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/1/exclusiveMaximum"] + } + ] + }, + { + "description": "combined maximum and exclusive maximum with draft04 compatiblity", + "compatibility": "=4", + "schema": { + "allOf": [ + { "maximum": 2, "exclusiveMaximum": true }, + { "maximum": 4 } + ] + }, + "instance": 5, + "errors": [ + { + "messageId": "exclusiveMaximum-message", + "messageParams": { + "exclusiveMaximum": "2" + }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/0/maximum","#/allOf/0/exclusiveMaximum"] + } + ] + }, + { + "description": "combined maximum and exclusive maximum ", + "compatibility": "6", + "schema": { + "allOf": [{ "maximum": 3 }, { "maximum": 2 }, { "exclusiveMaximum": 5 }] + }, + + "instance": 6, + "errors": [ + { + "messageId": "maximum-message", + "messageParams": { + "maximum": "2" + }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/1/maximum"] + } + ] } ] } diff --git a/src/test-suite/tests/minimum.json b/src/test-suite/tests/minimum.json index 6b11109..8b7252a 100644 --- a/src/test-suite/tests/minimum.json +++ b/src/test-suite/tests/minimum.json @@ -86,6 +86,78 @@ }, "instance": 4, "errors": [] + }, + { + "description": "minimum with multiple constraints (highest value considered)", + "schema": { + "allOf": [{ "minimum": 5 }, { "minimum": 3 }] + }, + "instance": 1, + "errors": [ + { + "messageId": "minimum-message", + "messageParams": { + "minimum": "5" + }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/0/minimum"] + } + ] + }, + { + "description": "exclusiveMinimum with multiple constraints (highest value considered)", + "compatibility": "6", + "schema": { + "allOf": [{ "exclusiveMinimum": 4 }, { "exclusiveMinimum": 6 }] + }, + "instance": 3, + "errors": [ + { + "messageId": "exclusiveMinimum-message", + "messageParams": { + "exclusiveMinimum": "6" + }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/1/exclusiveMinimum"] + } + ] + }, + { + "description": "combined minimum and exclusive minimum with draft04 compatiblity", + "compatibility": "=4", + "schema": { + "allOf": [{ "minimum": 4, "exclusiveMinimum": true }, { "minimum": 3 }] + }, + "instance": 2, + "errors": [ + { + "messageId": "exclusiveMinimum-message", + "messageParams": { + "exclusiveMinimum": "4" + }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/0/minimum","#/allOf/0/exclusiveMinimum"] + } + ] + }, + { + "description": "combined minimum and exclusive minimum ", + "compatibility": "6", + "schema": { + "allOf": [{ "minimum": 4 }, { "minimum": 7 }, { "exclusiveMinimum": 5 }] + }, + + "instance": 2, + "errors": [ + { + "messageId": "minimum-message", + "messageParams": { + "minimum": "7" + }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/1/minimum"] + } + ] } ] }