diff --git a/red/runtime/nodes/flows/util.js b/red/runtime/nodes/flows/util.js index 9017aaa02..c60a1687d 100644 --- a/red/runtime/nodes/flows/util.js +++ b/red/runtime/nodes/flows/util.js @@ -230,22 +230,32 @@ module.exports = { } } - for (id in newConfig.allNodes) { - if (newConfig.allNodes.hasOwnProperty(id)) { - node = newConfig.allNodes[id]; - for (var prop in node) { - if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") { - // This node has a property that references a changed/removed node - // Assume it is a config node change and mark this node as - // changed. - if (changed[node[prop]] || removed[node[prop]]) { - if (!changed[node.id]) { - changed[node.id] = node; - if (newConfig.allNodes[node.z]) { - changed[node.z] = newConfig.allNodes[node.z]; - if (changed[node.z].type === "subflow") { - changedSubflows[node.z] = changed[node.z]; - delete changed[node.id]; + var madeChange; + // Loop through the nodes looking for references to changed config nodes + // Repeat the loop if anything is marked as changed as it may need to be + // propagated to parent nodes. + // TODO: looping through all nodes every time is a bit inefficient - could be more targeted + do { + madeChange = false; + for (id in newConfig.allNodes) { + if (newConfig.allNodes.hasOwnProperty(id)) { + node = newConfig.allNodes[id]; + for (var prop in node) { + if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") { + // This node has a property that references a changed/removed node + // Assume it is a config node change and mark this node as + // changed. + if (changed[node[prop]] || removed[node[prop]]) { + if (!changed[node.id]) { + madeChange = true; + changed[node.id] = node; + // This node exists within subflow template + // Mark the template as having changed + if (newConfig.allNodes[node.z]) { + changed[node.z] = newConfig.allNodes[node.z]; + if (changed[node.z].type === "subflow") { + changedSubflows[node.z] = changed[node.z]; + } } } } @@ -253,8 +263,18 @@ module.exports = { } } } - } + } while(madeChange===true) + // Find any nodes that exist on a subflow template and remove from changed + // list as the parent subflow will now be marked as containing a change + for (id in newConfig.allNodes) { + if (newConfig.allNodes.hasOwnProperty(id)) { + node = newConfig.allNodes[id]; + if (newConfig.allNodes[node.z] && newConfig.allNodes[node.z].type === "subflow") { + delete changed[node.id]; + } + } + } // Recursively mark all instances of changed subflows as changed var changedSubflowStack = Object.keys(changedSubflows); diff --git a/test/red/runtime/nodes/flows/util_spec.js b/test/red/runtime/nodes/flows/util_spec.js index fa99424c5..4a3017121 100644 --- a/test/red/runtime/nodes/flows/util_spec.js +++ b/test/red/runtime/nodes/flows/util_spec.js @@ -308,7 +308,7 @@ describe('flows/util', function() { diffResult.linked.sort().should.eql(["3"]); }); - it('identifies config nodes changes', function() { + it('identifies config nodes changes, node->config', function() { var config = [ {id:"1",type:"test",foo:"configNode",wires:[["2"]]}, {id:"2",type:"test",bar:"b",wires:[["3"]]}, @@ -329,7 +329,30 @@ describe('flows/util', function() { diffResult.removed.should.have.length(0); diffResult.rewired.should.have.length(0); diffResult.linked.sort().should.eql(["2","3"]); + }); + it('identifies config nodes changes, node->config->config', function() { + var config = [ + {id:"1",type:"test",foo:"configNode1",wires:[["2"]]}, + {id:"2",type:"test",bar:"b",wires:[["3"]]}, + {id:"3",type:"test",foo:"a",wires:[]}, + {id:"configNode1",foo:"configNode2",type:"testConfig"}, + {id:"configNode2",type:"testConfig"} + ]; + var newConfig = clone(config); + newConfig[4].foo = "bar"; + + var originalConfig = flowUtil.parseConfig(config); + var changedConfig = flowUtil.parseConfig(newConfig); + + originalConfig.missingTypes.should.have.length(0); + + var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); + diffResult.added.should.have.length(0); + diffResult.changed.sort().should.eql(["1","configNode1","configNode2"]); + diffResult.removed.should.have.length(0); + diffResult.rewired.should.have.length(0); + diffResult.linked.sort().should.eql(["2","3"]); }); it('marks a parent subflow as changed for an internal property change', function() {