mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02: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:
commit
db108a37cf
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user