mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Evaluate all env vars as part of async flow start
This commit is contained in:
@@ -14,19 +14,20 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var clone = require("clone");
|
||||
var redUtil = require("@node-red/util").util;
|
||||
const clone = require("clone");
|
||||
const redUtil = require("@node-red/util").util;
|
||||
const events = require("@node-red/util").events;
|
||||
var flowUtil = require("./util");
|
||||
const flowUtil = require("./util");
|
||||
const context = require('../nodes/context');
|
||||
const hooks = require("@node-red/util").hooks;
|
||||
const credentials = require("../nodes/credentials");
|
||||
|
||||
var Subflow;
|
||||
var Log;
|
||||
let Subflow;
|
||||
let Log;
|
||||
let Group;
|
||||
|
||||
var nodeCloseTimeout = 15000;
|
||||
var asyncMessageDelivery = true;
|
||||
let nodeCloseTimeout = 15000;
|
||||
let asyncMessageDelivery = true;
|
||||
|
||||
/**
|
||||
* This class represents a flow within the runtime. It is responsible for
|
||||
@@ -52,6 +53,27 @@ class Flow {
|
||||
this.isGlobalFlow = false;
|
||||
}
|
||||
this.id = this.flow.id || "global";
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
this.activeNodes = {};
|
||||
this.subflowInstanceNodes = {};
|
||||
this.catchNodes = [];
|
||||
@@ -59,6 +81,11 @@ class Flow {
|
||||
this.path = this.id;
|
||||
// Ensure a context exists for this flow
|
||||
this.context = context.getFlowContext(this.id,this.parent.id);
|
||||
|
||||
// env is an array of env definitions
|
||||
// _env is an object for direct lookup of env name -> value
|
||||
this.env = this.flow.env
|
||||
this._env = {}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +163,7 @@ class Flow {
|
||||
* @param {[type]} msg [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
start(diff) {
|
||||
async start(diff) {
|
||||
this.trace("start "+this.TYPE+" ["+this.path+"]");
|
||||
var node;
|
||||
var newNode;
|
||||
@@ -145,6 +172,18 @@ class Flow {
|
||||
this.statusNodes = [];
|
||||
this.completeNodeMap = {};
|
||||
|
||||
if (this.env) {
|
||||
this._env = await flowUtil.evaluateEnvProperties(this, this.env, credentials.get(this.id))
|
||||
// console.log('env', this.env)
|
||||
// console.log('_env', this._env)
|
||||
}
|
||||
for (let i = 0; i < this.groupOrder.length; i++) {
|
||||
// Start the groups in the right order so they
|
||||
// can setup their env vars knowning their parent
|
||||
// will have been started
|
||||
await this.groups[this.groupOrder[i]].start()
|
||||
}
|
||||
|
||||
var configNodes = Object.keys(this.flow.configs);
|
||||
var configNodeAttempts = {};
|
||||
while (configNodes.length > 0) {
|
||||
@@ -177,7 +216,7 @@ class Flow {
|
||||
}
|
||||
}
|
||||
if (readyToCreate) {
|
||||
newNode = flowUtil.createNode(this,node);
|
||||
newNode = await flowUtil.createNode(this,node);
|
||||
if (newNode) {
|
||||
this.activeNodes[id] = newNode;
|
||||
}
|
||||
@@ -203,7 +242,7 @@ class Flow {
|
||||
if (node.d !== true) {
|
||||
if (!node.subflow) {
|
||||
if (!this.activeNodes[id]) {
|
||||
newNode = flowUtil.createNode(this,node);
|
||||
newNode = await flowUtil.createNode(this,node);
|
||||
if (newNode) {
|
||||
this.activeNodes[id] = newNode;
|
||||
}
|
||||
@@ -221,7 +260,7 @@ class Flow {
|
||||
node
|
||||
);
|
||||
this.subflowInstanceNodes[id] = subflow;
|
||||
subflow.start();
|
||||
await subflow.start();
|
||||
this.activeNodes[id] = subflow.node;
|
||||
|
||||
// this.subflowInstanceNodes[id] = nodes.map(function(n) { return n.id});
|
||||
@@ -404,8 +443,7 @@ class Flow {
|
||||
* @return {Node} group node
|
||||
*/
|
||||
getGroupNode(id) {
|
||||
const groups = this.global.groups;
|
||||
return groups[id];
|
||||
return this.groups[id];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,95 +454,8 @@ class Flow {
|
||||
return this.activeNodes;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get value of environment variable defined in group node.
|
||||
* @param {String} group - group node
|
||||
* @param {String} name - name of variable
|
||||
* @return {Object} object containing the value in val property or null if not defined
|
||||
*/
|
||||
getGroupEnvSetting(node, group, name) {
|
||||
if (group) {
|
||||
if (name === "NR_GROUP_NAME") {
|
||||
return [{
|
||||
val: group.name
|
||||
}, null];
|
||||
}
|
||||
if (name === "NR_GROUP_ID") {
|
||||
return [{
|
||||
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+"}");
|
||||
}
|
||||
if (type === "bool") {
|
||||
const val
|
||||
= ((value === "true") ||
|
||||
(value === true));
|
||||
return [{
|
||||
val: val
|
||||
}, null];
|
||||
}
|
||||
if (type === "cred") {
|
||||
return [{
|
||||
val: value
|
||||
}, null];
|
||||
}
|
||||
try {
|
||||
var val = redUtil.evaluateNodeProperty(value, type, node, null, null);
|
||||
return [{
|
||||
val: val
|
||||
}, null];
|
||||
}
|
||||
catch (e) {
|
||||
this.error(e);
|
||||
return [null, null];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
name = name.substring(8);
|
||||
}
|
||||
if (group.g) {
|
||||
const parent = this.getGroupNode(group.g);
|
||||
return this.getGroupEnvSetting(node, parent, name);
|
||||
}
|
||||
}
|
||||
return [null, name];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Get a flow setting value.
|
||||
* @param {[type]} key [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
@@ -516,54 +467,14 @@ class Flow {
|
||||
if (key === "NR_FLOW_ID") {
|
||||
return flow.id;
|
||||
}
|
||||
if (flow.credentials === undefined) {
|
||||
flow.credentials = credentials.get(flow.id) || {};
|
||||
}
|
||||
if (flow.env) {
|
||||
if (!key.startsWith("$parent.")) {
|
||||
if (!flow._env) {
|
||||
const envs = flow.env;
|
||||
const entries = envs.map((env) => {
|
||||
if (env.type === "cred") {
|
||||
const cred = flow.credentials;
|
||||
if (cred.hasOwnProperty(env.name)) {
|
||||
env.value = cred[env.name];
|
||||
}
|
||||
}
|
||||
return [env.name, env]
|
||||
});
|
||||
flow._env = Object.fromEntries(entries);
|
||||
}
|
||||
const env = flow._env[key];
|
||||
if (env) {
|
||||
let value = env.value;
|
||||
const type = env.type;
|
||||
if ((type !== "env") || (value !== key)) {
|
||||
if (type === "env") {
|
||||
value = value.replace(new RegExp("\\${"+key+"}","g"),"${$parent."+key+"}");
|
||||
}
|
||||
try {
|
||||
if (type === "bool") {
|
||||
const val = ((value === "true") ||
|
||||
(value === true));
|
||||
return val;
|
||||
}
|
||||
if (type === "cred") {
|
||||
return value;
|
||||
}
|
||||
var val = redUtil.evaluateNodeProperty(value, type, null, null, null);
|
||||
return val;
|
||||
}
|
||||
catch (e) {
|
||||
this.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!key.startsWith("$parent.")) {
|
||||
if (this._env.hasOwnProperty(key)) {
|
||||
return this._env[key]
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
key = key.substring(8);
|
||||
}
|
||||
}
|
||||
// Delegate to the parent flow.
|
||||
return this.parent.getSetting(key);
|
||||
}
|
||||
|
||||
@@ -618,10 +529,10 @@ class Flow {
|
||||
let distance = 0
|
||||
if (reportingNode.g) {
|
||||
// Reporting node inside a group. Calculate the distance between it and the status node
|
||||
let containingGroup = this.global.groups[reportingNode.g]
|
||||
let containingGroup = this.groups[reportingNode.g]
|
||||
while (containingGroup && containingGroup.id !== targetStatusNode.g) {
|
||||
distance++
|
||||
containingGroup = this.global.groups[containingGroup.g]
|
||||
containingGroup = this.groups[containingGroup.g]
|
||||
}
|
||||
if (!containingGroup && targetStatusNode.g && targetStatusNode.scope === 'group') {
|
||||
// This status node is in a group, but not in the same hierachy
|
||||
@@ -706,10 +617,10 @@ class Flow {
|
||||
let distance = 0
|
||||
if (reportingNode.g) {
|
||||
// Reporting node inside a group. Calculate the distance between it and the catch node
|
||||
let containingGroup = this.global.groups[reportingNode.g]
|
||||
let containingGroup = this.groups[reportingNode.g]
|
||||
while (containingGroup && containingGroup.id !== targetCatchNode.g) {
|
||||
distance++
|
||||
containingGroup = this.global.groups[containingGroup.g]
|
||||
containingGroup = this.groups[containingGroup.g]
|
||||
}
|
||||
if (!containingGroup && targetCatchNode.g && targetCatchNode.scope === 'group') {
|
||||
// This catch node is in a group, but not in the same hierachy
|
||||
@@ -909,9 +820,10 @@ module.exports = {
|
||||
asyncMessageDelivery = !runtime.settings.runtimeSyncDelivery
|
||||
Log = runtime.log;
|
||||
Subflow = require("./Subflow");
|
||||
Group = require("./Group").Group
|
||||
},
|
||||
create: function(parent,global,conf) {
|
||||
return new Flow(parent,global,conf);
|
||||
return new Flow(parent,global,conf)
|
||||
},
|
||||
Flow: Flow
|
||||
}
|
||||
|
55
packages/node_modules/@node-red/runtime/lib/flows/Group.js
vendored
Normal file
55
packages/node_modules/@node-red/runtime/lib/flows/Group.js
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
const flowUtil = require("./util");
|
||||
const credentials = require("../nodes/credentials");
|
||||
|
||||
/**
|
||||
* This class represents a group within the runtime.
|
||||
*/
|
||||
class Group {
|
||||
|
||||
/**
|
||||
* Create a Group object.
|
||||
* @param {[type]} parent The parent flow/group
|
||||
* @param {[type]} groupDef This group's definition
|
||||
*/
|
||||
constructor(parent, groupDef) {
|
||||
this.TYPE = 'group'
|
||||
this.name = groupDef.name
|
||||
this.parent = parent
|
||||
this.group = groupDef
|
||||
this.id = this.group.id
|
||||
this.g = this.group.g
|
||||
this.env = this.group.env
|
||||
this._env = {}
|
||||
}
|
||||
|
||||
async start() {
|
||||
if (this.env) {
|
||||
this._env = await flowUtil.evaluateEnvProperties(this, this.env, credentials.get(this.id))
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get a group setting value.
|
||||
* @param {[type]} key [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
getSetting(key) {
|
||||
if (key === "NR_GROUP_NAME") {
|
||||
return this.name;
|
||||
}
|
||||
if (key === "NR_GROUP_ID") {
|
||||
return this.id;
|
||||
}
|
||||
if (!key.startsWith("$parent.")) {
|
||||
if (this._env.hasOwnProperty(key)) {
|
||||
return this._env[key]
|
||||
}
|
||||
} else {
|
||||
key = key.substring(8);
|
||||
}
|
||||
return this.parent.getSetting(key);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Group
|
||||
}
|
@@ -119,7 +119,7 @@ class Subflow extends Flow {
|
||||
this.templateCredentials = credentials.get(subflowDef.id) || {};
|
||||
this.instanceCredentials = credentials.get(id) || {};
|
||||
|
||||
var env = [];
|
||||
var env = {};
|
||||
if (this.subflowDef.env) {
|
||||
this.subflowDef.env.forEach(e => {
|
||||
env[e.name] = e;
|
||||
@@ -145,7 +145,7 @@ class Subflow extends Flow {
|
||||
}
|
||||
});
|
||||
}
|
||||
this.env = env;
|
||||
this.env = Object.values(env);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +156,7 @@ class Subflow extends Flow {
|
||||
* @param {[type]} diff [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
start(diff) {
|
||||
async start(diff) {
|
||||
var self = this;
|
||||
// Create a subflow node to accept inbound messages and route appropriately
|
||||
var Node = require("../nodes/Node");
|
||||
@@ -310,7 +310,7 @@ class Subflow extends Flow {
|
||||
}
|
||||
}
|
||||
}
|
||||
super.start(diff);
|
||||
return super.start(diff);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,68 +335,35 @@ class Subflow extends Flow {
|
||||
}
|
||||
/**
|
||||
* Get environment variable of subflow
|
||||
* @param {String} name name of env var
|
||||
* @param {String} key name of env var
|
||||
* @return {Object} val value of env var
|
||||
*/
|
||||
getSetting(name) {
|
||||
if (!/^\$parent\./.test(name)) {
|
||||
var env = this.env;
|
||||
if (env && env.hasOwnProperty(name)) {
|
||||
var val = env[name];
|
||||
// If this is an env type property we need to be careful not
|
||||
// to get into lookup loops.
|
||||
// 1. if the value to lookup is the same as this one, go straight to parent
|
||||
// 2. otherwise, check if it is a compound env var ("foo $(bar)")
|
||||
// and if so, substitute any instances of `name` with $parent.name
|
||||
// See https://github.com/node-red/node-red/issues/2099
|
||||
if (val.type !== 'env' || val.value !== name) {
|
||||
let value = val.value;
|
||||
var type = val.type;
|
||||
if (type === 'env') {
|
||||
value = value.replace(new RegExp("\\${"+name+"}","g"),"${$parent."+name+"}");
|
||||
}
|
||||
try {
|
||||
return evaluateInputValue(value, type, this.node);
|
||||
}
|
||||
catch (e) {
|
||||
this.error(e);
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
// This _is_ an env property pointing at itself - go to parent
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// name starts $parent. ... so delegate to parent automatically
|
||||
name = name.substring(8);
|
||||
}
|
||||
getSetting(key) {
|
||||
const node = this.subflowInstance;
|
||||
if (node) {
|
||||
if (name === "NR_NODE_NAME") {
|
||||
if (key === "NR_NODE_NAME") {
|
||||
return node.name;
|
||||
}
|
||||
if (name === "NR_NODE_ID") {
|
||||
if (key === "NR_NODE_ID") {
|
||||
return node.id;
|
||||
}
|
||||
if (name === "NR_NODE_PATH") {
|
||||
if (key === "NR_NODE_PATH") {
|
||||
return node._path;
|
||||
}
|
||||
}
|
||||
if (node.g) {
|
||||
const group = this.getGroupNode(node.g);
|
||||
const [result, newName] = this.getGroupEnvSetting(node, group, name);
|
||||
if (result) {
|
||||
return result.val;
|
||||
if (!key.startsWith("$parent.")) {
|
||||
if (this._env.hasOwnProperty(key)) {
|
||||
return this._env[key]
|
||||
}
|
||||
name = newName;
|
||||
} else {
|
||||
key = key.substring(8);
|
||||
}
|
||||
|
||||
var parent = this.parent;
|
||||
if (parent) {
|
||||
var val = parent.getSetting(name);
|
||||
return val;
|
||||
// Push the request up to the parent.
|
||||
// Unlike a Flow, the parent of a Subflow could be a Group
|
||||
if (node.g) {
|
||||
return this.parent.getGroupNode(node.g).getSetting(key)
|
||||
}
|
||||
return undefined;
|
||||
return this.parent.getSetting(key)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -391,7 +391,7 @@ async function start(type,diff,muteLog,isDeploy) {
|
||||
for (id in activeFlows) {
|
||||
if (activeFlows.hasOwnProperty(id)) {
|
||||
try {
|
||||
activeFlows[id].start(diff);
|
||||
await activeFlows[id].start(diff);
|
||||
// Create a map of node id to flow id and also a subflowInstance lookup map
|
||||
var activeNodes = activeFlows[id].getActiveNodes();
|
||||
Object.keys(activeNodes).forEach(function(nid) {
|
||||
|
@@ -70,8 +70,58 @@ 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
|
||||
}
|
||||
|
||||
async function createNode(flow,config) {
|
||||
var newNode = null;
|
||||
var type = config.type;
|
||||
try {
|
||||
@@ -140,7 +190,7 @@ function createNode(flow,config) {
|
||||
// This allows nodes inside the subflow to get ahold of each other
|
||||
// such as a node accessing its config node
|
||||
flow.subflowInstanceNodes[config.id] = subflow
|
||||
subflow.start();
|
||||
await subflow.start();
|
||||
return subflow.node;
|
||||
}
|
||||
} catch(err) {
|
||||
@@ -150,123 +200,122 @@ function createNode(flow,config) {
|
||||
}
|
||||
|
||||
function parseConfig(config) {
|
||||
var flow = {};
|
||||
flow.allNodes = {};
|
||||
flow.subflows = {};
|
||||
flow.configs = {};
|
||||
flow.flows = {};
|
||||
flow.groups = {};
|
||||
flow.missingTypes = [];
|
||||
var flow = {};
|
||||
flow.allNodes = {};
|
||||
flow.subflows = {};
|
||||
flow.configs = {};
|
||||
flow.flows = {};
|
||||
flow.missingTypes = [];
|
||||
|
||||
config.forEach(function(n) {
|
||||
flow.allNodes[n.id] = clone(n);
|
||||
if (n.type === 'tab') {
|
||||
flow.flows[n.id] = n;
|
||||
flow.flows[n.id].subflows = {};
|
||||
flow.flows[n.id].configs = {};
|
||||
flow.flows[n.id].nodes = {};
|
||||
}
|
||||
if (n.type === 'group') {
|
||||
flow.groups[n.id] = n;
|
||||
}
|
||||
});
|
||||
config.forEach(function (n) {
|
||||
flow.allNodes[n.id] = clone(n);
|
||||
if (n.type === 'tab') {
|
||||
flow.flows[n.id] = n;
|
||||
flow.flows[n.id].subflows = {};
|
||||
flow.flows[n.id].configs = {};
|
||||
flow.flows[n.id].nodes = {};
|
||||
flow.flows[n.id].groups = {};
|
||||
} else if (n.type === 'subflow') {
|
||||
flow.subflows[n.id] = n;
|
||||
flow.subflows[n.id].configs = {};
|
||||
flow.subflows[n.id].nodes = {};
|
||||
flow.subflows[n.id].groups = {};
|
||||
flow.subflows[n.id].instances = [];
|
||||
}
|
||||
});
|
||||
|
||||
// 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].configs = {};
|
||||
flow.subflows[n.id].nodes = {};
|
||||
flow.subflows[n.id].instances = [];
|
||||
}
|
||||
});
|
||||
var linkWires = {};
|
||||
var linkOutNodes = [];
|
||||
config.forEach(function(n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
||||
var subflowDetails = subflowInstanceRE.exec(n.type);
|
||||
var linkWires = {};
|
||||
var linkOutNodes = [];
|
||||
config.forEach(function (n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
||||
var subflowDetails = subflowInstanceRE.exec(n.type);
|
||||
|
||||
if ( (subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type)) ) {
|
||||
if (flow.missingTypes.indexOf(n.type) === -1) {
|
||||
flow.missingTypes.push(n.type);
|
||||
}
|
||||
}
|
||||
var container = null;
|
||||
if (flow.flows[n.z]) {
|
||||
container = flow.flows[n.z];
|
||||
} else if (flow.subflows[n.z]) {
|
||||
container = flow.subflows[n.z];
|
||||
}
|
||||
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
||||
if (subflowDetails) {
|
||||
var subflowType = subflowDetails[1]
|
||||
n.subflow = subflowType;
|
||||
if (flow.subflows[subflowType]) {
|
||||
flow.subflows[subflowType].instances.push(n)
|
||||
}
|
||||
}
|
||||
if (container) {
|
||||
container.nodes[n.id] = n;
|
||||
}
|
||||
} else {
|
||||
if (container) {
|
||||
container.configs[n.id] = n;
|
||||
} else {
|
||||
flow.configs[n.id] = n;
|
||||
flow.configs[n.id]._users = [];
|
||||
}
|
||||
}
|
||||
if (n.type === 'link in' && n.links) {
|
||||
// Ensure wires are present in corresponding link out nodes
|
||||
n.links.forEach(function(id) {
|
||||
linkWires[id] = linkWires[id]||{};
|
||||
linkWires[id][n.id] = true;
|
||||
})
|
||||
} else if (n.type === 'link out' && n.links) {
|
||||
linkWires[n.id] = linkWires[n.id]||{};
|
||||
n.links.forEach(function(id) {
|
||||
linkWires[n.id][id] = true;
|
||||
})
|
||||
linkOutNodes.push(n);
|
||||
}
|
||||
}
|
||||
});
|
||||
linkOutNodes.forEach(function(n) {
|
||||
var links = linkWires[n.id];
|
||||
var targets = Object.keys(links);
|
||||
n.wires = [targets];
|
||||
});
|
||||
if ((subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type))) {
|
||||
if (flow.missingTypes.indexOf(n.type) === -1) {
|
||||
flow.missingTypes.push(n.type);
|
||||
}
|
||||
}
|
||||
var container = null;
|
||||
if (flow.flows[n.z]) {
|
||||
container = flow.flows[n.z];
|
||||
} else if (flow.subflows[n.z]) {
|
||||
container = flow.subflows[n.z];
|
||||
}
|
||||
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
||||
if (subflowDetails) {
|
||||
var subflowType = subflowDetails[1]
|
||||
n.subflow = subflowType;
|
||||
if (flow.subflows[subflowType]) {
|
||||
flow.subflows[subflowType].instances.push(n)
|
||||
}
|
||||
}
|
||||
if (container) {
|
||||
container.nodes[n.id] = n;
|
||||
}
|
||||
} else {
|
||||
if (container) {
|
||||
container.configs[n.id] = n;
|
||||
} else {
|
||||
flow.configs[n.id] = n;
|
||||
flow.configs[n.id]._users = [];
|
||||
}
|
||||
}
|
||||
if (n.type === 'link in' && n.links) {
|
||||
// Ensure wires are present in corresponding link out nodes
|
||||
n.links.forEach(function (id) {
|
||||
linkWires[id] = linkWires[id] || {};
|
||||
linkWires[id][n.id] = true;
|
||||
})
|
||||
} else if (n.type === 'link out' && n.links) {
|
||||
linkWires[n.id] = linkWires[n.id] || {};
|
||||
n.links.forEach(function (id) {
|
||||
linkWires[n.id][id] = true;
|
||||
})
|
||||
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) {
|
||||
var links = linkWires[n.id];
|
||||
var targets = Object.keys(links);
|
||||
n.wires = [targets];
|
||||
});
|
||||
|
||||
|
||||
var addedTabs = {};
|
||||
config.forEach(function(n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
||||
for (var prop in n) {
|
||||
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
|
||||
// This property references a global config node
|
||||
flow.configs[n[prop]]._users.push(n.id)
|
||||
}
|
||||
}
|
||||
if (n.z && !flow.subflows[n.z]) {
|
||||
var addedTabs = {};
|
||||
config.forEach(function (n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
||||
for (var prop in n) {
|
||||
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
|
||||
// This property references a global config node
|
||||
flow.configs[n[prop]]._users.push(n.id)
|
||||
}
|
||||
}
|
||||
if (n.z && !flow.subflows[n.z]) {
|
||||
|
||||
if (!flow.flows[n.z]) {
|
||||
flow.flows[n.z] = {type:'tab',id:n.z};
|
||||
flow.flows[n.z].subflows = {};
|
||||
flow.flows[n.z].configs = {};
|
||||
flow.flows[n.z].nodes = {};
|
||||
addedTabs[n.z] = flow.flows[n.z];
|
||||
}
|
||||
if (addedTabs[n.z]) {
|
||||
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
||||
addedTabs[n.z].nodes[n.id] = n;
|
||||
} else {
|
||||
addedTabs[n.z].configs[n.id] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return flow;
|
||||
if (!flow.flows[n.z]) {
|
||||
flow.flows[n.z] = { type: 'tab', id: n.z };
|
||||
flow.flows[n.z].subflows = {};
|
||||
flow.flows[n.z].configs = {};
|
||||
flow.flows[n.z].nodes = {};
|
||||
addedTabs[n.z] = flow.flows[n.z];
|
||||
}
|
||||
if (addedTabs[n.z]) {
|
||||
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
||||
addedTabs[n.z].nodes[n.id] = n;
|
||||
} else {
|
||||
addedTabs[n.z].configs[n.id] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return flow;
|
||||
}
|
||||
|
||||
function getGlobalEnv(name) {
|
||||
@@ -319,10 +368,11 @@ module.exports = {
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
diffNodes: diffNodes,
|
||||
mapEnvVarProperties: mapEnvVarProperties,
|
||||
|
||||
parseConfig: parseConfig,
|
||||
diffNodes,
|
||||
mapEnvVarProperties,
|
||||
evaluateEnvProperties,
|
||||
parseConfig,
|
||||
|
||||
diffConfigs: function(oldConfig, newConfig) {
|
||||
var id;
|
||||
@@ -615,5 +665,6 @@ module.exports = {
|
||||
* @param {object} config The node configuration object
|
||||
* @return {Node} The instance of the node
|
||||
*/
|
||||
createNode: createNode
|
||||
createNode: createNode,
|
||||
evaluateEnvProperties
|
||||
}
|
||||
|
@@ -548,11 +548,9 @@ function getSetting(node, name, flow_) {
|
||||
if (flow) {
|
||||
if (node && node.g) {
|
||||
const group = flow.getGroupNode(node.g);
|
||||
const [result, newName] = flow.getGroupEnvSetting(node, group, name);
|
||||
if (result) {
|
||||
return result.val;
|
||||
if (group) {
|
||||
return group.getSetting(name)
|
||||
}
|
||||
name = newName;
|
||||
}
|
||||
return flow.getSetting(name);
|
||||
}
|
||||
|
Reference in New Issue
Block a user