• expression.js
  • /**
     * @readonly
     * @enum {string}
     */
    const LOGICAL = {
        AND: "and",
        OR: "or",
    };
    
    /**
     * Recursively processes an array of raw condition objects and populates the given parent group with
     * corresponding ConditionExpression or ConditionExpressionGroup instances.
     * @param {Array} conditions Array of raw condition objects, where each object represents either a group or an expression.
     * @param {ConditionExpressionGroup} parentGroup The parent group to which the instantiated ConditionExpression or ConditionExpressionGroup objects will be added.
     * @returns {void}
     */
    function processMonitorConditions(conditions, parentGroup) {
        conditions.forEach(condition => {
            const andOr = condition.andOr === LOGICAL.OR ? LOGICAL.OR : LOGICAL.AND;
    
            if (condition.type === "group") {
                const group = new ConditionExpressionGroup([], andOr);
    
                // Recursively process the group's children
                processMonitorConditions(condition.children, group);
    
                parentGroup.children.push(group);
            } else if (condition.type === "expression") {
                const expression = new ConditionExpression(condition.variable, condition.operator, condition.value, andOr);
                parentGroup.children.push(expression);
            }
        });
    }
    
    class ConditionExpressionGroup {
        /**
         * @type {ConditionExpressionGroup[]|ConditionExpression[]} Groups and/or expressions to test
         */
        children = [];
    
        /**
         * @type {LOGICAL} Connects group result with previous group/expression results
         */
        andOr;
    
        /**
         * @param {ConditionExpressionGroup[]|ConditionExpression[]} children Groups and/or expressions to test
         * @param {LOGICAL} andOr Connects group result with previous group/expression results
         */
        constructor(children = [], andOr = LOGICAL.AND) {
            this.children = children;
            this.andOr = andOr;
        }
    
        /**
         * @param {Monitor} monitor Monitor instance
         * @returns {ConditionExpressionGroup|null} A ConditionExpressionGroup with the Monitor's conditions
         */
        static fromMonitor(monitor) {
            const conditions = JSON.parse(monitor.conditions);
            if (conditions.length === 0) {
                return null;
            }
    
            const root = new ConditionExpressionGroup();
            processMonitorConditions(conditions, root);
    
            return root;
        }
    }
    
    class ConditionExpression {
        /**
         * @type {string} ID of variable
         */
        variable;
    
        /**
         * @type {string} ID of operator
         */
        operator;
    
        /**
         * @type {string} Value to test with the operator
         */
        value;
    
        /**
         * @type {LOGICAL} Connects expression result with previous group/expression results
         */
        andOr;
    
        /**
         * @param {string} variable ID of variable to test against
         * @param {string} operator ID of operator to test the variable with
         * @param {string} value Value to test with the operator
         * @param {LOGICAL} andOr Connects expression result with previous group/expression results
         */
        constructor(variable, operator, value, andOr = LOGICAL.AND) {
            this.variable = variable;
            this.operator = operator;
            this.value = value;
            this.andOr = andOr;
        }
    }
    
    module.exports = {
        LOGICAL,
        ConditionExpressionGroup,
        ConditionExpression,
    };