From d534a8952d4a0e26f589774b0402421b07dc430e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 29 Jan 2019 21:49:20 +0000 Subject: [PATCH] Do not propagate Flow.getNode to parent when called from outside flow --- .../@node-red/runtime/lib/nodes/flows/Flow.js | 13 ++-- .../runtime/lib/nodes/flows/index.js | 4 +- .../runtime/lib/nodes/flows/Flow_spec.js | 63 +++++++++++++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js index 20399f19d..7ca6afb77 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js @@ -281,11 +281,13 @@ class Flow { /** * Get a node instance from this flow. If the node is not known to this * flow, pass the request up to the parent. - * @param {[type]} id [description] + * @param {String} id [description] + * @param {Boolean} cancelBubble if true, prevents the flow from passing the request to the parent + * This stops infinite loops when the parent asked this Flow for the + * node to begin with. * @return {[type]} [description] */ - getNode(id) { - // console.log('getNode',id,!!this.activeNodes[id]) + getNode(id, cancelBubble) { if (!id) { return undefined; } @@ -298,7 +300,10 @@ class Flow { // TEMP: this is a subflow internal node within this flow return this.activeNodes[id]; } - return this.parent.getNode(id); + if (!cancelBubble) { + return this.parent.getNode(id); + } + return undefined; } /** diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js index 20c3274d1..4cce74e83 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js @@ -207,11 +207,11 @@ function setFlows(_config,type,muteLog,forceStart) { function getNode(id) { var node; if (activeNodesToFlow[id] && activeFlows[activeNodesToFlow[id]]) { - return activeFlows[activeNodesToFlow[id]].getNode(id); + return activeFlows[activeNodesToFlow[id]].getNode(id,true); } for (var flowId in activeFlows) { if (activeFlows.hasOwnProperty(flowId)) { - node = activeFlows[flowId].getNode(id); + node = activeFlows[flowId].getNode(id,true); if (node) { return node; } diff --git a/test/unit/@node-red/runtime/lib/nodes/flows/Flow_spec.js b/test/unit/@node-red/runtime/lib/nodes/flows/Flow_spec.js index 14b1e58ab..feac88e22 100644 --- a/test/unit/@node-red/runtime/lib/nodes/flows/Flow_spec.js +++ b/test/unit/@node-red/runtime/lib/nodes/flows/Flow_spec.js @@ -413,6 +413,69 @@ describe('Flow', function() { }); }); + describe('#getNode',function() { + it("gets a node known to the flow",function(done) { + var config = flowUtils.parseConfig([ + {id:"t1",type:"tab"}, + {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:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, + {id:"4",z:"t1",type:"test",foo:"a"} + ]); + var flow = Flow.create({},config,config.flows["t1"]); + flow.start(); + + Object.keys(flow.getActiveNodes()).should.have.length(4); + + flow.getNode('1').should.have.a.property('id','1'); + + flow.stop().then(() => { done() }); + }); + + it("passes to parent if node not known locally",function(done) { + var config = flowUtils.parseConfig([ + {id:"t1",type:"tab"}, + {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:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, + {id:"4",z:"t1",type:"test",foo:"a"} + ]); + var flow = Flow.create({ + getNode: id => { return {id:id}} + },config,config.flows["t1"]); + flow.start(); + + Object.keys(flow.getActiveNodes()).should.have.length(4); + + flow.getNode('1').should.have.a.property('id','1'); + + flow.getNode('parentNode').should.have.a.property('id','parentNode'); + + + flow.stop().then(() => { done() }); + }); + + it("does not pass to parent if cancelBubble set",function(done) { + var config = flowUtils.parseConfig([ + {id:"t1",type:"tab"}, + {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:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, + {id:"4",z:"t1",type:"test",foo:"a"} + ]); + var flow = Flow.create({ + getNode: id => { return {id:id}} + },config,config.flows["t1"]); + flow.start(); + + Object.keys(flow.getActiveNodes()).should.have.length(4); + + flow.getNode('1').should.have.a.property('id','1'); + + should.not.exist(flow.getNode('parentNode',true)); + flow.stop().then(() => { done() }); + }); + }); describe("#handleStatus",function() { it("passes a status event to the adjacent status node",function(done) {