Evaluate all env vars as part of async flow start

This commit is contained in:
Nick O'Leary
2023-06-23 02:11:57 +01:00
parent 8db2972288
commit 1c5fdb6ab6
16 changed files with 1141 additions and 1290 deletions

View File

@@ -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
}