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", |         "mermaid": "^9.4.3", | ||||||
|         "minami": "1.2.3", |         "minami": "1.2.3", | ||||||
|         "mocha": "9.2.2", |         "mocha": "9.2.2", | ||||||
|         "node-red-node-test-helper": "^0.3.1", |         "node-red-node-test-helper": "^0.3.2", | ||||||
|         "nodemon": "2.0.20", |         "nodemon": "2.0.20", | ||||||
|         "proxy": "^1.0.2", |         "proxy": "^1.0.2", | ||||||
|         "sass": "1.62.1", |         "sass": "1.62.1", | ||||||
|   | |||||||
| @@ -281,21 +281,4 @@ declare class env { | |||||||
|      * ```const flowName = env.get("NR_FLOW_NAME");``` |      * ```const flowName = env.get("NR_FLOW_NAME");``` | ||||||
|      */ |      */ | ||||||
|     static get(name:string) :any; |     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: { |             env: { | ||||||
|                 get: function(envVar, callback) { |                 get: function(envVar) { | ||||||
|                     return RED.util.getSetting(node, envVar, node._flow, callback); |                     return RED.util.getSetting(node, envVar); | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             setTimeout: function () { |             setTimeout: function () { | ||||||
|   | |||||||
| @@ -14,19 +14,20 @@ | |||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  **/ |  **/ | ||||||
|  |  | ||||||
| var clone = require("clone"); | const clone = require("clone"); | ||||||
| var redUtil = require("@node-red/util").util; | const redUtil = require("@node-red/util").util; | ||||||
| const events = require("@node-red/util").events; | const events = require("@node-red/util").events; | ||||||
| var flowUtil = require("./util"); | const flowUtil = require("./util"); | ||||||
| const context = require('../nodes/context'); | const context = require('../nodes/context'); | ||||||
| const hooks = require("@node-red/util").hooks; | const hooks = require("@node-red/util").hooks; | ||||||
| const credentials = require("../nodes/credentials"); | const credentials = require("../nodes/credentials"); | ||||||
|  |  | ||||||
| var Subflow; | let Subflow; | ||||||
| var Log; | let Log; | ||||||
|  | let Group; | ||||||
|  |  | ||||||
| var nodeCloseTimeout = 15000; | let nodeCloseTimeout = 15000; | ||||||
| var asyncMessageDelivery = true; | let asyncMessageDelivery = true; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * This class represents a flow within the runtime. It is responsible for |  * This class represents a flow within the runtime. It is responsible for | ||||||
| @@ -52,6 +53,8 @@ class Flow { | |||||||
|             this.isGlobalFlow = false; |             this.isGlobalFlow = false; | ||||||
|         } |         } | ||||||
|         this.id = this.flow.id || "global"; |         this.id = this.flow.id || "global"; | ||||||
|  |         this.groups = {} | ||||||
|  |         this.groupOrder = [] | ||||||
|         this.activeNodes = {}; |         this.activeNodes = {}; | ||||||
|         this.subflowInstanceNodes = {}; |         this.subflowInstanceNodes = {}; | ||||||
|         this.catchNodes = []; |         this.catchNodes = []; | ||||||
| @@ -59,6 +62,11 @@ class Flow { | |||||||
|         this.path = this.id; |         this.path = this.id; | ||||||
|         // Ensure a context exists for this flow |         // Ensure a context exists for this flow | ||||||
|         this.context = context.getFlowContext(this.id,this.parent.id); |         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] |      * @param  {[type]} msg [description] | ||||||
|      * @return {[type]}     [description] |      * @return {[type]}     [description] | ||||||
|      */ |      */ | ||||||
|     start(diff) { |     async start(diff) { | ||||||
|         this.trace("start "+this.TYPE+" ["+this.path+"]"); |         this.trace("start "+this.TYPE+" ["+this.path+"]"); | ||||||
|         var node; |         var node; | ||||||
|         var newNode; |         var newNode; | ||||||
| @@ -145,6 +153,52 @@ class Flow { | |||||||
|         this.statusNodes = []; |         this.statusNodes = []; | ||||||
|         this.completeNodeMap = {}; |         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 configNodes = Object.keys(this.flow.configs); | ||||||
|         var configNodeAttempts = {}; |         var configNodeAttempts = {}; | ||||||
|         while (configNodes.length > 0) { |         while (configNodes.length > 0) { | ||||||
| @@ -177,7 +231,7 @@ class Flow { | |||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     if (readyToCreate) { |                     if (readyToCreate) { | ||||||
|                         newNode = flowUtil.createNode(this,node); |                         newNode = await flowUtil.createNode(this,node); | ||||||
|                         if (newNode) { |                         if (newNode) { | ||||||
|                             this.activeNodes[id] = newNode; |                             this.activeNodes[id] = newNode; | ||||||
|                         } |                         } | ||||||
| @@ -203,7 +257,7 @@ class Flow { | |||||||
|                 if (node.d !== true) { |                 if (node.d !== true) { | ||||||
|                     if (!node.subflow) { |                     if (!node.subflow) { | ||||||
|                         if (!this.activeNodes[id]) { |                         if (!this.activeNodes[id]) { | ||||||
|                             newNode = flowUtil.createNode(this,node); |                             newNode = await flowUtil.createNode(this,node); | ||||||
|                             if (newNode) { |                             if (newNode) { | ||||||
|                                 this.activeNodes[id] = newNode; |                                 this.activeNodes[id] = newNode; | ||||||
|                             } |                             } | ||||||
| @@ -221,7 +275,7 @@ class Flow { | |||||||
|                                     node |                                     node | ||||||
|                                 ); |                                 ); | ||||||
|                                 this.subflowInstanceNodes[id] = subflow; |                                 this.subflowInstanceNodes[id] = subflow; | ||||||
|                                 subflow.start(); |                                 await subflow.start(); | ||||||
|                                 this.activeNodes[id] = subflow.node; |                                 this.activeNodes[id] = subflow.node; | ||||||
|  |  | ||||||
|                                 // this.subflowInstanceNodes[id] = nodes.map(function(n) { return n.id}); |                                 // this.subflowInstanceNodes[id] = nodes.map(function(n) { return n.id}); | ||||||
| @@ -404,8 +458,7 @@ class Flow { | |||||||
|      * @return {Node}   group node |      * @return {Node}   group node | ||||||
|      */ |      */ | ||||||
|     getGroupNode(id) { |     getGroupNode(id) { | ||||||
|         const groups = this.global.groups; |         return this.groups[id]; | ||||||
|         return groups[id]; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -416,204 +469,28 @@ class Flow { | |||||||
|         return this.activeNodes; |         return this.activeNodes; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Group callback signature |      * Get a flow setting value. | ||||||
|      * |      * @param  {[type]} key [description] | ||||||
|      * @callback GroupEnvCallback |      * @return {[type]}     [description] | ||||||
|      * @param {Error} err The error object (or null) |  | ||||||
|      * @param {[result: {val:Any}, name: String]} result The result of the callback |  | ||||||
|      * @returns {void} |  | ||||||
|      */ |      */ | ||||||
|  |     getSetting(key) { | ||||||
|     /** |  | ||||||
|     * @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; |  | ||||||
|         } |  | ||||||
|         const flow = this.flow; |         const flow = this.flow; | ||||||
|         if (key === "NR_FLOW_NAME") { |         if (key === "NR_FLOW_NAME") { | ||||||
|             return returnOrCallback(null, flow.label); |             return flow.label; | ||||||
|         } |         } | ||||||
|         if (key === "NR_FLOW_ID") { |         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 (!key.startsWith("$parent.")) { | ||||||
|                 if (!flow._env) { |             if (this._env.hasOwnProperty(key)) { | ||||||
|                     const envs = flow.env; |                 return this._env[key] | ||||||
|                     const entries = envs.map((env) => { |  | ||||||
|                         if (env.type === "cred") { |  | ||||||
|                             const cred = flow.credentials; |  | ||||||
|                             if (cred.hasOwnProperty(env.name)) { |  | ||||||
|                                 env.value = cred[env.name]; |  | ||||||
|             } |             } | ||||||
|                         } |         } else { | ||||||
|                         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); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 key = key.substring(8); |                 key = key.substring(8); | ||||||
|         } |         } | ||||||
|         } |         // Delegate to the parent flow. | ||||||
|         const pVal = this.parent.getSetting(key, callback); |         return this.parent.getSetting(key); | ||||||
|         if (callback) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         return pVal; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -666,10 +543,10 @@ class Flow { | |||||||
|                 let distance = 0 |                 let distance = 0 | ||||||
|                 if (reportingNode.g) { |                 if (reportingNode.g) { | ||||||
|                     // Reporting node inside a group. Calculate the distance between it and the status node |                     // 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) { |                     while (containingGroup && containingGroup.id !== targetStatusNode.g) { | ||||||
|                         distance++ |                         distance++ | ||||||
|                         containingGroup = this.global.groups[containingGroup.g] |                         containingGroup = this.groups[containingGroup.g] | ||||||
|                     } |                     } | ||||||
|                     if (!containingGroup && targetStatusNode.g && targetStatusNode.scope === 'group') { |                     if (!containingGroup && targetStatusNode.g && targetStatusNode.scope === 'group') { | ||||||
|                         // This status node is in a group, but not in the same hierachy |                         // This status node is in a group, but not in the same hierachy | ||||||
| @@ -753,10 +630,10 @@ class Flow { | |||||||
|                 let distance = 0 |                 let distance = 0 | ||||||
|                 if (reportingNode.g) { |                 if (reportingNode.g) { | ||||||
|                     // Reporting node inside a group. Calculate the distance between it and the catch node |                     // 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) { |                     while (containingGroup && containingGroup.id !== targetCatchNode.g) { | ||||||
|                         distance++ |                         distance++ | ||||||
|                         containingGroup = this.global.groups[containingGroup.g] |                         containingGroup = this.groups[containingGroup.g] | ||||||
|                     } |                     } | ||||||
|                     if (!containingGroup && targetCatchNode.g && targetCatchNode.scope === 'group') { |                     if (!containingGroup && targetCatchNode.g && targetCatchNode.scope === 'group') { | ||||||
|                         // This catch node is in a group, but not in the same hierachy |                         // This catch node is in a group, but not in the same hierachy | ||||||
| @@ -956,9 +833,10 @@ module.exports = { | |||||||
|         asyncMessageDelivery = !runtime.settings.runtimeSyncDelivery |         asyncMessageDelivery = !runtime.settings.runtimeSyncDelivery | ||||||
|         Log = runtime.log; |         Log = runtime.log; | ||||||
|         Subflow = require("./Subflow"); |         Subflow = require("./Subflow"); | ||||||
|  |         Group = require("./Group").Group | ||||||
|     }, |     }, | ||||||
|     create: function(parent,global,conf) { |     create: function(parent,global,conf) { | ||||||
|         return new Flow(parent,global,conf); |         return new Flow(parent,global,conf) | ||||||
|     }, |     }, | ||||||
|     Flow: Flow |     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.templateCredentials = credentials.get(subflowDef.id) || {}; | ||||||
|         this.instanceCredentials = credentials.get(id) || {}; |         this.instanceCredentials = credentials.get(id) || {}; | ||||||
|  |  | ||||||
|         var env = []; |         var env = {}; | ||||||
|         if (this.subflowDef.env) { |         if (this.subflowDef.env) { | ||||||
|             this.subflowDef.env.forEach(e => { |             this.subflowDef.env.forEach(e => { | ||||||
|                 env[e.name] = 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] |      * @param  {[type]} diff [description] | ||||||
|      * @return {[type]}      [description] |      * @return {[type]}      [description] | ||||||
|      */ |      */ | ||||||
|     start(diff) { |     async start(diff) { | ||||||
|         var self = this; |         var self = this; | ||||||
|         // Create a subflow node to accept inbound messages and route appropriately |         // Create a subflow node to accept inbound messages and route appropriately | ||||||
|         var Node = require("../nodes/Node"); |         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 |      * 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 |      * @return {Object}  val    value of env var | ||||||
|      */ |      */ | ||||||
|     getSetting(name) { |     getSetting(key) { | ||||||
|         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); |  | ||||||
|         } |  | ||||||
|         const node = this.subflowInstance; |         const node = this.subflowInstance; | ||||||
|         if (node) { |         if (node) { | ||||||
|             if (name === "NR_NODE_NAME") { |             if (key === "NR_NODE_NAME") { | ||||||
|                 return node.name; |                 return node.name; | ||||||
|             } |             } | ||||||
|             if (name === "NR_NODE_ID") { |             if (key === "NR_NODE_ID") { | ||||||
|                 return node.id; |                 return node.id; | ||||||
|             } |             } | ||||||
|             if (name === "NR_NODE_PATH") { |             if (key === "NR_NODE_PATH") { | ||||||
|                 return node._path; |                 return node._path; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         if (!key.startsWith("$parent.")) { | ||||||
|  |             if (this._env.hasOwnProperty(key)) { | ||||||
|  |                 return this._env[key] | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             key = key.substring(8); | ||||||
|  |         } | ||||||
|  |         // Push the request up to the parent. | ||||||
|  |         // Unlike a Flow, the parent of a Subflow could be a Group | ||||||
|         if (node.g) { |         if (node.g) { | ||||||
|             const group = this.getGroupNode(node.g); |             return this.parent.getGroupNode(node.g).getSetting(key) | ||||||
|             const [result, newName] = this.getGroupEnvSetting(node, group, name); |  | ||||||
|             if (result) { |  | ||||||
|                 return result.val; |  | ||||||
|         } |         } | ||||||
|             name = newName; |         return this.parent.getSetting(key) | ||||||
|         } |  | ||||||
|  |  | ||||||
|         var parent = this.parent; |  | ||||||
|         if (parent) { |  | ||||||
|             var val = parent.getSetting(name); |  | ||||||
|             return val; |  | ||||||
|         } |  | ||||||
|         return undefined; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -271,6 +271,10 @@ function getFlows() { | |||||||
|  |  | ||||||
| async function start(type,diff,muteLog,isDeploy) { | async function start(type,diff,muteLog,isDeploy) { | ||||||
|     type = type || "full"; |     type = type || "full"; | ||||||
|  |     if (diff && diff.globalConfigChanged) { | ||||||
|  |         type = 'full' | ||||||
|  |     } | ||||||
|  |  | ||||||
|     started = true; |     started = true; | ||||||
|     state = 'start' |     state = 'start' | ||||||
|     var i; |     var i; | ||||||
| @@ -359,7 +363,7 @@ async function start(type,diff,muteLog,isDeploy) { | |||||||
|             if (activeFlowConfig.flows.hasOwnProperty(id)) { |             if (activeFlowConfig.flows.hasOwnProperty(id)) { | ||||||
|                 if (!activeFlowConfig.flows[id].disabled && !activeFlows[id]) { |                 if (!activeFlowConfig.flows[id].disabled && !activeFlows[id]) { | ||||||
|                     // This flow is not disabled, nor is it currently active, so create it |                     // 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); |                     log.debug("red/nodes/flows.start : starting flow : "+id); | ||||||
|                 } else { |                 } else { | ||||||
|                     log.debug("red/nodes/flows.start : not starting disabled flow : "+id); |                     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]); |                         activeFlows[id].update(activeFlowConfig,activeFlowConfig.flows[id]); | ||||||
|                     } else { |                     } else { | ||||||
|                         // This flow didn't previously exist, so create it |                         // 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); |                         log.debug("red/nodes/flows.start : starting flow : "+id); | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
| @@ -391,7 +395,7 @@ async function start(type,diff,muteLog,isDeploy) { | |||||||
|     for (id in activeFlows) { |     for (id in activeFlows) { | ||||||
|         if (activeFlows.hasOwnProperty(id)) { |         if (activeFlows.hasOwnProperty(id)) { | ||||||
|             try { |             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 |                 // Create a map of node id to flow id and also a subflowInstance lookup map | ||||||
|                 var activeNodes = activeFlows[id].getActiveNodes(); |                 var activeNodes = activeFlows[id].getActiveNodes(); | ||||||
|                 Object.keys(activeNodes).forEach(function(nid) { |                 Object.keys(activeNodes).forEach(function(nid) { | ||||||
| @@ -432,7 +436,8 @@ function stop(type,diff,muteLog,isDeploy) { | |||||||
|         changed:[], |         changed:[], | ||||||
|         removed:[], |         removed:[], | ||||||
|         rewired:[], |         rewired:[], | ||||||
|         linked:[] |         linked:[], | ||||||
|  |         flowChanged:[] | ||||||
|     }; |     }; | ||||||
|     if (!muteLog) { |     if (!muteLog) { | ||||||
|         if (type !== "full") { |         if (type !== "full") { | ||||||
| @@ -441,6 +446,9 @@ function stop(type,diff,muteLog,isDeploy) { | |||||||
|             log.info(log._("nodes.flows.stopping-flows")); |             log.info(log._("nodes.flows.stopping-flows")); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     if (diff.globalConfigChanged) { | ||||||
|  |         type = 'full' | ||||||
|  |     } | ||||||
|     started = false; |     started = false; | ||||||
|     state = 'stop' |     state = 'stop' | ||||||
|     var promises = []; |     var promises = []; | ||||||
| @@ -464,7 +472,7 @@ function stop(type,diff,muteLog,isDeploy) { | |||||||
|  |  | ||||||
|     activeFlowIds.forEach(id => { |     activeFlowIds.forEach(id => { | ||||||
|         if (activeFlows.hasOwnProperty(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); |             log.debug("red/nodes/flows.stop : stopping flow : "+id); | ||||||
|             promises.push(activeFlows[id].stop(flowStateChanged?null:stopList,removedList)); |             promises.push(activeFlows[id].stop(flowStateChanged?null:stopList,removedList)); | ||||||
|             if (type === "full" || flowStateChanged || diff.removed.indexOf(id)!==-1) { |             if (type === "full" || flowStateChanged || diff.removed.indexOf(id)!==-1) { | ||||||
| @@ -780,21 +788,10 @@ const flowAPI = { | |||||||
|     getNode: getNode, |     getNode: getNode, | ||||||
|     handleError: () => false, |     handleError: () => false, | ||||||
|     handleStatus: () => false, |     handleStatus: () => false, | ||||||
|     getSetting: (k, callback) => flowUtil.getEnvVar(k, callback), |     getSetting: k => flowUtil.getEnvVar(k), | ||||||
|     log: m => log.log(m) |     log: m => log.log(m) | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| function getGlobalConfig() { |  | ||||||
|     let gconf = null; |  | ||||||
|     eachNode((n) => { |  | ||||||
|         if (n.type === "global-config") { |  | ||||||
|             gconf = n; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
|     return gconf; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|     init: init, |     init: init, | ||||||
|  |  | ||||||
| @@ -808,9 +805,6 @@ module.exports = { | |||||||
|     get:getNode, |     get:getNode, | ||||||
|     eachNode: eachNode, |     eachNode: eachNode, | ||||||
|    |    | ||||||
|  |  | ||||||
|     getGlobalConfig: getGlobalConfig, |  | ||||||
|      |  | ||||||
|     /** |     /** | ||||||
|      * Gets the current flow configuration |      * Gets the current flow configuration | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -13,23 +13,32 @@ | |||||||
|  * See the License for the specific language governing permissions and |  * See the License for the specific language governing permissions and | ||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  **/ |  **/ | ||||||
| var clone = require("clone"); | const clone = require("clone"); | ||||||
| var redUtil = require("@node-red/util").util; | const redUtil = require("@node-red/util").util; | ||||||
| var Log = require("@node-red/util").log; | const Log = require("@node-red/util").log; | ||||||
| var subflowInstanceRE = /^subflow:(.+)$/; | const typeRegistry = require("@node-red/registry"); | ||||||
| var typeRegistry = require("@node-red/registry"); | const subflowInstanceRE = /^subflow:(.+)$/; | ||||||
| const credentials = require("../nodes/credentials"); |  | ||||||
|  |  | ||||||
| let _runtime = null; | let _runtime = null; | ||||||
|  | let envVarExcludes = {}; | ||||||
|  |  | ||||||
| var envVarExcludes = {}; | function init(runtime) { | ||||||
|  |     _runtime = runtime; | ||||||
|  |     envVarExcludes = {}; | ||||||
|  |     if (runtime.settings.hasOwnProperty('envVarExcludes') && Array.isArray(runtime.settings.envVarExcludes)) { | ||||||
|  |         runtime.settings.envVarExcludes.forEach(v => envVarExcludes[v] = true); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| function diffNodes(oldNode,newNode) { | function diffNodes(oldNode,newNode) { | ||||||
|     if (oldNode == null) { |     if (oldNode == null) { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|     var oldKeys = Object.keys(oldNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" }); |     const keyFilter = p => p != 'x' && p != 'y' && p != 'wires' | ||||||
|     var newKeys = Object.keys(newNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" }); |     const groupKeyFilter = p => keyFilter(p) && p != 'nodes' && p != 'style' && p != 'w' && p != 'h' | ||||||
|  |     var oldKeys = Object.keys(oldNode).filter(oldNode.type === 'group' ? groupKeyFilter : keyFilter); | ||||||
|  |     var newKeys = Object.keys(newNode).filter(newNode.type === 'group' ? groupKeyFilter : keyFilter); | ||||||
|  |  | ||||||
|     if (oldKeys.length != newKeys.length) { |     if (oldKeys.length != newKeys.length) { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @@ -70,8 +79,64 @@ function mapEnvVarProperties(obj,prop,flow,config) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | async function evaluateEnvProperties(flow, env, credentials) { | ||||||
|  |     const pendingEvaluations = [] | ||||||
|  |     const evaluatedEnv = {} | ||||||
|  |     const envTypes = [] | ||||||
|  |     for (let i = 0; i < env.length; i++) { | ||||||
|  |         let { name, value, type } = env[i] | ||||||
|  |         if (type === "env") { | ||||||
|  |             // Do env types last as they may include references to other env vars | ||||||
|  |             // at this level which need to be resolved before they can be looked-up | ||||||
|  |             envTypes.push(env[i]) | ||||||
|  |         } else if (type === "bool") { | ||||||
|  |             value = (value === "true") || (value === true); | ||||||
|  |         } else if (type === "cred") { | ||||||
|  |             if (credentials.hasOwnProperty(name)) { | ||||||
|  |                 value = credentials[name]; | ||||||
|  |             } | ||||||
|  |         } else if (type ==='jsonata') { | ||||||
|  |             pendingEvaluations.push(new Promise((resolve, _) => { | ||||||
|  |                 redUtil.evaluateNodeProperty(value, 'jsonata', {_flow: flow}, null, (err, result) => { | ||||||
|  |                     if (!err) { | ||||||
|  |                         evaluatedEnv[name] = result | ||||||
|  |                     } | ||||||
|  |                     resolve() | ||||||
|  |                 }); | ||||||
|  |             })) | ||||||
|  |         } else { | ||||||
|  |             value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null); | ||||||
|  |         } | ||||||
|  |         evaluatedEnv[name] = value | ||||||
|  |     } | ||||||
|  |     if (pendingEvaluations.length > 0) { | ||||||
|  |         await Promise.all(pendingEvaluations) | ||||||
|  |     } | ||||||
|  |     for (let i = 0; i < envTypes.length; i++) { | ||||||
|  |         let { name, value, type } = envTypes[i] | ||||||
|  |         // If an env-var wants to lookup itself, delegate straight to the parent | ||||||
|  |         // https://github.com/node-red/node-red/issues/2099 | ||||||
|  |         if (value === name) { | ||||||
|  |             value = `$parent.${name}` | ||||||
|  |         } | ||||||
|  |         if (evaluatedEnv.hasOwnProperty(value)) { | ||||||
|  |             value = evaluatedEnv[value] | ||||||
|  |         } else { | ||||||
|  |             value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null); | ||||||
|  |         } | ||||||
|  |         evaluatedEnv[name] = value | ||||||
|  |     } | ||||||
|  |  | ||||||
| function createNode(flow,config) { |     return evaluatedEnv | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Create a new instance of a node | ||||||
|  |  * @param  {Flow} flow     The containing flow | ||||||
|  |  * @param  {object} config The node configuration object | ||||||
|  |  * @return {Node}          The instance of the node | ||||||
|  |  */ | ||||||
|  | async function createNode(flow,config) { | ||||||
|     var newNode = null; |     var newNode = null; | ||||||
|     var type = config.type; |     var type = config.type; | ||||||
|     try { |     try { | ||||||
| @@ -140,7 +205,7 @@ function createNode(flow,config) { | |||||||
|             // This allows nodes inside the subflow to get ahold of each other |             // This allows nodes inside the subflow to get ahold of each other | ||||||
|             // such as a node accessing its config node |             // such as a node accessing its config node | ||||||
|             flow.subflowInstanceNodes[config.id] = subflow |             flow.subflowInstanceNodes[config.id] = subflow | ||||||
|             subflow.start(); |             await subflow.start(); | ||||||
|             return subflow.node; |             return subflow.node; | ||||||
|         } |         } | ||||||
|     } catch(err) { |     } catch(err) { | ||||||
| @@ -155,7 +220,6 @@ function parseConfig(config) { | |||||||
|     flow.subflows = {}; |     flow.subflows = {}; | ||||||
|     flow.configs = {}; |     flow.configs = {}; | ||||||
|     flow.flows = {}; |     flow.flows = {}; | ||||||
|    flow.groups = {}; |  | ||||||
|     flow.missingTypes = []; |     flow.missingTypes = []; | ||||||
|  |  | ||||||
|     config.forEach(function (n) { |     config.forEach(function (n) { | ||||||
| @@ -165,21 +229,16 @@ function parseConfig(config) { | |||||||
|             flow.flows[n.id].subflows = {}; |             flow.flows[n.id].subflows = {}; | ||||||
|             flow.flows[n.id].configs = {}; |             flow.flows[n.id].configs = {}; | ||||||
|             flow.flows[n.id].nodes = {}; |             flow.flows[n.id].nodes = {}; | ||||||
|        } |             flow.flows[n.id].groups = {}; | ||||||
|        if (n.type === 'group') { |         } else if (n.type === 'subflow') { | ||||||
|            flow.groups[n.id] = n; |  | ||||||
|        } |  | ||||||
|    }); |  | ||||||
|  |  | ||||||
|    // TODO: why a separate forEach? this can be merged with above |  | ||||||
|    config.forEach(function(n) { |  | ||||||
|        if (n.type === 'subflow') { |  | ||||||
|             flow.subflows[n.id] = n; |             flow.subflows[n.id] = n; | ||||||
|             flow.subflows[n.id].configs = {}; |             flow.subflows[n.id].configs = {}; | ||||||
|             flow.subflows[n.id].nodes = {}; |             flow.subflows[n.id].nodes = {}; | ||||||
|  |             flow.subflows[n.id].groups = {}; | ||||||
|             flow.subflows[n.id].instances = []; |             flow.subflows[n.id].instances = []; | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     var linkWires = {}; |     var linkWires = {}; | ||||||
|     var linkOutNodes = []; |     var linkOutNodes = []; | ||||||
|     config.forEach(function (n) { |     config.forEach(function (n) { | ||||||
| @@ -229,6 +288,11 @@ function parseConfig(config) { | |||||||
|                 }) |                 }) | ||||||
|                 linkOutNodes.push(n); |                 linkOutNodes.push(n); | ||||||
|             } |             } | ||||||
|  |         } else if (n.type === 'group') { | ||||||
|  |             const parentContainer = flow.flows[n.z] || flow.subflows[n.z] | ||||||
|  |             if (parentContainer) { | ||||||
|  |                 parentContainer.groups[n.id] = n | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|     linkOutNodes.forEach(function (n) { |     linkOutNodes.forEach(function (n) { | ||||||
| @@ -268,81 +332,13 @@ function parseConfig(config) { | |||||||
|     }); |     }); | ||||||
|     return flow; |     return flow; | ||||||
| } | } | ||||||
|  | function getEnvVar(k) { | ||||||
| function getGlobalEnv(name) { |     if (!envVarExcludes[k]) { | ||||||
|     const nodes = _runtime.nodes; |         return process.env[k]; | ||||||
|     if (!nodes) { |  | ||||||
|         return null; |  | ||||||
|     } |     } | ||||||
|     const gconf = nodes.getGlobalConfig(); |     return undefined; | ||||||
|     const env = gconf ? gconf.env : null; |  | ||||||
|  |  | ||||||
|     if (env) { |  | ||||||
|         const cred = (gconf ? credentials.get(gconf.id) : null) || { |  | ||||||
|             map: {} |  | ||||||
|         }; |  | ||||||
|         const map = cred.map; |  | ||||||
|  |  | ||||||
|         for (let i = 0; i < env.length; i++) { |  | ||||||
|             const item = env[i]; |  | ||||||
|             if (item.name === name) { |  | ||||||
|                 if (item.type === "cred") { |  | ||||||
|                     return { |  | ||||||
|                         name: name, |  | ||||||
|                         value: map[name], |  | ||||||
|                         type: "cred" |  | ||||||
|                     }; |  | ||||||
| } | } | ||||||
|                 return item; | function diffConfigs(oldConfig, newConfig) { | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return null; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
|     init: function(runtime) { |  | ||||||
|         _runtime = runtime; |  | ||||||
|         envVarExcludes = {}; |  | ||||||
|         if (runtime.settings.hasOwnProperty('envVarExcludes') && Array.isArray(runtime.settings.envVarExcludes)) { |  | ||||||
|             runtime.settings.envVarExcludes.forEach(v => envVarExcludes[v] = true); |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     /** |  | ||||||
|      * Get the value of an environment variable |  | ||||||
|      * Call with a callback to get the value asynchronously |  | ||||||
|      * or without to get the value synchronously |  | ||||||
|      * @param {String} key The name of the environment variable |  | ||||||
|      * @param {(err: Error, val: Any)} [callback] Optional callback for asynchronous call |  | ||||||
|      * @returns {Any | void} The value of the environment variable or undefined if not found |  | ||||||
|      */ |  | ||||||
|     getEnvVar: function(key, callback) { |  | ||||||
|         const returnOrCallback = function(err, val) { |  | ||||||
|             if (callback) { |  | ||||||
|                 callback(err, val); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             return val; |  | ||||||
|         } |  | ||||||
|         if (!envVarExcludes[key]) { |  | ||||||
|             const item = getGlobalEnv(key); |  | ||||||
|             if (item) { |  | ||||||
|                 const val = redUtil.evaluateNodeProperty(item.value, item.type, null, null, callback); |  | ||||||
|                 if (callback) { |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                 return val; |  | ||||||
|             } |  | ||||||
|             return returnOrCallback(null, process.env[key]); |  | ||||||
|         } |  | ||||||
|         return returnOrCallback(undefined); |  | ||||||
|     }, |  | ||||||
|     diffNodes: diffNodes, |  | ||||||
|     mapEnvVarProperties: mapEnvVarProperties, |  | ||||||
|  |  | ||||||
|     parseConfig: parseConfig, |  | ||||||
|  |  | ||||||
|     diffConfigs: function(oldConfig, newConfig) { |  | ||||||
|     var id; |     var id; | ||||||
|     var node; |     var node; | ||||||
|     var nn; |     var nn; | ||||||
| @@ -360,11 +356,11 @@ module.exports = { | |||||||
|     var added = {}; |     var added = {}; | ||||||
|     var removed = {}; |     var removed = {}; | ||||||
|     var changed = {}; |     var changed = {}; | ||||||
|  |     var flowChanged = {}; | ||||||
|     var wiringChanged = {}; |     var wiringChanged = {}; | ||||||
|  |     var globalConfigChanged = false; | ||||||
|     var linkMap = {}; |     var linkMap = {}; | ||||||
|  |     var allNestedGroups = [] | ||||||
|         var changedTabs = {}; |  | ||||||
|  |  | ||||||
|     // Look for tabs that have been removed |     // Look for tabs that have been removed | ||||||
|     for (id in oldConfig.flows) { |     for (id in oldConfig.flows) { | ||||||
| @@ -379,7 +375,6 @@ module.exports = { | |||||||
|             var originalState = oldConfig.flows[id].disabled||false; |             var originalState = oldConfig.flows[id].disabled||false; | ||||||
|             var newState = newConfig.flows[id].disabled||false; |             var newState = newConfig.flows[id].disabled||false; | ||||||
|             if (originalState !== newState) { |             if (originalState !== newState) { | ||||||
|                     changedTabs[id] = true; |  | ||||||
|                 if (originalState) { |                 if (originalState) { | ||||||
|                     added[id] = oldConfig.allNodes[id]; |                     added[id] = oldConfig.allNodes[id]; | ||||||
|                 } else { |                 } else { | ||||||
| @@ -442,6 +437,9 @@ module.exports = { | |||||||
|                                     delete changed[id]; |                                     delete changed[id]; | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|  |                             if (newConfig.allNodes[id].type === 'global-config') { | ||||||
|  |                                 globalConfigChanged = true | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|                         // This node's wiring has changed |                         // This node's wiring has changed | ||||||
|                         if (!redUtil.compareObjects(node.wires,newConfig.allNodes[id].wires)) { |                         if (!redUtil.compareObjects(node.wires,newConfig.allNodes[id].wires)) { | ||||||
| @@ -457,6 +455,10 @@ module.exports = { | |||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |             } else if (!removed[id]) { | ||||||
|  |                 if (JSON.stringify(node.env) !== JSON.stringify(newConfig.allNodes[id].env)) { | ||||||
|  |                     flowChanged[id] = newConfig.allNodes[id]; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -464,6 +466,20 @@ module.exports = { | |||||||
|     for (id in newConfig.allNodes) { |     for (id in newConfig.allNodes) { | ||||||
|         if (newConfig.allNodes.hasOwnProperty(id)) { |         if (newConfig.allNodes.hasOwnProperty(id)) { | ||||||
|             node = newConfig.allNodes[id]; |             node = newConfig.allNodes[id]; | ||||||
|  |             if (node.type === 'group') { | ||||||
|  |                 if (node.g) { | ||||||
|  |                     allNestedGroups.push(node) | ||||||
|  |                 } | ||||||
|  |                 if (changed[node.id]) { | ||||||
|  |                     if (node.nodes) { | ||||||
|  |                         node.nodes.forEach(nid => { | ||||||
|  |                             if (!changed[nid]) { | ||||||
|  |                                 changed[nid] = true | ||||||
|  |                             } | ||||||
|  |                         }) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|             // build the map of what this node is now wired to |             // build the map of what this node is now wired to | ||||||
|             if (node.wires) { |             if (node.wires) { | ||||||
|                 linkMap[node.id] = linkMap[node.id] || []; |                 linkMap[node.id] = linkMap[node.id] || []; | ||||||
| @@ -557,6 +573,26 @@ module.exports = { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Recursively mark all children of changed groups as changed | ||||||
|  |     do { | ||||||
|  |         madeChange = false | ||||||
|  |         for (let i = 0; i < allNestedGroups.length; i++) { | ||||||
|  |             const group = allNestedGroups[i] | ||||||
|  |             if (!changed[group.id] && group.g && changed[group.g]) { | ||||||
|  |                 changed[group.id] = true | ||||||
|  |                 madeChange = true | ||||||
|  |             } | ||||||
|  |             if (changed[group.id] && group.nodes) { | ||||||
|  |                 group.nodes.forEach(nid => { | ||||||
|  |                     if (!changed[nid]) { | ||||||
|  |                         changed[nid] = true | ||||||
|  |                         madeChange = true | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } while(madeChange) | ||||||
|  |  | ||||||
|     // Recursively mark all instances of changed subflows as changed |     // Recursively mark all instances of changed subflows as changed | ||||||
|     var changedSubflowStack = Object.keys(changedSubflows); |     var changedSubflowStack = Object.keys(changedSubflows); | ||||||
|     while (changedSubflowStack.length > 0) { |     while (changedSubflowStack.length > 0) { | ||||||
| @@ -582,12 +618,16 @@ module.exports = { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     var diff = { |     var diff = { | ||||||
|         added:Object.keys(added), |         added:Object.keys(added), | ||||||
|         changed:Object.keys(changed), |         changed:Object.keys(changed), | ||||||
|         removed:Object.keys(removed), |         removed:Object.keys(removed), | ||||||
|         rewired:Object.keys(wiringChanged), |         rewired:Object.keys(wiringChanged), | ||||||
|             linked:[] |         linked:[], | ||||||
|  |         flowChanged: Object.keys(flowChanged), | ||||||
|  |         globalConfigChanged | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Traverse the links of all modified nodes to mark the connected nodes |     // Traverse the links of all modified nodes to mark the connected nodes | ||||||
| @@ -607,6 +647,7 @@ module.exports = { | |||||||
|     } |     } | ||||||
|     // console.log(diff); |     // console.log(diff); | ||||||
|     // for (id in newConfig.allNodes) { |     // for (id in newConfig.allNodes) { | ||||||
|  |     //     if (added[id] || changed[id] || wiringChanged[id] || diff.linked.indexOf(id)!==-1) { | ||||||
|     //         console.log( |     //         console.log( | ||||||
|     //             (added[id]?"a":(changed[id]?"c":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"l":" "), |     //             (added[id]?"a":(changed[id]?"c":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"l":" "), | ||||||
|     //             newConfig.allNodes[id].type.padEnd(10), |     //             newConfig.allNodes[id].type.padEnd(10), | ||||||
| @@ -615,6 +656,7 @@ module.exports = { | |||||||
|     //             newConfig.allNodes[id].name||newConfig.allNodes[id].label||"" |     //             newConfig.allNodes[id].name||newConfig.allNodes[id].label||"" | ||||||
|     //         ); |     //         ); | ||||||
|     //     } |     //     } | ||||||
|  |     // } | ||||||
|     // for (id in removed) { |     // for (id in removed) { | ||||||
|     //     console.log( |     //     console.log( | ||||||
|     //         "- "+(diff.linked.indexOf(id)!==-1?"~":" "), |     //         "- "+(diff.linked.indexOf(id)!==-1?"~":" "), | ||||||
| @@ -625,13 +667,15 @@ module.exports = { | |||||||
|     // } |     // } | ||||||
|  |  | ||||||
|     return diff; |     return diff; | ||||||
|     }, | } | ||||||
|  |  | ||||||
|     /** | module.exports = { | ||||||
|      * Create a new instance of a node |     init, | ||||||
|      * @param  {Flow} flow     The containing flow |     createNode, | ||||||
|      * @param  {object} config The node configuration object |     parseConfig, | ||||||
|      * @return {Node}          The instance of the node |     diffConfigs, | ||||||
|      */ |     diffNodes, | ||||||
|     createNode: createNode |     getEnvVar, | ||||||
|  |     mapEnvVarProperties, | ||||||
|  |     evaluateEnvProperties | ||||||
| } | } | ||||||
|   | |||||||
| @@ -205,7 +205,6 @@ module.exports = { | |||||||
|     getNode: flows.get, |     getNode: flows.get, | ||||||
|     eachNode: flows.eachNode, |     eachNode: flows.eachNode, | ||||||
|     getContext: context.get, |     getContext: context.get, | ||||||
|     getGlobalConfig: flows.getGlobalConfig, |  | ||||||
|  |  | ||||||
|     clearContext: context.clear, |     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 |  * @mixin @node-red/util_util | ||||||
|  */ |  */ | ||||||
| /** @typedef {import('../../runtime/lib/flows/Flow.js').Flow} RuntimeLibFlowsFlow */ |  | ||||||
|  |  | ||||||
| const clonedeep = require("lodash.clonedeep"); | const clonedeep = require("lodash.clonedeep"); | ||||||
| const jsonata = require("jsonata"); | const jsonata = require("jsonata"); | ||||||
| @@ -531,64 +530,31 @@ function setObjectProperty(msg,prop,value,createMissing) { | |||||||
|  * Get value of environment variable. |  * Get value of environment variable. | ||||||
|  * @param {Node} node - accessing node |  * @param {Node} node - accessing node | ||||||
|  * @param {String} name - name of variable |  * @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 |  * @return {String} value of env var | ||||||
|  */ |  */ | ||||||
| function getSetting(node, name, flow_, callback) { | function getSetting(node, name, flow_) { | ||||||
|     const returnOrCallback = (err, result) => { |  | ||||||
|         if (callback) { |  | ||||||
|             callback(err, result); |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
|     if (node) { |     if (node) { | ||||||
|         if (name === "NR_NODE_NAME") { |         if (name === "NR_NODE_NAME") { | ||||||
|             return returnOrCallback(null, node.name); |             return node.name; | ||||||
|         } |         } | ||||||
|         if (name === "NR_NODE_ID") { |         if (name === "NR_NODE_ID") { | ||||||
|             return returnOrCallback(null, node.id); |             return node.id; | ||||||
|         } |         } | ||||||
|         if (name === "NR_NODE_PATH") { |         if (name === "NR_NODE_PATH") { | ||||||
|             return returnOrCallback(null, node._path); |             return node._path; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @type {RuntimeLibFlowsFlow} */ |  | ||||||
|     var flow = (flow_ ? flow_ : (node ? node._flow : null)); |     var flow = (flow_ ? flow_ : (node ? node._flow : null)); | ||||||
|     if (flow) { |     if (flow) { | ||||||
|         if (node && node.g) { |         if (node && node.g) { | ||||||
|             const group = flow.getGroupNode(node.g); |             const group = flow.getGroupNode(node.g); | ||||||
|             if (callback) { |             if (group) { | ||||||
|                 flow.getGroupEnvSetting(node, group, name, (e, [result, newName]) => { |                 return group.getSetting(name) | ||||||
|                     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; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         const fVal = flow.getSetting(name, callback) |         return flow.getSetting(name); | ||||||
|         if (callback) { |  | ||||||
|             return |  | ||||||
|     } |     } | ||||||
|         return fVal; |     return process.env[name]; | ||||||
|     } |  | ||||||
|     return returnOrCallback(null, process.env[name]); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -600,34 +566,19 @@ function getSetting(node, name, flow_, callback) { | |||||||
|  * will return `Hello Joe!`. |  * will return `Hello Joe!`. | ||||||
|  * @param  {String} value - the string to parse |  * @param  {String} value - the string to parse | ||||||
|  * @param  {Node} node - the node evaluating the property |  * @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 |  * @return {String} The parsed string | ||||||
|  * @memberof @node-red/util_util |  * @memberof @node-red/util_util | ||||||
|  */ |  */ | ||||||
| function evaluateEnvProperty(value, node, callback) { | function evaluateEnvProperty(value, node) { | ||||||
|     const returnOrCallback = (err, result) => { |  | ||||||
|         if (callback) { |  | ||||||
|             callback(err, result); |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
|     /** @type {RuntimeLibFlowsFlow}  */ |  | ||||||
|     var flow = (node && hasOwnProperty.call(node, "_flow")) ? node._flow : null; |     var flow = (node && hasOwnProperty.call(node, "_flow")) ? node._flow : null; | ||||||
|     var result; |     var result; | ||||||
|     if (/^\${[^}]+}$/.test(value)) { |     if (/^\${[^}]+}$/.test(value)) { | ||||||
|         // ${ENV_VAR} |         // ${ENV_VAR} | ||||||
|         var name = value.substring(2,value.length-1); |         var name = value.substring(2,value.length-1); | ||||||
|         result = getSetting(node, name, flow, callback); |         result = getSetting(node, name, flow); | ||||||
|         if (callback) { |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|     } else if (!/\${\S+}/.test(value)) { |     } else if (!/\${\S+}/.test(value)) { | ||||||
|         // ENV_VAR |         // ENV_VAR | ||||||
|         result = getSetting(node, value, flow, callback); |         result = getSetting(node, value, flow); | ||||||
|         if (callback) { |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|     } else { |     } else { | ||||||
|         // FOO${ENV_VAR}BAR |         // FOO${ENV_VAR}BAR | ||||||
|         return value.replace(/\${([^}]+)}/g, function(match, name) { |         return value.replace(/\${([^}]+)}/g, function(match, name) { | ||||||
| @@ -635,7 +586,8 @@ function evaluateEnvProperty(value, node, callback) { | |||||||
|             return (val === undefined)?"":val; |             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 |             return | ||||||
|         } |         } | ||||||
|     } else if (type === 'env') { |     } else if (type === 'env') { | ||||||
|         result = evaluateEnvProperty(value, node, callback); |         result = evaluateEnvProperty(value, node); | ||||||
|         if (callback) { |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|     if (callback) { |     if (callback) { | ||||||
|         callback(null,result); |         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 path = require("path"); | ||||||
| const fs = require("fs"); |  | ||||||
|  |  | ||||||
| const PACKAGE_ROOT = "../../../packages/node_modules"; | const PACKAGE_ROOT = "../../../packages/node_modules"; | ||||||
|  |  | ||||||
| @@ -27,5 +26,10 @@ module.exports = { | |||||||
|     }, |     }, | ||||||
|     resolve: function(file) { |     resolve: function(file) { | ||||||
|         return path.resolve(path.join(__dirname,PACKAGE_ROOT,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() { | describe('inject node', function() { | ||||||
|  |  | ||||||
|     beforeEach(function(done) { |     beforeEach(function(done) { | ||||||
|         helper.startServer(done); |         helper.startServer(() => { | ||||||
|  |             done() | ||||||
|  |         }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     function initContext(done) { |     function initContext(done) { | ||||||
| @@ -41,7 +43,7 @@ describe('inject node', function() { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     afterEach(function(done) { |     afterEach(async function() { | ||||||
|         helper.unload().then(function () { |         helper.unload().then(function () { | ||||||
|             return Context.clean({allNodes: {}}); |             return Context.clean({allNodes: {}}); | ||||||
|         }).then(function () { |         }).then(function () { | ||||||
| @@ -53,8 +55,11 @@ describe('inject node', function() { | |||||||
|  |  | ||||||
|     function basicTest(type, val, rval) { |     function basicTest(type, val, rval) { | ||||||
|         it('inject value ('+type+')', function (done) { |         it('inject value ('+type+')', function (done) { | ||||||
|             var flow = [{id: "n1", type: "inject", topic: "t1", payload: val, payloadType: type, wires: [["n2"]], z: "flow"}, |             var flow = [ | ||||||
|             {id: "n2", type: "helper"}]; |                 {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 () { |             helper.load(injectNode, flow, function () { | ||||||
|                 var n1 = helper.getNode("n1"); |                 var n1 = helper.getNode("n1"); | ||||||
|                 var n2 = helper.getNode("n2"); |                 var n2 = helper.getNode("n2"); | ||||||
| @@ -93,6 +98,7 @@ describe('inject node', function() { | |||||||
|             var n1 = helper.getNode("n1"); |             var n1 = helper.getNode("n1"); | ||||||
|             var n2 = helper.getNode("n2"); |             var n2 = helper.getNode("n2"); | ||||||
|             n2.on("input", function (msg) { |             n2.on("input", function (msg) { | ||||||
|  |                 delete process.env.NR_TEST | ||||||
|                 try { |                 try { | ||||||
|                     msg.should.have.property("topic", "t1"); |                     msg.should.have.property("topic", "t1"); | ||||||
|                     msg.should.have.property("payload", "foo"); |                     msg.should.have.property("payload", "foo"); | ||||||
| @@ -202,9 +208,10 @@ describe('inject node', function() { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('inject name of group as environment variable ', function (done) { |     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"}, |         var flow = [{id: "flow", type: "tab" }, | ||||||
|                     {id: "n2", type: "helper"}, |                     {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_NAME", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"}, | ||||||
|                     {id: "g0", type: "group", name: "GROUP" }, |                     {id: "n2", type: "helper", z: "flow"}, | ||||||
|  |                     {id: "g0", type: "group", name: "GROUP", z: "flow" }, | ||||||
|                    ]; |                    ]; | ||||||
|         helper.load(injectNode, flow, function () { |         helper.load(injectNode, flow, function () { | ||||||
|             var n1 = helper.getNode("n1"); |             var n1 = helper.getNode("n1"); | ||||||
| @@ -222,9 +229,10 @@ describe('inject node', function() { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('inject id of group as environment variable ', function (done) { |     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"}, |         var flow = [{id: "flow", type: "tab" }, | ||||||
|                     {id: "n2", type: "helper"}, |                     {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_ID", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"}, | ||||||
|                     {id: "g0", type: "group", name: "GROUP" }, |                     {id: "n2", type: "helper", z: "flow"}, | ||||||
|  |                     {id: "g0", type: "group", name: "GROUP", z: "flow" }, | ||||||
|                    ]; |                    ]; | ||||||
|         helper.load(injectNode, flow, function () { |         helper.load(injectNode, flow, function () { | ||||||
|             var n1 = helper.getNode("n1"); |             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) { |     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"}, |         var flow = [{id: "flow", type: "tab" }, | ||||||
|                     {id: "n2", type: "helper"}]; |                     {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 () { |         helper.load(injectNode, flow, function () { | ||||||
|             var n1 = helper.getNode("n1"); |             var n1 = helper.getNode("n1"); | ||||||
|             var n2 = helper.getNode("n2"); |             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) { |     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"}, |         var flow = [{id: "flow", type: "tab" }, | ||||||
|                     {id: "n2", type: "helper"}, |                     {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_NAME}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"}, | ||||||
|                     {id: "g0", type: "group", name: "GROUP" }, |                     {id: "n2", type: "helper", z: "flow"}, | ||||||
|  |                     {id: "g0", type: "group", name: "GROUP", z: "flow" }, | ||||||
|                    ]; |                    ]; | ||||||
|         helper.load(injectNode, flow, function () { |         helper.load(injectNode, flow, function () { | ||||||
|             var n1 = helper.getNode("n1"); |             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) { |     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"}, |         var flow = [{id: "flow", type: "tab" }, | ||||||
|                     {id: "n2", type: "helper"}, |                     {id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_ID}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"}, | ||||||
|                     {id: "g0", type: "group", name: "GROUP" }, |                     {id: "n2", type: "helper", z: "flow"}, | ||||||
|  |                     {id: "g0", type: "group", name: "GROUP", z: "flow" }, | ||||||
|                    ]; |                    ]; | ||||||
|         helper.load(injectNode, flow, function () { |         helper.load(injectNode, flow, function () { | ||||||
|             var n1 = helper.getNode("n1"); |             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 inject = require("nr-test-utils").require("@node-red/nodes/core/common/20-inject.js"); | ||||||
| var helper = require("node-red-node-test-helper"); | var helper = require("node-red-node-test-helper"); | ||||||
|  |  | ||||||
| describe('unknown Node', function() { | describe('Global Config Node', function() { | ||||||
|  |  | ||||||
|     afterEach(function() { |     afterEach(function() { | ||||||
|         helper.unload(); |         helper.unload(); | ||||||
|   | |||||||
| @@ -568,11 +568,12 @@ describe('change Node', function() { | |||||||
|  |  | ||||||
|             it('sets the value using env property from group', function(done) { |             it('sets the value using env property from group', function(done) { | ||||||
|                 var flow = [ |                 var flow = [ | ||||||
|  |                     {"id": "flow", type:"tab"}, | ||||||
|                     {"id":"group1","type":"group","env":[ |                     {"id":"group1","type":"group","env":[ | ||||||
|                         {"name":"NR_TEST_A", "value":"bar", "type": "str"} |                         {"name":"NR_TEST_A", "value":"bar", "type": "str"} | ||||||
|                     ]}, |                     ], 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"]]}, |                     {"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:[]} |                     {id:"helperNode1", type:"helper", wires:[], z: "flow"} | ||||||
|                 ]; |                 ]; | ||||||
|                 helper.load(changeNode, flow, function() { |                 helper.load(changeNode, flow, function() { | ||||||
|                     var changeNode1 = helper.getNode("changeNode1"); |                     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) { |             it('sets the value using env property from nested group', function(done) { | ||||||
|                 var flow = [ |                 var flow = [ | ||||||
|  |                     {"id": "flow", type:"tab"}, | ||||||
|                     {"id":"group1","type":"group","env":[ |                     {"id":"group1","type":"group","env":[ | ||||||
|                         {"name":"NR_TEST_A", "value":"bar", "type": "str"} |                         {"name":"NR_TEST_A", "value":"bar", "type": "str"} | ||||||
|                     ]}, |                     ], z: "flow"}, | ||||||
|                     {"id":"group2","type":"group","g":"group1","env":[]}, |                     {"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"]]}, |                     {"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:[]} |                     {id:"helperNode1", type:"helper", wires:[], z: "flow"} | ||||||
|                 ]; |                 ]; | ||||||
|                 helper.load(changeNode, flow, function() { |                 helper.load(changeNode, flow, function() { | ||||||
|                     var changeNode1 = helper.getNode("changeNode1"); |                     var changeNode1 = helper.getNode("changeNode1"); | ||||||
|   | |||||||
| @@ -254,7 +254,8 @@ describe('subflow', function() { | |||||||
|     it('should access typed value of env var', function(done) { |     it('should access typed value of env var', function(done) { | ||||||
|         var flow = [ |         var flow = [ | ||||||
|             { id: "t0", type: "tab", label: "", disabled: false, info: "" }, |             { id: "t0", type: "tab", label: "", disabled: false, info: "" }, | ||||||
|             {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", |             { | ||||||
|  |                 id: "n1", x: 10, y: 10, z: "t0", type: "subflow:s1", | ||||||
|                 env: [ |                 env: [ | ||||||
|                     { name: "KN", type: "num", value: "100" }, |                     { name: "KN", type: "num", value: "100" }, | ||||||
|                     { name: "KB", type: "bool", value: "true" }, |                     { name: "KB", type: "bool", value: "true" }, | ||||||
| @@ -263,25 +264,21 @@ describe('subflow', function() { | |||||||
|                     { name: "Ke", type: "env", value: "KS" }, |                     { name: "Ke", type: "env", value: "KS" }, | ||||||
|                     { name: "Kj", type: "jsonata", value: "1+2" }, |                     { name: "Kj", type: "jsonata", value: "1+2" }, | ||||||
|                 ], |                 ], | ||||||
|              wires:[["n2"]]}, |                 wires: [["n2"]] | ||||||
|  |             }, | ||||||
|             { id: "n2", x: 10, y: 10, z: "t0", type: "helper", wires: [] }, |             { id: "n2", x: 10, y: 10, z: "t0", type: "helper", wires: [] }, | ||||||
|             // Subflow |             // Subflow | ||||||
|             {id:"s1", type:"subflow", name:"Subflow", info:"", |             { | ||||||
|              in:[{ |                 id: "s1", type: "subflow", name: "Subflow", info: "", | ||||||
|                  x:10, y:10, |                 in: [{ x: 10, y: 10, wires: [{ id: "s1-n1" }] }], | ||||||
|                  wires:[ {id:"s1-n1"} ] |                 out: [{ x: 10, y: 10, wires: [{ id: "s1-n1", port: 0 }] }], | ||||||
|              }], |                 env: [{ name: "KS", type: "str", value: "STR" }] | ||||||
|              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", |             { | ||||||
|  |                 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;", |                 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:[]} |                 wires: [] | ||||||
|  |             } | ||||||
|         ]; |         ]; | ||||||
|         helper.load(functionNode, flow, function() { |         helper.load(functionNode, flow, function() { | ||||||
|             var n1 = helper.getNode("n1"); |             var n1 = helper.getNode("n1"); | ||||||
|   | |||||||
| @@ -29,7 +29,6 @@ var Node = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); | |||||||
| var hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); | var hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); | ||||||
| var typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); | var typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); | ||||||
|  |  | ||||||
|  |  | ||||||
| describe('Flow', function() { | describe('Flow', function() { | ||||||
|     var getType; |     var getType; | ||||||
|  |  | ||||||
| @@ -200,7 +199,7 @@ describe('Flow', function() { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     describe('#start',function() { |     describe('#start',function() { | ||||||
|         it("instantiates an initial configuration and stops it",function(done) { |         it("instantiates an initial configuration and stops it", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -209,8 +208,8 @@ describe('Flow', function() { | |||||||
|                 {id:"4",z:"t1",type:"test",foo:"a"} |                 {id:"4",z:"t1",type:"test",foo:"a"} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |             return new Promise((done) => { | ||||||
|                 Object.keys(flow.getActiveNodes()).should.have.length(4); |                 Object.keys(flow.getActiveNodes()).should.have.length(4); | ||||||
|  |  | ||||||
|                 flow.getNode('1').should.have.a.property('id','1'); |                 flow.getNode('1').should.have.a.property('id','1'); | ||||||
| @@ -249,9 +248,10 @@ describe('Flow', function() { | |||||||
|                     }); |                     }); | ||||||
|                 }); |                 }); | ||||||
|                 currentNodes["1"].receive({payload:"test"}); |                 currentNodes["1"].receive({payload:"test"}); | ||||||
|  |             }) | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("instantiates config nodes in the right order",function(done) { |         it("instantiates config nodes in the right order",async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -261,11 +261,8 @@ describe('Flow', function() { | |||||||
|                 {id:"5",z:"t1",type:"test"} |                 {id:"5",z:"t1",type:"test"} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(5); |             Object.keys(flow.getActiveNodes()).should.have.length(5); | ||||||
|  |  | ||||||
|  |  | ||||||
|             currentNodes.should.have.a.property("1"); |             currentNodes.should.have.a.property("1"); | ||||||
|             currentNodes.should.have.a.property("2"); |             currentNodes.should.have.a.property("2"); | ||||||
|             currentNodes.should.have.a.property("3"); |             currentNodes.should.have.a.property("3"); | ||||||
| @@ -278,7 +275,7 @@ describe('Flow', function() { | |||||||
|             currentNodes["4"].should.have.a.property("_index",1); |             currentNodes["4"].should.have.a.property("_index",1); | ||||||
|             currentNodes["5"].should.have.a.property("_index",0); |             currentNodes["5"].should.have.a.property("_index",0); | ||||||
|  |  | ||||||
|             flow.stop().then(function() { |             return flow.stop().then(function() { | ||||||
|                 currentNodes.should.not.have.a.property("1"); |                 currentNodes.should.not.have.a.property("1"); | ||||||
|                 currentNodes.should.not.have.a.property("2"); |                 currentNodes.should.not.have.a.property("2"); | ||||||
|                 currentNodes.should.not.have.a.property("3"); |                 currentNodes.should.not.have.a.property("3"); | ||||||
| @@ -289,12 +286,11 @@ describe('Flow', function() { | |||||||
|                 stoppedNodes.should.have.a.property("3"); |                 stoppedNodes.should.have.a.property("3"); | ||||||
|                 stoppedNodes.should.have.a.property("4"); |                 stoppedNodes.should.have.a.property("4"); | ||||||
|                 stoppedNodes.should.have.a.property("5"); |                 stoppedNodes.should.have.a.property("5"); | ||||||
|                 done(); |  | ||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |  | ||||||
|         it("detects dependency loops in config nodes",function() { |         it("detects dependency loops in config nodes",async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"node1",z:"t1",type:"test",foo:"node2"}, // This node depends on #5 |                 {id:"node1",z:"t1",type:"test",foo:"node2"}, // This node depends on #5 | ||||||
| @@ -302,12 +298,12 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             /*jshint immed: false */ |             /*jshint immed: false */ | ||||||
|             (function(){ |             return flow.start().catch(err => { | ||||||
|                 flow.start(); |                 err.toString().should.equal("Error: Circular config node dependency detected: node1") | ||||||
|             }).should.throw("Circular config node dependency detected: node1"); |             }) | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("rewires nodes specified by diff",function(done) { |         it("rewires nodes specified by diff", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -317,16 +313,15 @@ describe('Flow', function() { | |||||||
|  |  | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             createCount.should.equal(0); |             createCount.should.equal(0); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|             //TODO: use update to pass in new wiring and verify the change |             //TODO: use update to pass in new wiring and verify the change | ||||||
|             createCount.should.equal(3); |             createCount.should.equal(3); | ||||||
|             flow.start({rewired:["2"]}); |             flow.start({rewired:["2"]}); | ||||||
|             createCount.should.equal(3); |             createCount.should.equal(3); | ||||||
|             rewiredNodes.should.have.a.property("2"); |             rewiredNodes.should.have.a.property("2"); | ||||||
|             done(); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("instantiates a node with environment variable property values",function(done) { |         it("instantiates a node with environment variable property values", async function() { | ||||||
|             after(function() { |             after(function() { | ||||||
|                 delete process.env.NODE_RED_TEST_VALUE; |                 delete process.env.NODE_RED_TEST_VALUE; | ||||||
|             }) |             }) | ||||||
| @@ -341,7 +336,7 @@ describe('Flow', function() { | |||||||
|                 {id:"6",x:10,y:10,z:"t1",type:"test",foo:["$(NODE_RED_TEST_VALUE)"],wires:[]} |                 {id:"6",x:10,y:10,z:"t1",type:"test",foo:["$(NODE_RED_TEST_VALUE)"],wires:[]} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); |             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
| @@ -352,12 +347,10 @@ describe('Flow', function() { | |||||||
|             activeNodes["5"].foo.should.equal("$(NODE_RED_TEST_VALUE_NONE)"); |             activeNodes["5"].foo.should.equal("$(NODE_RED_TEST_VALUE_NONE)"); | ||||||
|             activeNodes["6"].foo[0].should.equal("a-value"); |             activeNodes["6"].foo[0].should.equal("a-value"); | ||||||
|  |  | ||||||
|             flow.stop().then(function() { |             await flow.stop() | ||||||
|                 done(); |  | ||||||
|             }); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("ignores disabled nodes",function(done) { |         it("ignores disabled nodes", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -368,7 +361,7 @@ describe('Flow', function() { | |||||||
|  |  | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(3); |             Object.keys(flow.getActiveNodes()).should.have.length(3); | ||||||
|  |  | ||||||
| @@ -387,14 +380,12 @@ describe('Flow', function() { | |||||||
|             currentNodes["3"].should.have.a.property("handled",0); |             currentNodes["3"].should.have.a.property("handled",0); | ||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload:"test"}); |             currentNodes["1"].receive({payload:"test"}); | ||||||
|  |             await NR_TEST_UTILS.sleep(50) | ||||||
|             setTimeout(function() { |  | ||||||
|             currentNodes["1"].should.have.a.property("handled",1); |             currentNodes["1"].should.have.a.property("handled",1); | ||||||
|             // Message doesn't reach 3 as 2 is disabled |             // Message doesn't reach 3 as 2 is disabled | ||||||
|             currentNodes["3"].should.have.a.property("handled",0); |             currentNodes["3"].should.have.a.property("handled",0); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     try { |  | ||||||
|             currentNodes.should.not.have.a.property("1"); |             currentNodes.should.not.have.a.property("1"); | ||||||
|             currentNodes.should.not.have.a.property("2"); |             currentNodes.should.not.have.a.property("2"); | ||||||
|             currentNodes.should.not.have.a.property("3"); |             currentNodes.should.not.have.a.property("3"); | ||||||
| @@ -403,12 +394,6 @@ describe('Flow', function() { | |||||||
|             stoppedNodes.should.not.have.a.property("2"); |             stoppedNodes.should.not.have.a.property("2"); | ||||||
|             stoppedNodes.should.have.a.property("3"); |             stoppedNodes.should.have.a.property("3"); | ||||||
|             stoppedNodes.should.have.a.property("4"); |             stoppedNodes.should.have.a.property("4"); | ||||||
|                         done(); |  | ||||||
|                     } catch(err) { |  | ||||||
|                         done(err); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             },50); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     }); |     }); | ||||||
| @@ -416,7 +401,7 @@ describe('Flow', function() { | |||||||
|     describe('#stop', function() { |     describe('#stop', function() { | ||||||
|  |  | ||||||
|  |  | ||||||
|         it("stops all nodes",function(done) { |         it("stops all nodes", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -424,25 +409,21 @@ describe('Flow', function() { | |||||||
|                 {id:"3",x:10,y:10,z:"t1",type:"asyncTest",foo:"a",wires:[]} |                 {id:"3",x:10,y:10,z:"t1",type:"asyncTest",foo:"a",wires:[]} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|  |  | ||||||
|             currentNodes.should.have.a.property("1"); |             currentNodes.should.have.a.property("1"); | ||||||
|             currentNodes.should.have.a.property("2"); |             currentNodes.should.have.a.property("2"); | ||||||
|             currentNodes.should.have.a.property("3"); |             currentNodes.should.have.a.property("3"); | ||||||
|  |  | ||||||
|             flow.stop().then(function() { |             await flow.stop() | ||||||
|             currentNodes.should.not.have.a.property("1"); |             currentNodes.should.not.have.a.property("1"); | ||||||
|             currentNodes.should.not.have.a.property("2"); |             currentNodes.should.not.have.a.property("2"); | ||||||
|             currentNodes.should.not.have.a.property("3"); |             currentNodes.should.not.have.a.property("3"); | ||||||
|             stoppedNodes.should.have.a.property("1"); |             stoppedNodes.should.have.a.property("1"); | ||||||
|             stoppedNodes.should.have.a.property("2"); |             stoppedNodes.should.have.a.property("2"); | ||||||
|             stoppedNodes.should.have.a.property("3"); |             stoppedNodes.should.have.a.property("3"); | ||||||
|                 done(); |  | ||||||
|             }).catch(done); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("stops specified nodes",function(done) { |         it("stops specified nodes", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -450,24 +431,21 @@ describe('Flow', function() { | |||||||
|                 {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} |                 {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             currentNodes.should.have.a.property("1"); |             currentNodes.should.have.a.property("1"); | ||||||
|             currentNodes.should.have.a.property("2"); |             currentNodes.should.have.a.property("2"); | ||||||
|             currentNodes.should.have.a.property("3"); |             currentNodes.should.have.a.property("3"); | ||||||
|  |  | ||||||
|             flow.stop(["2"]).then(function() { |             await flow.stop(["2"]) | ||||||
|             currentNodes.should.have.a.property("1"); |             currentNodes.should.have.a.property("1"); | ||||||
|             currentNodes.should.not.have.a.property("2"); |             currentNodes.should.not.have.a.property("2"); | ||||||
|             currentNodes.should.have.a.property("3"); |             currentNodes.should.have.a.property("3"); | ||||||
|             stoppedNodes.should.not.have.a.property("1"); |             stoppedNodes.should.not.have.a.property("1"); | ||||||
|             stoppedNodes.should.have.a.property("2"); |             stoppedNodes.should.have.a.property("2"); | ||||||
|             stoppedNodes.should.not.have.a.property("3"); |             stoppedNodes.should.not.have.a.property("3"); | ||||||
|                 done(); |  | ||||||
|             }); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("stops config nodes last",function(done) { |         it("stops config nodes last", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -478,7 +456,7 @@ describe('Flow', function() { | |||||||
|                 {id:"c3",z:"t1",type:"test"} |                 {id:"c3",z:"t1",type:"test"} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             currentNodes.should.have.a.property("1"); |             currentNodes.should.have.a.property("1"); | ||||||
|             currentNodes.should.have.a.property("2"); |             currentNodes.should.have.a.property("2"); | ||||||
| @@ -488,14 +466,12 @@ describe('Flow', function() { | |||||||
|             currentNodes.should.have.a.property("c3"); |             currentNodes.should.have.a.property("c3"); | ||||||
|             stoppedOrder.should.have.a.length(0); |             stoppedOrder.should.have.a.length(0); | ||||||
|  |  | ||||||
|             flow.stop().then(function() { |             await flow.stop() | ||||||
|             stoppedOrder.should.eql([ '1', '2', '3', 'c1', 'c2', 'c3' ]); |             stoppedOrder.should.eql([ '1', '2', '3', 'c1', 'c2', 'c3' ]); | ||||||
|                 done(); |  | ||||||
|             }).catch(done); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |  | ||||||
|         it("Times out a node that fails to close", function(done) { |         it("Times out a node that fails to close", async function() { | ||||||
|             Flow.init({settings:{nodeCloseTimeout:50},log:{ |             Flow.init({settings:{nodeCloseTimeout:50},log:{ | ||||||
|                 log: sinon.stub(), |                 log: sinon.stub(), | ||||||
|                 debug: sinon.stub(), |                 debug: sinon.stub(), | ||||||
| @@ -512,31 +488,28 @@ describe('Flow', function() { | |||||||
|                 {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} |                 {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             currentNodes.should.have.a.property("1"); |             currentNodes.should.have.a.property("1"); | ||||||
|             currentNodes.should.have.a.property("2"); |             currentNodes.should.have.a.property("2"); | ||||||
|             currentNodes.should.have.a.property("3"); |             currentNodes.should.have.a.property("3"); | ||||||
|  |  | ||||||
|             flow.stop().then(function() { |             await flow.stop() | ||||||
|             currentNodes.should.have.a.property("1"); |             currentNodes.should.have.a.property("1"); | ||||||
|             currentNodes.should.not.have.a.property("2"); |             currentNodes.should.not.have.a.property("2"); | ||||||
|             currentNodes.should.not.have.a.property("3"); |             currentNodes.should.not.have.a.property("3"); | ||||||
|             stoppedNodes.should.not.have.a.property("1"); |             stoppedNodes.should.not.have.a.property("1"); | ||||||
|             stoppedNodes.should.have.a.property("2"); |             stoppedNodes.should.have.a.property("2"); | ||||||
|             stoppedNodes.should.have.a.property("3"); |             stoppedNodes.should.have.a.property("3"); | ||||||
|                 setTimeout(function() { |             await NR_TEST_UTILS.sleep(40) | ||||||
|             currentNodes.should.not.have.a.property("1"); |             currentNodes.should.not.have.a.property("1"); | ||||||
|             stoppedNodes.should.have.a.property("1"); |             stoppedNodes.should.have.a.property("1"); | ||||||
|                     done(); |  | ||||||
|                 },40) |  | ||||||
|             }); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     describe('#getNode',function() { |     describe('#getNode',function() { | ||||||
|         it("gets a node known to the flow",function(done) { |         it("gets a node known to the flow", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -545,16 +518,13 @@ describe('Flow', function() { | |||||||
|                 {id:"4",z:"t1",type:"test",foo:"a"} |                 {id:"4",z:"t1",type:"test",foo:"a"} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(4); |             Object.keys(flow.getActiveNodes()).should.have.length(4); | ||||||
|  |  | ||||||
|             flow.getNode('1').should.have.a.property('id','1'); |             flow.getNode('1').should.have.a.property('id','1'); | ||||||
|  |             await flow.stop(); | ||||||
|             flow.stop().then(() => { done() }); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("passes to parent if node not known locally",function(done) { |         it("passes to parent if node not known locally", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -565,19 +535,14 @@ describe('Flow', function() { | |||||||
|             var flow = Flow.create({ |             var flow = Flow.create({ | ||||||
|                 getNode: id => { return {id:id}} |                 getNode: id => { return {id:id}} | ||||||
|             },config,config.flows["t1"]); |             },config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(4); |             Object.keys(flow.getActiveNodes()).should.have.length(4); | ||||||
|  |  | ||||||
|             flow.getNode('1').should.have.a.property('id','1'); |             flow.getNode('1').should.have.a.property('id','1'); | ||||||
|  |  | ||||||
|             flow.getNode('parentNode').should.have.a.property('id','parentNode'); |             flow.getNode('parentNode').should.have.a.property('id','parentNode'); | ||||||
|  |             await flow.stop() | ||||||
|  |  | ||||||
|             flow.stop().then(() => { done() }); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("does not pass to parent if cancelBubble set",function(done) { |         it("does not pass to parent if cancelBubble set", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
| @@ -588,19 +553,16 @@ describe('Flow', function() { | |||||||
|             var flow = Flow.create({ |             var flow = Flow.create({ | ||||||
|                 getNode: id => { return {id:id}} |                 getNode: id => { return {id:id}} | ||||||
|             },config,config.flows["t1"]); |             },config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(4); |             Object.keys(flow.getActiveNodes()).should.have.length(4); | ||||||
|  |  | ||||||
|             flow.getNode('1').should.have.a.property('id','1'); |             flow.getNode('1').should.have.a.property('id','1'); | ||||||
|  |  | ||||||
|             should.not.exist(flow.getNode('parentNode',true)); |             should.not.exist(flow.getNode('parentNode',true)); | ||||||
|             flow.stop().then(() => { done() }); |             await flow.stop() | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     describe("#handleStatus",function() { |     describe("#handleStatus",function() { | ||||||
|         it("passes a status event to the adjacent status node",function(done) { |         it("passes a status event to the adjacent status node", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -611,29 +573,24 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             Object.keys(activeNodes).should.have.length(5); |             Object.keys(activeNodes).should.have.length(5); | ||||||
|  |  | ||||||
|  |  | ||||||
|             flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status",random:"otherProperty"}); |             flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status",random:"otherProperty"}); | ||||||
|  |             await NR_TEST_UTILS.sleep(50) | ||||||
|             setTimeout(function() { |  | ||||||
|  |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn"].messages[0]; |             var statusMessage = currentNodes["sn"].messages[0]; | ||||||
|  |  | ||||||
|             statusMessage.should.have.a.property("status"); |             statusMessage.should.have.a.property("status"); | ||||||
|             statusMessage.status.should.have.a.property("text","my-status"); |             statusMessage.status.should.have.a.property("text","my-status"); | ||||||
|             statusMessage.status.should.have.a.property("source"); |             statusMessage.status.should.have.a.property("source"); | ||||||
|             statusMessage.status.source.should.have.a.property("id","1"); |             statusMessage.status.source.should.have.a.property("id","1"); | ||||||
|             statusMessage.status.source.should.have.a.property("type","test"); |             statusMessage.status.source.should.have.a.property("type","test"); | ||||||
|             statusMessage.status.source.should.have.a.property("name","a"); |             statusMessage.status.source.should.have.a.property("name","a"); | ||||||
|  |  | ||||||
|             currentNodes["sn2"].should.have.a.property("handled",1); |             currentNodes["sn2"].should.have.a.property("handled",1); | ||||||
|             statusMessage = currentNodes["sn2"].messages[0]; |             statusMessage = currentNodes["sn2"].messages[0]; | ||||||
|  |  | ||||||
|             statusMessage.should.have.a.property("status"); |             statusMessage.should.have.a.property("status"); | ||||||
|             statusMessage.status.should.have.a.property("text","my-status"); |             statusMessage.status.should.have.a.property("text","my-status"); | ||||||
|             statusMessage.status.should.have.a.property("random","otherProperty"); |             statusMessage.status.should.have.a.property("random","otherProperty"); | ||||||
| @@ -641,14 +598,9 @@ describe('Flow', function() { | |||||||
|             statusMessage.status.source.should.have.a.property("id","1"); |             statusMessage.status.source.should.have.a.property("id","1"); | ||||||
|             statusMessage.status.source.should.have.a.property("type","test"); |             statusMessage.status.source.should.have.a.property("type","test"); | ||||||
|             statusMessage.status.source.should.have.a.property("name","a"); |             statusMessage.status.source.should.have.a.property("name","a"); | ||||||
|  |             await flow.stop() | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |  | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },50) |         it("passes a status event to the adjacent scoped status node ", async function() { | ||||||
|         }); |  | ||||||
|         it("passes a status event to the adjacent scoped status node ",function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -659,39 +611,32 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             Object.keys(activeNodes).should.have.length(5); |             Object.keys(activeNodes).should.have.length(5); | ||||||
|  |  | ||||||
|  |  | ||||||
|             flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"}); |             flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"}); | ||||||
|  |  | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(50) | ||||||
|             currentNodes["sn"].should.have.a.property("handled",0); |             currentNodes["sn"].should.have.a.property("handled",0); | ||||||
|             currentNodes["sn2"].should.have.a.property("handled",1); |             currentNodes["sn2"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn2"].messages[0]; |             var statusMessage = currentNodes["sn2"].messages[0]; | ||||||
|  |  | ||||||
|             statusMessage.should.have.a.property("status"); |             statusMessage.should.have.a.property("status"); | ||||||
|             statusMessage.status.should.have.a.property("text","my-status"); |             statusMessage.status.should.have.a.property("text","my-status"); | ||||||
|             statusMessage.status.should.have.a.property("source"); |             statusMessage.status.should.have.a.property("source"); | ||||||
|             statusMessage.status.source.should.have.a.property("id","1"); |             statusMessage.status.source.should.have.a.property("id","1"); | ||||||
|             statusMessage.status.source.should.have.a.property("type","test"); |             statusMessage.status.source.should.have.a.property("type","test"); | ||||||
|             statusMessage.status.source.should.have.a.property("name","a"); |             statusMessage.status.source.should.have.a.property("name","a"); | ||||||
|  |             await flow.stop() | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |  | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             },50); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("passes a status event to the group scoped status node",function(done) { |         it("passes a status event to the group scoped status node", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id: "g1", type: "group", g: "g3" }, |                 {id: "g1", type: "group", g: "g3", z:"t1" }, | ||||||
|                 {id: "g2", type: "group" }, |                 {id: "g2", type: "group", z:"t1" }, | ||||||
|                 {id: "g3", type: "group" }, |                 {id: "g3", type: "group", z:"t1" }, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",g:"g1", type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",g:"g1", type:"test",name:"a",wires:["2"]}, | ||||||
|                 // sn - in the same group as source node |                 // sn - in the same group as source node | ||||||
|                 {id:"sn",x:10,y:10,z:"t1",g:"g1", type:"status",scope:"group",wires:[]}, |                 {id:"sn",x:10,y:10,z:"t1",g:"g1", type:"status",scope:"group",wires:[]}, | ||||||
| @@ -705,29 +650,21 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"}); |             flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(50) | ||||||
|                 try { |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             currentNodes["sn2"].should.have.a.property("handled",0); |             currentNodes["sn2"].should.have.a.property("handled",0); | ||||||
|             currentNodes["sn3"].should.have.a.property("handled",1); |             currentNodes["sn3"].should.have.a.property("handled",1); | ||||||
|             currentNodes["sn3"].should.have.a.property("handled",1); |             currentNodes["sn3"].should.have.a.property("handled",1); | ||||||
|                     done() |             await flow.stop() | ||||||
|                 } catch(err) { |  | ||||||
|                     done(err) |  | ||||||
|                 } |  | ||||||
|             },50); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     describe("#handleError",function() { |     describe("#handleError",function() { | ||||||
|         it("passes an error event to the adjacent catch node",function(done) { |         it("passes an error event to the adjacent catch node", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -739,15 +676,12 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             Object.keys(activeNodes).should.have.length(6); |             Object.keys(activeNodes).should.have.length(6); | ||||||
|  |  | ||||||
|  |  | ||||||
|             flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); |             flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); | ||||||
|  |             await NR_TEST_UTILS.sleep(50) | ||||||
|             setTimeout(function() { |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn"].messages[0]; |             var statusMessage = currentNodes["sn"].messages[0]; | ||||||
|  |  | ||||||
| @@ -770,14 +704,10 @@ describe('Flow', function() { | |||||||
|  |  | ||||||
|             // Node sn3 has uncaught:true - so should not get called |             // Node sn3 has uncaught:true - so should not get called | ||||||
|             currentNodes["sn3"].should.have.a.property("handled",0); |             currentNodes["sn3"].should.have.a.property("handled",0); | ||||||
|  |             await flow.stop() | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |  | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },50); |  | ||||||
|         }); |         it("passes an error event to the adjacent scoped catch node ", async function() { | ||||||
|         it("passes an error event to the adjacent scoped catch node ",function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -790,14 +720,13 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             Object.keys(activeNodes).should.have.length(7); |             Object.keys(activeNodes).should.have.length(7); | ||||||
|  |  | ||||||
|             flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); |             flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); | ||||||
|  |             await NR_TEST_UTILS.sleep(50) | ||||||
|             setTimeout(function() { |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",0); |             currentNodes["sn"].should.have.a.property("handled",0); | ||||||
|             currentNodes["sn2"].should.have.a.property("handled",1); |             currentNodes["sn2"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn2"].messages[0]; |             var statusMessage = currentNodes["sn2"].messages[0]; | ||||||
| @@ -815,31 +744,26 @@ describe('Flow', function() { | |||||||
|  |  | ||||||
|             // Inject error that sn1/2 will ignore - so should get picked up by sn3 |             // Inject error that sn1/2 will ignore - so should get picked up by sn3 | ||||||
|             flow.handleError(config.flows["t1"].nodes["3"],"my-error-2",{a:"foo-2"}); |             flow.handleError(config.flows["t1"].nodes["3"],"my-error-2",{a:"foo-2"}); | ||||||
|                 setTimeout(function() { |  | ||||||
|  |             await NR_TEST_UTILS.sleep(50) | ||||||
|             currentNodes["sn"].should.have.a.property("handled",0); |             currentNodes["sn"].should.have.a.property("handled",0); | ||||||
|             currentNodes["sn2"].should.have.a.property("handled",1); |             currentNodes["sn2"].should.have.a.property("handled",1); | ||||||
|             currentNodes["sn3"].should.have.a.property("handled",1); |             currentNodes["sn3"].should.have.a.property("handled",1); | ||||||
|             currentNodes["sn4"].should.have.a.property("handled",1); |             currentNodes["sn4"].should.have.a.property("handled",1); | ||||||
|  |  | ||||||
|             statusMessage = currentNodes["sn3"].messages[0]; |             statusMessage = currentNodes["sn3"].messages[0]; | ||||||
|             statusMessage.should.have.a.property("error"); |             statusMessage.should.have.a.property("error"); | ||||||
|             statusMessage.error.should.have.a.property("message","my-error-2"); |             statusMessage.error.should.have.a.property("message","my-error-2"); | ||||||
|             statusMessage.error.should.have.a.property("source"); |             statusMessage.error.should.have.a.property("source"); | ||||||
|             statusMessage.error.source.should.have.a.property("id","3"); |             statusMessage.error.source.should.have.a.property("id","3"); | ||||||
|             statusMessage.error.source.should.have.a.property("type","test"); |             statusMessage.error.source.should.have.a.property("type","test"); | ||||||
|  |             await flow.stop() | ||||||
|                     flow.stop().then(function() { |  | ||||||
|                         done(); |  | ||||||
|         }); |         }); | ||||||
|                 },50); |         it("passes an error event to the group scoped catch node",async function() { | ||||||
|             },50); |  | ||||||
|         }); |  | ||||||
|         it("passes an error event to the group scoped catch node",function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id: "g1", type: "group", g: "g3" }, |                 {id: "g1", type: "group", g: "g3", z:"t1" }, | ||||||
|                 {id: "g2", type: "group" }, |                 {id: "g2", type: "group", z:"t1" }, | ||||||
|                 {id: "g3", type: "group" }, |                 {id: "g3", type: "group", z:"t1" }, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",g:"g1", type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",g:"g1", type:"test",name:"a",wires:["2"]}, | ||||||
|                 // sn - in the same group as source node |                 // sn - in the same group as source node | ||||||
|                 {id:"sn",x:10,y:10,z:"t1",g:"g1", type:"catch",scope:"group",wires:[]}, |                 {id:"sn",x:10,y:10,z:"t1",g:"g1", type:"catch",scope:"group",wires:[]}, | ||||||
| @@ -853,24 +777,20 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); |             flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(50) | ||||||
|                 try { |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             currentNodes["sn2"].should.have.a.property("handled",0); |             currentNodes["sn2"].should.have.a.property("handled",0); | ||||||
|             currentNodes["sn3"].should.have.a.property("handled",1); |             currentNodes["sn3"].should.have.a.property("handled",1); | ||||||
|             currentNodes["sn3"].should.have.a.property("handled",1); |             currentNodes["sn3"].should.have.a.property("handled",1); | ||||||
|                     done() |             await flow.stop() | ||||||
|                 } catch(err) { |  | ||||||
|                     done(err) |  | ||||||
|                 } |  | ||||||
|             },50); |  | ||||||
|         }); |         }); | ||||||
|         it("moves any existing error object sideways",function(done){ |  | ||||||
|  |         it("moves any existing error object sideways", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -880,12 +800,12 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo",error:"existing"}); |             flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo",error:"existing"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(50) | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn"].messages[0]; |             var statusMessage = currentNodes["sn"].messages[0]; | ||||||
|  |  | ||||||
| @@ -897,16 +817,13 @@ describe('Flow', function() { | |||||||
|             statusMessage.error.source.should.have.a.property("type","test"); |             statusMessage.error.source.should.have.a.property("type","test"); | ||||||
|             statusMessage.error.source.should.have.a.property("name","a"); |             statusMessage.error.source.should.have.a.property("name","a"); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             },50); |  | ||||||
|         }); |         }); | ||||||
|         it("prevents an error looping more than 10 times",function(){}); |         it("prevents an error looping more than 10 times",function(){}); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     describe("#handleComplete",function() { |     describe("#handleComplete",function() { | ||||||
|         it("passes a complete event to the adjacent Complete node",function(done) { |         it("passes a complete event to the adjacent Complete node",async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"testDone",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"testDone",name:"a",wires:["2"]}, | ||||||
| @@ -916,44 +833,46 @@ describe('Flow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             Object.keys(activeNodes).should.have.length(4); |             Object.keys(activeNodes).should.have.length(4); | ||||||
|  |  | ||||||
|             var msg = {payload: "hello world"} |             var msg = {payload: "hello world"} | ||||||
|             var n1 = currentNodes["1"].receive(msg); |             var n1 = currentNodes["1"].receive(msg); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(50) | ||||||
|  |  | ||||||
|             currentNodes["cn"].should.have.a.property("handled",2); |             currentNodes["cn"].should.have.a.property("handled",2); | ||||||
|             currentNodes["cn"].messages[0].should.have.a.property("handled",1); |             currentNodes["cn"].messages[0].should.have.a.property("handled",1); | ||||||
|             currentNodes["cn"].messages[1].should.have.a.property("handled",2); |             currentNodes["cn"].messages[1].should.have.a.property("handled",2); | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             },50); |  | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|     describe("#send", function() { |     describe("#send", function() { | ||||||
|         it("sends a message - no cloning", function(done) { |         it("sends a message - no cloning", async function() { | ||||||
|             var shutdownTest = function(err) { |  | ||||||
|                 hooks.clear(); |  | ||||||
|                 flow.stop().then(() => { done(err) }); |  | ||||||
|             } |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
|                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} |                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(2); |             Object.keys(flow.getActiveNodes()).should.have.length(2); | ||||||
|  |  | ||||||
|             var n1 = flow.getNode('1'); |             var n1 = flow.getNode('1'); | ||||||
|             var n2 = flow.getNode('2'); |             var n2 = flow.getNode('2'); | ||||||
|             var messageReceived = false; |             var messageReceived = false; | ||||||
|  |  | ||||||
|  |             return new Promise((resolve, reject) => { | ||||||
|  |                 const shutdownTest = async function(err) { | ||||||
|  |                     hooks.clear(); | ||||||
|  |                     await flow.stop() | ||||||
|  |                     if (err) { reject(err) } | ||||||
|  |                     else { resolve() } | ||||||
|  |                 } | ||||||
|                 n2.receive = function(msg) { |                 n2.receive = function(msg) { | ||||||
|                     messageReceived = true; |                     messageReceived = true; | ||||||
|                     try { |                     try { | ||||||
| @@ -973,24 +892,28 @@ describe('Flow', function() { | |||||||
|                 }]) |                 }]) | ||||||
|                 messageReceived.should.be.false() |                 messageReceived.should.be.false() | ||||||
|             }) |             }) | ||||||
|         it("sends a message - cloning", function(done) { |         }) | ||||||
|             var shutdownTest = function(err) { |         it("sends a message - cloning", async function() { | ||||||
|                 hooks.clear(); |  | ||||||
|                 flow.stop().then(() => { done(err) }); |  | ||||||
|             } |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
|                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} |                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(2); |             Object.keys(flow.getActiveNodes()).should.have.length(2); | ||||||
|  |  | ||||||
|             var n1 = flow.getNode('1'); |             var n1 = flow.getNode('1'); | ||||||
|             var n2 = flow.getNode('2'); |             var n2 = flow.getNode('2'); | ||||||
|  |  | ||||||
|  |             return new Promise((resolve, reject) => { | ||||||
|  |                 const shutdownTest = async function(err) { | ||||||
|  |                     hooks.clear(); | ||||||
|  |                     await flow.stop() | ||||||
|  |                     if (err) { reject(err) } | ||||||
|  |                     else { resolve() } | ||||||
|  |                 } | ||||||
|                 n2.receive = function(msg) { |                 n2.receive = function(msg) { | ||||||
|                     try { |                     try { | ||||||
|                         // Message should be cloned |                         // Message should be cloned | ||||||
| @@ -1010,24 +933,27 @@ describe('Flow', function() { | |||||||
|                     cloneMessage: true |                     cloneMessage: true | ||||||
|                 }]) |                 }]) | ||||||
|             }) |             }) | ||||||
|         it("sends multiple messages", function(done) { |         }) | ||||||
|             var shutdownTest = function(err) { |         it("sends multiple messages", async function() { | ||||||
|                 hooks.clear(); |  | ||||||
|                 flow.stop().then(() => { done(err) }); |  | ||||||
|             } |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
|                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} |                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(2); |             Object.keys(flow.getActiveNodes()).should.have.length(2); | ||||||
|  |  | ||||||
|             var n1 = flow.getNode('1'); |             var n1 = flow.getNode('1'); | ||||||
|             var n2 = flow.getNode('2'); |             var n2 = flow.getNode('2'); | ||||||
|  |             return new Promise((resolve, reject) => { | ||||||
|  |                 const shutdownTest = async function(err) { | ||||||
|  |                     hooks.clear(); | ||||||
|  |                     await flow.stop() | ||||||
|  |                     if (err) { reject(err) } | ||||||
|  |                     else { resolve() } | ||||||
|  |                 } | ||||||
|                 var messageCount = 0; |                 var messageCount = 0; | ||||||
|                 n2.receive = function(msg) { |                 n2.receive = function(msg) { | ||||||
|                     try { |                     try { | ||||||
| @@ -1052,7 +978,9 @@ describe('Flow', function() { | |||||||
|                     destination: { id:"2", node: undefined } |                     destination: { id:"2", node: undefined } | ||||||
|                 }]) |                 }]) | ||||||
|             })   |             })   | ||||||
|         it("sends a message - triggers hooks", function(done) { |         }) | ||||||
|  |         it("sends a message - triggers hooks", async function() { | ||||||
|  |             const message = {payload:"hello"} | ||||||
|             var hookErrors = []; |             var hookErrors = []; | ||||||
|             var messageReceived = false; |             var messageReceived = false; | ||||||
|             var hooksCalled = []; |             var hooksCalled = []; | ||||||
| @@ -1100,22 +1028,25 @@ describe('Flow', function() { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|             }) |             }) | ||||||
|             var shutdownTest = function(err) { |  | ||||||
|                 hooks.clear(); |  | ||||||
|                 flow.stop().then(() => { done(err) }); |  | ||||||
|             } |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||||
|                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} |                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             Object.keys(flow.getActiveNodes()).should.have.length(2); |             Object.keys(flow.getActiveNodes()).should.have.length(2); | ||||||
|  |  | ||||||
|             var n1 = flow.getNode('1'); |             var n1 = flow.getNode('1'); | ||||||
|             var n2 = flow.getNode('2'); |             var n2 = flow.getNode('2'); | ||||||
|  |             return new Promise((resolve, reject) => { | ||||||
|  |                 const shutdownTest = async function(err) { | ||||||
|  |                     hooks.clear(); | ||||||
|  |                     await flow.stop() | ||||||
|  |                     if (err) { reject(err) } | ||||||
|  |                     else { resolve() } | ||||||
|  |                 } | ||||||
|                 n2.receive = function(msg) { |                 n2.receive = function(msg) { | ||||||
|                     messageReceived = true; |                     messageReceived = true; | ||||||
|                     try { |                     try { | ||||||
| @@ -1132,7 +1063,7 @@ describe('Flow', function() { | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|             var message = {payload:"hello"} |                  | ||||||
|                 flow.send([{ |                 flow.send([{ | ||||||
|                     msg: message, |                     msg: message, | ||||||
|                     source: { id:"1", node: n1 }, |                     source: { id:"1", node: n1 }, | ||||||
| @@ -1140,13 +1071,14 @@ describe('Flow', function() { | |||||||
|                     cloneMessage: true |                     cloneMessage: true | ||||||
|                 }]) |                 }]) | ||||||
|             }) |             }) | ||||||
|  |         }) | ||||||
|  |  | ||||||
|         describe("errors thrown by hooks are reported to the sending node", function() { |         describe("errors thrown by hooks are reported to the sending node", function() { | ||||||
|             var flow; |             var flow; | ||||||
|             var n1,n2; |             var n1,n2; | ||||||
|             var messageReceived = false; |             var messageReceived = false; | ||||||
|             var errorReceived = null; |             var errorReceived = null; | ||||||
|             before(function() { |             before(async function() { | ||||||
|                 hooks.add("onSend", function(sendEvents) { |                 hooks.add("onSend", function(sendEvents) { | ||||||
|                     if (sendEvents[0].msg.payload === "trigger-onSend") { |                     if (sendEvents[0].msg.payload === "trigger-onSend") { | ||||||
|                         throw new Error("onSend Error"); |                         throw new Error("onSend Error"); | ||||||
| @@ -1173,7 +1105,7 @@ describe('Flow', function() { | |||||||
|                     {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} |                     {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} | ||||||
|                 ]); |                 ]); | ||||||
|                 flow = Flow.create({},config,config.flows["t1"]); |                 flow = Flow.create({},config,config.flows["t1"]); | ||||||
|                 flow.start(); |                 await flow.start(); | ||||||
|                 n1 = flow.getNode('1'); |                 n1 = flow.getNode('1'); | ||||||
|                 n2 = flow.getNode('2'); |                 n2 = flow.getNode('2'); | ||||||
|                 n2.receive = function(msg) { |                 n2.receive = function(msg) { | ||||||
| @@ -1184,9 +1116,9 @@ describe('Flow', function() { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|             }) |             }) | ||||||
|             after(function(done) { |             after(async function() { | ||||||
|                 hooks.clear(); |                 hooks.clear(); | ||||||
|                 flow.stop().then(() => { done() }); |                 await flow.stop() | ||||||
|             }) |             }) | ||||||
|             beforeEach(function() { |             beforeEach(function() { | ||||||
|                 messageReceived = false; |                 messageReceived = false; | ||||||
| @@ -1223,7 +1155,7 @@ describe('Flow', function() { | |||||||
|             var n1,n2; |             var n1,n2; | ||||||
|             var messageReceived = false; |             var messageReceived = false; | ||||||
|             var errorReceived = false; |             var errorReceived = false; | ||||||
|             before(function() { |             before(async function() { | ||||||
|                 hooks.add("onSend", function(sendEvents) { |                 hooks.add("onSend", function(sendEvents) { | ||||||
|                     if (sendEvents[0].msg.payload === "trigger-onSend") { |                     if (sendEvents[0].msg.payload === "trigger-onSend") { | ||||||
|                         return false |                         return false | ||||||
| @@ -1250,7 +1182,7 @@ describe('Flow', function() { | |||||||
|                     {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} |                     {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} | ||||||
|                 ]); |                 ]); | ||||||
|                 flow = Flow.create({},config,config.flows["t1"]); |                 flow = Flow.create({},config,config.flows["t1"]); | ||||||
|                 flow.start(); |                 await flow.start(); | ||||||
|                 n1 = flow.getNode('1'); |                 n1 = flow.getNode('1'); | ||||||
|                 n2 = flow.getNode('2'); |                 n2 = flow.getNode('2'); | ||||||
|                 n2.receive = function(msg) { |                 n2.receive = function(msg) { | ||||||
| @@ -1261,9 +1193,9 @@ describe('Flow', function() { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|             }) |             }) | ||||||
|             after(function(done) { |             after(async function() { | ||||||
|                 hooks.clear(); |                 hooks.clear(); | ||||||
|                 flow.stop().then(() => { done() }); |                 await flow.stop() | ||||||
|             }) |             }) | ||||||
|             function testSend(payload,messageReceivedExpected,errorReceivedExpected,done) { |             function testSend(payload,messageReceivedExpected,errorReceivedExpected,done) { | ||||||
|                 messageReceived = false; |                 messageReceived = false; | ||||||
| @@ -1303,8 +1235,7 @@ describe('Flow', function() { | |||||||
|     }) |     }) | ||||||
|  |  | ||||||
|     describe("#env", function () { |     describe("#env", function () { | ||||||
|         it("can instantiate a node with environment variable property values of group and tab", function (done) { |         it("can instantiate a node with environment variable property values of group and tab", async function () { | ||||||
|             try { |  | ||||||
|             after(function() { |             after(function() { | ||||||
|                 delete process.env.V0; |                 delete process.env.V0; | ||||||
|                 delete process.env.V1; |                 delete process.env.V1; | ||||||
| @@ -1335,7 +1266,7 @@ describe('Flow', function() { | |||||||
|                 {id:"t1__V1",x:10,y:10,z:"t1",type:"test",foo:"${V1}",wires:[]}, |                 {id:"t1__V1",x:10,y:10,z:"t1",type:"test",foo:"${V1}",wires:[]}, | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); |             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); | ||||||
|                 flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
| @@ -1348,17 +1279,10 @@ describe('Flow', function() { | |||||||
|             activeNodes.t1g2V2.foo.should.equal("t1v2"); // node in group 2, get V2, (group 2 no V2) --> parent (tab 1 has V2) |             activeNodes.t1g2V2.foo.should.equal("t1v2"); // node in group 2, get V2, (group 2 no V2) --> parent (tab 1 has V2) | ||||||
|             activeNodes.t1g2V3.foo.should.equal("gv3"); // node in group 2, get V3, (group 2 no V3) --> parent (tab 1 no V2) --> parent (global has V3) |             activeNodes.t1g2V3.foo.should.equal("gv3"); // node in group 2, get V3, (group 2 no V3) --> parent (tab 1 no V2) --> parent (global has V3) | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             } |  | ||||||
|             catch (e) { |         it("can access environment variable property using $parent", async function () { | ||||||
|                 console.log(e.stack); |  | ||||||
|                 done(e); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         it("can access environment variable property using $parent", function (done) { |  | ||||||
|             try { |  | ||||||
|             after(function() { |             after(function() { | ||||||
|                 delete process.env.V0; |                 delete process.env.V0; | ||||||
|                 delete process.env.V1; |                 delete process.env.V1; | ||||||
| @@ -1383,7 +1307,7 @@ describe('Flow', function() { | |||||||
|                 {id:"5",x:10,y:10,z:"t1",type:"test",foo:"${$parent.V1}",wires:[]}, |                 {id:"5",x:10,y:10,z:"t1",type:"test",foo:"${$parent.V1}",wires:[]}, | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); |             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); | ||||||
|                 flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
| @@ -1393,18 +1317,10 @@ describe('Flow', function() { | |||||||
|             activeNodes["4"].foo.should.equal("v2"); |             activeNodes["4"].foo.should.equal("v2"); | ||||||
|             activeNodes["5"].foo.should.equal("gv1"); |             activeNodes["5"].foo.should.equal("gv1"); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             catch (e) { |  | ||||||
|                 console.log(e.stack); |  | ||||||
|                 done(e); |  | ||||||
|             } |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("can define environment variable using JSONata", function (done) { |         it("can define environment variable using JSONata", async function () { | ||||||
|             try { |  | ||||||
|             after(function() { |             after(function() { | ||||||
|                 delete process.env.V0; |                 delete process.env.V0; | ||||||
|             }) |             }) | ||||||
| @@ -1419,25 +1335,17 @@ describe('Flow', function() { | |||||||
|                 {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V1)",wires:[]}, |                 {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V1)",wires:[]}, | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); |             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); | ||||||
|                 flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].foo.should.equal(3); |             activeNodes["1"].foo.should.equal(3); | ||||||
|             activeNodes["2"].foo.should.equal(5); |             activeNodes["2"].foo.should.equal(5); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             catch (e) { |  | ||||||
|                 console.log(e.stack); |  | ||||||
|                 done(e); |  | ||||||
|             } |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("can access global environment variables defined as JSONata values", function (done) { |         it("can access global environment variables defined as JSONata values", async function () { | ||||||
|             try { |  | ||||||
|             after(function() { |             after(function() { | ||||||
|                 delete process.env.V0; |                 delete process.env.V0; | ||||||
|             }) |             }) | ||||||
| @@ -1452,21 +1360,39 @@ describe('Flow', function() { | |||||||
|                 {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V1)",wires:[]}, |                 {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V1)",wires:[]}, | ||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); |             var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); | ||||||
|                 flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].foo.should.equal(3); |             activeNodes["1"].foo.should.equal(3); | ||||||
|             activeNodes["2"].foo.should.equal(5); |             activeNodes["2"].foo.should.equal(5); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             } |         it("global flow can access global-config defined environment variables", async function () { | ||||||
|             catch (e) { |             after(function() { | ||||||
|                 console.log(e.stack); |                 delete process.env.V0; | ||||||
|                 done(e); |             }) | ||||||
|             } |             const config = flowUtils.parseConfig([ | ||||||
|  |                 {id:"gc", type:"global-config", env:[ | ||||||
|  |                     {"name": "GC0", value: "3+4", type: "jsonata"} | ||||||
|  |                 ]}, | ||||||
|  |                 {id:"t1",type:"tab" }, | ||||||
|  |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"${GC0}",wires:[]}, | ||||||
|  |             ]); | ||||||
|  |             // Two-arg call - makes this the global flow that handles global-config nodes | ||||||
|  |             const globalFlow = Flow.create({getSetting:v=>process.env[v]},config); | ||||||
|  |             await globalFlow.start(); | ||||||
|  |  | ||||||
|  |             // Pass the globalFlow in as the parent flow to allow global-config lookup | ||||||
|  |             const flow = Flow.create(globalFlow,config,config.flows["t1"]); | ||||||
|  |             await flow.start(); | ||||||
|  |  | ||||||
|  |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |             activeNodes["1"].foo.should.equal(7); | ||||||
|  |  | ||||||
|  |             await flow.stop() | ||||||
|  |             await globalFlow.stop() | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										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.handled = 0; | ||||||
|         this.stopped = false; |         this.stopped = false; | ||||||
|         this.received = null; |         this.received = null; | ||||||
|  |         this.receivedEnv = null; | ||||||
|         currentNodes[node.id] = node; |         currentNodes[node.id] = node; | ||||||
|         this.on('input',function(msg) { |         this.on('input',function(msg) { | ||||||
|             // console.log(this.id,msg.payload); |             // console.log(this.id,msg.payload); | ||||||
|             node.handled++; |             node.handled++; | ||||||
|             node.received = msg.payload; |             node.received = msg.payload; | ||||||
|  |             node.receivedEnv = msg.receivedEnv; | ||||||
|             node.send(msg); |             node.send(msg); | ||||||
|         }); |         }); | ||||||
|         this.on('close',function() { |         this.on('close',function() { | ||||||
| @@ -185,7 +187,15 @@ describe('Subflow', function() { | |||||||
|             var flow = node._flow; |             var flow = node._flow; | ||||||
|             var val = flow.getSetting("__KEY__"); |             var val = flow.getSetting("__KEY__"); | ||||||
|             node.received = val; |             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() { |         this.on('close',function() { | ||||||
|             node.stopped = true; |             node.stopped = true; | ||||||
| @@ -282,7 +292,7 @@ describe('Subflow', function() { | |||||||
|         getType.restore(); |         getType.restore(); | ||||||
|     }); |     }); | ||||||
|     describe('#start',function() { |     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([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {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"]); |             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(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             Object.keys(activeNodes).should.have.length(4); |             Object.keys(activeNodes).should.have.length(4); | ||||||
| @@ -333,36 +343,20 @@ describe('Subflow', function() { | |||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload:"test"}); |             currentNodes["1"].receive({payload:"test"}); | ||||||
|             |             | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|  |  | ||||||
|             currentNodes["1"].should.have.a.property("handled",1); |             currentNodes["1"].should.have.a.property("handled",1); | ||||||
|             // currentNodes[sfInstanceId].should.have.a.property("handled",1); |             // currentNodes[sfInstanceId].should.have.a.property("handled",1); | ||||||
|             // currentNodes[sfInstanceId2].should.have.a.property("handled",1); |             // currentNodes[sfInstanceId2].should.have.a.property("handled",1); | ||||||
|             currentNodes["3"].should.have.a.property("handled",1); |             currentNodes["3"].should.have.a.property("handled",1); | ||||||
|             currentNodes["4"].should.have.a.property("handled",1); |             currentNodes["4"].should.have.a.property("handled",1); | ||||||
|  |  | ||||||
|  |             await flow.stop() | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |  | ||||||
|             Object.keys(currentNodes).should.have.length(0); |             Object.keys(currentNodes).should.have.length(0); | ||||||
|             Object.keys(stoppedNodes).should.have.length(6); |             Object.keys(stoppedNodes).should.have.length(6); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|                     // currentNodes.should.not.have.a.property("1"); |         it("instantiates a subflow inside a subflow and stops it", async function() { | ||||||
|                     // 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); |  | ||||||
|         }); |  | ||||||
|         it("instantiates a subflow inside a subflow and stops it",function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {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"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             currentNodes["1"].should.have.a.property("handled",0); |             currentNodes["1"].should.have.a.property("handled",0); | ||||||
|             currentNodes["3"].should.have.a.property("handled",0); |             currentNodes["3"].should.have.a.property("handled",0); | ||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload:"test"}); |             currentNodes["1"].receive({payload:"test"}); | ||||||
|  |             await NR_TEST_UTILS.sleep(150) | ||||||
|             setTimeout(function() { |  | ||||||
|             currentNodes["1"].should.have.a.property("handled",1); |             currentNodes["1"].should.have.a.property("handled",1); | ||||||
|             currentNodes["3"].should.have.a.property("handled",1); |             currentNodes["3"].should.have.a.property("handled",1); | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|             Object.keys(currentNodes).should.have.length(0); |             Object.keys(currentNodes).should.have.length(0); | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },150); |  | ||||||
|         }); |  | ||||||
|         it("rewires a subflow node on update/start",function(done){ |  | ||||||
|  |  | ||||||
|  |         it("rewires a subflow node on update/start", async function(){ | ||||||
|             var rawConfig = [ |             var rawConfig = [ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {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 diff = flowUtils.diffConfigs(config,newConfig); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             Object.keys(activeNodes).should.have.length(4); |             Object.keys(activeNodes).should.have.length(4); | ||||||
| @@ -429,8 +419,7 @@ describe('Subflow', function() { | |||||||
|             currentNodes["4"].should.have.a.property("handled",0); |             currentNodes["4"].should.have.a.property("handled",0); | ||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload:"test"}); |             currentNodes["1"].receive({payload:"test"}); | ||||||
|  |             await NR_TEST_UTILS.sleep(150) | ||||||
|             setTimeout(function() { |  | ||||||
|             currentNodes["1"].should.have.a.property("handled",1); |             currentNodes["1"].should.have.a.property("handled",1); | ||||||
|             // currentNodes[sfInstanceId].should.have.a.property("handled",1); |             // currentNodes[sfInstanceId].should.have.a.property("handled",1); | ||||||
|             // currentNodes[sfInstanceId2].should.have.a.property("handled",1); |             // currentNodes[sfInstanceId2].should.have.a.property("handled",1); | ||||||
| @@ -438,27 +427,20 @@ describe('Subflow', function() { | |||||||
|             currentNodes["4"].should.have.a.property("handled",0); |             currentNodes["4"].should.have.a.property("handled",0); | ||||||
|  |  | ||||||
|             flow.update(newConfig,newConfig.flows["t1"]); |             flow.update(newConfig,newConfig.flows["t1"]); | ||||||
|                 flow.start(diff) |             await flow.start(diff) | ||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload:"test2"}); |             currentNodes["1"].receive({payload:"test2"}); | ||||||
|                 setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|  |  | ||||||
|             currentNodes["1"].should.have.a.property("handled",2); |             currentNodes["1"].should.have.a.property("handled",2); | ||||||
|             // currentNodes[sfInstanceId].should.have.a.property("handled",2); |             // currentNodes[sfInstanceId].should.have.a.property("handled",2); | ||||||
|             // currentNodes[sfInstanceId2].should.have.a.property("handled",2); |             // currentNodes[sfInstanceId2].should.have.a.property("handled",2); | ||||||
|             currentNodes["3"].should.have.a.property("handled",1); |             currentNodes["3"].should.have.a.property("handled",1); | ||||||
|             currentNodes["4"].should.have.a.property("handled",1); |             currentNodes["4"].should.have.a.property("handled",1); | ||||||
|  |  | ||||||
|  |             await flow.stop() | ||||||
|                     flow.stop().then(function() { |  | ||||||
|                         done(); |  | ||||||
|                     }); |  | ||||||
|                 },150); |  | ||||||
|             },150); |  | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|     describe('#stop', function() { |     describe('#stop', function() { | ||||||
|         it("stops subflow instance nodes",function(done) { |         it("stops subflow instance nodes", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, |                 {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"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|             Object.keys(activeNodes).should.have.length(3); |             Object.keys(activeNodes).should.have.length(3); | ||||||
|             Object.keys(stoppedNodes).should.have.length(0); |             Object.keys(stoppedNodes).should.have.length(0); | ||||||
|             flow.stop(["2"]).then(function() { |             await flow.stop(["2"]) | ||||||
|             Object.keys(currentNodes).should.have.length(2); |             Object.keys(currentNodes).should.have.length(2); | ||||||
|             Object.keys(stoppedNodes).should.have.length(1); |             Object.keys(stoppedNodes).should.have.length(1); | ||||||
|                 done(); |  | ||||||
|             }).catch(done); |  | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|     describe("#handleStatus",function() { |     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([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -496,12 +476,12 @@ describe('Subflow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].receive({payload:"test"}); |             activeNodes["1"].receive({payload:"test"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn"].messages[0]; |             var statusMessage = currentNodes["sn"].messages[0]; | ||||||
|  |  | ||||||
| @@ -511,12 +491,9 @@ describe('Subflow', function() { | |||||||
|             statusMessage.status.source.should.have.a.property("type","testStatus"); |             statusMessage.status.source.should.have.a.property("type","testStatus"); | ||||||
|             statusMessage.status.source.should.have.a.property("name","test-status-node"); |             statusMessage.status.source.should.have.a.property("name","test-status-node"); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },150); |         it("passes a status event to the subflow's parent tab status node - targetted scope", async function() { | ||||||
|         }); |  | ||||||
|         it("passes a status event to the subflow's parent tab status node - targetted scope",function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -531,13 +508,13 @@ describe('Subflow', function() { | |||||||
|  |  | ||||||
|             var flow = Flow.create({handleStatus:() => { parentFlowStatusCalled = true} },config,config.flows["t1"]); |             var flow = Flow.create({handleStatus:() => { parentFlowStatusCalled = true} },config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].receive({payload:"test"}); |             activeNodes["1"].receive({payload:"test"}); | ||||||
|  |  | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|             parentFlowStatusCalled.should.be.false(); |             parentFlowStatusCalled.should.be.false(); | ||||||
|  |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
| @@ -549,16 +526,12 @@ describe('Subflow', function() { | |||||||
|             statusMessage.status.source.should.have.a.property("type","testStatus"); |             statusMessage.status.source.should.have.a.property("type","testStatus"); | ||||||
|             statusMessage.status.source.should.have.a.property("name","test-status-node"); |             statusMessage.status.source.should.have.a.property("name","test-status-node"); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|  |  | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             },150); |  | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     describe("status node", function() { |     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([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -578,13 +551,13 @@ describe('Subflow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].receive({payload:"test-payload"}); |             activeNodes["1"].receive({payload:"test-payload"}); | ||||||
|  |             await NR_TEST_UTILS.sleep(150) | ||||||
|  |  | ||||||
|             setTimeout(function() { |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn"].messages[0]; |             var statusMessage = currentNodes["sn"].messages[0]; | ||||||
|  |  | ||||||
| @@ -593,14 +566,9 @@ describe('Subflow', function() { | |||||||
|             statusMessage.status.should.have.a.property("source"); |             statusMessage.status.should.have.a.property("source"); | ||||||
|             statusMessage.status.source.should.have.a.property("id","2"); |             statusMessage.status.source.should.have.a.property("id","2"); | ||||||
|             statusMessage.status.source.should.have.a.property("type","subflow:sf1"); |             statusMessage.status.source.should.have.a.property("type","subflow:sf1"); | ||||||
|  |             await flow.stop() | ||||||
|                 flow.stop().then(function() { |  | ||||||
|  |  | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },150); |         it("emits a status event when a message is passed to a subflow-status node - msg.payload as status obj", async function() { | ||||||
|         }); |  | ||||||
|         it("emits a status event when a message is passed to a subflow-status node - msg.payload as status obj", function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -620,13 +588,14 @@ describe('Subflow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].receive({payload:{text:"payload-obj"}}); |             activeNodes["1"].receive({payload:{text:"payload-obj"}}); | ||||||
|  |  | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|  |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn"].messages[0]; |             var statusMessage = currentNodes["sn"].messages[0]; | ||||||
|  |  | ||||||
| @@ -636,13 +605,9 @@ describe('Subflow', function() { | |||||||
|             statusMessage.status.source.should.have.a.property("id","2"); |             statusMessage.status.source.should.have.a.property("id","2"); | ||||||
|             statusMessage.status.source.should.have.a.property("type","subflow:sf1"); |             statusMessage.status.source.should.have.a.property("type","subflow:sf1"); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|  |  | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },150); |         it("emits a status event when a message is passed to a subflow-status node - msg.status", async function() { | ||||||
|         }); |  | ||||||
|         it("emits a status event when a message is passed to a subflow-status node - msg.status", function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -662,13 +627,14 @@ describe('Subflow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].receive({status:{text:"status-obj"}}); |             activeNodes["1"].receive({status:{text:"status-obj"}}); | ||||||
|  |  | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|  |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn"].messages[0]; |             var statusMessage = currentNodes["sn"].messages[0]; | ||||||
|  |  | ||||||
| @@ -678,13 +644,9 @@ describe('Subflow', function() { | |||||||
|             statusMessage.status.source.should.have.a.property("id","2"); |             statusMessage.status.source.should.have.a.property("id","2"); | ||||||
|             statusMessage.status.source.should.have.a.property("type","subflow:sf1"); |             statusMessage.status.source.should.have.a.property("type","subflow:sf1"); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             flow.stop() | ||||||
|  |  | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },150); |         it("does not emit a regular status event if it contains a subflow-status node", async function() { | ||||||
|         }); |  | ||||||
|         it("does not emit a regular status event if it contains a subflow-status node", function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {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"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
| @@ -712,15 +674,12 @@ describe('Subflow', function() { | |||||||
|  |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",0); |             currentNodes["sn"].should.have.a.property("handled",0); | ||||||
|  |  | ||||||
|             flow.stop().then(function() { |             await flow.stop() | ||||||
|  |  | ||||||
|                 done(); |  | ||||||
|             }); |  | ||||||
|         }); |         }); | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
|     describe("#handleError",function() { |     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([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -733,13 +692,14 @@ describe('Subflow', function() { | |||||||
|             ]); |             ]); | ||||||
|             var flow = Flow.create({},config,config.flows["t1"]); |             var flow = Flow.create({},config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].receive({payload:"test"}); |             activeNodes["1"].receive({payload:"test"}); | ||||||
|              |              | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|  |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
|             var statusMessage = currentNodes["sn"].messages[0]; |             var statusMessage = currentNodes["sn"].messages[0]; | ||||||
|  |  | ||||||
| @@ -749,12 +709,9 @@ describe('Subflow', function() { | |||||||
|             statusMessage.error.source.should.have.a.property("type","testError"); |             statusMessage.error.source.should.have.a.property("type","testError"); | ||||||
|             statusMessage.error.source.should.have.a.property("name","test-error-node"); |             statusMessage.error.source.should.have.a.property("name","test-error-node"); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },150); |         it("passes an error event to the subflow's parent tab catch node - targetted scope", async function() { | ||||||
|         }); |  | ||||||
|         it("passes an error event to the subflow's parent tab catch node - targetted scope",function(done) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, | ||||||
| @@ -768,13 +725,14 @@ describe('Subflow', function() { | |||||||
|             var parentFlowErrorCalled = false; |             var parentFlowErrorCalled = false; | ||||||
|             var flow = Flow.create({handleError:() => { parentFlowErrorCalled = true} },config,config.flows["t1"]); |             var flow = Flow.create({handleError:() => { parentFlowErrorCalled = true} },config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var activeNodes = flow.getActiveNodes(); |             var activeNodes = flow.getActiveNodes(); | ||||||
|  |  | ||||||
|             activeNodes["1"].receive({payload:"test"}); |             activeNodes["1"].receive({payload:"test"}); | ||||||
|  |  | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|  |              | ||||||
|             parentFlowErrorCalled.should.be.false(); |             parentFlowErrorCalled.should.be.false(); | ||||||
|  |  | ||||||
|             currentNodes["sn"].should.have.a.property("handled",1); |             currentNodes["sn"].should.have.a.property("handled",1); | ||||||
| @@ -786,32 +744,12 @@ describe('Subflow', function() { | |||||||
|             statusMessage.error.source.should.have.a.property("type","testError"); |             statusMessage.error.source.should.have.a.property("type","testError"); | ||||||
|             statusMessage.error.source.should.have.a.property("name","test-error-node"); |             statusMessage.error.source.should.have.a.property("name","test-error-node"); | ||||||
|  |  | ||||||
|                 flow.stop().then(function() { |             await flow.stop() | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             },150); |  | ||||||
|  |  | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     describe("#env var", function() { |     describe("#env var", function() { | ||||||
|         // should be changed according to internal env var representation |         it("can access process env var", async function() { | ||||||
|         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) { |  | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, |                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, | ||||||
| @@ -828,27 +766,23 @@ describe('Subflow', function() { | |||||||
|                 handleError: (a,b,c) => { console.log(a,b,c); } |                 handleError: (a,b,c) => { console.log(a,b,c); } | ||||||
|             },config,config.flows["t1"]); |             },config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             process.env["__KEY__"] = "__VAL__"; |             process.env["__KEY__"] = "__VAL__"; | ||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload: "test"}); |             currentNodes["1"].receive({payload: "test"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|             currentNodes["3"].should.have.a.property("received", "__VAL__"); |             currentNodes["3"].should.have.a.property("received", "__VAL__"); | ||||||
|  |             await flow.stop() | ||||||
|                 flow.stop().then(function() { |  | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             },150); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("can access subflow env var", function(done) { |         it("can access subflow env var", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, |                 {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:"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:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]}, | ||||||
|                 {id:"sf1",type:"subflow",name:"Subflow 2",info:"", |                 {id:"sf1",type:"subflow",name:"Subflow 2",info:"",env: [{name: '__KEY__', value: '__VAL1__', type: 'str'}], | ||||||
|                     "in":[ {wires:[{id:"sf1-1"}]} ], |                     "in":[ {wires:[{id:"sf1-1"}]} ], | ||||||
|                     "out":[ {wires:[{id:"sf1-2",port:0}]} ]}, |                     "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-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]}, | ||||||
| @@ -859,7 +793,7 @@ describe('Subflow', function() { | |||||||
|                 handleError: (a,b,c) => { console.log(a,b,c); } |                 handleError: (a,b,c) => { console.log(a,b,c); } | ||||||
|             },config,config.flows["t1"]); |             },config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             var testenv_node = null; |             var testenv_node = null; | ||||||
|             for (var n in currentNodes) { |             for (var n in currentNodes) { | ||||||
| @@ -870,32 +804,30 @@ describe('Subflow', function() { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             process.env["__KEY__"] = "__VAL0__"; |             process.env["__KEY__"] = "__VAL0__"; | ||||||
|             setEnv(testenv_node, "__KEY__", "__VAL1__"); |  | ||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload: "test"}); |             currentNodes["1"].receive({payload: "test"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|  |  | ||||||
|             currentNodes["3"].should.have.a.property("received", "__VAL1__"); |             currentNodes["3"].should.have.a.property("received", "__VAL1__"); | ||||||
|  |             await flow.stop() | ||||||
|                 flow.stop().then(function() { |  | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             },150); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it("can access nested subflow env var", function(done) { |         it("can access nested subflow env var", async function() { | ||||||
|             var config = flowUtils.parseConfig([ |             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:"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:"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:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]}, | ||||||
|                 {id:"sf1",type:"subflow",name:"Subflow 1",info:"", |                 {id:"sf1",type:"subflow",name:"Subflow 1",info:"", | ||||||
|  |                     env: [{name: '__KEY2__', value: 'sf1', type: 'str'}], | ||||||
|                     in:[{wires:[{id:"sf1-1"}]}], |                     in:[{wires:[{id:"sf1-1"}]}], | ||||||
|                     out:[{wires:[{id:"sf1-2",port:0}]}]}, |                     out:[{wires:[{id:"sf1-2",port:0}]}]}, | ||||||
|                 {id:"sf2",type:"subflow",name:"Subflow 2",info:"", |                 {id:"sf2",type:"subflow",name:"Subflow 2",info:"", | ||||||
|  |                     env: [{name: '__KEY3__', value: 'sf2', type: 'str'}], | ||||||
|                     in:[{wires:[{id:"sf2-1"}]}], |                     in:[{wires:[{id:"sf2-1"}]}], | ||||||
|                     out:[{wires:[{id:"sf2-2",port:0}]}]}, |                     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-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-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:[[]]}, |                 {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); } |                 handleError: (a,b,c) => { console.log(a,b,c); } | ||||||
|             },config,config.flows["t1"]); |             },config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await 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; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             process.env["__KEY__"] = "__VAL0__"; |             process.env["__KEY__"] = "__VAL0__"; | ||||||
|             currentNodes["1"].receive({payload: "test"}); |             currentNodes["1"].receive({payload: "test"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|                 currentNodes["3"].should.have.a.property("received", "__VAL0__"); |             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') | ||||||
|              |              | ||||||
|                 setEnv(node_sf1_1, "__KEY__", "__VAL1__"); |             await flow.stop() | ||||||
|                 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); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         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([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, |                 {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); } |                 handleError: (a,b,c) => { console.log(a,b,c); } | ||||||
|             },config,config.flows["t1"]); |             },config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload: "test"}); |             currentNodes["1"].receive({payload: "test"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|             currentNodes["3"].should.have.a.property("received", "SFN"); |             currentNodes["3"].should.have.a.property("received", "SFN"); | ||||||
|  |             await flow.stop() | ||||||
|                 flow.stop().then(function() { |  | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             },150); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         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([ |             var config = flowUtils.parseConfig([ | ||||||
|                 {id:"t1",type:"tab"}, |                 {id:"t1",type:"tab"}, | ||||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, |                 {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); } |                 handleError: (a,b,c) => { console.log(a,b,c); } | ||||||
|             },config,config.flows["t1"]); |             },config,config.flows["t1"]); | ||||||
|  |  | ||||||
|             flow.start(); |             await flow.start(); | ||||||
|  |  | ||||||
|             currentNodes["1"].receive({payload: "test"}); |             currentNodes["1"].receive({payload: "test"}); | ||||||
|             setTimeout(function() { |             await NR_TEST_UTILS.sleep(150) | ||||||
|             currentNodes["3"].should.have.a.property("received", "2"); |             currentNodes["3"].should.have.a.property("received", "2"); | ||||||
|  |             await flow.stop() | ||||||
|                 flow.stop().then(function() { |  | ||||||
|                     done(); |  | ||||||
|         }); |         }); | ||||||
|             },150); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ describe('flows/index', function() { | |||||||
|             flowCreate.flows[id] = { |             flowCreate.flows[id] = { | ||||||
|                 flow: flow, |                 flow: flow, | ||||||
|                 global: global, |                 global: global, | ||||||
|                 start: sinon.spy(), |                 start: sinon.spy(async() => {}), | ||||||
|                 update: sinon.spy(), |                 update: sinon.spy(), | ||||||
|                 stop: sinon.spy(), |                 stop: sinon.spy(), | ||||||
|                 getActiveNodes: function() { |                 getActiveNodes: function() { | ||||||
| @@ -221,13 +221,18 @@ describe('flows/index', function() { | |||||||
|                 return Promise.resolve({flows:originalConfig}); |                 return Promise.resolve({flows:originalConfig}); | ||||||
|             } |             } | ||||||
|             events.once('flows:started',function() { |             events.once('flows:started',function() { | ||||||
|                 flows.setFlows(newConfig,"nodes").then(function() { |                 events.once('flows:started', function() { | ||||||
|  |                     try { | ||||||
|                         flows.getFlows().flows.should.eql(newConfig); |                         flows.getFlows().flows.should.eql(newConfig); | ||||||
|                         flowCreate.flows['t1'].update.called.should.be.true(); |                         flowCreate.flows['t1'].update.called.should.be.true(); | ||||||
|                         flowCreate.flows['t2'].start.called.should.be.true(); |                         flowCreate.flows['t2'].start.called.should.be.true(); | ||||||
|                         flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); |                         flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); | ||||||
|                         done(); |                         done(); | ||||||
|  |                     } catch(err) { | ||||||
|  |                         done(err) | ||||||
|  |                     } | ||||||
|                 }) |                 }) | ||||||
|  |                 flows.setFlows(newConfig,"nodes") | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             flows.init({log:mockLog, settings:{},storage:storage}); |             flows.init({log:mockLog, settings:{},storage:storage}); | ||||||
| @@ -250,13 +255,14 @@ describe('flows/index', function() { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             events.once('flows:started',function() { |             events.once('flows:started',function() { | ||||||
|                 flows.setFlows(newConfig,"nodes").then(function() { |                 events.once('flows:started',function() { | ||||||
|                     flows.getFlows().flows.should.eql(newConfig); |                     flows.getFlows().flows.should.eql(newConfig); | ||||||
|                     flowCreate.flows['t1'].update.called.should.be.true(); |                     flowCreate.flows['t1'].update.called.should.be.true(); | ||||||
|                     flowCreate.flows['t2'].start.called.should.be.true(); |                     flowCreate.flows['t2'].start.called.should.be.true(); | ||||||
|                     flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); |                     flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); | ||||||
|                     flows.stopFlows().then(done); |                     flows.stopFlows().then(done); | ||||||
|                 }) |                 }) | ||||||
|  |                 flows.setFlows(newConfig,"nodes") | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             flows.init({log:mockLog, settings:{},storage:storage}); |             flows.init({log:mockLog, settings:{},storage:storage}); | ||||||
|   | |||||||
| @@ -149,7 +149,7 @@ describe('flows/util', function() { | |||||||
|                 {id:"t1",type:"tab"} |                 {id:"t1",type:"tab"} | ||||||
|             ]; |             ]; | ||||||
|             var parsedConfig = flowUtil.parseConfig(originalConfig); |             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); |             parsedConfig.should.eql(expectedConfig); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -160,7 +160,7 @@ describe('flows/util', function() { | |||||||
|                 {id:"t1",type:"tab"} |                 {id:"t1",type:"tab"} | ||||||
|             ]; |             ]; | ||||||
|             var parsedConfig = flowUtil.parseConfig(originalConfig); |             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); |             parsedConfig.should.eql(expectedConfig); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -172,7 +172,7 @@ describe('flows/util', function() { | |||||||
|                 {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]} |                 {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]} | ||||||
|             ]; |             ]; | ||||||
|             var parsedConfig = flowUtil.parseConfig(originalConfig); |             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); |             parsedConfig.should.eql(expectedConfig); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -184,7 +184,7 @@ describe('flows/util', function() { | |||||||
|                 {id:"sf1-1",x:10,y:10,z:"sf1",type:"test",wires:[]} |                 {id:"sf1-1",x:10,y:10,z:"sf1",type:"test",wires:[]} | ||||||
|             ]; |             ]; | ||||||
|             var parsedConfig = flowUtil.parseConfig(originalConfig); |             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); |             parsedConfig.should.eql(expectedConfig); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -196,7 +196,7 @@ describe('flows/util', function() { | |||||||
|             ]; |             ]; | ||||||
|             var parsedConfig = flowUtil.parseConfig(originalConfig); |             var parsedConfig = flowUtil.parseConfig(originalConfig); | ||||||
|             parsedConfig.missingTypes.should.eql(['missing']); |             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(); |             redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true(); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -206,7 +206,7 @@ describe('flows/util', function() { | |||||||
|                 {id:"cn",type:"test"}, |                 {id:"cn",type:"test"}, | ||||||
|             ]; |             ]; | ||||||
|             var parsedConfig = flowUtil.parseConfig(originalConfig); |             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); |             parsedConfig.should.eql(expectedConfig); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -217,7 +217,7 @@ describe('flows/util', function() { | |||||||
|                 {id:"g1",type:"group",z:"t1"} |                 {id:"g1",type:"group",z:"t1"} | ||||||
|             ]; |             ]; | ||||||
|             var parsedConfig = flowUtil.parseConfig(originalConfig); |             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); |             parsedConfig.should.eql(expectedConfig); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user