Skip to content

Commit ec980da

Browse files
authored
Merge pull request #21 from arpitkuriyal/fix/options-param
added options param in the main function
2 parents 6612f06 + d72126d commit ec980da

File tree

7 files changed

+672
-120
lines changed

7 files changed

+672
-120
lines changed

package-lock.json

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
},
3939
"dependencies": {
4040
"@hyperjump/browser": "^1.3.1",
41-
"@hyperjump/json-schema": "^1.16.0"
41+
"@hyperjump/json-schema": "^1.16.0",
42+
"leven": "^4.0.0"
4243
}
4344
}

src/index.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export const betterJsonSchemaErrors: (
22
instance: Json,
3-
schema: SchemaObject,
4-
errorOutput: OutputFormat
3+
errorOutput: OutputFormat,
4+
schemaUri: string
55
) => Promise<BetterJsonSchemaErrors>;
66

77
export type BetterJsonSchemaErrors = {
@@ -31,6 +31,7 @@ export type OutputFormat = {
3131

3232
export type OutputUnit = {
3333
valid?: boolean;
34+
keyword?: string;
3435
absoluteKeywordLocation?: string;
3536
keywordLocation?: string;
3637
instanceLocation: string;
@@ -40,6 +41,7 @@ export type OutputUnit = {
4041

4142
export type NormalizedError = {
4243
valid: false;
44+
keyword: string;
4345
absoluteKeywordLocation: string;
4446
instanceLocation: string;
4547
};

src/index.js

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,122 @@
11
import { normalizeOutputFormat } from "./normalizeOutputFormat/normalizeOutput.js";
2+
import * as Schema from "@hyperjump/browser";
3+
import { getSchema, getKeyword } from "@hyperjump/json-schema/experimental";
4+
import * as Instance from "@hyperjump/json-pointer";
5+
import leven from "leven";
26

37
/**
4-
* @import {betterJsonSchemaErrors} from "./index.d.ts"
8+
* @import { Browser } from "@hyperjump/browser";
9+
* @import { SchemaDocument } from "@hyperjump/json-schema/experimental";
10+
* @import { Json } from "@hyperjump/json-pointer";
11+
* @import {betterJsonSchemaErrors, OutputUnit } from "./index.d.ts"
512
*/
613

714
/** @type betterJsonSchemaErrors */
8-
export async function betterJsonSchemaErrors(instance, schema, errorOutput) {
15+
export async function betterJsonSchemaErrors(instance, errorOutput, schemaUri) {
16+
const schema = await getSchema(schemaUri);
917
const normalizedErrors = await normalizeOutputFormat(errorOutput, schema);
10-
1118
const errors = [];
1219
for (const error of normalizedErrors) {
20+
const keywordHandler = getKeyword(error.keyword);
21+
if (keywordHandler.simpleApplicator) {
22+
continue;
23+
}
24+
25+
/** @type Browser<SchemaDocument> */
26+
const schema = await getSchema(error.absoluteKeywordLocation);
1327
errors.push({
14-
message: "The instance should be at least 3 characters",
28+
message: getErrorMessage(error, schema, instance),
1529
instanceLocation: error.instanceLocation,
1630
schemaLocation: error.absoluteKeywordLocation
1731
});
1832
}
1933

2034
return { errors };
2135
}
36+
37+
/** @type (outputUnit: OutputUnit, schema: Browser<SchemaDocument>, instance: Json) => string */
38+
const getErrorMessage = (outputUnit, schema, instance) => {
39+
if (outputUnit.keyword === "https://json-schema.org/keyword/minLength") {
40+
return `The instance should be at least ${Schema.value(schema)} characters`;
41+
}
42+
43+
if (outputUnit.keyword === "https://json-schema.org/keyword/maxLength") {
44+
return `The instance should be atmost ${Schema.value(schema)} characters long.`;
45+
}
46+
47+
if (outputUnit.keyword === "https://json-schema.org/keyword/type") {
48+
const pointer = outputUnit.instanceLocation.replace(/^#/, "");
49+
const actualValue = Instance.get(pointer, instance);
50+
return `The instance should be of type "${Schema.value(schema)}" but found "${typeof actualValue}".`;
51+
}
52+
53+
if (outputUnit.keyword === "https://json-schema.org/keyword/maximum") {
54+
return `The instance should be less than or equal to ${Schema.value(schema)}.`;
55+
}
56+
57+
if (outputUnit.keyword === "https://json-schema.org/keyword/minimum") {
58+
return `The instance should be greater than or equal to ${Schema.value(schema)}.`;
59+
}
60+
61+
if (outputUnit.keyword === "https://json-schema.org/keyword/exclusiveMaximum") {
62+
return `The instance should be less than ${Schema.value(schema)}.`;
63+
}
64+
65+
if (outputUnit.keyword === "https://json-schema.org/keyword/exclusiveMinimum") {
66+
return `The instance should be greater than ${Schema.value(schema)}.`;
67+
}
68+
69+
if (outputUnit.keyword === "https://json-schema.org/keyword/required") {
70+
/** @type {Set<string>} */
71+
const required = new Set(Schema.value(schema));
72+
const pointer = outputUnit.instanceLocation.replace(/^#/, "");
73+
const object = /** @type Object */ (Instance.get(pointer, instance));
74+
for (const propertyName of Object.keys(object)) {
75+
required.delete(propertyName);
76+
}
77+
78+
return `"${outputUnit.instanceLocation}" is missing required property(s): ${[...required].join(", ")}.`;
79+
}
80+
81+
if (outputUnit.keyword === "https://json-schema.org/keyword/multipleOf") {
82+
return `The instance should be of multiple of ${Schema.value(schema)}.`;
83+
}
84+
85+
if (outputUnit.keyword === "https://json-schema.org/keyword/maxProperties") {
86+
return `The instance should have maximum ${Schema.value(schema)} properties.`;
87+
}
88+
89+
if (outputUnit.keyword === "https://json-schema.org/keyword/minProperties") {
90+
return `The instance should have minimum ${Schema.value(schema)} properties.`;
91+
}
92+
93+
if (outputUnit.keyword === "https://json-schema.org/keyword/const") {
94+
return `The instance should be equal to ${Schema.value(schema)}.`;
95+
}
96+
97+
if (outputUnit.keyword === "https://json-schema.org/keyword/enum") {
98+
/** @type {Array<string>} */
99+
const allowedValues = Schema.value(schema);
100+
const pointer = outputUnit.instanceLocation.replace(/^#/, "");
101+
const currentValue = /** @type {string} */ (Instance.get(pointer, instance));
102+
103+
const bestMatch = allowedValues
104+
.map((value) => ({
105+
value,
106+
weight: leven(value, currentValue)
107+
}))
108+
.sort((a, b) => a.weight - b.weight)[0];
109+
110+
let suggestion = "";
111+
if (
112+
allowedValues.length === 1
113+
|| (bestMatch && bestMatch.weight < bestMatch.value.length)
114+
) {
115+
suggestion = ` Did you mean "${bestMatch.value}"?`;
116+
return `Unexpected value "${currentValue}". ${suggestion}`;
117+
}
118+
119+
return `Unexpected value "${currentValue}". Expected one of: ${allowedValues.join(",")}.`;
120+
}
121+
throw Error("TODO: Error message not implemented");
122+
};

0 commit comments

Comments
 (0)