UNPKG

rulepilot

Version:

Rule parsing engine for JSON rules

213 lines (212 loc) 8.83 kB
"use strict"; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _Validator_instances, _Validator_objectDiscovery, _Validator_validateCondition, _Validator_validateConstraint, _Validator_isValidCondition; Object.defineProperty(exports, "__esModule", { value: true }); exports.Validator = void 0; const object_discovery_1 = require("./object-discovery"); class Validator { constructor() { _Validator_instances.add(this); _Validator_objectDiscovery.set(this, new object_discovery_1.ObjectDiscovery()); } /** * Takes in a rule as a parameter and returns a boolean indicating whether the rule is valid or not. * @param rule The rule to validate. */ validate(rule) { var _a; // Assume the rule is valid. const result = { isValid: true }; // Check the rule is a valid JSON if (!__classPrivateFieldGet(this, _Validator_objectDiscovery, "f").isObject(rule)) { return { isValid: false, error: { message: "The rule must be a valid JSON object.", element: rule, }, }; } // Cater for the case where the conditions property is not an array. const conditions = rule.conditions instanceof Array ? rule.conditions : [rule.conditions]; // Validate the 'conditions' property. if (conditions.length === 0 || (__classPrivateFieldGet(this, _Validator_objectDiscovery, "f").isObject(conditions[0]) && !Object.keys(conditions[0]).length)) { return { isValid: false, error: { message: "The conditions property must contain at least one condition.", element: rule, }, }; } // Validate each condition in the rule. for (const condition of conditions) { const subResult = __classPrivateFieldGet(this, _Validator_instances, "m", _Validator_validateCondition).call(this, condition); result.isValid = result.isValid && subResult.isValid; result.error = (_a = result === null || result === void 0 ? void 0 : result.error) !== null && _a !== void 0 ? _a : subResult === null || subResult === void 0 ? void 0 : subResult.error; } return result; } } exports.Validator = Validator; _Validator_objectDiscovery = new WeakMap(), _Validator_instances = new WeakSet(), _Validator_validateCondition = function _Validator_validateCondition(condition, depth = 0) { var _a, _b; // Check to see if the condition is valid. const result = __classPrivateFieldGet(this, _Validator_instances, "m", _Validator_isValidCondition).call(this, condition); if (!result.isValid) return result; // Set the type of condition. const type = __classPrivateFieldGet(this, _Validator_objectDiscovery, "f").conditionType(condition); // Check if the condition is iterable if (!Array.isArray(condition[type])) { return { isValid: false, error: { message: `The condition '${type}' should be iterable.`, element: condition, }, }; } // Check if the condition is iterable if (!condition[type].length) { return { isValid: false, error: { message: `The condition '${type}' should not be empty.`, element: condition, }, }; } // Validate each item in the condition. for (const node of condition[type]) { // The object should be a condition or constraint. const isCondition = __classPrivateFieldGet(this, _Validator_objectDiscovery, "f").isCondition(node); if (isCondition) { const subResult = __classPrivateFieldGet(this, _Validator_instances, "m", _Validator_validateCondition).call(this, node, depth + 1); result.isValid = result.isValid && subResult.isValid; result.error = (_a = result === null || result === void 0 ? void 0 : result.error) !== null && _a !== void 0 ? _a : subResult === null || subResult === void 0 ? void 0 : subResult.error; } const isConstraint = __classPrivateFieldGet(this, _Validator_objectDiscovery, "f").isConstraint(node); if (isConstraint) { const subResult = __classPrivateFieldGet(this, _Validator_instances, "m", _Validator_validateConstraint).call(this, node); result.isValid = result.isValid && subResult.isValid; result.error = (_b = result === null || result === void 0 ? void 0 : result.error) !== null && _b !== void 0 ? _b : subResult === null || subResult === void 0 ? void 0 : subResult.error; } if (!isConstraint && !isCondition) { return { isValid: false, error: { message: "Each node should be a condition or a constraint.", element: node, }, }; } // If any part fails validation there is no point to continue. if (!result.isValid) break; } return result; }, _Validator_validateConstraint = function _Validator_validateConstraint(constraint) { if ("string" !== typeof constraint.field) { return { isValid: false, error: { message: 'Constraint "field" must be of type string.', element: constraint, }, }; } const operators = [ "==", "!=", ">", "<", ">=", "<=", "in", "not in", "contains", "not contains", "contains any", "not contains any", "matches", "not matches", ]; if (!operators.includes(constraint.operator)) { return { isValid: false, error: { message: 'Constraint "operator" has invalid type.', element: constraint, }, }; } if (constraint.value === null && !["==", "!=", "contains", "not contains"].includes(constraint.operator)) { return { isValid: false, error: { message: '"operator" must be in ["==", "!=", "contains", "not contains"] if "value" is null.', element: constraint, }, }; } // We must check that the value is an array if the operator is 'in' or 'not in'. if (["in", "not in", "contains any", "not contains any"].includes(constraint.operator) && !Array.isArray(constraint.value)) { return { isValid: false, error: { message: 'Constraint "value" must be an array if the "operator" is in ["in", "not in", "contains any", "not contains any"]', element: constraint, }, }; } if (["matches", "not matches"].includes(constraint.operator)) { try { new RegExp(constraint.value); } catch (e) { return { isValid: false, error: { message: 'Constraint "value" must be a valid regular expression if the "operator" is in ["matches", "not matches"]', element: constraint, }, }; } } return { isValid: true }; }, _Validator_isValidCondition = function _Validator_isValidCondition(obj) { // Otherwise, the object should be a condition. if (!__classPrivateFieldGet(this, _Validator_objectDiscovery, "f").isCondition(obj)) { return { isValid: false, error: { message: "Invalid condition structure.", element: obj, }, }; } const isAny = "any" in obj; const isAll = "all" in obj; const isNone = "none" in obj; // A valid condition must have an 'any', 'all', or 'none' property, // but cannot have more than one. if ((isAny && isAll) || (isAny && isNone) || (isAll && isNone)) { return { isValid: false, error: { message: 'A condition cannot have more than one "any", "all", or "none" property.', element: obj, }, }; } return { isValid: true }; };