json-schema-library
Version:
Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation
118 lines (117 loc) • 5.22 kB
JavaScript
import { isSchemaNode } from "../types";
import { isObject } from "../utils/isObject";
import { mergeNode } from "../mergeNode";
import { hasProperty } from "../utils/hasProperty";
import { validateDependentRequired } from "./dependentRequired";
import { validateDependentSchemas } from "./dependentSchemas";
export const dependenciesKeyword = {
id: "dependencies",
keyword: "dependencies",
parse: parseDependencies,
order: -9,
addReduce: (node) => node.schema.dependencies != null,
reduce: reduceDependencies,
addValidate: (node) => node.schema.dependencies != null,
validate: validateDependencies
};
export function parseDependencies(node) {
const { dependencies } = node.schema;
if (!isObject(dependencies)) {
return;
}
const schemas = Object.keys(dependencies);
schemas.forEach((property) => {
var _a, _b;
const schema = dependencies[property];
if (isObject(schema) || typeof schema === "boolean") {
node.dependentSchemas = (_a = node.dependentSchemas) !== null && _a !== void 0 ? _a : {};
node.dependentSchemas[property] = node.compileSchema(schema, `${node.evaluationPath}/dependencies/${property}`, `${node.schemaLocation}/dependencies/${property}`);
}
else {
node.dependentRequired = (_b = node.dependentRequired) !== null && _b !== void 0 ? _b : {};
node.dependentRequired[property] = schema;
}
});
}
export function reduceDependencies({ node, data, key, pointer, path }) {
var _a;
if (!isObject(data)) {
// @todo remove dependentSchemas
return node;
}
if (node.dependentRequired == null && node.dependentSchemas == null) {
return node;
}
let workingNode = node.compileSchema(node.schema, node.evaluationPath, node.schemaLocation);
let required = (_a = workingNode.schema.required) !== null && _a !== void 0 ? _a : [];
let dynamicId = "";
if (node.dependentRequired) {
Object.keys(node.dependentRequired).forEach((propertyName) => {
if (!hasProperty(data, propertyName) && !required.includes(propertyName)) {
return;
}
if (node.dependentRequired[propertyName] == null) {
return;
}
required.push(...node.dependentRequired[propertyName]);
// @dynamicId
const localDynamicId = `dependencies/${propertyName}`;
dynamicId += `${dynamicId === "" ? "" : ","}${localDynamicId}`;
});
}
if (node.dependentSchemas) {
Object.keys(node.dependentSchemas).forEach((propertyName) => {
var _a, _b;
if (!hasProperty(data, propertyName) && !required.includes(propertyName)) {
return true;
}
const dependency = node.dependentSchemas[propertyName];
if (!isSchemaNode(dependency)) {
return true;
}
if (Array.isArray(dependency.schema.required)) {
required.push(...dependency.schema.required);
}
// @note pass on updated required-list to resolve nested dependencies. This is currently supported,
// but probably not how json-schema spec defines this behaviour (resolve only within sub-schema)
const reducedDependency = { ...dependency, schema: { ...dependency.schema, required } }.reduceNode(data, {
key,
pointer: `${pointer}/dependencies/${propertyName}`,
path
}).node;
workingNode = mergeNode(workingNode, reducedDependency);
// @dynamicId
const nestedDynamicId = (_b = (_a = reducedDependency.dynamicId) === null || _a === void 0 ? void 0 : _a.replace(node.dynamicId, "")) !== null && _b !== void 0 ? _b : "";
const localDynamicId = nestedDynamicId === "" ? `dependencies/${propertyName}` : nestedDynamicId;
dynamicId += `${dynamicId === "" ? "" : ","}${localDynamicId}`;
});
}
if (workingNode === node) {
return node;
}
if (required.length === 0) {
return workingNode;
}
required = workingNode.schema.required ? workingNode.schema.required.concat(...required) : required;
required = required.filter((r, index, list) => list.indexOf(r) === index);
workingNode = mergeNode(workingNode, workingNode, "dependencies");
return workingNode.compileSchema({ ...workingNode.schema, required }, workingNode.evaluationPath, workingNode.schemaLocation, `${node.schemaLocation}(${dynamicId})`);
}
function validateDependencies({ node, data, pointer, path }) {
var _a;
if (!isObject(data)) {
return undefined;
}
let errors;
if (node.dependentRequired) {
errors = (_a = validateDependentRequired({ node, data, pointer, path })) !== null && _a !== void 0 ? _a : [];
}
if (node.dependentSchemas) {
const schemaErrors = validateDependentSchemas({ node, data, pointer, path });
if (schemaErrors) {
errors = errors !== null && errors !== void 0 ? errors : [];
errors.push(...schemaErrors);
}
}
return errors;
}