From 7481b78b16acb848c3d1a47a4507ed45e21892af Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 10 Jul 2023 12:04:52 +0100 Subject: [PATCH] Force reeval of env vars if group/flow/global envs change --- .../@node-red/runtime/lib/flows/Flow.js | 40 +++++----- .../@node-red/runtime/lib/flows/index.js | 9 ++- .../@node-red/runtime/lib/flows/util.js | 75 +++++++++++++++---- 3 files changed, 90 insertions(+), 34 deletions(-) diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index e7ca490d2..ddea3f07f 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -53,27 +53,8 @@ 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 = []; @@ -190,6 +171,27 @@ class Flow { 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 diff --git a/packages/node_modules/@node-red/runtime/lib/flows/index.js b/packages/node_modules/@node-red/runtime/lib/flows/index.js index 16bd8a867..89ba9d822 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/index.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/index.js @@ -271,6 +271,10 @@ function getFlows() { async function start(type,diff,muteLog,isDeploy) { type = type || "full"; + if (diff && diff.globalConfigChanged) { + type = 'full' + } + started = true; state = 'start' var i; @@ -441,6 +445,9 @@ function stop(type,diff,muteLog,isDeploy) { log.info(log._("nodes.flows.stopping-flows")); } } + if (diff.globalConfigChanged) { + type = 'full' + } started = false; state = 'stop' var promises = []; @@ -464,7 +471,7 @@ function stop(type,diff,muteLog,isDeploy) { activeFlowIds.forEach(id => { if (activeFlows.hasOwnProperty(id)) { - var flowStateChanged = diff && (diff.added.indexOf(id) !== -1 || diff.removed.indexOf(id) !== -1); + var flowStateChanged = diff && (diff.changed.indexOf(id) !== -1 || diff.added.indexOf(id) !== -1 || diff.removed.indexOf(id) !== -1); log.debug("red/nodes/flows.stop : stopping flow : "+id); promises.push(activeFlows[id].stop(flowStateChanged?null:stopList,removedList)); if (type === "full" || flowStateChanged || diff.removed.indexOf(id)!==-1) { diff --git a/packages/node_modules/@node-red/runtime/lib/flows/util.js b/packages/node_modules/@node-red/runtime/lib/flows/util.js index e619e2b2a..ed30a3816 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/util.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/util.js @@ -34,8 +34,11 @@ function diffNodes(oldNode,newNode) { if (oldNode == null) { return true; } - var oldKeys = Object.keys(oldNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" }); - var newKeys = Object.keys(newNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" }); + const keyFilter = p => 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) { return true; } @@ -354,10 +357,9 @@ function diffConfigs(oldConfig, newConfig) { var removed = {}; var changed = {}; var wiringChanged = {}; - + var globalConfigChanged = false; var linkMap = {}; - - var changedTabs = {}; + var allNestedGroups = [] // Look for tabs that have been removed for (id in oldConfig.flows) { @@ -372,7 +374,6 @@ function diffConfigs(oldConfig, newConfig) { var originalState = oldConfig.flows[id].disabled||false; var newState = newConfig.flows[id].disabled||false; if (originalState !== newState) { - changedTabs[id] = true; if (originalState) { added[id] = oldConfig.allNodes[id]; } else { @@ -435,6 +436,9 @@ function diffConfigs(oldConfig, newConfig) { delete changed[id]; } } + if (newConfig.allNodes[id].type === 'global-config') { + globalConfigChanged = true + } } // This node's wiring has changed if (!redUtil.compareObjects(node.wires,newConfig.allNodes[id].wires)) { @@ -450,6 +454,10 @@ function diffConfigs(oldConfig, newConfig) { } } } + } else { + if (JSON.stringify(node.env) !== JSON.stringify(newConfig.allNodes[id].env)) { + changed[id] = newConfig.allNodes[id]; + } } } } @@ -457,6 +465,20 @@ function diffConfigs(oldConfig, newConfig) { for (id in newConfig.allNodes) { if (newConfig.allNodes.hasOwnProperty(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 if (node.wires) { linkMap[node.id] = linkMap[node.id] || []; @@ -550,6 +572,26 @@ function diffConfigs(oldConfig, newConfig) { } } + // 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 var changedSubflowStack = Object.keys(changedSubflows); while (changedSubflowStack.length > 0) { @@ -575,12 +617,15 @@ function diffConfigs(oldConfig, newConfig) { } } + + var diff = { added:Object.keys(added), changed:Object.keys(changed), removed:Object.keys(removed), rewired:Object.keys(wiringChanged), - linked:[] + linked:[], + globalConfigChanged } // Traverse the links of all modified nodes to mark the connected nodes @@ -600,13 +645,15 @@ function diffConfigs(oldConfig, newConfig) { } // console.log(diff); // for (id in newConfig.allNodes) { - // console.log( - // (added[id]?"a":(changed[id]?"c":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"l":" "), - // newConfig.allNodes[id].type.padEnd(10), - // id.padEnd(16), - // (newConfig.allNodes[id].z||"").padEnd(16), - // newConfig.allNodes[id].name||newConfig.allNodes[id].label||"" - // ); + // if (added[id] || changed[id] || wiringChanged[id] || diff.linked.indexOf(id)!==-1) { + // console.log( + // (added[id]?"a":(changed[id]?"c":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"l":" "), + // newConfig.allNodes[id].type.padEnd(10), + // id.padEnd(16), + // (newConfig.allNodes[id].z||"").padEnd(16), + // newConfig.allNodes[id].name||newConfig.allNodes[id].label||"" + // ); + // } // } // for (id in removed) { // console.log(