Custom Keyword Errors #78
-
InfoNode.js v22.12.0 Detailsvalidator.ts import maxUniqueItemsKeyword from "./keywords/maxUniqueItemsKeyword";
import minUniqueItemsKeyword from "./keywords/minUniqueItemsKeyword";
import { readFileSync } from "fs";
import {
registerSchema,
validate,
OutputFormat,
} from "@hyperjump/json-schema/draft-2019-09";
import {
addKeyword,
defineVocabulary,
loadDialect,
} from "@hyperjump/json-schema/experimental";
const customVocabularyId =
"https://schemas.amazon.com/selling-partners/definitions/product-types/vocabulary/v1";
const schemaId =
"https://schemas.amazon.com/selling-partners/definitions/product-types/schema/v1/COAT";
const metaSchemaId =
"https://schemas.amazon.com/selling-partners/definitions/product-types/meta-schema/v1";
const schemaPath = "./schema.json";
const metaSchemaPath = "./meta_schema.json";
const payloadPath = "./variant_payload.json";
const outputFormat: OutputFormat = "BASIC";
addKeyword(maxUniqueItemsKeyword);
addKeyword(minUniqueItemsKeyword);
defineVocabulary(customVocabularyId, {
maxUniqueItems: maxUniqueItemsKeyword.id,
minUniqueItems: minUniqueItemsKeyword.id,
});
loadDialect(
metaSchemaId,
{
"https://json-schema.org/draft/2019-09/vocab/core": true,
"https://json-schema.org/draft/2019-09/vocab/applicator": true,
"https://json-schema.org/draft/2019-09/vocab/validation": true,
"https://json-schema.org/draft/2019-09/vocab/meta-data": true,
"https://json-schema.org/draft/2019-09/vocab/format": true,
"https://json-schema.org/draft/2019-09/vocab/content": true,
[customVocabularyId]: true,
},
true
);
// Add meta schema
const metaSchemaJSON = JSON.parse(readFileSync(metaSchemaPath, "utf8"));
registerSchema(metaSchemaJSON, metaSchemaId);
// Add product type schema
const schemaTypeJSON = JSON.parse(readFileSync(schemaPath, "utf8"));
registerSchema(schemaTypeJSON, schemaId, metaSchemaId);
const payloadFile = readFileSync(payloadPath, "utf-8");
const payload = JSON.parse(payloadFile);
validate(schemaId, payload as any, outputFormat).then((result) =>
console.log(result)
); maxUniqueItemsKeyword.ts import {
SchemaDocument,
AST,
Anchors,
} from "@hyperjump/json-schema/experimental";
import { JsonNode, value } from "@hyperjump/json-schema/instance/experimental";
import { Browser as BrowserType, value as browserValue } from "@hyperjump/browser";
const id =
"https://schemas.amazon.com/selling-partners/definitions/product-types/keyword/maxUniqueItems";
const compile = async (
schema: BrowserType<SchemaDocument>,
ast: AST,
parentSchema: BrowserType<SchemaDocument>
) => {
const maxUniqueItems = await browserValue(schema);
// Extract selectors from parentSchema if available
let selectors: any[] = [];
try {
const selectorsSchema = (await browserValue(parentSchema)) as any;
selectors = selectorsSchema.selectors;
} catch (e) {
console.error(e);
}
return { maxUniqueItems, selectors };
};
const interpret = (
compiledKeywordValue: any,
instance: JsonNode,
ast: AST,
dynamicAnchors: Anchors,
quiet: boolean,
schemaLocation: string
) => {
const { maxUniqueItems, selectors } = compiledKeywordValue;
// Check if instance is an array
if (instance.type !== "array") {
console.log("returned true");
return true; // Not applicable to non-arrays
}
// Process each item in the array
let uniqueItems = [];
for (const item of value(instance) as any[]) {
let selectorCombination: { [key: string]: any } = {};
// Extract selector values if selectors are defined
if (Array.isArray(selectors) && selectors.length > 0) {
for (const selector of selectors) {
if (item[selector] !== undefined) {
selectorCombination[selector] = item[selector];
}
}
} else {
// If no selectors are defined, use the entire item
selectorCombination = { ...item };
}
uniqueItems.push(JSON.stringify(selectorCombination));
}
// Count occurrences of each unique combination
const countMap = new Map();
for (const item of uniqueItems) {
countMap.set(item, (countMap.get(item) || 0) + 1);
}
// Check if any combination exceeds maxUniqueItems
for (const [_, count] of countMap) {
if (count > (maxUniqueItems as number)) {
instance.valid = false;
return false;
}
}
return true;
};
const maxUniqueItemsKeyword = { id, compile, interpret };
export default maxUniqueItemsKeyword; QuestionI've created this custom keyword, "list_price": [
{
"value": 100.0,
"currency": "USD"
},
{
"value": 100.0,
"currency": "USD"
}
] When validating, it fails as I expect it to. However, the error messages are not descriptive at all. This is the result from the validation: {
"valid": false,
"errors": [
{
"keyword": "https://json-schema.org/keyword/properties",
"absoluteKeywordLocation": "https://schemas.amazon.com/selling-partners/definitions/product-types/schema/v1/COAT#/properties",
"instanceLocation": "#",
"valid": false
}
]
} How can I make the error more descriptive? The {
"valid": false,
"errors": [
{
"keyword": "https://schemas.amazon.com/selling-partners/definitions/product-types/keyword/maxUniqueItems",
"absoluteKeywordLocation": "https://schemas.amazon.com/selling-partners/definitions/product-types/schema/v1/COAT#/properties/list_price/maxUniqueItems",
"instanceLocation": "#",
"valid": false
}
]
} |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 6 replies
-
To make this even weirder, the validation works on some properties but not others. It doesn't give the error details when I tried with the
|
Beta Was this translation helpful? Give feedback.
-
I'll try to take a closer look tomorrow, but one thing I noticed immediately is that the meta-schema is missing |
Beta Was this translation helpful? Give feedback.
-
This is a bug. It should never report a I was able to work out a minimal reproduction. {
"properties": {
"list_price": {
"maxUniqueItems": 1
}
},
"allOf": [
{
"properties": {
"list_price": {}
}
}
]
} For some reason, the "list_price" in the The good news is that you can just move those constraints into |
Beta Was this translation helpful? Give feedback.
The fix is now published as v1.12.0.