Combine required and dependentRequired error messages#160
Combine required and dependentRequired error messages#160jdesrosiers merged 8 commits intohyperjump-io:mainfrom
Conversation
jdesrosiers
left a comment
There was a problem hiding this comment.
Try again. You should end up with one error handler that handles required, dependentRequired, and the array form of dependencies. There doesn't need to be a separate error handler for each keyword. They're defined separately specifically for that reason.
|
Thanks for the clarification. I plan to unify The unified handler will consolidate missing properties into a single Does that match your expectation? |
|
Yep. That sounds right. |
|
I’ve updated the PR based on your guidance. There is now a single unified error handler in required.js that handles required, dependentRequired, and the array form of dependencies. |
jdesrosiers
left a comment
There was a problem hiding this comment.
Looks good so far. I left a few comments inline, but there's one more thing I'd like you to update in general. When contributing to project, you should try to match the code style used in the project. The way you did it isn't wrong or bad, but it looks significantly different than the rest of the error handlers. It can make it harder to review when the patterns are different. So, I'd like you to rework this a bit so it looks more like other error handlers in the repo. This includes the JSON files. Especially avoid making a lot of unnecessary whitespace changes (like in dependencies.json).
There was a problem hiding this comment.
Pull request overview
This PR consolidates validation errors that previously produced multiple required-message entries when required and dependentRequired (and some dependency cases) fail at the same instance location, deduplicating the missing property list while preserving schema locations.
Changes:
- Update
requirederror handling to aggregate missing properties acrossrequired,dependentRequired, and array-form draft-04dependenciesinto a singlerequired-message. - Remove the standalone
dependentRequirederror handler and remove array-formdependenciesrequired-message generation from the draft-04dependencieserror handler. - Update/expand the JSON test-suite expectations to cover the consolidated-message behavior (including the reported issue scenario).
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/test-suite/tests/required.json | Adjusts existing required expectations and adds a new combined required+dependentRequired test case. |
| src/test-suite/tests/dependencies.json | Updates expected errors to reflect consolidated required-message behavior for dependentRequired/array-form dependencies and some dependentSchemas cases. |
| src/normalization-handlers/draft-04/dependencies.js | Minor cleanup (removes blank lines) in dependencies normalization handler. |
| src/index.js | Unregisters the removed dependentRequired error handler. |
| src/error-handlers/required.js | Implements combined missing-property aggregation across required/dependentRequired/dependencies. |
| src/error-handlers/draft-04/dependencies.js | Removes array-form dependency required-message generation; keeps dependent-schema traversal behavior. |
| src/error-handlers/dependentRequired.js | Deletes the standalone dependentRequired error handler (logic moved into required handler). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
13273dd to
6b96e42
Compare
|
I'm curious, did you ask Copilot for a review, or did it happen automatically? |
|
Yep — that one’s on me |
|
No worries. I just wanted to make sure it wasn't something new Github was doing that I needed to figure out how to turn off. I don't mind Copilot reviews, but only when it's asked for, not automatically. It can be helpful sometimes, but it really did a terrible job this time. |
|
I just pushed a commit to show what I mean by matching the code style used in the project. I'll follow up with a review shortly. |
src/error-handlers/required.js
Outdated
| } | ||
|
|
||
| const keyword = await getSchema(schemaLocation); | ||
| const dependencies = /** @type Record<string, string[] | object> */ (Schema.value(keyword)); |
There was a problem hiding this comment.
Any time the value could be a schema (even if we're ignoring that case like we are here), you should be using the Schema AST functions to traverse the schema. In this case, you'll want to use Schema.entries(keyword) to iterate over the dependencies. Then when you know it's an array (Schema.typeOf), then you can get the value.
src/error-handlers/required.js
Outdated
| /** @type {string[]} */ | ||
| const missingProperties = [...allMissingRequired].sort(); | ||
| /** @type {string[]} */ | ||
| const locations = [...allSchemaLocations].sort(); |
There was a problem hiding this comment.
There shouldn't be any reason to sort these.
Thanks, I understand now. I’ll make sure to follow the project’s code style in future contributions. |
jdesrosiers
left a comment
There was a problem hiding this comment.
Looks good. I just have a couple of small clean up things I'd like you to have a look at.
src/error-handlers/required.js
Outdated
| if (Schema.typeOf(dependencyNode) !== "array") { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
This is depndentRequired. It's always an array. You don't need to check.
src/error-handlers/required.js
Outdated
| if (!Instance.has(propertyName, instance)) { | ||
| continue; | ||
| } | ||
|
|
||
| if (Schema.typeOf(dependency) !== "array") { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
I think this can be combined to a single expression.
src/error-handlers/required.js
Outdated
| /** @type {string[]} */ | ||
| const missingProperties = [...allMissingRequired]; | ||
| /** @type {string[]} */ | ||
| const locations = [...allSchemaLocations]; |
There was a problem hiding this comment.
There doesn't seem to be any reason for these to be variables. They can be inlined without losing any functionality or clarity.
src/error-handlers/required.js
Outdated
| for (const requiredPropertyName of requiredProperties) { | ||
| if (!Instance.has(requiredPropertyName, instance)) { | ||
| allMissingRequired.add(requiredPropertyName); | ||
| } | ||
| } |
There was a problem hiding this comment.
This exact block is repeated for each keyword. Is there a better way we could handle this so there's less duplication? If not, that's ok. Sometimes duplication is worth the trade-offs. I just want to make sure we've considered those trade-offs.
e9ea194 to
b29b3a2
Compare
src/error-handlers/required.js
Outdated
| /** | ||
| * @param {string[]} requiredProperties | ||
| * @param {import("@hyperjump/json-schema/instance/experimental").JsonNode} instance | ||
| * @param {Set<string>} missingSet | ||
| */ | ||
| const addMissingProperties = (requiredProperties, instance, missingSet) => { | ||
| for (const propertyName of requiredProperties) { | ||
| if (!Instance.has(propertyName, instance)) { | ||
| missingSet.add(propertyName); | ||
| } | ||
| } | ||
| }; |
There was a problem hiding this comment.
This isn't what I had in mind, but it's a good solution. Except, the function shouldn't be declared inside another function unless it needs to be a closure. So, let's move this out to the top level and let's put it at the bottom. I like the have help functions below the main code, not at the top.
e9ad103 to
6f0d7a2
Compare
When
requiredanddependentRequiredfail at the same instance location, they previously produced multiple duplicate messages. This change consolidates those failures into a singlerequired-message, combining and deduplicating missing properties while preserving schema locations.Adds test coverage for the original issue scenario and keeps existing
dependentRequiredand dependency behavior unchanged.Fixes #134