Fix catch/status group scoping to handle group hierarchies

This commit is contained in:
Nick O'Leary 2023-05-22 22:33:31 +01:00
parent 11ded1e497
commit 2388232179
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
2 changed files with 135 additions and 22 deletions

View File

@ -665,16 +665,36 @@ class Flow {
}
handled = true;
} else {
const flow = this;
this.statusNodes.forEach(function(targetStatusNode) {
const candidateNodes = [];
this.statusNodes.forEach(targetStatusNode => {
if (targetStatusNode.g && targetStatusNode.scope === 'group' && !reportingNode.g) {
// Status node inside a group, reporting node not in a group - skip it
return
}
if (Array.isArray(targetStatusNode.scope) && targetStatusNode.scope.indexOf(reportingNode.id) === -1) {
return;
}
if (targetStatusNode.scope === "group" && flow.isNodeInSameGroup(targetStatusNode, reportingNode) === false){
return;
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]
while (containingGroup && containingGroup.id !== targetStatusNode.g) {
distance++
containingGroup = this.global.groups[containingGroup.g]
}
if (!containingGroup && targetStatusNode.g && targetStatusNode.scope === 'group') {
// This status node is in a group, but not in the same hierachy
// the reporting node is in
return
}
}
candidateNodes.push({ d: distance, n: targetStatusNode })
})
candidateNodes.sort((A,B) => {
return A.d - B.d
})
candidateNodes.forEach(candidate => {
const targetStatusNode = candidate.n
var message = {
status: clone(statusMessage)
}
@ -732,27 +752,46 @@ class Flow {
}
handled = true;
} else {
var handledByUncaught = false;
const flow = this;
this.catchNodes.forEach(function(targetCatchNode) {
const candidateNodes = [];
this.catchNodes.forEach(targetCatchNode => {
if (targetCatchNode.g && targetCatchNode.scope === 'group' && !reportingNode.g) {
// Catch node inside a group, reporting node not in a group - skip it
return
}
if (Array.isArray(targetCatchNode.scope) && targetCatchNode.scope.indexOf(reportingNode.id) === -1) {
// Catch node has a scope set and it doesn't include the reporting node
return;
}
if (targetCatchNode.scope === "group" && flow.isNodeInSameGroup(targetCatchNode, reportingNode) === false){
return;
}
if ((!targetCatchNode.scope || targetCatchNode.scope === "group") && targetCatchNode.uncaught && !handledByUncaught) {
if (handled) {
// This has been handled by a !uncaught catch node
return;
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]
while (containingGroup && containingGroup.id !== targetCatchNode.g) {
distance++
containingGroup = this.global.groups[containingGroup.g]
}
if (!containingGroup && targetCatchNode.g && targetCatchNode.scope === 'group') {
// This catch node is in a group, but not in the same hierachy
// the reporting node is in
return
}
// This is an uncaught error
handledByUncaught = true;
}
var errorMessage;
candidateNodes.push({ d: distance, n: targetCatchNode })
})
candidateNodes.sort((A,B) => {
return A.d - B.d
})
let handledByUncaught = false
candidateNodes.forEach(candidate => {
const targetCatchNode = candidate.n
if (targetCatchNode.uncaught && !handledByUncaught) {
// This node only wants errors that haven't already been handled
if (handled) {
return
}
handledByUncaught = true
}
let errorMessage;
if (msg) {
errorMessage = redUtil.cloneMessage(msg);
} else {

View File

@ -686,6 +686,44 @@ describe('Flow', function() {
},50);
});
it.only("passes a status event to the group scoped status node",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id: "g1", type: "group", g: "g3" },
{id: "g2", type: "group" },
{id: "g3", type: "group" },
{id:"1",x:10,y:10,z:"t1",g:"g1", type:"test",name:"a",wires:["2"]},
// sn - in the same group as source node
{id:"sn",x:10,y:10,z:"t1",g:"g1", type:"status",scope:"group",wires:[]},
// sn2 - in a different group hierarchy to the source node
{id:"sn2",x:10,y:10,z:"t1", g:"g2", type:"status",scope:"group",wires:[]},
// sn3 - in a higher-level group to the source node
{id:"sn3",x:10,y:10,z:"t1", g:"g3", type:"status",scope:"group",wires:[]},
// sn2 - in a different group hierarchy, but not scope to the group
{id:"sn4",x:10,y:10,z:"t1", g:"g2", type:"status",wires:[]},
]);
var flow = Flow.create({},config,config.flows["t1"]);
flow.start();
var activeNodes = flow.getActiveNodes();
flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"});
setTimeout(function() {
try {
currentNodes["sn"].should.have.a.property("handled",1);
currentNodes["sn2"].should.have.a.property("handled",0);
currentNodes["sn3"].should.have.a.property("handled",1);
currentNodes["sn3"].should.have.a.property("handled",1);
done()
} catch(err) {
done(err)
}
},50);
});
});
describe("#handleError",function() {
@ -796,6 +834,42 @@ describe('Flow', function() {
},50);
},50);
});
it("passes an error event to the group scoped catch node",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id: "g1", type: "group", g: "g3" },
{id: "g2", type: "group" },
{id: "g3", type: "group" },
{id:"1",x:10,y:10,z:"t1",g:"g1", type:"test",name:"a",wires:["2"]},
// sn - in the same group as source node
{id:"sn",x:10,y:10,z:"t1",g:"g1", type:"catch",scope:"group",wires:[]},
// sn2 - in a different group hierarchy to the source node
{id:"sn2",x:10,y:10,z:"t1", g:"g2", type:"catch",scope:"group",wires:[]},
// sn3 - in a higher-level group to the source node
{id:"sn3",x:10,y:10,z:"t1", g:"g3", type:"catch",scope:"group",wires:[]},
// sn2 - in a different group hierarchy, but not scope to the group
{id:"sn4",x:10,y:10,z:"t1", g:"g2", type:"catch",wires:[]},
]);
var flow = Flow.create({},config,config.flows["t1"]);
flow.start();
var activeNodes = flow.getActiveNodes();
flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"});
setTimeout(function() {
try {
currentNodes["sn"].should.have.a.property("handled",1);
currentNodes["sn2"].should.have.a.property("handled",0);
currentNodes["sn3"].should.have.a.property("handled",1);
currentNodes["sn3"].should.have.a.property("handled",1);
done()
} catch(err) {
done(err)
}
},50);
});
it("moves any existing error object sideways",function(done){
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},