Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 0 additions & 78 deletions src/error-handlers/constEnum.js

This file was deleted.

61 changes: 0 additions & 61 deletions src/error-handlers/type.js

This file was deleted.

118 changes: 118 additions & 0 deletions src/error-handlers/typeConstEnum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { getSchema } from "@hyperjump/json-schema/experimental";
import * as Schema from "@hyperjump/browser";
import * as Instance from "@hyperjump/json-schema/instance/experimental";
import jsonStringify from "json-stringify-deterministic";

/**
* @import { ErrorHandler, Json } from "../index.d.ts"
*/

const ALL_TYPES = new Set(["null", "boolean", "number", "string", "array", "object", "integer"]);

/** @type {ErrorHandler} */
const typeConstEnumErrorHandler = async (normalizedErrors, instance, localization) => {
let allowedTypes = new Set(ALL_TYPES);
/** @type {string[]} */
const failedTypeLocations = [];

for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/type"]) {
if (!normalizedErrors["https://json-schema.org/keyword/type"][schemaLocation]) {
failedTypeLocations.push(schemaLocation);

const keyword = await getSchema(schemaLocation);
/** @type {string | string[]} */
const value = Schema.value(keyword);
const types = Array.isArray(value) ? value : [value];
/** @type {Set<string>} */
const keywordTypes = new Set(types);
if (keywordTypes.has("number")) {
keywordTypes.add("integer");
}
allowedTypes = allowedTypes.intersection(keywordTypes);
}
}
if (allowedTypes.has("number")) {
allowedTypes.delete("integer");
}

/** @type {Set<string> | undefined} */
let allowedJson;

/** @type {string[]} */
const constEnumLocations = [];
/** @type {string[]} */
const failedConstLocations = [];
/** @type {string[]} */
const failedEnumLocations = [];
let typeFiltered = false;

for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/const"]) {
constEnumLocations.push(schemaLocation);
if (!normalizedErrors["https://json-schema.org/keyword/const"][schemaLocation]) {
failedConstLocations.push(schemaLocation);
}

const keyword = await getSchema(schemaLocation);
const keywordJson = new Set();
if (allowedTypes.has(Schema.typeOf(keyword))) {
keywordJson.add(jsonStringify(Schema.value(keyword)));
} else {
typeFiltered = true;
}

allowedJson = allowedJson?.intersection(keywordJson) ?? keywordJson;
}

for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/enum"]) {
constEnumLocations.push(schemaLocation);
if (!normalizedErrors["https://json-schema.org/keyword/enum"][schemaLocation]) {
failedEnumLocations.push(schemaLocation);
}

const keyword = await getSchema(schemaLocation);
const keywordJson = new Set();
for await (const enumValueNode of Schema.iter(keyword)) {
if (allowedTypes.has(Schema.typeOf(enumValueNode))) {
keywordJson.add(jsonStringify(Schema.value(enumValueNode)));
} else {
typeFiltered = true;
}
}

allowedJson = allowedJson?.intersection(keywordJson) ?? keywordJson;
}

const failedLocations = failedConstLocations.length > 0
? failedConstLocations
: failedEnumLocations;

if (failedLocations.length === 0 && failedTypeLocations.length === 0) {
return [];
} else if (allowedTypes.size === 0 || allowedJson?.size === 0) {
return [{
message: localization.getBooleanSchemaErrorMessage(),
instanceLocation: Instance.uri(instance),
schemaLocations: [...failedTypeLocations, ...constEnumLocations]
}];
} else if (allowedJson?.size) {
/** @type Json[] */
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
const allowedValues = [...allowedJson ?? []].map((json) => JSON.parse(json));

return [{
message: localization.getEnumErrorMessage(allowedValues),
instanceLocation: Instance.uri(instance),
schemaLocations: typeFiltered
? [...failedTypeLocations, ...constEnumLocations]
: failedLocations
}];
} else {
return [{
message: localization.getTypeErrorMessage([...allowedTypes]),
instanceLocation: Instance.uri(instance),
schemaLocations: failedTypeLocations
}];
}
};

export default typeConstEnumErrorHandler;
6 changes: 2 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import unknownNormalizationHandler from "./normalization-handlers/unknown.js";
// Error Handlers
import anyOfErrorHandler from "./error-handlers/anyOf.js";
import booleanSchemaErrorHandler from "./error-handlers/boolean-schema.js";
import constEnumErrorHandler from "./error-handlers/constEnum.js";
import containsErrorHandler from "./error-handlers/contains.js";
import dependenciesErrorHandler from "./error-handlers/draft-04/dependencies.js";
import exclusiveMaximumErrorHandler from "./error-handlers/exclusiveMaximum.js";
Expand All @@ -76,7 +75,7 @@ import notErrorHandler from "./error-handlers/not.js";
import oneOfErrorHandler from "./error-handlers/oneOf.js";
import patternErrorHandler from "./error-handlers/pattern.js";
import requiredErrorHandler from "./error-handlers/required.js";
import typeErrorHandler from "./error-handlers/type.js";
import typeConstEnumErrorHandler from "./error-handlers/typeConstEnum.js";
import uniqueItemsErrorHandler from "./error-handlers/uniqueItems.js";
import unknownErrorHandler from "./error-handlers/unknown.js";

Expand Down Expand Up @@ -139,7 +138,6 @@ setNormalizationHandler("https://json-schema.org/keyword/unknown", unknownNormal

addErrorHandler(anyOfErrorHandler);
addErrorHandler(booleanSchemaErrorHandler);
addErrorHandler(constEnumErrorHandler);
addErrorHandler(containsErrorHandler);
addErrorHandler(dependenciesErrorHandler);
addErrorHandler(exclusiveMaximumErrorHandler);
Expand All @@ -160,7 +158,7 @@ addErrorHandler(notErrorHandler);
addErrorHandler(oneOfErrorHandler);
addErrorHandler(patternErrorHandler);
addErrorHandler(requiredErrorHandler);
addErrorHandler(typeErrorHandler);
addErrorHandler(typeConstEnumErrorHandler);
addErrorHandler(uniqueItemsErrorHandler);
addErrorHandler(unknownErrorHandler);

Expand Down
43 changes: 43 additions & 0 deletions src/test-suite/tests/const.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,49 @@
]
}
]
},
{
"description": "const with type - filter by type",
"compatibility": "6",
"schema": {
"allOf": [
{ "type": "number" },
{ "const": "foo" }
]
},
"instance": "foo",
"errors": [
{
"messageId": "boolean-schema-message",
"messageParams": {},
"instanceLocation": "#",
"schemaLocations": [
"#/allOf/0/type",
"#/allOf/1/const"
]
}
]
},
{
"description": "const with matching type",
"compatibility": "6",
"schema": {
"allOf": [
{ "type": "string" },
{ "const": "foo" }
]
},
"instance": 42,
"errors": [
{
"messageId": "const-message",
"messageParams": {
"expected": "\"foo\""
},
"instanceLocation": "#",
"schemaLocations": ["#/allOf/1/const"]
}
]
}
]
}
Loading