From 3a3be1aac6f40dc91994073dff6cff37973fa8a3 Mon Sep 17 00:00:00 2001 From: Arpit Kuriyal Date: Tue, 12 Aug 2025 23:29:32 +0530 Subject: [PATCH] combine errorMessage of required and dependentRequired --- src/error-handlers/dependentRequired.js | 40 ------------------- src/error-handlers/required.js | 51 ++++++++++++++++++++----- src/index.js | 2 - src/keyword-error-message.test.js | 12 ++++-- src/localization.js | 13 +------ src/translations/en-US.ftl | 3 +- 6 files changed, 52 insertions(+), 69 deletions(-) delete mode 100644 src/error-handlers/dependentRequired.js diff --git a/src/error-handlers/dependentRequired.js b/src/error-handlers/dependentRequired.js deleted file mode 100644 index 22aa7ae..0000000 --- a/src/error-handlers/dependentRequired.js +++ /dev/null @@ -1,40 +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 dependentRequired = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; - - if (normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) { - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) { - if (!normalizedErrors["https://json-schema.org/keyword/dependentRequired"][schemaLocation]) { - const keyword = await getSchema(schemaLocation); - const dependentRequired = /** @type {Record} */(Schema.value(keyword)); - for (const propertyName in dependentRequired) { - if (Instance.has(propertyName, instance)) { - const required = dependentRequired[propertyName]; - const missing = required.filter((prop) => !Instance.has(prop, instance)); - - if (missing.length > 0) { - errors.push({ - message: localization.getDependentRequiredErrorMessage(propertyName, [...missing]), - instanceLocation: Instance.uri(instance), - schemaLocation: schemaLocation - }); - } - } - } - } - } - } - - return errors; -}; - -export default dependentRequired; diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index 680f43b..bb137ff 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -3,33 +3,64 @@ 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 required = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; + /** @type Set */ + const required = new Set(); + + /** @type string[] */ + const failedSchemaLocations = []; if (normalizedErrors["https://json-schema.org/keyword/required"]) { for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/required"]) { if (!normalizedErrors["https://json-schema.org/keyword/required"][schemaLocation]) { + failedSchemaLocations.push(schemaLocation); const keyword = await getSchema(schemaLocation); /** @type Set */ - const required = new Set(Schema.value(keyword)); + for (const propertyName of /** @type string[] */ (Schema.value(keyword))) { + required.add(propertyName); + } for (const propertyName in Instance.value(instance)) { required.delete(propertyName); } - errors.push({ - message: localization.getRequiredErrorMessage(Instance.uri(instance), [...required]), - instanceLocation: Instance.uri(instance), - schemaLocation: schemaLocation - }); } } } - return errors; + if (normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) { + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) { + if (!normalizedErrors["https://json-schema.org/keyword/dependentRequired"][schemaLocation]) { + const keyword = await getSchema(schemaLocation); + const dependentRequired = /** @type {Record} */(Schema.value(keyword)); + for (const propertyName in dependentRequired) { + if (Instance.has(propertyName, instance)) { + failedSchemaLocations.push(schemaLocation); + for (const requiredPropertyName of dependentRequired[propertyName]) { + required.add(requiredPropertyName); + } + for (const propertyName in Instance.value(instance)) { + required.delete(propertyName); + } + } + } + } + } + } + + if (required.size > 0) { + return [ + { + message: localization.getRequiredErrorMessage([...required]), + instanceLocation: Instance.uri(instance), + schemaLocation: failedSchemaLocations + } + ]; + } else { + return []; + } }; export default required; diff --git a/src/index.js b/src/index.js index afad914..1b34ee5 100644 --- a/src/index.js +++ b/src/index.js @@ -49,7 +49,6 @@ import additionalPropertiesErrorHandler from "./error-handlers/additionalPropert import arrayRangeErrorHandler from "./error-handlers/array-range-handler.js"; import constErrorHandler from "./error-handlers/const.js"; import containsErrorHandler from "./error-handlers/contains.js"; -import dependentRequiredErrorHandler from "./error-handlers/dependentRequired.js"; import enumErrorHandler from "./error-handlers/enum.js"; import formatErrorHandler from "./error-handlers/format.js"; import multipleOfErrorHandler from "./error-handlers/multipleOf.js"; @@ -117,7 +116,6 @@ addErrorHandler(anyOfErrorHandler); addErrorHandler(additionalPropertiesErrorHandler); addErrorHandler(constErrorHandler); addErrorHandler(containsErrorHandler); -addErrorHandler(dependentRequiredErrorHandler); addErrorHandler(enumErrorHandler); addErrorHandler(formatErrorHandler); addErrorHandler(arrayRangeErrorHandler); diff --git a/src/keyword-error-message.test.js b/src/keyword-error-message.test.js index ffbbd03..9a30f5d 100644 --- a/src/keyword-error-message.test.js +++ b/src/keyword-error-message.test.js @@ -338,9 +338,11 @@ describe("Error messages", async () => { const result = await betterJsonSchemaErrors(output, schemaUri, instance); expect(result.errors).to.eql([{ - schemaLocation: "https://example.com/main#/required", + schemaLocation: [ + "https://example.com/main#/required" + ], instanceLocation: "#", - message: localization.getRequiredErrorMessage("#", ["baz"]) + message: localization.getRequiredErrorMessage(["baz"]) }]); }); @@ -1563,8 +1565,10 @@ describe("Error messages", async () => { expect(result.errors).to.eql([ { instanceLocation: "#", - message: localization.getDependentRequiredErrorMessage("foo", ["baz"]), - schemaLocation: "https://example.com/main#/dependentRequired" + message: localization.getRequiredErrorMessage(["baz"]), + schemaLocation: [ + "https://example.com/main#/dependentRequired" + ] } ]); }); diff --git a/src/localization.js b/src/localization.js index e246596..746d635 100644 --- a/src/localization.js +++ b/src/localization.js @@ -137,10 +137,9 @@ export class Localization { }); } - /** @type (instanceLocation: string, missingProperties: string[]) => string */ - getRequiredErrorMessage(instanceLocation, missingProperties) { + /** @type (missingProperties: string[]) => string */ + getRequiredErrorMessage(missingProperties) { return this._formatMessage("required-error", { - instanceLocation, missingProperties: new Intl.ListFormat(this.locale, { type: "conjunction" }).format(missingProperties) }); } @@ -225,14 +224,6 @@ export class Localization { return this._formatMessage("additional-properties-error", { propertyName }); } - /** @type (property: string, missingDependents: string[]) => string */ - getDependentRequiredErrorMessage(property, missingDependents) { - return this._formatMessage("dependent-required-error", { - property, - missingDependents: new Intl.ListFormat(this.locale, { type: "conjunction" }).format(missingDependents) - }); - } - /** * @typedef {Object} EnumSuggestionArgs * @property {"suggestion"} variant diff --git a/src/translations/en-US.ftl b/src/translations/en-US.ftl index b210cd8..057d26b 100644 --- a/src/translations/en-US.ftl +++ b/src/translations/en-US.ftl @@ -10,7 +10,7 @@ number-error-exclusive-minimum = greater than or equal to {$minimum} number-error-maximum = less than {$maximum} number-error-exclusive-maximum = less than or equal to {$maximum} -required-error = "{$instanceLocation}" is missing required property(s): {$missingProperties}. +required-error = This instance is missing required property(s): {$missingProperties}. multiple-of-error = The instance should be a multiple of {$divisor}. properties-error = Expected object to have {$constraints} @@ -38,7 +38,6 @@ contains-error-min-max = The array must contain at least {$minContains} and at m not-error = The instance is not allowed to be used in this schema. additional-properties-error = The property "{$propertyName}" is not allowed. -dependent-required-error = Property "{$property}" requires property(s): {$missingDependents}. enum-error = { $variant -> [suggestion] Unexpected value {$instanceValue}. Did you mean {$suggestion}? *[fallback] Unexpected value {$instanceValue}. Expected one of: {$allowedValues}.