Skip to content

Commit a54f90d

Browse files
committed
added keywordHandlers and update normalizedErrorOuput format
1 parent 12d2e54 commit a54f90d

File tree

3 files changed

+309
-56
lines changed

3 files changed

+309
-56
lines changed

src/index.js

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,19 @@ const errorHandlers = [
8080
instanceLocation: Instance.uri(instance),
8181
schemaLocation: schemaLocation
8282
});
83-
} else if (alternatives.length === 1) {
83+
} else if (alternatives.length === 1) { // case 2 when only one type match
8484
return getErrors(alternatives[0], instance);
85+
} else if (instance.type === "object") {
86+
let targetAlternativeIndex = -1;
87+
for (const alternative of alternatives) {
88+
targetAlternativeIndex++;
89+
for (const instanceLocation in alternative) {
90+
if (instanceLocation !== "#") {
91+
return getErrors(alternatives[targetAlternativeIndex], instance);
92+
}
93+
}
94+
}
8595
}
86-
87-
// const anyOfSchema = await getSchema(schemaLocation);
88-
// const numberOfAlternatives = Schema.length(anyOfSchema);
89-
// Instance.typeOf(instance);
90-
// const instance = Instance.fromJs(instance)
91-
// if(numberOfAlternatives == )
92-
// errors.push({
93-
// message: `The instance should be at least ${Schema.value(keyword)} characters`,
94-
// instanceLocation: Instance.uri(instance),
95-
// schemaLocation: schemaLocation
96-
// });
9796
}
9897
}
9998

@@ -470,6 +469,7 @@ const errorHandlers = [
470469

471470
return errors;
472471
},
472+
473473
async (normalizedErrors, instance) => {
474474
/** @type ErrorObject[] */
475475
const errors = [];
@@ -488,31 +488,50 @@ const errorHandlers = [
488488
}
489489

490490
return errors;
491-
}
492-
];
491+
},
493492

494-
/** @type (value: Json) => "null" | "boolean" | "number" | "string" | "array" | "object" | "undefined" */
495-
const jsonTypeOf = (value) => {
496-
const jsType = typeof value;
497-
498-
switch (jsType) {
499-
case "number":
500-
case "string":
501-
case "boolean":
502-
case "undefined":
503-
return jsType;
504-
case "object":
505-
if (Array.isArray(value)) {
506-
return "array";
507-
} else if (value === null) {
508-
return "null";
509-
} else if (Object.getPrototypeOf(value) === Object.prototype) {
510-
return "object";
493+
// eslint-disable-next-line @typescript-eslint/require-await
494+
async (normalizedErrors, instance) => {
495+
/** @type ErrorObject[] */
496+
const errors = [];
497+
498+
if (normalizedErrors["https://json-schema.org/keyword/contains"]) {
499+
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/contains"]) {
500+
// const keyword = await getSchema(schemaLocation);
501+
errors.push({
502+
message: `TODO - contains`,
503+
instanceLocation: Instance.uri(instance),
504+
schemaLocation: schemaLocation
505+
});
511506
}
512-
default: {
513-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
514-
const type = jsType === "object" ? Object.getPrototypeOf(value).constructor.name ?? "anonymous" : jsType;
515-
throw Error(`Not a JSON compatible type: ${type}`);
516507
}
508+
509+
return errors;
517510
}
518-
};
511+
];
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+
// };

src/keywordErrorMessage.test.js

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ describe("Error messages", () => {
743743
]);
744744
});
745745

746-
test("anyOf - const-based discriminator mismatch", async () => {
746+
test.skip("anyOf - const-based discriminator mismatch", async () => {
747747
registerSchema({
748748
$schema: "https://json-schema.org/draft/2020-12/schema",
749749
anyOf: [
@@ -885,4 +885,116 @@ describe("Error messages", () => {
885885
}
886886
]);
887887
});
888+
889+
test("normalized output for a failing 'contains' keyword", async () => {
890+
registerSchema({
891+
$schema: "https://json-schema.org/draft/2020-12/schema",
892+
contains: {
893+
type: "number",
894+
multipleOf: 2
895+
},
896+
minContains: 1
897+
}, schemaUri);
898+
const instance = [3, 3, 5];
899+
const output = {
900+
valid: false,
901+
errors: [
902+
{
903+
valid: false,
904+
keywordLocation: "/contains",
905+
instanceLocation: "#",
906+
absoluteKeywordLocation: "https://example.com/main#/contains",
907+
errors: [
908+
{
909+
valid: false,
910+
instanceLocation: "#/0",
911+
absoluteKeywordLocation: "https://example.com/main#/contains/multipleOf"
912+
},
913+
{
914+
valid: false,
915+
instanceLocation: "#/1",
916+
absoluteKeywordLocation: "https://example.com/main#/contains/multipleOf"
917+
},
918+
{
919+
valid: false,
920+
instanceLocation: "#/2",
921+
absoluteKeywordLocation: "https://example.com/main#/contains/multipleOf"
922+
}
923+
]
924+
}
925+
]
926+
};
927+
const result = await betterJsonSchemaErrors(instance, output, schemaUri);
928+
expect(result.errors).to.eql([
929+
{
930+
instanceLocation: "#",
931+
message: "TODO - contains",
932+
schemaLocation: "https://example.com/main#/contains"
933+
}
934+
]);
935+
});
936+
937+
test("when then fails in if-then-else", async () => {
938+
registerSchema({
939+
$schema: "https://json-schema.org/draft/2020-12/schema",
940+
if: { multipleOf: 2 },
941+
then: { minimum: 0 }
942+
}, schemaUri);
943+
const instance = -2;
944+
const errorOutput = {
945+
valid: false,
946+
errors: [
947+
{
948+
valid: false,
949+
absoluteKeywordLocation: "https://example.com/main#/then/minimum",
950+
instanceLocation: "#"
951+
}
952+
]
953+
};
954+
955+
const result = await betterJsonSchemaErrors(instance, errorOutput, schemaUri);
956+
expect(result.errors).to.eql([
957+
{
958+
instanceLocation: "#",
959+
message: `The instance should be greater than or equal to 0.`,
960+
schemaLocation: "https://example.com/main#/then/minimum"
961+
}
962+
]);
963+
});
964+
965+
test("when else fails in if-then-else", async () => {
966+
registerSchema({
967+
$schema: "https://json-schema.org/draft/2020-12/schema",
968+
if: { multipleOf: 2 },
969+
else: { minimum: 0 }
970+
}, schemaUri);
971+
const instance = -3;
972+
const errorOutput = {
973+
valid: false,
974+
errors: [
975+
{
976+
valid: false,
977+
absoluteKeywordLocation: "https://example.com/main#/else/minimum",
978+
instanceLocation: "#"
979+
}
980+
]
981+
};
982+
983+
const result = await betterJsonSchemaErrors(instance, errorOutput, schemaUri);
984+
expect(result.errors).to.eql([
985+
{
986+
instanceLocation: "#",
987+
message: `The instance should be greater than or equal to 0.`,
988+
schemaLocation: "https://example.com/main#/else/minimum"
989+
}
990+
]);
991+
});
992+
993+
// not
994+
// dependentRequired
995+
// patternProperties
996+
// propertyNames
997+
// additionalProperties
998+
// unevaluatedProperties
999+
// unevaluatedItems
8881000
});

0 commit comments

Comments
 (0)