json-schema-library
Version:
Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation
90 lines (80 loc) • 2.89 kB
text/typescript
import { isObject } from "../utils/isObject";
import { isBooleanSchema, isJsonSchema, SchemaNode } from "../types";
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
import { validateNode } from "../validateNode";
const KEYWORD = "contains";
export const containsKeyword: Keyword = {
id: KEYWORD,
keyword: KEYWORD,
parse: parseContains,
addValidate: (node) => node[KEYWORD] != null,
validate: validateContains,
addReduce: (node) => node[KEYWORD] != null,
reduce: ({ node }) => {
return node.compileSchema(
{
items: {
anyOf: [node[KEYWORD]!.schema] // we tested for contains in addReduce
}
},
node.evaluationPath,
node.schemaLocation
);
}
};
export function parseContains(node: SchemaNode) {
const contains = node.schema[KEYWORD];
if (contains == null) {
return;
}
if (!(isJsonSchema(contains) || isBooleanSchema(contains))) {
return node.createError("schema-error", {
pointer: `${node.schemaLocation}/${KEYWORD}`,
schema: node.schema,
value: contains,
message: `Keyword '${KEYWORD}' must be a valid JSON Schema - received '${typeof contains}'`
});
}
node[KEYWORD] = node.compileSchema(contains, `${node.evaluationPath}/${KEYWORD}`);
return node[KEYWORD].schemaValidation;
}
function validateContains({ node, data, pointer, path }: JsonSchemaValidatorParams) {
const { schema } = node;
if (!Array.isArray(data)) {
return;
}
if (schema.contains === false) {
return node.createError("contains-array-error", { pointer, value: data, schema });
}
if (schema.contains === true) {
if (Array.isArray(data) && data.length === 0) {
return node.createError("contains-any-error", { pointer, value: data, schema });
}
return undefined;
}
if (!isObject(schema.contains) || !Array.isArray(data)) {
// - ignore invalid schema
// - ignore invalid dara
return undefined;
}
let count = 0;
for (const d of data) {
// we tested for contains in addValidate
if (validateNode(node.contains!, d, pointer, path).length === 0) {
count++;
}
}
// @draft >= 2019-09
const max = schema.maxContains ?? Infinity;
const min = schema.minContains ?? 1;
if (max >= count && min <= count) {
return undefined;
}
if (max < count) {
return node.createError("contains-max-error", { pointer, schema, delta: count - max, value: data });
}
if (min > count) {
return node.createError("contains-min-error", { pointer, schema, delta: min - count, value: data });
}
return node.createError("contains-error", { pointer, schema, value: data });
}