Skip to content

Commit 12ad849

Browse files
authored
Merge pull request #40 from arpitkuriyal/keywordHandlers
Keyword handlers: Added four new keywordHandlers
2 parents e6e0886 + fc10ba3 commit 12ad849

File tree

7 files changed

+416
-55
lines changed

7 files changed

+416
-55
lines changed

package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"vitest": "*"
3838
},
3939
"dependencies": {
40+
"@fluent/bundle": "^0.19.1",
4041
"@hyperjump/browser": "^1.3.1",
4142
"@hyperjump/json-schema": "^1.16.0",
4243
"leven": "^4.0.0"

src/index.js

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { getSchema } from "@hyperjump/json-schema/experimental";
33
import * as Instance from "@hyperjump/json-schema/instance/experimental";
44
import leven from "leven";
55
import { normalizedErrorOuput } from "./normalizeOutputFormat/normalizeOutput.js";
6+
import { Localization } from "./localization.js";
67

78
/**
89
* @import { Browser } from "@hyperjump/browser";
@@ -13,6 +14,8 @@ import { normalizedErrorOuput } from "./normalizeOutputFormat/normalizeOutput.js
1314
* @import { NormalizedOutput, InstanceOutput } from "./normalizeOutputFormat/normalizeOutput.js"
1415
*/
1516

17+
const localization = await Localization.forLocale("en-US");
18+
1619
/** @type betterJsonSchemaErrors */
1720
export async function betterJsonSchemaErrors(instance, errorOutput, schemaUri) {
1821
const normalizedErrors = await normalizedErrorOuput(instance, errorOutput, schemaUri);
@@ -76,7 +79,7 @@ const errorHandlers = [
7679
}
7780
}
7881
errors.push({
79-
message: `The instance must be a ${[...expectedTypes].join(", ")}. Found '${Instance.typeOf(instance)}'.`,
82+
message: localization.getTypeErrorMessage([...expectedTypes], Instance.typeOf(instance)),
8083
instanceLocation: Instance.uri(instance),
8184
schemaLocation: schemaLocation
8285
});
@@ -148,7 +151,7 @@ const errorHandlers = [
148151
if (!normalizedErrors["https://json-schema.org/keyword/type"][schemaLocation]) {
149152
const keyword = await getSchema(schemaLocation);
150153
errors.push({
151-
message: `The instance should be of type "${Schema.value(keyword)}" but found "${Instance.typeOf(instance)}".`,
154+
message: localization.getTypeErrorMessage(Schema.value(keyword), Instance.typeOf(instance)),
152155
instanceLocation: Instance.uri(instance),
153156
schemaLocation: schemaLocation
154157
});
@@ -490,48 +493,91 @@ const errorHandlers = [
490493
return errors;
491494
},
492495

493-
// eslint-disable-next-line @typescript-eslint/require-await
494496
async (normalizedErrors, instance) => {
495497
/** @type ErrorObject[] */
496498
const errors = [];
497-
498499
if (normalizedErrors["https://json-schema.org/keyword/contains"]) {
499500
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/contains"]) {
500-
// const keyword = await getSchema(schemaLocation);
501501
errors.push({
502-
message: `TODO - contains`,
502+
message: `A required value is missing from the list`,
503503
instanceLocation: Instance.uri(instance),
504504
schemaLocation: schemaLocation
505505
});
506+
const containsNodes = /** @type NormalizedOutput[] */(normalizedErrors["https://json-schema.org/keyword/contains"][schemaLocation]);
507+
for (const errorOutput of containsNodes) {
508+
const containsSubErrors = await getErrors(errorOutput, instance);
509+
errors.push(...containsSubErrors);
510+
}
511+
}
512+
}
513+
514+
return errors;
515+
},
516+
517+
// eslint-disable-next-line @typescript-eslint/require-await
518+
async (normalizedErrors, instance) => {
519+
/** @type ErrorObject[] */
520+
const errors = [];
521+
522+
if (normalizedErrors["https://json-schema.org/keyword/not"]) {
523+
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/not"]) {
524+
errors.push({
525+
message: `The instance is not allowed to be used in this schema.`,
526+
instanceLocation: Instance.uri(instance),
527+
schemaLocation: schemaLocation
528+
});
529+
}
530+
}
531+
532+
return errors;
533+
},
534+
535+
// eslint-disable-next-line @typescript-eslint/require-await
536+
async (normalizedErrors, instance) => {
537+
/** @type ErrorObject[] */
538+
const errors = [];
539+
if (normalizedErrors["https://json-schema.org/validation"]) {
540+
for (const schemaLocation in normalizedErrors["https://json-schema.org/validation"]) {
541+
if (!normalizedErrors["https://json-schema.org/validation"][schemaLocation] && schemaLocation.endsWith("/additionalProperties")) {
542+
const notAllowedValue = Instance.uri(instance).split("/").pop();
543+
errors.push({
544+
message: `The property "${notAllowedValue}" is not allowed.`,
545+
instanceLocation: Instance.uri(instance),
546+
schemaLocation: schemaLocation
547+
});
548+
}
549+
}
550+
}
551+
return errors;
552+
},
553+
554+
async (normalizedErrors, instance) => {
555+
/** @type ErrorObject[] */
556+
const errors = [];
557+
558+
if (normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) {
559+
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) {
560+
if (!normalizedErrors["https://json-schema.org/keyword/dependentRequired"][schemaLocation]) {
561+
const keyword = await getSchema(schemaLocation);
562+
const dependentRequired = /** @type {Record<string, string[]>} */(Schema.value(keyword));
563+
for (const propertyName in dependentRequired) {
564+
if (Instance.has(propertyName, instance)) {
565+
const required = dependentRequired[propertyName];
566+
const missing = required.filter((prop) => !Instance.has(prop, instance));
567+
568+
if (missing.length > 0) {
569+
errors.push({
570+
message: `Property "${propertyName}" requires property(s): ${missing.join(", ")}.`,
571+
instanceLocation: Instance.uri(instance),
572+
schemaLocation: schemaLocation
573+
});
574+
}
575+
}
576+
}
577+
}
506578
}
507579
}
508580

509581
return errors;
510582
}
511583
];
512-
513-
// /** @type (value: Json) => "null" | "boolean" | "number" | "string" | "array" | "object" | "undefined" */
514-
// const jsonTypeOf = (value) => {
515-
// const jsType = typeof value;
516-
517-
// switch (jsType) {
518-
// case "number":
519-
// case "string":
520-
// case "boolean":
521-
// case "undefined":
522-
// return jsType;
523-
// case "object":
524-
// if (Array.isArray(value)) {
525-
// return "array";
526-
// } else if (value === null) {
527-
// return "null";
528-
// } else if (Object.getPrototypeOf(value) === Object.prototype) {
529-
// return "object";
530-
// }
531-
// default: {
532-
// // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
533-
// const type = jsType === "object" ? Object.getPrototypeOf(value).constructor.name ?? "anonymous" : jsType;
534-
// throw Error(`Not a JSON compatible type: ${type}`);
535-
// }
536-
// }
537-
// };

0 commit comments

Comments
 (0)