mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge pull request #4230 from node-red/revert-4225-4196-fix-jsonata-env-var-async
Evaluate all env vars as part of async flow start
This commit is contained in:
		@@ -112,7 +112,7 @@
 | 
			
		||||
        "mermaid": "^9.4.3",
 | 
			
		||||
        "minami": "1.2.3",
 | 
			
		||||
        "mocha": "9.2.2",
 | 
			
		||||
        "node-red-node-test-helper": "^0.3.1",
 | 
			
		||||
        "node-red-node-test-helper": "^0.3.2",
 | 
			
		||||
        "nodemon": "2.0.20",
 | 
			
		||||
        "proxy": "^1.0.2",
 | 
			
		||||
        "sass": "1.62.1",
 | 
			
		||||
 
 | 
			
		||||
@@ -281,21 +281,4 @@ declare class env {
 | 
			
		||||
     * ```const flowName = env.get("NR_FLOW_NAME");```
 | 
			
		||||
     */
 | 
			
		||||
    static get(name:string) :any;
 | 
			
		||||
    /** 
 | 
			
		||||
     * Get an environment variable value (asynchronous).
 | 
			
		||||
     * 
 | 
			
		||||
     * Predefined node-red variables...  
 | 
			
		||||
     *   * `NR_NODE_ID` - the ID of the node
 | 
			
		||||
     *   * `NR_NODE_NAME` - the Name of the node
 | 
			
		||||
     *   * `NR_NODE_PATH` - the Path of the node
 | 
			
		||||
     *   * `NR_GROUP_ID` - the ID of the containing group
 | 
			
		||||
     *   * `NR_GROUP_NAME` - the Name of the containing group
 | 
			
		||||
     *   * `NR_FLOW_ID` - the ID of the flow the node is on
 | 
			
		||||
     *   * `NR_FLOW_NAME` - the Name of the flow the node is on
 | 
			
		||||
     * @param name Name of the environment variable to get
 | 
			
		||||
     * @param callback Callback function (`(err,value) => {}`)
 | 
			
		||||
     * @example 
 | 
			
		||||
     * ```const flowName = env.get("NR_FLOW_NAME");```
 | 
			
		||||
     */
 | 
			
		||||
    static get(name:string, callback: Function) :void;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -242,8 +242,8 @@ module.exports = function(RED) {
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            env: {
 | 
			
		||||
                get: function(envVar, callback) {
 | 
			
		||||
                    return RED.util.getSetting(node, envVar, node._flow, callback);
 | 
			
		||||
                get: function(envVar) {
 | 
			
		||||
                    return RED.util.getSetting(node, envVar);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            setTimeout: function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,19 +14,20 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
var clone = require("clone");
 | 
			
		||||
var redUtil = require("@node-red/util").util;
 | 
			
		||||
const clone = require("clone");
 | 
			
		||||
const redUtil = require("@node-red/util").util;
 | 
			
		||||
const events = require("@node-red/util").events;
 | 
			
		||||
var flowUtil = require("./util");
 | 
			
		||||
const flowUtil = require("./util");
 | 
			
		||||
const context = require('../nodes/context');
 | 
			
		||||
const hooks = require("@node-red/util").hooks;
 | 
			
		||||
const credentials = require("../nodes/credentials");
 | 
			
		||||
 | 
			
		||||
var Subflow;
 | 
			
		||||
var Log;
 | 
			
		||||
let Subflow;
 | 
			
		||||
let Log;
 | 
			
		||||
let Group;
 | 
			
		||||
 | 
			
		||||
var nodeCloseTimeout = 15000;
 | 
			
		||||
var asyncMessageDelivery = true;
 | 
			
		||||
let nodeCloseTimeout = 15000;
 | 
			
		||||
let asyncMessageDelivery = true;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class represents a flow within the runtime. It is responsible for
 | 
			
		||||
@@ -52,6 +53,8 @@ class Flow {
 | 
			
		||||
            this.isGlobalFlow = false;
 | 
			
		||||
        }
 | 
			
		||||
        this.id = this.flow.id || "global";
 | 
			
		||||
        this.groups = {}
 | 
			
		||||
        this.groupOrder = []
 | 
			
		||||
        this.activeNodes = {};
 | 
			
		||||
        this.subflowInstanceNodes = {};
 | 
			
		||||
        this.catchNodes = [];
 | 
			
		||||
@@ -59,6 +62,11 @@ class Flow {
 | 
			
		||||
        this.path = this.id;
 | 
			
		||||
        // Ensure a context exists for this flow
 | 
			
		||||
        this.context = context.getFlowContext(this.id,this.parent.id);
 | 
			
		||||
        
 | 
			
		||||
        // env is an array of env definitions
 | 
			
		||||
        // _env is an object for direct lookup of env name -> value
 | 
			
		||||
        this.env = this.flow.env
 | 
			
		||||
        this._env = {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -136,7 +144,7 @@ class Flow {
 | 
			
		||||
     * @param  {[type]} msg [description]
 | 
			
		||||
     * @return {[type]}     [description]
 | 
			
		||||
     */
 | 
			
		||||
    start(diff) {
 | 
			
		||||
    async start(diff) {
 | 
			
		||||
        this.trace("start "+this.TYPE+" ["+this.path+"]");
 | 
			
		||||
        var node;
 | 
			
		||||
        var newNode;
 | 
			
		||||
@@ -145,6 +153,52 @@ class Flow {
 | 
			
		||||
        this.statusNodes = [];
 | 
			
		||||
        this.completeNodeMap = {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (this.isGlobalFlow) {
 | 
			
		||||
            // This is the global flow. It needs to go find the `global-config`
 | 
			
		||||
            // node and extract any env properties from it
 | 
			
		||||
            const configNodes = Object.keys(this.flow.configs);
 | 
			
		||||
            for (let i = 0; i < configNodes.length; i++) {
 | 
			
		||||
                const node = this.flow.configs[configNodes[i]]
 | 
			
		||||
                if (node.type === 'global-config' && node.env) {
 | 
			
		||||
                    const nodeEnv = await flowUtil.evaluateEnvProperties(this, node.env, credentials.get(node.id))
 | 
			
		||||
                    this._env = { ...this._env, ...nodeEnv }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.env) {
 | 
			
		||||
            this._env = { ...this._env, ...await flowUtil.evaluateEnvProperties(this, this.env, credentials.get(this.id)) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Initialise the group objects. These must be done in the right order
 | 
			
		||||
        // starting from outer-most to inner-most so that the parent hierarchy
 | 
			
		||||
        // is maintained.
 | 
			
		||||
        this.groups = {}
 | 
			
		||||
        this.groupOrder = []
 | 
			
		||||
        const groupIds = Object.keys(this.flow.groups || {})
 | 
			
		||||
        while (groupIds.length > 0) {
 | 
			
		||||
            const id = groupIds.shift()
 | 
			
		||||
            const groupDef = this.flow.groups[id]
 | 
			
		||||
            if (!groupDef.g || this.groups[groupDef.g]) {
 | 
			
		||||
                // The parent of this group is available - either another group
 | 
			
		||||
                // or the top-level flow (this)
 | 
			
		||||
                const parent = this.groups[groupDef.g] || this
 | 
			
		||||
                this.groups[groupDef.id] = new Group(parent, groupDef)
 | 
			
		||||
                this.groupOrder.push(groupDef.id)
 | 
			
		||||
            } else {
 | 
			
		||||
                // Try again once we've processed the other groups
 | 
			
		||||
                groupIds.push(id)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this.groupOrder.length; i++) {
 | 
			
		||||
            // Start the groups in the right order so they
 | 
			
		||||
            // can setup their env vars knowning their parent
 | 
			
		||||
            // will have been started
 | 
			
		||||
            await this.groups[this.groupOrder[i]].start()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var configNodes = Object.keys(this.flow.configs);
 | 
			
		||||
        var configNodeAttempts = {};
 | 
			
		||||
        while (configNodes.length > 0) {
 | 
			
		||||
@@ -177,7 +231,7 @@ class Flow {
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (readyToCreate) {
 | 
			
		||||
                        newNode = flowUtil.createNode(this,node);
 | 
			
		||||
                        newNode = await flowUtil.createNode(this,node);
 | 
			
		||||
                        if (newNode) {
 | 
			
		||||
                            this.activeNodes[id] = newNode;
 | 
			
		||||
                        }
 | 
			
		||||
@@ -203,7 +257,7 @@ class Flow {
 | 
			
		||||
                if (node.d !== true) {
 | 
			
		||||
                    if (!node.subflow) {
 | 
			
		||||
                        if (!this.activeNodes[id]) {
 | 
			
		||||
                            newNode = flowUtil.createNode(this,node);
 | 
			
		||||
                            newNode = await flowUtil.createNode(this,node);
 | 
			
		||||
                            if (newNode) {
 | 
			
		||||
                                this.activeNodes[id] = newNode;
 | 
			
		||||
                            }
 | 
			
		||||
@@ -221,7 +275,7 @@ class Flow {
 | 
			
		||||
                                    node
 | 
			
		||||
                                );
 | 
			
		||||
                                this.subflowInstanceNodes[id] = subflow;
 | 
			
		||||
                                subflow.start();
 | 
			
		||||
                                await subflow.start();
 | 
			
		||||
                                this.activeNodes[id] = subflow.node;
 | 
			
		||||
 | 
			
		||||
                                // this.subflowInstanceNodes[id] = nodes.map(function(n) { return n.id});
 | 
			
		||||
@@ -404,8 +458,7 @@ class Flow {
 | 
			
		||||
     * @return {Node}   group node
 | 
			
		||||
     */
 | 
			
		||||
    getGroupNode(id) {
 | 
			
		||||
        const groups = this.global.groups;
 | 
			
		||||
        return groups[id];
 | 
			
		||||
        return this.groups[id];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -416,204 +469,28 @@ class Flow {
 | 
			
		||||
        return this.activeNodes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Group callback signature
 | 
			
		||||
     *
 | 
			
		||||
     * @callback GroupEnvCallback
 | 
			
		||||
     * @param {Error} err The error object (or null)
 | 
			
		||||
     * @param {[result: {val:Any}, name: String]} result The result of the callback
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     * Get a flow setting value.
 | 
			
		||||
     * @param  {[type]} key [description]
 | 
			
		||||
     * @return {[type]}     [description]
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * @function getGroupEnvSetting
 | 
			
		||||
    * Get a group setting value synchronously.
 | 
			
		||||
    * This currently automatically defers to the parent
 | 
			
		||||
    * @overload
 | 
			
		||||
     * @param {Object} node
 | 
			
		||||
     * @param {Object} group
 | 
			
		||||
     * @param {String} name
 | 
			
		||||
     * @returns {Any}
 | 
			
		||||
     *
 | 
			
		||||
     * Get a group setting value asynchronously.
 | 
			
		||||
     * @overload
 | 
			
		||||
     * @param {Object} node
 | 
			
		||||
     * @param {Object} group
 | 
			
		||||
     * @param {String} name
 | 
			
		||||
     * @param {GroupEnvCallback} callback
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    getGroupEnvSetting(node, group, name, callback) {
 | 
			
		||||
        /** @type {GroupEnvCallback} */
 | 
			
		||||
        const returnOrCallback = (err, [result, newName]) => {
 | 
			
		||||
            if (callback) {
 | 
			
		||||
                callback(err, [result, newName]);
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            return [result, newName];
 | 
			
		||||
        }
 | 
			
		||||
        if (group) {
 | 
			
		||||
            if (name === "NR_GROUP_NAME") {
 | 
			
		||||
                return returnOrCallback(null, [{ val: group.name }, null]);
 | 
			
		||||
            }
 | 
			
		||||
            if (name === "NR_GROUP_ID") {
 | 
			
		||||
                return returnOrCallback(null, [{ val: group.id }, null]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (group.credentials === undefined) {
 | 
			
		||||
                group.credentials = credentials.get(group.id) || {};
 | 
			
		||||
            }
 | 
			
		||||
            if (!name.startsWith("$parent.")) {
 | 
			
		||||
                if (group.env) {
 | 
			
		||||
                    if (!group._env) {
 | 
			
		||||
                        const envs = group.env;
 | 
			
		||||
                        const entries = envs.map((env) => {
 | 
			
		||||
                            if (env.type === "cred") {
 | 
			
		||||
                                const cred = group.credentials;
 | 
			
		||||
                                if (cred.hasOwnProperty(env.name)) {
 | 
			
		||||
                                    env.value = cred[env.name];
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            return [env.name, env];
 | 
			
		||||
                        });
 | 
			
		||||
                        group._env = Object.fromEntries(entries);
 | 
			
		||||
                    }
 | 
			
		||||
                    const env = group._env[name];
 | 
			
		||||
                    if (env) {
 | 
			
		||||
                        let value = env.value;
 | 
			
		||||
                        const type = env.type;
 | 
			
		||||
                        if ((type !== "env") || (value !== name)) {
 | 
			
		||||
                            if (type === "env") {
 | 
			
		||||
                                value = value.replace(new RegExp("\\${"+name+"}","g"),"${$parent."+name+"}");
 | 
			
		||||
                            } else if (type === "bool") {
 | 
			
		||||
                                const val = ((value === "true") || (value === true));
 | 
			
		||||
                                return returnOrCallback(null, [{ val: val }, null])
 | 
			
		||||
                            }
 | 
			
		||||
                            if (type === "cred") {
 | 
			
		||||
                                return returnOrCallback(null, [{ val: value }, null])
 | 
			
		||||
                            }
 | 
			
		||||
                            try {
 | 
			
		||||
                                if (!callback) {
 | 
			
		||||
                                    var val = redUtil.evaluateNodeProperty(value, type, node, null, null);
 | 
			
		||||
                                    return [{ val: val }, null];
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    redUtil.evaluateNodeProperty(value, type, node, null, (err, value) => {
 | 
			
		||||
                                        return returnOrCallback(err, [{ val: value }, null])
 | 
			
		||||
                                    });
 | 
			
		||||
                                    return
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            catch (e) {
 | 
			
		||||
                                if (!callback) {
 | 
			
		||||
                                    this.error(e);
 | 
			
		||||
                                }
 | 
			
		||||
                                return returnOrCallback(e, null);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                name = name.substring(8);
 | 
			
		||||
            }
 | 
			
		||||
            if (group.g) {
 | 
			
		||||
                const parent = this.getGroupNode(group.g);
 | 
			
		||||
                const gVal = this.getGroupEnvSetting(node, parent, name, callback);
 | 
			
		||||
                if (callback) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                return gVal;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return returnOrCallback(null, [null, name]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Settings callback signature
 | 
			
		||||
     *
 | 
			
		||||
     * @callback SettingsCallback
 | 
			
		||||
     * @param {Error} err The error object (or null)
 | 
			
		||||
     * @param {Any} result The result of the callback
 | 
			
		||||
     * @returns {void}
 | 
			
		||||
     */
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a flow setting value. This currently automatically defers to the parent
 | 
			
		||||
     * flow which, as defined in ./index.js returns `process.env[key]`.
 | 
			
		||||
     * This lays the groundwork for Subflow to have instance-specific settings
 | 
			
		||||
     * @param  {String} key The settings key
 | 
			
		||||
     * @param  {SettingsCallback} callback Optional callback function
 | 
			
		||||
     * @return {Any}
 | 
			
		||||
     */
 | 
			
		||||
    getSetting(key, callback) {
 | 
			
		||||
        /** @type {SettingsCallback} */
 | 
			
		||||
        const returnOrCallback = (err, result) => {
 | 
			
		||||
            if (callback) {
 | 
			
		||||
                callback(err, result);
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
    getSetting(key) {
 | 
			
		||||
        const flow = this.flow;
 | 
			
		||||
        if (key === "NR_FLOW_NAME") {
 | 
			
		||||
            return returnOrCallback(null, flow.label);
 | 
			
		||||
            return flow.label;
 | 
			
		||||
        }
 | 
			
		||||
        if (key === "NR_FLOW_ID") {
 | 
			
		||||
            return returnOrCallback(null, flow.id);
 | 
			
		||||
            return flow.id;
 | 
			
		||||
        }
 | 
			
		||||
        if (flow.credentials === undefined) {
 | 
			
		||||
            flow.credentials = credentials.get(flow.id) || {};
 | 
			
		||||
        }
 | 
			
		||||
        if (flow.env) {
 | 
			
		||||
            if (!key.startsWith("$parent.")) {
 | 
			
		||||
                if (!flow._env) {
 | 
			
		||||
                    const envs = flow.env;
 | 
			
		||||
                    const entries = envs.map((env) => {
 | 
			
		||||
                        if (env.type === "cred") {
 | 
			
		||||
                            const cred = flow.credentials;
 | 
			
		||||
                            if (cred.hasOwnProperty(env.name)) {
 | 
			
		||||
                                env.value = cred[env.name];
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        return [env.name, env]
 | 
			
		||||
                    });
 | 
			
		||||
                    flow._env = Object.fromEntries(entries);
 | 
			
		||||
                }
 | 
			
		||||
                const env = flow._env[key];
 | 
			
		||||
                if (env) {
 | 
			
		||||
                    let value = env.value;
 | 
			
		||||
                    const type = env.type;
 | 
			
		||||
                    if ((type !== "env") || (value !== key)) {
 | 
			
		||||
                        if (type === "env") {
 | 
			
		||||
                            value = value.replace(new RegExp("\\${"+key+"}","g"),"${$parent."+key+"}");
 | 
			
		||||
                        }
 | 
			
		||||
                        try {
 | 
			
		||||
                            if (type === "bool") {
 | 
			
		||||
                                const val = ((value === "true") || (value === true));
 | 
			
		||||
                                return returnOrCallback(null, val);
 | 
			
		||||
                            }
 | 
			
		||||
                            if (type === "cred") {
 | 
			
		||||
                                return returnOrCallback(null, value);
 | 
			
		||||
                            }
 | 
			
		||||
                            var val = redUtil.evaluateNodeProperty(value, type, null, null, null);
 | 
			
		||||
                            return returnOrCallback(null, val);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (e) {
 | 
			
		||||
                            this.error(e);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
        if (!key.startsWith("$parent.")) {
 | 
			
		||||
            if (this._env.hasOwnProperty(key)) {
 | 
			
		||||
                return this._env[key]
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
        } else {
 | 
			
		||||
                key = key.substring(8);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        const pVal = this.parent.getSetting(key, callback);
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        return pVal;
 | 
			
		||||
        // Delegate to the parent flow.
 | 
			
		||||
        return this.parent.getSetting(key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -666,10 +543,10 @@ class Flow {
 | 
			
		||||
                let distance = 0
 | 
			
		||||
                if (reportingNode.g) {
 | 
			
		||||
                    // Reporting node inside a group. Calculate the distance between it and the status node
 | 
			
		||||
                    let containingGroup = this.global.groups[reportingNode.g]
 | 
			
		||||
                    let containingGroup = this.groups[reportingNode.g]
 | 
			
		||||
                    while (containingGroup && containingGroup.id !== targetStatusNode.g) {
 | 
			
		||||
                        distance++
 | 
			
		||||
                        containingGroup = this.global.groups[containingGroup.g]
 | 
			
		||||
                        containingGroup = this.groups[containingGroup.g]
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!containingGroup && targetStatusNode.g && targetStatusNode.scope === 'group') {
 | 
			
		||||
                        // This status node is in a group, but not in the same hierachy
 | 
			
		||||
@@ -753,10 +630,10 @@ class Flow {
 | 
			
		||||
                let distance = 0
 | 
			
		||||
                if (reportingNode.g) {
 | 
			
		||||
                    // Reporting node inside a group. Calculate the distance between it and the catch node
 | 
			
		||||
                    let containingGroup = this.global.groups[reportingNode.g]
 | 
			
		||||
                    let containingGroup = this.groups[reportingNode.g]
 | 
			
		||||
                    while (containingGroup && containingGroup.id !== targetCatchNode.g) {
 | 
			
		||||
                        distance++
 | 
			
		||||
                        containingGroup = this.global.groups[containingGroup.g]
 | 
			
		||||
                        containingGroup = this.groups[containingGroup.g]
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!containingGroup && targetCatchNode.g && targetCatchNode.scope === 'group') {
 | 
			
		||||
                        // This catch node is in a group, but not in the same hierachy
 | 
			
		||||
@@ -956,9 +833,10 @@ module.exports = {
 | 
			
		||||
        asyncMessageDelivery = !runtime.settings.runtimeSyncDelivery
 | 
			
		||||
        Log = runtime.log;
 | 
			
		||||
        Subflow = require("./Subflow");
 | 
			
		||||
        Group = require("./Group").Group
 | 
			
		||||
    },
 | 
			
		||||
    create: function(parent,global,conf) {
 | 
			
		||||
        return new Flow(parent,global,conf);
 | 
			
		||||
        return new Flow(parent,global,conf)
 | 
			
		||||
    },
 | 
			
		||||
    Flow: Flow
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								packages/node_modules/@node-red/runtime/lib/flows/Group.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								packages/node_modules/@node-red/runtime/lib/flows/Group.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
const flowUtil = require("./util");
 | 
			
		||||
const credentials = require("../nodes/credentials");
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class represents a group within the runtime.
 | 
			
		||||
 */
 | 
			
		||||
class Group {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a Group object.
 | 
			
		||||
     * @param {[type]} parent     The parent flow/group
 | 
			
		||||
     * @param {[type]} groupDef   This group's definition
 | 
			
		||||
     */
 | 
			
		||||
    constructor(parent, groupDef) {
 | 
			
		||||
        this.TYPE = 'group'
 | 
			
		||||
        this.name = groupDef.name
 | 
			
		||||
        this.parent = parent
 | 
			
		||||
        this.group = groupDef
 | 
			
		||||
        this.id = this.group.id
 | 
			
		||||
        this.g = this.group.g
 | 
			
		||||
        this.env = this.group.env
 | 
			
		||||
        this._env = {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async start() {
 | 
			
		||||
        if (this.env) {
 | 
			
		||||
            this._env = await flowUtil.evaluateEnvProperties(this, this.env, credentials.get(this.id))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a group setting value.
 | 
			
		||||
     * @param  {[type]} key [description]
 | 
			
		||||
     * @return {[type]}     [description]
 | 
			
		||||
     */
 | 
			
		||||
    getSetting(key) {
 | 
			
		||||
        if (key === "NR_GROUP_NAME") {
 | 
			
		||||
            return this.name;
 | 
			
		||||
        }
 | 
			
		||||
        if (key === "NR_GROUP_ID") {
 | 
			
		||||
            return this.id;
 | 
			
		||||
        }
 | 
			
		||||
        if (!key.startsWith("$parent.")) {
 | 
			
		||||
            if (this._env.hasOwnProperty(key)) {
 | 
			
		||||
                return this._env[key]
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            key = key.substring(8);
 | 
			
		||||
        }
 | 
			
		||||
        return this.parent.getSetting(key);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    Group
 | 
			
		||||
}
 | 
			
		||||
@@ -119,7 +119,7 @@ class Subflow extends Flow {
 | 
			
		||||
        this.templateCredentials = credentials.get(subflowDef.id) || {};
 | 
			
		||||
        this.instanceCredentials = credentials.get(id) || {};
 | 
			
		||||
 | 
			
		||||
        var env = [];
 | 
			
		||||
        var env = {};
 | 
			
		||||
        if (this.subflowDef.env) {
 | 
			
		||||
            this.subflowDef.env.forEach(e => {
 | 
			
		||||
                env[e.name] = e;
 | 
			
		||||
@@ -145,7 +145,7 @@ class Subflow extends Flow {
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        this.env = env;
 | 
			
		||||
        this.env = Object.values(env);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -156,7 +156,7 @@ class Subflow extends Flow {
 | 
			
		||||
     * @param  {[type]} diff [description]
 | 
			
		||||
     * @return {[type]}      [description]
 | 
			
		||||
     */
 | 
			
		||||
    start(diff) {
 | 
			
		||||
    async start(diff) {
 | 
			
		||||
        var self = this;
 | 
			
		||||
        // Create a subflow node to accept inbound messages and route appropriately
 | 
			
		||||
        var Node = require("../nodes/Node");
 | 
			
		||||
@@ -310,7 +310,7 @@ class Subflow extends Flow {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        super.start(diff);
 | 
			
		||||
        return super.start(diff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -335,68 +335,35 @@ class Subflow extends Flow {
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Get environment variable of subflow
 | 
			
		||||
     * @param {String}   name   name of env var
 | 
			
		||||
     * @param {String}   key   name of env var
 | 
			
		||||
     * @return {Object}  val    value of env var
 | 
			
		||||
     */
 | 
			
		||||
    getSetting(name) {
 | 
			
		||||
        if (!/^\$parent\./.test(name)) {
 | 
			
		||||
            var env = this.env;
 | 
			
		||||
            if (env && env.hasOwnProperty(name)) {
 | 
			
		||||
                var val = env[name];
 | 
			
		||||
                // If this is an env type property we need to be careful not
 | 
			
		||||
                // to get into lookup loops.
 | 
			
		||||
                // 1. if the value to lookup is the same as this one, go straight to parent
 | 
			
		||||
                // 2. otherwise, check if it is a compound env var ("foo $(bar)")
 | 
			
		||||
                //    and if so, substitute any instances of `name` with $parent.name
 | 
			
		||||
                // See https://github.com/node-red/node-red/issues/2099
 | 
			
		||||
                if (val.type !== 'env' || val.value !== name) {
 | 
			
		||||
                    let value = val.value;
 | 
			
		||||
                    var type = val.type;
 | 
			
		||||
                    if (type === 'env') {
 | 
			
		||||
                        value = value.replace(new RegExp("\\${"+name+"}","g"),"${$parent."+name+"}");
 | 
			
		||||
                    }
 | 
			
		||||
                    try {
 | 
			
		||||
                        return evaluateInputValue(value, type, this.node);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (e) {
 | 
			
		||||
                        this.error(e);
 | 
			
		||||
                        return undefined;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    // This _is_ an env property pointing at itself - go to parent
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // name starts $parent. ... so delegate to parent automatically
 | 
			
		||||
            name = name.substring(8);
 | 
			
		||||
        }
 | 
			
		||||
    getSetting(key) {
 | 
			
		||||
        const node = this.subflowInstance;
 | 
			
		||||
        if (node) {
 | 
			
		||||
            if (name === "NR_NODE_NAME") {
 | 
			
		||||
            if (key === "NR_NODE_NAME") {
 | 
			
		||||
                return node.name;
 | 
			
		||||
            }
 | 
			
		||||
            if (name === "NR_NODE_ID") {
 | 
			
		||||
            if (key === "NR_NODE_ID") {
 | 
			
		||||
                return node.id;
 | 
			
		||||
            }
 | 
			
		||||
            if (name === "NR_NODE_PATH") {
 | 
			
		||||
            if (key === "NR_NODE_PATH") {
 | 
			
		||||
                return node._path;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (node.g) {
 | 
			
		||||
            const group = this.getGroupNode(node.g);
 | 
			
		||||
            const [result, newName] = this.getGroupEnvSetting(node, group, name);
 | 
			
		||||
            if (result) {
 | 
			
		||||
                return result.val;
 | 
			
		||||
        if (!key.startsWith("$parent.")) {
 | 
			
		||||
            if (this._env.hasOwnProperty(key)) {
 | 
			
		||||
                return this._env[key]
 | 
			
		||||
            }
 | 
			
		||||
            name = newName;
 | 
			
		||||
        } else {
 | 
			
		||||
            key = key.substring(8);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var parent = this.parent;
 | 
			
		||||
        if (parent) {
 | 
			
		||||
            var val = parent.getSetting(name);
 | 
			
		||||
            return val;
 | 
			
		||||
        // Push the request up to the parent.
 | 
			
		||||
        // Unlike a Flow, the parent of a Subflow could be a Group
 | 
			
		||||
        if (node.g) {
 | 
			
		||||
            return this.parent.getGroupNode(node.g).getSetting(key)
 | 
			
		||||
        }
 | 
			
		||||
        return undefined;
 | 
			
		||||
        return this.parent.getSetting(key)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -271,6 +271,10 @@ function getFlows() {
 | 
			
		||||
 | 
			
		||||
async function start(type,diff,muteLog,isDeploy) {
 | 
			
		||||
    type = type || "full";
 | 
			
		||||
    if (diff && diff.globalConfigChanged) {
 | 
			
		||||
        type = 'full'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    started = true;
 | 
			
		||||
    state = 'start'
 | 
			
		||||
    var i;
 | 
			
		||||
@@ -359,7 +363,7 @@ async function start(type,diff,muteLog,isDeploy) {
 | 
			
		||||
            if (activeFlowConfig.flows.hasOwnProperty(id)) {
 | 
			
		||||
                if (!activeFlowConfig.flows[id].disabled && !activeFlows[id]) {
 | 
			
		||||
                    // This flow is not disabled, nor is it currently active, so create it
 | 
			
		||||
                    activeFlows[id] = Flow.create(flowAPI,activeFlowConfig,activeFlowConfig.flows[id]);
 | 
			
		||||
                    activeFlows[id] = Flow.create(activeFlows['global'],activeFlowConfig,activeFlowConfig.flows[id]);
 | 
			
		||||
                    log.debug("red/nodes/flows.start : starting flow : "+id);
 | 
			
		||||
                } else {
 | 
			
		||||
                    log.debug("red/nodes/flows.start : not starting disabled flow : "+id);
 | 
			
		||||
@@ -379,7 +383,7 @@ async function start(type,diff,muteLog,isDeploy) {
 | 
			
		||||
                        activeFlows[id].update(activeFlowConfig,activeFlowConfig.flows[id]);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // This flow didn't previously exist, so create it
 | 
			
		||||
                        activeFlows[id] = Flow.create(flowAPI,activeFlowConfig,activeFlowConfig.flows[id]);
 | 
			
		||||
                        activeFlows[id] = Flow.create(activeFlows['global'],activeFlowConfig,activeFlowConfig.flows[id]);
 | 
			
		||||
                        log.debug("red/nodes/flows.start : starting flow : "+id);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -391,7 +395,7 @@ async function start(type,diff,muteLog,isDeploy) {
 | 
			
		||||
    for (id in activeFlows) {
 | 
			
		||||
        if (activeFlows.hasOwnProperty(id)) {
 | 
			
		||||
            try {
 | 
			
		||||
                activeFlows[id].start(diff);
 | 
			
		||||
                await activeFlows[id].start(diff);
 | 
			
		||||
                // Create a map of node id to flow id and also a subflowInstance lookup map
 | 
			
		||||
                var activeNodes = activeFlows[id].getActiveNodes();
 | 
			
		||||
                Object.keys(activeNodes).forEach(function(nid) {
 | 
			
		||||
@@ -432,7 +436,8 @@ function stop(type,diff,muteLog,isDeploy) {
 | 
			
		||||
        changed:[],
 | 
			
		||||
        removed:[],
 | 
			
		||||
        rewired:[],
 | 
			
		||||
        linked:[]
 | 
			
		||||
        linked:[],
 | 
			
		||||
        flowChanged:[]
 | 
			
		||||
    };
 | 
			
		||||
    if (!muteLog) {
 | 
			
		||||
        if (type !== "full") {
 | 
			
		||||
@@ -441,6 +446,9 @@ function stop(type,diff,muteLog,isDeploy) {
 | 
			
		||||
            log.info(log._("nodes.flows.stopping-flows"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (diff.globalConfigChanged) {
 | 
			
		||||
        type = 'full'
 | 
			
		||||
    }
 | 
			
		||||
    started = false;
 | 
			
		||||
    state = 'stop'
 | 
			
		||||
    var promises = [];
 | 
			
		||||
@@ -464,7 +472,7 @@ function stop(type,diff,muteLog,isDeploy) {
 | 
			
		||||
 | 
			
		||||
    activeFlowIds.forEach(id => {
 | 
			
		||||
        if (activeFlows.hasOwnProperty(id)) {
 | 
			
		||||
            var flowStateChanged = diff && (diff.added.indexOf(id) !== -1 || diff.removed.indexOf(id) !== -1);
 | 
			
		||||
            var flowStateChanged = diff && (diff.flowChanged.indexOf(id) !== -1 || diff.added.indexOf(id) !== -1 || diff.removed.indexOf(id) !== -1);
 | 
			
		||||
            log.debug("red/nodes/flows.stop : stopping flow : "+id);
 | 
			
		||||
            promises.push(activeFlows[id].stop(flowStateChanged?null:stopList,removedList));
 | 
			
		||||
            if (type === "full" || flowStateChanged || diff.removed.indexOf(id)!==-1) {
 | 
			
		||||
@@ -780,21 +788,10 @@ const flowAPI = {
 | 
			
		||||
    getNode: getNode,
 | 
			
		||||
    handleError: () => false,
 | 
			
		||||
    handleStatus: () => false,
 | 
			
		||||
    getSetting: (k, callback) => flowUtil.getEnvVar(k, callback),
 | 
			
		||||
    getSetting: k => flowUtil.getEnvVar(k),
 | 
			
		||||
    log: m => log.log(m)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function getGlobalConfig() {
 | 
			
		||||
    let gconf = null;
 | 
			
		||||
    eachNode((n) => {
 | 
			
		||||
        if (n.type === "global-config") {
 | 
			
		||||
            gconf = n;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    return gconf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    init: init,
 | 
			
		||||
 | 
			
		||||
@@ -807,10 +804,7 @@ module.exports = {
 | 
			
		||||
 | 
			
		||||
    get:getNode,
 | 
			
		||||
    eachNode: eachNode,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    getGlobalConfig: getGlobalConfig,
 | 
			
		||||
    
 | 
			
		||||
  
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the current flow configuration
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -205,7 +205,6 @@ module.exports = {
 | 
			
		||||
    getNode: flows.get,
 | 
			
		||||
    eachNode: flows.eachNode,
 | 
			
		||||
    getContext: context.get,
 | 
			
		||||
    getGlobalConfig: flows.getGlobalConfig,
 | 
			
		||||
 | 
			
		||||
    clearContext: context.clear,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										79
									
								
								packages/node_modules/@node-red/util/lib/util.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								packages/node_modules/@node-red/util/lib/util.js
									
									
									
									
										vendored
									
									
								
							@@ -18,7 +18,6 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @mixin @node-red/util_util
 | 
			
		||||
 */
 | 
			
		||||
/** @typedef {import('../../runtime/lib/flows/Flow.js').Flow} RuntimeLibFlowsFlow */
 | 
			
		||||
 | 
			
		||||
const clonedeep = require("lodash.clonedeep");
 | 
			
		||||
const jsonata = require("jsonata");
 | 
			
		||||
@@ -531,64 +530,31 @@ function setObjectProperty(msg,prop,value,createMissing) {
 | 
			
		||||
 * Get value of environment variable.
 | 
			
		||||
 * @param {Node} node - accessing node
 | 
			
		||||
 * @param {String} name - name of variable
 | 
			
		||||
 * @param {RuntimeLibFlowsFlow} flow_ - (optional) flow to check for setting
 | 
			
		||||
 * @param  {(err: Error, result: Any) => void} callback - (optional) called when the property is evaluated
 | 
			
		||||
 * @return {String} value of env var
 | 
			
		||||
 */
 | 
			
		||||
function getSetting(node, name, flow_, callback) {
 | 
			
		||||
    const returnOrCallback = (err, result) => {
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            callback(err, result);
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
function getSetting(node, name, flow_) {
 | 
			
		||||
    if (node) {
 | 
			
		||||
        if (name === "NR_NODE_NAME") {
 | 
			
		||||
            return returnOrCallback(null, node.name);
 | 
			
		||||
            return node.name;
 | 
			
		||||
        }
 | 
			
		||||
        if (name === "NR_NODE_ID") {
 | 
			
		||||
            return returnOrCallback(null, node.id);
 | 
			
		||||
            return node.id;
 | 
			
		||||
        }
 | 
			
		||||
        if (name === "NR_NODE_PATH") {
 | 
			
		||||
            return returnOrCallback(null, node._path);
 | 
			
		||||
            return node._path;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @type {RuntimeLibFlowsFlow} */
 | 
			
		||||
    var flow = (flow_ ? flow_ : (node ? node._flow : null));
 | 
			
		||||
    if (flow) {
 | 
			
		||||
        if (node && node.g) {
 | 
			
		||||
            const group = flow.getGroupNode(node.g);
 | 
			
		||||
            if (callback) {
 | 
			
		||||
                flow.getGroupEnvSetting(node, group, name, (e, [result, newName]) => {
 | 
			
		||||
                    if (e) {
 | 
			
		||||
                        callback(e);
 | 
			
		||||
                        return
 | 
			
		||||
                    }
 | 
			
		||||
                    if (result) {
 | 
			
		||||
                        callback(null, result.val);
 | 
			
		||||
                        return
 | 
			
		||||
                    }
 | 
			
		||||
                    name = newName;
 | 
			
		||||
                    flow.getSetting(name, callback);
 | 
			
		||||
                });
 | 
			
		||||
                return
 | 
			
		||||
            } else {
 | 
			
		||||
                const [result, newName] = flow.getGroupEnvSetting(node, group, name);
 | 
			
		||||
                if (result) {
 | 
			
		||||
                    return result.val;
 | 
			
		||||
                }
 | 
			
		||||
                name = newName;
 | 
			
		||||
            if (group) {
 | 
			
		||||
                return group.getSetting(name)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        const fVal = flow.getSetting(name, callback)
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        return fVal;
 | 
			
		||||
        return flow.getSetting(name);
 | 
			
		||||
    }
 | 
			
		||||
    return returnOrCallback(null, process.env[name]);
 | 
			
		||||
    return process.env[name];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -600,34 +566,19 @@ function getSetting(node, name, flow_, callback) {
 | 
			
		||||
 * will return `Hello Joe!`.
 | 
			
		||||
 * @param  {String} value - the string to parse
 | 
			
		||||
 * @param  {Node} node - the node evaluating the property
 | 
			
		||||
 * @param  {(err: Error, result: Any) => void} callback - (optional) called when the property is evaluated
 | 
			
		||||
 * @return {String} The parsed string
 | 
			
		||||
 * @memberof @node-red/util_util
 | 
			
		||||
 */
 | 
			
		||||
function evaluateEnvProperty(value, node, callback) {
 | 
			
		||||
    const returnOrCallback = (err, result) => {
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            callback(err, result);
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
    /** @type {RuntimeLibFlowsFlow}  */
 | 
			
		||||
function evaluateEnvProperty(value, node) {
 | 
			
		||||
    var flow = (node && hasOwnProperty.call(node, "_flow")) ? node._flow : null;
 | 
			
		||||
    var result;
 | 
			
		||||
    if (/^\${[^}]+}$/.test(value)) {
 | 
			
		||||
        // ${ENV_VAR}
 | 
			
		||||
        var name = value.substring(2,value.length-1);
 | 
			
		||||
        result = getSetting(node, name, flow, callback);
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        result = getSetting(node, name, flow);
 | 
			
		||||
    } else if (!/\${\S+}/.test(value)) {
 | 
			
		||||
        // ENV_VAR
 | 
			
		||||
        result = getSetting(node, value, flow, callback);
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        result = getSetting(node, value, flow);
 | 
			
		||||
    } else {
 | 
			
		||||
        // FOO${ENV_VAR}BAR
 | 
			
		||||
        return value.replace(/\${([^}]+)}/g, function(match, name) {
 | 
			
		||||
@@ -635,7 +586,8 @@ function evaluateEnvProperty(value, node, callback) {
 | 
			
		||||
            return (val === undefined)?"":val;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    return returnOrCallback(null, (result === undefined)?"":result);
 | 
			
		||||
    return (result === undefined)?"":result;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -723,10 +675,7 @@ function evaluateNodeProperty(value, type, node, msg, callback) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
    } else if (type === 'env') {
 | 
			
		||||
        result = evaluateEnvProperty(value, node, callback);
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        result = evaluateEnvProperty(value, node);
 | 
			
		||||
    }
 | 
			
		||||
    if (callback) {
 | 
			
		||||
        callback(null,result);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								test/node_modules/nr-test-utils/index.js
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								test/node_modules/nr-test-utils/index.js
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -16,7 +16,6 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const path = require("path");
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
 | 
			
		||||
const PACKAGE_ROOT = "../../../packages/node_modules";
 | 
			
		||||
 | 
			
		||||
@@ -27,5 +26,10 @@ module.exports = {
 | 
			
		||||
    },
 | 
			
		||||
    resolve: function(file) {
 | 
			
		||||
        return path.resolve(path.join(__dirname,PACKAGE_ROOT,file));
 | 
			
		||||
    },
 | 
			
		||||
    sleep: async (time) => {
 | 
			
		||||
        return new Promise(resolve => {
 | 
			
		||||
            setTimeout(resolve, time)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,9 @@ var helper = require("node-red-node-test-helper");
 | 
			
		||||
describe('inject node', function() {
 | 
			
		||||
 | 
			
		||||
    beforeEach(function(done) {
 | 
			
		||||
        helper.startServer(done);
 | 
			
		||||
        helper.startServer(() => {
 | 
			
		||||
            done()
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    function initContext(done) {
 | 
			
		||||
@@ -41,7 +43,7 @@ describe('inject node', function() {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    afterEach(function(done) {
 | 
			
		||||
    afterEach(async function() {
 | 
			
		||||
        helper.unload().then(function () {
 | 
			
		||||
            return Context.clean({allNodes: {}});
 | 
			
		||||
        }).then(function () {
 | 
			
		||||
@@ -53,8 +55,11 @@ describe('inject node', function() {
 | 
			
		||||
 | 
			
		||||
    function basicTest(type, val, rval) {
 | 
			
		||||
        it('inject value ('+type+')', function (done) {
 | 
			
		||||
            var flow = [{id: "n1", type: "inject", topic: "t1", payload: val, payloadType: type, wires: [["n2"]], z: "flow"},
 | 
			
		||||
            {id: "n2", type: "helper"}];
 | 
			
		||||
            var flow = [
 | 
			
		||||
                {id:'flow', type:'tab'},
 | 
			
		||||
                {id: "n1", type: "inject", topic: "t1", payload: val, payloadType: type, wires: [["n2"]], z: "flow"},
 | 
			
		||||
                {id: "n2", type: "helper", z:'flow'}
 | 
			
		||||
            ];
 | 
			
		||||
            helper.load(injectNode, flow, function () {
 | 
			
		||||
                var n1 = helper.getNode("n1");
 | 
			
		||||
                var n2 = helper.getNode("n2");
 | 
			
		||||
@@ -93,6 +98,7 @@ describe('inject node', function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            n2.on("input", function (msg) {
 | 
			
		||||
                delete process.env.NR_TEST
 | 
			
		||||
                try {
 | 
			
		||||
                    msg.should.have.property("topic", "t1");
 | 
			
		||||
                    msg.should.have.property("payload", "foo");
 | 
			
		||||
@@ -101,7 +107,7 @@ describe('inject node', function() {
 | 
			
		||||
                    done(err);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
	    process.env.NR_TEST = 'foo';
 | 
			
		||||
            process.env.NR_TEST = 'foo';
 | 
			
		||||
            n1.receive({});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
@@ -202,9 +208,10 @@ describe('inject node', function() {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('inject name of group as environment variable ', function (done) {
 | 
			
		||||
        var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_NAME", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"},
 | 
			
		||||
                    {id: "n2", type: "helper"},
 | 
			
		||||
                    {id: "g0", type: "group", name: "GROUP" },
 | 
			
		||||
        var flow = [{id: "flow", type: "tab" },
 | 
			
		||||
                    {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_NAME", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"},
 | 
			
		||||
                    {id: "n2", type: "helper", z: "flow"},
 | 
			
		||||
                    {id: "g0", type: "group", name: "GROUP", z: "flow" },
 | 
			
		||||
                   ];
 | 
			
		||||
        helper.load(injectNode, flow, function () {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
@@ -222,9 +229,10 @@ describe('inject node', function() {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('inject id of group as environment variable ', function (done) {
 | 
			
		||||
        var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_ID", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"},
 | 
			
		||||
                    {id: "n2", type: "helper"},
 | 
			
		||||
                    {id: "g0", type: "group", name: "GROUP" },
 | 
			
		||||
        var flow = [{id: "flow", type: "tab" },
 | 
			
		||||
                    {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_ID", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"},
 | 
			
		||||
                    {id: "n2", type: "helper", z: "flow"},
 | 
			
		||||
                    {id: "g0", type: "group", name: "GROUP", z: "flow" },
 | 
			
		||||
                   ];
 | 
			
		||||
        helper.load(injectNode, flow, function () {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
@@ -243,8 +251,9 @@ describe('inject node', function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    it('inject name of node as environment variable by substitution ', function (done) {
 | 
			
		||||
        var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_NODE_NAME}", payloadType: "str", wires: [["n2"]], z: "flow"},
 | 
			
		||||
                    {id: "n2", type: "helper"}];
 | 
			
		||||
        var flow = [{id: "flow", type: "tab" },
 | 
			
		||||
                    {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_NODE_NAME}", payloadType: "str", wires: [["n2"]], z: "flow"},
 | 
			
		||||
                    {id: "n2", type: "helper", z: "flow"}];
 | 
			
		||||
        helper.load(injectNode, flow, function () {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
@@ -338,9 +347,10 @@ describe('inject node', function() {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('inject name of group as environment variable by substitution ', function (done) {
 | 
			
		||||
        var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_NAME}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"},
 | 
			
		||||
                    {id: "n2", type: "helper"},
 | 
			
		||||
                    {id: "g0", type: "group", name: "GROUP" },
 | 
			
		||||
        var flow = [{id: "flow", type: "tab" },
 | 
			
		||||
                    {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_NAME}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"},
 | 
			
		||||
                    {id: "n2", type: "helper", z: "flow"},
 | 
			
		||||
                    {id: "g0", type: "group", name: "GROUP", z: "flow" },
 | 
			
		||||
                   ];
 | 
			
		||||
        helper.load(injectNode, flow, function () {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
@@ -358,9 +368,10 @@ describe('inject node', function() {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('inject id of group as environment variable by substitution ', function (done) {
 | 
			
		||||
        var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_ID}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"},
 | 
			
		||||
                    {id: "n2", type: "helper"},
 | 
			
		||||
                    {id: "g0", type: "group", name: "GROUP" },
 | 
			
		||||
        var flow = [{id: "flow", type: "tab" },
 | 
			
		||||
                    {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_ID}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"},
 | 
			
		||||
                    {id: "n2", type: "helper", z: "flow"},
 | 
			
		||||
                    {id: "g0", type: "group", name: "GROUP", z: "flow" },
 | 
			
		||||
                   ];
 | 
			
		||||
        helper.load(injectNode, flow, function () {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ var config = require("nr-test-utils").require("@node-red/nodes/core/common/91-gl
 | 
			
		||||
var inject = require("nr-test-utils").require("@node-red/nodes/core/common/20-inject.js");
 | 
			
		||||
var helper = require("node-red-node-test-helper");
 | 
			
		||||
 | 
			
		||||
describe('unknown Node', function() {
 | 
			
		||||
describe('Global Config Node', function() {
 | 
			
		||||
 | 
			
		||||
    afterEach(function() {
 | 
			
		||||
        helper.unload();
 | 
			
		||||
 
 | 
			
		||||
@@ -568,11 +568,12 @@ describe('change Node', function() {
 | 
			
		||||
 | 
			
		||||
            it('sets the value using env property from group', function(done) {
 | 
			
		||||
                var flow = [
 | 
			
		||||
                    {"id": "flow", type:"tab"},
 | 
			
		||||
                    {"id":"group1","type":"group","env":[
 | 
			
		||||
                        {"name":"NR_TEST_A", "value":"bar", "type": "str"}
 | 
			
		||||
                    ]},
 | 
			
		||||
                    {"id":"changeNode1","type":"change","g":"group1",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]},
 | 
			
		||||
                    {id:"helperNode1", type:"helper", wires:[]}
 | 
			
		||||
                    ], z: "flow"},
 | 
			
		||||
                    {"id":"changeNode1","type":"change","g":"group1",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]], z: "flow"},
 | 
			
		||||
                    {id:"helperNode1", type:"helper", wires:[], z: "flow"}
 | 
			
		||||
                ];
 | 
			
		||||
                helper.load(changeNode, flow, function() {
 | 
			
		||||
                    var changeNode1 = helper.getNode("changeNode1");
 | 
			
		||||
@@ -591,12 +592,13 @@ describe('change Node', function() {
 | 
			
		||||
 | 
			
		||||
            it('sets the value using env property from nested group', function(done) {
 | 
			
		||||
                var flow = [
 | 
			
		||||
                    {"id": "flow", type:"tab"},
 | 
			
		||||
                    {"id":"group1","type":"group","env":[
 | 
			
		||||
                        {"name":"NR_TEST_A", "value":"bar", "type": "str"}
 | 
			
		||||
                    ]},
 | 
			
		||||
                    {"id":"group2","type":"group","g":"group1","env":[]},
 | 
			
		||||
                    {"id":"changeNode1","type":"change","g":"group2",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]},
 | 
			
		||||
                    {id:"helperNode1", type:"helper", wires:[]}
 | 
			
		||||
                    ], z: "flow"},
 | 
			
		||||
                    {"id":"group2","type":"group","g":"group1","env":[], z: "flow"},
 | 
			
		||||
                    {"id":"changeNode1","type":"change","g":"group2",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]], z: "flow"},
 | 
			
		||||
                    {id:"helperNode1", type:"helper", wires:[], z: "flow"}
 | 
			
		||||
                ];
 | 
			
		||||
                helper.load(changeNode, flow, function() {
 | 
			
		||||
                    var changeNode1 = helper.getNode("changeNode1");
 | 
			
		||||
 
 | 
			
		||||
@@ -253,35 +253,32 @@ describe('subflow', function() {
 | 
			
		||||
 | 
			
		||||
    it('should access typed value of env var', function(done) {
 | 
			
		||||
        var flow = [
 | 
			
		||||
            {id:"t0", type:"tab", label:"", disabled:false, info:""},
 | 
			
		||||
            {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1",
 | 
			
		||||
             env: [
 | 
			
		||||
                 {name: "KN", type: "num", value: "100"},
 | 
			
		||||
                 {name: "KB", type: "bool", value: "true"},
 | 
			
		||||
                 {name: "KJ", type: "json", value: "[1,2,3]"},
 | 
			
		||||
                 {name: "Kb", type: "bin", value: "[65,65]"},
 | 
			
		||||
                 {name: "Ke", type: "env", value: "KS"},
 | 
			
		||||
                 {name: "Kj", type: "jsonata", value: "1+2"},
 | 
			
		||||
             ],
 | 
			
		||||
             wires:[["n2"]]},
 | 
			
		||||
            {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]},
 | 
			
		||||
            // Subflow
 | 
			
		||||
            {id:"s1", type:"subflow", name:"Subflow", info:"",
 | 
			
		||||
             in:[{
 | 
			
		||||
                 x:10, y:10,
 | 
			
		||||
                 wires:[ {id:"s1-n1"} ]
 | 
			
		||||
             }],
 | 
			
		||||
             out:[{
 | 
			
		||||
                 x:10, y:10,
 | 
			
		||||
                 wires:[ {id:"s1-n1", port:0} ]
 | 
			
		||||
             }],
 | 
			
		||||
             env: [
 | 
			
		||||
                 {name: "KS", type: "str", value: "STR"}
 | 
			
		||||
             ]
 | 
			
		||||
            { id: "t0", type: "tab", label: "", disabled: false, info: "" },
 | 
			
		||||
            {
 | 
			
		||||
                id: "n1", x: 10, y: 10, z: "t0", type: "subflow:s1",
 | 
			
		||||
                env: [
 | 
			
		||||
                    { name: "KN", type: "num", value: "100" },
 | 
			
		||||
                    { name: "KB", type: "bool", value: "true" },
 | 
			
		||||
                    { name: "KJ", type: "json", value: "[1,2,3]" },
 | 
			
		||||
                    { name: "Kb", type: "bin", value: "[65,65]" },
 | 
			
		||||
                    { name: "Ke", type: "env", value: "KS" },
 | 
			
		||||
                    { name: "Kj", type: "jsonata", value: "1+2" },
 | 
			
		||||
                ],
 | 
			
		||||
                wires: [["n2"]]
 | 
			
		||||
            },
 | 
			
		||||
            {id:"s1-n1", x:10, y:10, z:"s1", type:"function",
 | 
			
		||||
             func:"msg.VE = env.get('Ke'); msg.VS = env.get('KS'); msg.VN = env.get('KN'); msg.VB = env.get('KB'); msg.VJ = env.get('KJ'); msg.Vb = env.get('Kb'); msg.Vj = env.get('Kj'); return msg;",
 | 
			
		||||
             wires:[]}
 | 
			
		||||
            { id: "n2", x: 10, y: 10, z: "t0", type: "helper", wires: [] },
 | 
			
		||||
            // Subflow
 | 
			
		||||
            {
 | 
			
		||||
                id: "s1", type: "subflow", name: "Subflow", info: "",
 | 
			
		||||
                in: [{ x: 10, y: 10, wires: [{ id: "s1-n1" }] }],
 | 
			
		||||
                out: [{ x: 10, y: 10, wires: [{ id: "s1-n1", port: 0 }] }],
 | 
			
		||||
                env: [{ name: "KS", type: "str", value: "STR" }]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id: "s1-n1", x: 10, y: 10, z: "s1", type: "function",
 | 
			
		||||
                func: "msg.VE = env.get('Ke'); msg.VS = env.get('KS'); msg.VN = env.get('KN'); msg.VB = env.get('KB'); msg.VJ = env.get('KJ'); msg.Vb = env.get('Kb'); msg.Vj = env.get('Kj'); return msg;",
 | 
			
		||||
                wires: []
 | 
			
		||||
            }
 | 
			
		||||
        ];
 | 
			
		||||
        helper.load(functionNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										48
									
								
								test/unit/@node-red/runtime/lib/flows/Group_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								test/unit/@node-red/runtime/lib/flows/Group_spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
const should = require("should");
 | 
			
		||||
const NR_TEST_UTILS = require("nr-test-utils");
 | 
			
		||||
const { Group } = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Group");
 | 
			
		||||
 | 
			
		||||
describe('Group', function () {
 | 
			
		||||
    describe('getSetting', function () {
 | 
			
		||||
        it("returns group name/id", async function () {
 | 
			
		||||
            const group = new Group({
 | 
			
		||||
                getSetting: v => v+v
 | 
			
		||||
            }, {
 | 
			
		||||
                name: "g1",
 | 
			
		||||
                id: "group1"
 | 
			
		||||
            })
 | 
			
		||||
            await group.start()
 | 
			
		||||
 | 
			
		||||
            group.getSetting("NR_GROUP_NAME").should.equal("g1")
 | 
			
		||||
            group.getSetting("NR_GROUP_ID").should.equal("group1")
 | 
			
		||||
        })
 | 
			
		||||
        it("delegates to parent if not found", async function () {
 | 
			
		||||
            const group = new Group({
 | 
			
		||||
                getSetting: v => v+v
 | 
			
		||||
            }, {
 | 
			
		||||
                name: "g1",
 | 
			
		||||
                id: "group1"
 | 
			
		||||
            })
 | 
			
		||||
            await group.start()
 | 
			
		||||
 | 
			
		||||
            group.getSetting("123").should.equal("123123")
 | 
			
		||||
        })
 | 
			
		||||
        it("delegates to parent if explicit requested", async function () {
 | 
			
		||||
            const parentGroup = new Group({
 | 
			
		||||
                getSetting: v => v+v
 | 
			
		||||
            }, {
 | 
			
		||||
                name: "g0",
 | 
			
		||||
                id: "group0"
 | 
			
		||||
            })
 | 
			
		||||
            const group = new Group(parentGroup, {
 | 
			
		||||
                name: "g1",
 | 
			
		||||
                id: "group1"
 | 
			
		||||
            })
 | 
			
		||||
            await parentGroup.start()
 | 
			
		||||
            await group.start()
 | 
			
		||||
 | 
			
		||||
            group.getSetting("$parent.NR_GROUP_NAME").should.equal("g0")
 | 
			
		||||
            group.getSetting("$parent.NR_GROUP_ID").should.equal("group0")
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
})
 | 
			
		||||
@@ -68,11 +68,13 @@ describe('Subflow', function() {
 | 
			
		||||
        this.handled = 0;
 | 
			
		||||
        this.stopped = false;
 | 
			
		||||
        this.received = null;
 | 
			
		||||
        this.receivedEnv = null;
 | 
			
		||||
        currentNodes[node.id] = node;
 | 
			
		||||
        this.on('input',function(msg) {
 | 
			
		||||
            // console.log(this.id,msg.payload);
 | 
			
		||||
            node.handled++;
 | 
			
		||||
            node.received = msg.payload;
 | 
			
		||||
            node.receivedEnv = msg.receivedEnv;
 | 
			
		||||
            node.send(msg);
 | 
			
		||||
        });
 | 
			
		||||
        this.on('close',function() {
 | 
			
		||||
@@ -185,7 +187,15 @@ describe('Subflow', function() {
 | 
			
		||||
            var flow = node._flow;
 | 
			
		||||
            var val = flow.getSetting("__KEY__");
 | 
			
		||||
            node.received = val;
 | 
			
		||||
            node.send({payload: val});
 | 
			
		||||
            const receivedEnv = {}
 | 
			
		||||
            try {
 | 
			
		||||
                ['__KEY__','__KEY1__','__KEY2__','__KEY3__','__KEY4__'].forEach(k => {
 | 
			
		||||
                    receivedEnv[k] = flow.getSetting(k)
 | 
			
		||||
                })
 | 
			
		||||
            } catch (err) {
 | 
			
		||||
                console.log(err)
 | 
			
		||||
            }
 | 
			
		||||
            node.send({payload: val, receivedEnv});
 | 
			
		||||
        });
 | 
			
		||||
        this.on('close',function() {
 | 
			
		||||
            node.stopped = true;
 | 
			
		||||
@@ -282,7 +292,7 @@ describe('Subflow', function() {
 | 
			
		||||
        getType.restore();
 | 
			
		||||
    });
 | 
			
		||||
    describe('#start',function() {
 | 
			
		||||
        it("instantiates a subflow and stops it",function(done) {
 | 
			
		||||
        it("instantiates a subflow and stops it", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
 | 
			
		||||
@@ -297,7 +307,7 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({handleError: (a,b,c) => { console.log(a,b,c); }},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
            Object.keys(activeNodes).should.have.length(4);
 | 
			
		||||
@@ -332,37 +342,21 @@ describe('Subflow', function() {
 | 
			
		||||
            // currentNodes[sfInstanceId2].should.have.a.property("handled",0);
 | 
			
		||||
 | 
			
		||||
            currentNodes["1"].receive({payload:"test"});
 | 
			
		||||
           
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["1"].should.have.a.property("handled",1);
 | 
			
		||||
                // currentNodes[sfInstanceId].should.have.a.property("handled",1);
 | 
			
		||||
                // currentNodes[sfInstanceId2].should.have.a.property("handled",1);
 | 
			
		||||
                currentNodes["3"].should.have.a.property("handled",1);
 | 
			
		||||
                currentNodes["4"].should.have.a.property("handled",1);
 | 
			
		||||
            currentNodes["1"].should.have.a.property("handled",1);
 | 
			
		||||
            // currentNodes[sfInstanceId].should.have.a.property("handled",1);
 | 
			
		||||
            // currentNodes[sfInstanceId2].should.have.a.property("handled",1);
 | 
			
		||||
            currentNodes["3"].should.have.a.property("handled",1);
 | 
			
		||||
            currentNodes["4"].should.have.a.property("handled",1);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    Object.keys(currentNodes).should.have.length(0);
 | 
			
		||||
                    Object.keys(stoppedNodes).should.have.length(6);
 | 
			
		||||
 | 
			
		||||
                    // currentNodes.should.not.have.a.property("1");
 | 
			
		||||
                    // currentNodes.should.not.have.a.property("3");
 | 
			
		||||
                    // currentNodes.should.not.have.a.property("4");
 | 
			
		||||
                    // // currentNodes.should.not.have.a.property(sfInstanceId);
 | 
			
		||||
                    // // currentNodes.should.not.have.a.property(sfInstanceId2);
 | 
			
		||||
                    // // currentNodes.should.not.have.a.property(sfConfigId);
 | 
			
		||||
                    // stoppedNodes.should.have.a.property("1");
 | 
			
		||||
                    // stoppedNodes.should.have.a.property("3");
 | 
			
		||||
                    // stoppedNodes.should.have.a.property("4");
 | 
			
		||||
                    // // stoppedNodes.should.have.a.property(sfInstanceId);
 | 
			
		||||
                    // // stoppedNodes.should.have.a.property(sfInstanceId2);
 | 
			
		||||
                    // // stoppedNodes.should.have.a.property(sfConfigId);
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
            Object.keys(currentNodes).should.have.length(0);
 | 
			
		||||
            Object.keys(stoppedNodes).should.have.length(6);
 | 
			
		||||
        });
 | 
			
		||||
        it("instantiates a subflow inside a subflow and stops it",function(done) {
 | 
			
		||||
 | 
			
		||||
        it("instantiates a subflow inside a subflow and stops it", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
 | 
			
		||||
@@ -379,24 +373,20 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            currentNodes["1"].should.have.a.property("handled",0);
 | 
			
		||||
            currentNodes["3"].should.have.a.property("handled",0);
 | 
			
		||||
 | 
			
		||||
            currentNodes["1"].receive({payload:"test"});
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["1"].should.have.a.property("handled",1);
 | 
			
		||||
                currentNodes["3"].should.have.a.property("handled",1);
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    Object.keys(currentNodes).should.have.length(0);
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            currentNodes["1"].should.have.a.property("handled",1);
 | 
			
		||||
            currentNodes["3"].should.have.a.property("handled",1);
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
            Object.keys(currentNodes).should.have.length(0);
 | 
			
		||||
        });
 | 
			
		||||
        it("rewires a subflow node on update/start",function(done){
 | 
			
		||||
 | 
			
		||||
        it("rewires a subflow node on update/start", async function(){
 | 
			
		||||
            var rawConfig = [
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
 | 
			
		||||
@@ -417,7 +407,7 @@ describe('Subflow', function() {
 | 
			
		||||
            var diff = flowUtils.diffConfigs(config,newConfig);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
            Object.keys(activeNodes).should.have.length(4);
 | 
			
		||||
@@ -429,36 +419,28 @@ describe('Subflow', function() {
 | 
			
		||||
            currentNodes["4"].should.have.a.property("handled",0);
 | 
			
		||||
 | 
			
		||||
            currentNodes["1"].receive({payload:"test"});
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            currentNodes["1"].should.have.a.property("handled",1);
 | 
			
		||||
            // currentNodes[sfInstanceId].should.have.a.property("handled",1);
 | 
			
		||||
            // currentNodes[sfInstanceId2].should.have.a.property("handled",1);
 | 
			
		||||
            currentNodes["3"].should.have.a.property("handled",1);
 | 
			
		||||
            currentNodes["4"].should.have.a.property("handled",0);
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["1"].should.have.a.property("handled",1);
 | 
			
		||||
                // currentNodes[sfInstanceId].should.have.a.property("handled",1);
 | 
			
		||||
                // currentNodes[sfInstanceId2].should.have.a.property("handled",1);
 | 
			
		||||
                currentNodes["3"].should.have.a.property("handled",1);
 | 
			
		||||
                currentNodes["4"].should.have.a.property("handled",0);
 | 
			
		||||
            flow.update(newConfig,newConfig.flows["t1"]);
 | 
			
		||||
            await flow.start(diff)
 | 
			
		||||
            currentNodes["1"].receive({payload:"test2"});
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            currentNodes["1"].should.have.a.property("handled",2);
 | 
			
		||||
            // currentNodes[sfInstanceId].should.have.a.property("handled",2);
 | 
			
		||||
            // currentNodes[sfInstanceId2].should.have.a.property("handled",2);
 | 
			
		||||
            currentNodes["3"].should.have.a.property("handled",1);
 | 
			
		||||
            currentNodes["4"].should.have.a.property("handled",1);
 | 
			
		||||
 | 
			
		||||
                flow.update(newConfig,newConfig.flows["t1"]);
 | 
			
		||||
                flow.start(diff)
 | 
			
		||||
 | 
			
		||||
                currentNodes["1"].receive({payload:"test2"});
 | 
			
		||||
                setTimeout(function() {
 | 
			
		||||
 | 
			
		||||
                    currentNodes["1"].should.have.a.property("handled",2);
 | 
			
		||||
                    // currentNodes[sfInstanceId].should.have.a.property("handled",2);
 | 
			
		||||
                    // currentNodes[sfInstanceId2].should.have.a.property("handled",2);
 | 
			
		||||
                    currentNodes["3"].should.have.a.property("handled",1);
 | 
			
		||||
                    currentNodes["4"].should.have.a.property("handled",1);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    flow.stop().then(function() {
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                },150);
 | 
			
		||||
            },150);
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    describe('#stop', function() {
 | 
			
		||||
        it("stops subflow instance nodes",function(done) {
 | 
			
		||||
        it("stops subflow instance nodes", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
 | 
			
		||||
@@ -470,20 +452,18 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
            Object.keys(activeNodes).should.have.length(3);
 | 
			
		||||
            Object.keys(stoppedNodes).should.have.length(0);
 | 
			
		||||
            flow.stop(["2"]).then(function() {
 | 
			
		||||
                Object.keys(currentNodes).should.have.length(2);
 | 
			
		||||
                Object.keys(stoppedNodes).should.have.length(1);
 | 
			
		||||
                done();
 | 
			
		||||
            }).catch(done);
 | 
			
		||||
            await flow.stop(["2"])
 | 
			
		||||
            Object.keys(currentNodes).should.have.length(2);
 | 
			
		||||
            Object.keys(stoppedNodes).should.have.length(1);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    describe("#handleStatus",function() {
 | 
			
		||||
        it("passes a status event to the subflow's parent tab status node - all scope",function(done) {
 | 
			
		||||
        it("passes a status event to the subflow's parent tab status node - all scope", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
 | 
			
		||||
@@ -496,27 +476,24 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
 | 
			
		||||
            activeNodes["1"].receive({payload:"test"});
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
                var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
            var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
 | 
			
		||||
                statusMessage.should.have.a.property("status");
 | 
			
		||||
                statusMessage.status.should.have.a.property("text","test status");
 | 
			
		||||
                statusMessage.status.should.have.a.property("source");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("type","testStatus");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("name","test-status-node");
 | 
			
		||||
            statusMessage.should.have.a.property("status");
 | 
			
		||||
            statusMessage.status.should.have.a.property("text","test status");
 | 
			
		||||
            statusMessage.status.should.have.a.property("source");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("type","testStatus");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("name","test-status-node");
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
        it("passes a status event to the subflow's parent tab status node - targetted scope",function(done) {
 | 
			
		||||
        it("passes a status event to the subflow's parent tab status node - targetted scope", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
 | 
			
		||||
@@ -531,34 +508,30 @@ describe('Subflow', function() {
 | 
			
		||||
 | 
			
		||||
            var flow = Flow.create({handleStatus:() => { parentFlowStatusCalled = true} },config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
 | 
			
		||||
            activeNodes["1"].receive({payload:"test"});
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                parentFlowStatusCalled.should.be.false();
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            parentFlowStatusCalled.should.be.false();
 | 
			
		||||
 | 
			
		||||
                currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
                var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
            currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
            var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
 | 
			
		||||
                statusMessage.should.have.a.property("status");
 | 
			
		||||
                statusMessage.status.should.have.a.property("text","test status");
 | 
			
		||||
                statusMessage.status.should.have.a.property("source");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("type","testStatus");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("name","test-status-node");
 | 
			
		||||
            statusMessage.should.have.a.property("status");
 | 
			
		||||
            statusMessage.status.should.have.a.property("text","test status");
 | 
			
		||||
            statusMessage.status.should.have.a.property("source");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("type","testStatus");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("name","test-status-node");
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe("status node", function() {
 | 
			
		||||
        it("emits a status event when a message is passed to a subflow-status node - msg.payload as string", function(done) {
 | 
			
		||||
        it("emits a status event when a message is passed to a subflow-status node - msg.payload as string", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
 | 
			
		||||
@@ -578,29 +551,24 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
 | 
			
		||||
            activeNodes["1"].receive({payload:"test-payload"});
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
                var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
            currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
            var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
 | 
			
		||||
                statusMessage.should.have.a.property("status");
 | 
			
		||||
                statusMessage.status.should.have.a.property("text","test-payload");
 | 
			
		||||
                statusMessage.status.should.have.a.property("source");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("id","2");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("type","subflow:sf1");
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            statusMessage.should.have.a.property("status");
 | 
			
		||||
            statusMessage.status.should.have.a.property("text","test-payload");
 | 
			
		||||
            statusMessage.status.should.have.a.property("source");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("id","2");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("type","subflow:sf1");
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
        it("emits a status event when a message is passed to a subflow-status node - msg.payload as status obj", function(done) {
 | 
			
		||||
        it("emits a status event when a message is passed to a subflow-status node - msg.payload as status obj", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
 | 
			
		||||
@@ -620,29 +588,26 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
 | 
			
		||||
            activeNodes["1"].receive({payload:{text:"payload-obj"}});
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
                var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
 | 
			
		||||
                statusMessage.should.have.a.property("status");
 | 
			
		||||
                statusMessage.status.should.have.a.property("text","payload-obj");
 | 
			
		||||
                statusMessage.status.should.have.a.property("source");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("id","2");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("type","subflow:sf1");
 | 
			
		||||
            currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
            var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
            statusMessage.should.have.a.property("status");
 | 
			
		||||
            statusMessage.status.should.have.a.property("text","payload-obj");
 | 
			
		||||
            statusMessage.status.should.have.a.property("source");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("id","2");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("type","subflow:sf1");
 | 
			
		||||
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
        it("emits a status event when a message is passed to a subflow-status node - msg.status", function(done) {
 | 
			
		||||
        it("emits a status event when a message is passed to a subflow-status node - msg.status", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
 | 
			
		||||
@@ -662,29 +627,26 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
 | 
			
		||||
            activeNodes["1"].receive({status:{text:"status-obj"}});
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
                var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
 | 
			
		||||
                statusMessage.should.have.a.property("status");
 | 
			
		||||
                statusMessage.status.should.have.a.property("text","status-obj");
 | 
			
		||||
                statusMessage.status.should.have.a.property("source");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("id","2");
 | 
			
		||||
                statusMessage.status.source.should.have.a.property("type","subflow:sf1");
 | 
			
		||||
            currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
            var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
            statusMessage.should.have.a.property("status");
 | 
			
		||||
            statusMessage.status.should.have.a.property("text","status-obj");
 | 
			
		||||
            statusMessage.status.should.have.a.property("source");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("id","2");
 | 
			
		||||
            statusMessage.status.source.should.have.a.property("type","subflow:sf1");
 | 
			
		||||
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
        it("does not emit a regular status event if it contains a subflow-status node", function(done) {
 | 
			
		||||
        it("does not emit a regular status event if it contains a subflow-status node", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
 | 
			
		||||
@@ -704,7 +666,7 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
 | 
			
		||||
@@ -712,15 +674,12 @@ describe('Subflow', function() {
 | 
			
		||||
 | 
			
		||||
            currentNodes["sn"].should.have.a.property("handled",0);
 | 
			
		||||
 | 
			
		||||
            flow.stop().then(function() {
 | 
			
		||||
 | 
			
		||||
                done();
 | 
			
		||||
            });
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    describe("#handleError",function() {
 | 
			
		||||
        it("passes an error event to the subflow's parent tab catch node - all scope",function(done) {
 | 
			
		||||
        it("passes an error event to the subflow's parent tab catch node - all scope",async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
 | 
			
		||||
@@ -733,28 +692,26 @@ describe('Subflow', function() {
 | 
			
		||||
            ]);
 | 
			
		||||
            var flow = Flow.create({},config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
 | 
			
		||||
            activeNodes["1"].receive({payload:"test"});
 | 
			
		||||
            
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
                var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
            currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
            var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
 | 
			
		||||
                statusMessage.should.have.a.property("error");
 | 
			
		||||
                statusMessage.error.should.have.a.property("message","test error");
 | 
			
		||||
                statusMessage.error.should.have.a.property("source");
 | 
			
		||||
                statusMessage.error.source.should.have.a.property("type","testError");
 | 
			
		||||
                statusMessage.error.source.should.have.a.property("name","test-error-node");
 | 
			
		||||
            statusMessage.should.have.a.property("error");
 | 
			
		||||
            statusMessage.error.should.have.a.property("message","test error");
 | 
			
		||||
            statusMessage.error.should.have.a.property("source");
 | 
			
		||||
            statusMessage.error.source.should.have.a.property("type","testError");
 | 
			
		||||
            statusMessage.error.source.should.have.a.property("name","test-error-node");
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
        it("passes an error event to the subflow's parent tab catch node - targetted scope",function(done) {
 | 
			
		||||
        it("passes an error event to the subflow's parent tab catch node - targetted scope", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
 | 
			
		||||
@@ -768,50 +725,31 @@ describe('Subflow', function() {
 | 
			
		||||
            var parentFlowErrorCalled = false;
 | 
			
		||||
            var flow = Flow.create({handleError:() => { parentFlowErrorCalled = true} },config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var activeNodes = flow.getActiveNodes();
 | 
			
		||||
 | 
			
		||||
            activeNodes["1"].receive({payload:"test"});
 | 
			
		||||
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                parentFlowErrorCalled.should.be.false();
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            
 | 
			
		||||
            parentFlowErrorCalled.should.be.false();
 | 
			
		||||
 | 
			
		||||
                currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
                var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
            currentNodes["sn"].should.have.a.property("handled",1);
 | 
			
		||||
            var statusMessage = currentNodes["sn"].messages[0];
 | 
			
		||||
 | 
			
		||||
                statusMessage.should.have.a.property("error");
 | 
			
		||||
                statusMessage.error.should.have.a.property("message","test error");
 | 
			
		||||
                statusMessage.error.should.have.a.property("source");
 | 
			
		||||
                statusMessage.error.source.should.have.a.property("type","testError");
 | 
			
		||||
                statusMessage.error.source.should.have.a.property("name","test-error-node");
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            statusMessage.should.have.a.property("error");
 | 
			
		||||
            statusMessage.error.should.have.a.property("message","test error");
 | 
			
		||||
            statusMessage.error.should.have.a.property("source");
 | 
			
		||||
            statusMessage.error.source.should.have.a.property("type","testError");
 | 
			
		||||
            statusMessage.error.source.should.have.a.property("name","test-error-node");
 | 
			
		||||
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe("#env var", function() {
 | 
			
		||||
        // should be changed according to internal env var representation
 | 
			
		||||
        function setEnv(node, key, val) {
 | 
			
		||||
            var flow = node._flow;
 | 
			
		||||
            if (flow) {
 | 
			
		||||
                var env = flow.env;
 | 
			
		||||
                if (!env) {
 | 
			
		||||
                    env = flow.env = {};
 | 
			
		||||
                }
 | 
			
		||||
                env[key] = {
 | 
			
		||||
                        name: key,
 | 
			
		||||
                        type: "str",
 | 
			
		||||
                        value: val
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        it("can access process env var", function(done) {
 | 
			
		||||
        it("can access process env var", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]},
 | 
			
		||||
@@ -828,29 +766,25 @@ describe('Subflow', function() {
 | 
			
		||||
                handleError: (a,b,c) => { console.log(a,b,c); }
 | 
			
		||||
            },config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            process.env["__KEY__"] = "__VAL__";
 | 
			
		||||
 | 
			
		||||
            currentNodes["1"].receive({payload: "test"});
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["3"].should.have.a.property("received", "__VAL__");
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            currentNodes["3"].should.have.a.property("received", "__VAL__");
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("can access subflow env var", function(done) {
 | 
			
		||||
        it("can access subflow env var", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]},
 | 
			
		||||
                {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
 | 
			
		||||
                {id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]},
 | 
			
		||||
                {id:"sf1",type:"subflow",name:"Subflow 2",info:"",
 | 
			
		||||
                 "in":[ {wires:[{id:"sf1-1"}]} ],
 | 
			
		||||
                 "out":[ {wires:[{id:"sf1-2",port:0}]} ]},
 | 
			
		||||
                {id:"sf1",type:"subflow",name:"Subflow 2",info:"",env: [{name: '__KEY__', value: '__VAL1__', type: 'str'}],
 | 
			
		||||
                    "in":[ {wires:[{id:"sf1-1"}]} ],
 | 
			
		||||
                    "out":[ {wires:[{id:"sf1-2",port:0}]} ]},
 | 
			
		||||
                {id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]},
 | 
			
		||||
                {id:"sf1-2",type:"testEnv",z:"sf1",foo:"sf1.2",x:166,y:99,wires:[[]]}
 | 
			
		||||
            ]);
 | 
			
		||||
@@ -859,7 +793,7 @@ describe('Subflow', function() {
 | 
			
		||||
                handleError: (a,b,c) => { console.log(a,b,c); }
 | 
			
		||||
            },config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            var testenv_node = null;
 | 
			
		||||
            for (var n in currentNodes) {
 | 
			
		||||
@@ -870,32 +804,30 @@ describe('Subflow', function() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            process.env["__KEY__"] = "__VAL0__";
 | 
			
		||||
            setEnv(testenv_node, "__KEY__", "__VAL1__");
 | 
			
		||||
 | 
			
		||||
            currentNodes["1"].receive({payload: "test"});
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["3"].should.have.a.property("received", "__VAL1__");
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            currentNodes["3"].should.have.a.property("received", "__VAL1__");
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("can access nested subflow env var", function(done) {
 | 
			
		||||
        it("can access nested subflow env var", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"t1",type:"tab", env: [{name: '__KEY1__', value: 't1', type: 'str'}]},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]},
 | 
			
		||||
                {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
 | 
			
		||||
                {id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]},
 | 
			
		||||
                {id:"sf1",type:"subflow",name:"Subflow 1",info:"",
 | 
			
		||||
                 in:[{wires:[{id:"sf1-1"}]}],
 | 
			
		||||
                 out:[{wires:[{id:"sf1-2",port:0}]}]},
 | 
			
		||||
                    env: [{name: '__KEY2__', value: 'sf1', type: 'str'}],
 | 
			
		||||
                    in:[{wires:[{id:"sf1-1"}]}],
 | 
			
		||||
                    out:[{wires:[{id:"sf1-2",port:0}]}]},
 | 
			
		||||
                {id:"sf2",type:"subflow",name:"Subflow 2",info:"",
 | 
			
		||||
                 in:[{wires:[{id:"sf2-1"}]}],
 | 
			
		||||
                 out:[{wires:[{id:"sf2-2",port:0}]}]},
 | 
			
		||||
                    env: [{name: '__KEY3__', value: 'sf2', type: 'str'}],
 | 
			
		||||
                    in:[{wires:[{id:"sf2-1"}]}],
 | 
			
		||||
                    out:[{wires:[{id:"sf2-2",port:0}]}]},
 | 
			
		||||
                {id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]},
 | 
			
		||||
                {id:"sf1-2",type:"subflow:sf2",z:"sf1",x:166,y:99,wires:[[]]},
 | 
			
		||||
                {id:"sf1-2",type:"subflow:sf2",z:"sf1",x:166,y:99,wires:[[]], env: [{name: '__KEY4__', value: 'sf1-2', type: 'str'}] },
 | 
			
		||||
                {id:"sf2-1",type:"test",z:"sf2",foo:"sf2.1",x:166,y:99,wires:[["sf2-2"]]},
 | 
			
		||||
                {id:"sf2-2",type:"testEnv",z:"sf2",foo:"sf2.2",x:166,y:99,wires:[[]]},
 | 
			
		||||
            ]);
 | 
			
		||||
@@ -904,45 +836,22 @@ describe('Subflow', function() {
 | 
			
		||||
                handleError: (a,b,c) => { console.log(a,b,c); }
 | 
			
		||||
            },config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
 | 
			
		||||
            var node_sf1_1 = null;
 | 
			
		||||
            var node_sf2_1 = null;
 | 
			
		||||
            var testenv_node = null;
 | 
			
		||||
            for (var n in currentNodes) {
 | 
			
		||||
                var node = currentNodes[n];
 | 
			
		||||
                if (node.foo === "sf1.1") {
 | 
			
		||||
                    node_sf1_1 = node;
 | 
			
		||||
                }
 | 
			
		||||
                if (node.foo === "sf2.1") {
 | 
			
		||||
                    node_sf2_1 = node;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            process.env["__KEY__"] = "__VAL0__";
 | 
			
		||||
            currentNodes["1"].receive({payload: "test"});
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["3"].should.have.a.property("received", "__VAL0__");
 | 
			
		||||
 | 
			
		||||
                setEnv(node_sf1_1, "__KEY__", "__VAL1__");
 | 
			
		||||
                currentNodes["1"].receive({payload: "test"});
 | 
			
		||||
                setTimeout(function() {
 | 
			
		||||
                    currentNodes["3"].should.have.a.property("received", "__VAL1__");
 | 
			
		||||
 | 
			
		||||
                    setEnv(node_sf2_1, "__KEY__", "__VAL2__");
 | 
			
		||||
                    currentNodes["1"].receive({payload: "test"});
 | 
			
		||||
                    setTimeout(function() {
 | 
			
		||||
                        currentNodes["3"].should.have.a.property("received", "__VAL2__");
 | 
			
		||||
 | 
			
		||||
                        flow.stop().then(function() {
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    },150);
 | 
			
		||||
                },150);
 | 
			
		||||
            },150);
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            currentNodes["3"].should.have.a.property("receivedEnv");
 | 
			
		||||
            currentNodes["3"].receivedEnv.should.have.a.property('__KEY__', '__VAL0__')
 | 
			
		||||
            currentNodes["3"].receivedEnv.should.have.a.property('__KEY1__', 't1')
 | 
			
		||||
            currentNodes["3"].receivedEnv.should.have.a.property('__KEY2__', 'sf1')
 | 
			
		||||
            currentNodes["3"].receivedEnv.should.have.a.property('__KEY3__', 'sf2')
 | 
			
		||||
            currentNodes["3"].receivedEnv.should.have.a.property('__KEY4__', 'sf1-2')
 | 
			
		||||
            
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("can access name of subflow as env var", function(done) {
 | 
			
		||||
        it("can access name of subflow as env var", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]},
 | 
			
		||||
@@ -959,19 +868,15 @@ describe('Subflow', function() {
 | 
			
		||||
                handleError: (a,b,c) => { console.log(a,b,c); }
 | 
			
		||||
            },config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            currentNodes["1"].receive({payload: "test"});
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["3"].should.have.a.property("received", "SFN");
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            currentNodes["3"].should.have.a.property("received", "SFN");
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("can access id of subflow as env var", function(done) {
 | 
			
		||||
        it("can access id of subflow as env var", async function() {
 | 
			
		||||
            var config = flowUtils.parseConfig([
 | 
			
		||||
                {id:"t1",type:"tab"},
 | 
			
		||||
                {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]},
 | 
			
		||||
@@ -988,19 +893,13 @@ describe('Subflow', function() {
 | 
			
		||||
                handleError: (a,b,c) => { console.log(a,b,c); }
 | 
			
		||||
            },config,config.flows["t1"]);
 | 
			
		||||
 | 
			
		||||
            flow.start();
 | 
			
		||||
            await flow.start();
 | 
			
		||||
 | 
			
		||||
            currentNodes["1"].receive({payload: "test"});
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                currentNodes["3"].should.have.a.property("received", "2");
 | 
			
		||||
 | 
			
		||||
                flow.stop().then(function() {
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            },150);
 | 
			
		||||
            await NR_TEST_UTILS.sleep(150)
 | 
			
		||||
            currentNodes["3"].should.have.a.property("received", "2");
 | 
			
		||||
            await flow.stop()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,7 @@ describe('flows/index', function() {
 | 
			
		||||
            flowCreate.flows[id] = {
 | 
			
		||||
                flow: flow,
 | 
			
		||||
                global: global,
 | 
			
		||||
                start: sinon.spy(),
 | 
			
		||||
                start: sinon.spy(async() => {}),
 | 
			
		||||
                update: sinon.spy(),
 | 
			
		||||
                stop: sinon.spy(),
 | 
			
		||||
                getActiveNodes: function() {
 | 
			
		||||
@@ -221,13 +221,18 @@ describe('flows/index', function() {
 | 
			
		||||
                return Promise.resolve({flows:originalConfig});
 | 
			
		||||
            }
 | 
			
		||||
            events.once('flows:started',function() {
 | 
			
		||||
                flows.setFlows(newConfig,"nodes").then(function() {
 | 
			
		||||
                    flows.getFlows().flows.should.eql(newConfig);
 | 
			
		||||
                    flowCreate.flows['t1'].update.called.should.be.true();
 | 
			
		||||
                    flowCreate.flows['t2'].start.called.should.be.true();
 | 
			
		||||
                    flowCreate.flows['_GLOBAL_'].update.called.should.be.true();
 | 
			
		||||
                    done();
 | 
			
		||||
                events.once('flows:started', function() {
 | 
			
		||||
                    try {
 | 
			
		||||
                        flows.getFlows().flows.should.eql(newConfig);
 | 
			
		||||
                        flowCreate.flows['t1'].update.called.should.be.true();
 | 
			
		||||
                        flowCreate.flows['t2'].start.called.should.be.true();
 | 
			
		||||
                        flowCreate.flows['_GLOBAL_'].update.called.should.be.true();
 | 
			
		||||
                        done();
 | 
			
		||||
                    } catch(err) {
 | 
			
		||||
                        done(err)
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                flows.setFlows(newConfig,"nodes")
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            flows.init({log:mockLog, settings:{},storage:storage});
 | 
			
		||||
@@ -250,13 +255,14 @@ describe('flows/index', function() {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            events.once('flows:started',function() {
 | 
			
		||||
                flows.setFlows(newConfig,"nodes").then(function() {
 | 
			
		||||
                events.once('flows:started',function() {
 | 
			
		||||
                    flows.getFlows().flows.should.eql(newConfig);
 | 
			
		||||
                    flowCreate.flows['t1'].update.called.should.be.true();
 | 
			
		||||
                    flowCreate.flows['t2'].start.called.should.be.true();
 | 
			
		||||
                    flowCreate.flows['_GLOBAL_'].update.called.should.be.true();
 | 
			
		||||
                    flows.stopFlows().then(done);
 | 
			
		||||
                })
 | 
			
		||||
                flows.setFlows(newConfig,"nodes")
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            flows.init({log:mockLog, settings:{},storage:storage});
 | 
			
		||||
 
 | 
			
		||||
@@ -149,7 +149,7 @@ describe('flows/util', function() {
 | 
			
		||||
                {id:"t1",type:"tab"}
 | 
			
		||||
            ];
 | 
			
		||||
            var parsedConfig = flowUtil.parseConfig(originalConfig);
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"groups":{},"missingTypes":[]};
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"groups":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"missingTypes":[]};
 | 
			
		||||
            parsedConfig.should.eql(expectedConfig);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +160,7 @@ describe('flows/util', function() {
 | 
			
		||||
                {id:"t1",type:"tab"}
 | 
			
		||||
            ];
 | 
			
		||||
            var parsedConfig = flowUtil.parseConfig(originalConfig);
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"groups":{},"missingTypes":[]};
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"groups":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"missingTypes":[]};
 | 
			
		||||
            parsedConfig.should.eql(expectedConfig);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -172,7 +172,7 @@ describe('flows/util', function() {
 | 
			
		||||
                {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}
 | 
			
		||||
            ];
 | 
			
		||||
            var parsedConfig = flowUtil.parseConfig(originalConfig);
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t2":{"id":"t2","type":"tab"},"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}},"t2":{"id":"t2","type":"tab","subflows":{},"configs":{},"nodes":{"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}}}},"groups":{},"missingTypes":[]};
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t2":{"id":"t2","type":"tab"},"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"groups":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}},"t2":{"id":"t2","type":"tab","subflows":{},"configs":{},"groups":{},"nodes":{"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}}}},"missingTypes":[]};
 | 
			
		||||
            parsedConfig.should.eql(expectedConfig);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -184,7 +184,7 @@ describe('flows/util', function() {
 | 
			
		||||
                {id:"sf1-1",x:10,y:10,z:"sf1",type:"test",wires:[]}
 | 
			
		||||
            ];
 | 
			
		||||
            var parsedConfig = flowUtil.parseConfig(originalConfig);
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[]},"sf1":{"id":"sf1","type":"subflow"},"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"subflows":{"sf1":{"id":"sf1","type":"subflow","configs":{},"nodes":{"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"instances":[{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}]}},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}}}},"groups":{},"missingTypes":[]};
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[]},"sf1":{"id":"sf1","type":"subflow"},"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"subflows":{"sf1":{"id":"sf1","type":"subflow","configs":{},"groups":{},"nodes":{"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"instances":[{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}]}},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"groups":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}}}},"missingTypes":[]};
 | 
			
		||||
            parsedConfig.should.eql(expectedConfig);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -196,7 +196,7 @@ describe('flows/util', function() {
 | 
			
		||||
            ];
 | 
			
		||||
            var parsedConfig = flowUtil.parseConfig(originalConfig);
 | 
			
		||||
            parsedConfig.missingTypes.should.eql(['missing']);
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},"t1-2":{"id":"t1-2","x":10,"y":10,"z":"t1","type":"missing","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},'t1-2': { id: 't1-2', x: 10, y: 10, z: 't1', type: 'missing', wires: [] }}}},"groups":{},"missingTypes":["missing"]};
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},"t1-2":{"id":"t1-2","x":10,"y":10,"z":"t1","type":"missing","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"groups":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},'t1-2': { id: 't1-2', x: 10, y: 10, z: 't1', type: 'missing', wires: [] }}}},"missingTypes":["missing"]};
 | 
			
		||||
            redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -206,7 +206,7 @@ describe('flows/util', function() {
 | 
			
		||||
                {id:"cn",type:"test"},
 | 
			
		||||
            ];
 | 
			
		||||
            var parsedConfig = flowUtil.parseConfig(originalConfig);
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"groups":{},"missingTypes":[]};
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"missingTypes":[]};
 | 
			
		||||
            parsedConfig.should.eql(expectedConfig);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -217,7 +217,7 @@ describe('flows/util', function() {
 | 
			
		||||
                {id:"g1",type:"group",z:"t1"}
 | 
			
		||||
            ];
 | 
			
		||||
            var parsedConfig = flowUtil.parseConfig(originalConfig);
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"g1":{"id":"g1","type":"group","z":"t1"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"groups":{"g1":{"id":"g1","type":"group","z":"t1"}},"missingTypes":[]}
 | 
			
		||||
            var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"g1":{"id":"g1","type":"group","z":"t1"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"groups":{"g1":{"id":"g1","type":"group","z":"t1"}},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"missingTypes":[]}
 | 
			
		||||
            parsedConfig.should.eql(expectedConfig);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user