Skip to content

Commit e8dbcac

Browse files
Sam-61sjdesrosiers
andauthored
Combine required and dependentRequired error messages (#160)
* combined required and dependentRequired error messages * unified error handler for each keyword * code style is fixed * Update code style * Schema AST function is now used in all interations * clean up is done * helper function is positioned at bottom now * Some cleanup --------- Co-authored-by: Jason Desrosiers <jdesrosi@gmail.com>
1 parent a362ad4 commit e8dbcac

6 files changed

Lines changed: 96 additions & 104 deletions

File tree

src/error-handlers/dependentRequired.js

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/error-handlers/draft-04/dependencies.js

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import { getSchema } from "@hyperjump/json-schema/experimental";
2-
import * as Schema from "@hyperjump/browser";
3-
import * as Instance from "@hyperjump/json-schema/instance/experimental";
41
import { getErrors } from "../../json-schema-errors.js";
52

63
/**
@@ -22,25 +19,6 @@ const dependenciesErrorHandler = async (normalizedErrors, instance, localization
2219
const dependentSchemaErrors = await getErrors(dependentSchemaOutput, instance, localization);
2320
errors.push(...dependentSchemaErrors);
2421
}
25-
26-
const dependencies = await getSchema(schemaLocation);
27-
for await (const [propertyName, dependency] of Schema.entries(dependencies)) {
28-
if (!Instance.has(propertyName, instance)) {
29-
continue;
30-
}
31-
32-
if (Schema.typeOf(dependency) !== "array") {
33-
continue;
34-
}
35-
36-
const dependentRequired = /** @type {string[]} */ (Schema.value(dependency));
37-
const missing = dependentRequired.filter((required) => !Instance.has(required, instance));
38-
errors.push({
39-
message: localization.getRequiredErrorMessage(missing),
40-
instanceLocation: Instance.uri(instance),
41-
schemaLocations: [schemaLocation]
42-
});
43-
}
4422
}
4523

4624
return errors;

src/error-handlers/required.js

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,87 @@ import * as Schema from "@hyperjump/browser";
33
import * as Instance from "@hyperjump/json-schema/instance/experimental";
44

55
/**
6-
* @import { ErrorHandler, ErrorObject } from "../index.d.ts"
6+
* @import { ErrorHandler } from "../index.d.ts"
7+
* @import { JsonNode } from "@hyperjump/json-schema/instance/experimental"
78
*/
89

910
/** @type ErrorHandler */
1011
const requiredErrorHandler = async (normalizedErrors, instance, localization) => {
11-
/** @type ErrorObject[] */
12-
const errors = [];
12+
/** @type {Set<string>} */
13+
const allMissingRequired = new Set();
14+
const allSchemaLocations = [];
1315

1416
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/required"]) {
1517
if (normalizedErrors["https://json-schema.org/keyword/required"][schemaLocation]) {
1618
continue;
1719
}
1820

21+
allSchemaLocations.push(schemaLocation);
1922
const keyword = await getSchema(schemaLocation);
2023
const required = /** @type string[] */ (Schema.value(keyword));
2124

22-
const missingRequired = [];
23-
for (const propertyName of required) {
25+
addMissingProperties(required, instance, allMissingRequired);
26+
}
27+
28+
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) {
29+
if (normalizedErrors["https://json-schema.org/keyword/dependentRequired"][schemaLocation]) {
30+
continue;
31+
}
32+
33+
allSchemaLocations.push(schemaLocation);
34+
const keyword = await getSchema(schemaLocation);
35+
36+
for await (const [propertyName, dependencyNode] of Schema.entries(keyword)) {
2437
if (!Instance.has(propertyName, instance)) {
25-
missingRequired.push(propertyName);
38+
continue;
2639
}
40+
41+
const requiredProperties = /** @type string[] */ (Schema.value(dependencyNode));
42+
addMissingProperties(requiredProperties, instance, allMissingRequired);
43+
}
44+
}
45+
46+
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/dependencies"]) {
47+
if (typeof normalizedErrors["https://json-schema.org/keyword/draft-04/dependencies"][schemaLocation] === "boolean") {
48+
continue;
2749
}
2850

29-
errors.push({
30-
message: localization.getRequiredErrorMessage(missingRequired),
31-
instanceLocation: Instance.uri(instance),
32-
schemaLocations: [schemaLocation]
33-
});
51+
const keyword = await getSchema(schemaLocation);
52+
53+
let hasArrayFormDependencies = false;
54+
for await (const [propertyName, dependency] of Schema.entries(keyword)) {
55+
if (!Instance.has(propertyName, instance) || Schema.typeOf(dependency) !== "array") {
56+
continue;
57+
}
58+
59+
hasArrayFormDependencies = true;
60+
const dependencyArray = /** @type {string[]} */ (Schema.value(dependency));
61+
addMissingProperties(dependencyArray, instance, allMissingRequired);
62+
}
63+
64+
if (hasArrayFormDependencies) {
65+
allSchemaLocations.push(schemaLocation);
66+
}
3467
}
3568

36-
return errors;
69+
if (allMissingRequired.size === 0) {
70+
return [];
71+
}
72+
73+
return [{
74+
message: localization.getRequiredErrorMessage([...allMissingRequired]),
75+
instanceLocation: Instance.uri(instance),
76+
schemaLocations: /** @type {string[]} */ ([...allSchemaLocations])
77+
}];
78+
};
79+
80+
/** @type (requiredProperties: string[], instance: JsonNode, missingSet: Set<string>) => void */
81+
const addMissingProperties = (requiredProperties, instance, missingSet) => {
82+
for (const propertyName of requiredProperties) {
83+
if (!Instance.has(propertyName, instance)) {
84+
missingSet.add(propertyName);
85+
}
86+
}
3787
};
3888

3989
export default requiredErrorHandler;

src/index.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ import booleanSchemaErrorHandler from "./error-handlers/boolean-schema.js";
5858
import constErrorHandler from "./error-handlers/const.js";
5959
import containsErrorHandler from "./error-handlers/contains.js";
6060
import dependenciesErrorHandler from "./error-handlers/draft-04/dependencies.js";
61-
import dependentRequiredErrorHandler from "./error-handlers/dependentRequired.js";
6261
import enumErrorHandler from "./error-handlers/enum.js";
6362
import exclusiveMaximumErrorHandler from "./error-handlers/exclusiveMaximum.js";
6463
import exclusiveMinimumErrorHandler from "./error-handlers/exclusiveMinimum.js";
@@ -144,7 +143,6 @@ addErrorHandler(booleanSchemaErrorHandler);
144143
addErrorHandler(constErrorHandler);
145144
addErrorHandler(containsErrorHandler);
146145
addErrorHandler(dependenciesErrorHandler);
147-
addErrorHandler(dependentRequiredErrorHandler);
148146
addErrorHandler(enumErrorHandler);
149147
addErrorHandler(exclusiveMaximumErrorHandler);
150148
addErrorHandler(exclusiveMinimumErrorHandler);

src/test-suite/tests/dependencies.json

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,8 @@
4747
{
4848
"messageId": "required-message",
4949
"messageParams": {
50-
"count": 1,
51-
"required": { "and": ["baz"] }
52-
},
53-
"instanceLocation": "#",
54-
"schemaLocations": ["#/dependencies"]
55-
},
56-
{
57-
"messageId": "required-message",
58-
"messageParams": {
59-
"count": 1,
60-
"required": { "and": ["bbb"] }
50+
"count": 2,
51+
"required": { "and": ["baz", "bbb"] }
6152
},
6253
"instanceLocation": "#",
6354
"schemaLocations": ["#/dependencies"]
@@ -110,20 +101,14 @@
110101
{
111102
"messageId": "required-message",
112103
"messageParams": {
113-
"count": 1,
114-
"required": { "and": ["baz"] }
115-
},
116-
"instanceLocation": "#",
117-
"schemaLocations": ["#/dependentSchemas/foo/required"]
118-
},
119-
{
120-
"messageId": "required-message",
121-
"messageParams": {
122-
"count": 1,
123-
"required": { "and": ["bbb"] }
104+
"count": 2,
105+
"required": { "and": ["baz", "bbb"] }
124106
},
125107
"instanceLocation": "#",
126-
"schemaLocations": ["#/dependentSchemas/aaa/required"]
108+
"schemaLocations": [
109+
"#/dependentSchemas/foo/required",
110+
"#/dependentSchemas/aaa/required"
111+
]
127112
}
128113
]
129114
},

src/test-suite/tests/required.json

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,38 @@
1414
"messageId": "required-message",
1515
"messageParams": {
1616
"count": 1,
17-
"required": { "or": ["bar"] }
17+
"required": { "and": ["bar"] }
1818
},
1919
"instanceLocation": "#",
2020
"schemaLocations": ["#/required"]
2121
}
2222
]
2323
},
24+
{
25+
"description": "combined required and dependentRequired",
26+
"compatibility": "2019",
27+
"schema": {
28+
"required": ["foo", "bar"],
29+
"dependentRequired": {
30+
"a": ["b"]
31+
}
32+
},
33+
"instance": { "a": 1 },
34+
"errors": [
35+
{
36+
"messageId": "required-message",
37+
"messageParams": {
38+
"count": 3,
39+
"required": { "and": ["foo", "bar", "b"] }
40+
},
41+
"instanceLocation": "#",
42+
"schemaLocations": [
43+
"#/required",
44+
"#/dependentRequired"
45+
]
46+
}
47+
]
48+
},
2449
{
2550
"description": "required pass",
2651
"schema": {

0 commit comments

Comments
 (0)