Add path property to debug messages

Fixes #2358

This property can be used to identify the full path to the node that
logged a given message. If the node is inside a subflow (and maybe
nested many levels deep), this path can be used to help find the
node, rather than just the top-level subflow instance node.

A side-effect of this change is the Debug sidebar is now able to
show the message tools for a message coming from a deeply nested
subflow
This commit is contained in:
Nick O'Leary
2020-01-17 16:53:01 +00:00
parent 5e7cd79ed9
commit 95a51aafdc
7 changed files with 89 additions and 16 deletions

View File

@@ -131,8 +131,32 @@
RED.view.redraw();
}
},
messageSourceClick: function(sourceId) {
RED.view.reveal(sourceId);
messageSourceClick: function(sourceId, aliasId, path) {
// Get all of the nodes that could have logged this message
var candidateNodes = [RED.nodes.node(sourceId)]
if (path) {
for (var i=2;i<path.length;i++) {
candidateNodes.push(RED.nodes.node(path[i]))
}
}
if (aliasId) {
candidateNodes.push(RED.nodes.node(aliasId));
}
if (candidateNodes.length > 1) {
// The node is in a subflow. Check to see if the active
// workspace is a subflow in the node's parentage. If
// so, reveal the relevant subflow instance node.
var ws = RED.workspaces.active();
for (var i=0;i<candidateNodes.length;i++) {
if (candidateNodes[i].z === ws) {
RED.view.reveal(candidateNodes[i].id);
return
}
}
// The active workspace is unrelated to the node. So
// fall back to revealing the top most node
}
RED.view.reveal(candidateNodes[0].id);
},
clear: function() {
RED.nodes.eachNode(function(node) {
@@ -179,9 +203,44 @@
RED.events.on("workspace:change", this.refreshMessageList);
this.handleDebugMessage = function(t,o) {
var sourceNode = RED.nodes.node(o.id) || RED.nodes.node(o.z);
// console.log("->",o.id,o.z,o._alias);
//
// sourceNode should be the top-level node - one that is on a flow.
var sourceNode;
var pathParts;
if (o.path) {
// Path is a `/`-separated list of ids that identifies the
// complete parentage of the node that generated this message.
// flow-id/subflow-A-instance/subflow-A-type/subflow-B-instance/subflow-B-type/node-id
// If it has one id, that is a top level flow
// each subsequent id is the instance id of a subflow node
//
pathParts = o.path.split("/");
if (pathParts.length === 1) {
// The source node is on a flow - so can use its id to find
sourceNode = RED.nodes.node(o.id);
} else if (pathParts.length > 1) {
// Highlight the subflow instance node.
sourceNode = RED.nodes.node(pathParts[1]);
}
} else {
// This is probably redundant...
sourceNode = RED.nodes.node(o.id) || RED.nodes.node(o.z);
}
if (sourceNode) {
o._source = {id:sourceNode.id,z:sourceNode.z,name:sourceNode.name,type:sourceNode.type,_alias:o._alias};
o._source = {
id:sourceNode.id,
z:sourceNode.z,
name:sourceNode.name,
type:sourceNode.type,
// _alias identifies the actual logging node. This is
// not necessarily the same as sourceNode, which will be
// the top-level subflow instance node.
// This means the node's name is displayed in the sidebar.
_alias:o._alias,
path: pathParts
};
}
RED.debug.handleDebugMessage(o);
if (subWindow) {
@@ -235,7 +294,7 @@
} else if (msg.event === "mouseLeave") {
options.messageMouseLeave(msg.id);
} else if (msg.event === "mouseClick") {
options.messageSourceClick(msg.id);
options.messageSourceClick(msg.id,msg._alias,msg.path);
} else if (msg.event === "clear") {
options.clear();
}

View File

@@ -62,7 +62,7 @@ module.exports = function(RED) {
if (err) {
done(RED._("debug.invalid-exp", {error: editExpression}));
} else {
done(null,{id:node.id, z:node.z, name:node.name, topic:msg.topic, msg:value, _path:msg._path});
done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value});
}
});
} else {
@@ -77,7 +77,7 @@ module.exports = function(RED) {
output = undefined;
}
}
done(null,{id:node.id, z:node.z, name:node.name, topic:msg.topic, property:property, msg:output, _path:msg._path});
done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, property:property, msg:output});
}
}
@@ -88,7 +88,7 @@ module.exports = function(RED) {
node.log("\n"+util.inspect(msg, {colors:useColors, depth:10}));
}
if (this.active && this.tosidebar) {
sendDebug({id:node.id, z:node.z, name:node.name, topic:msg.topic, msg:msg, _path:msg._path});
sendDebug({id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:msg});
}
done();
} else {

View File

@@ -406,10 +406,16 @@ RED.debug = (function() {
msg.on("mouseenter", function() {
msg.addClass('red-ui-debug-msg-hover');
if (o._source) {
// highlight the top-level node (could be subflow instance)
config.messageMouseEnter(o._source.id);
if (o._source._alias) {
// this is inside a subflow - highlight the node itself
config.messageMouseEnter(o._source._alias);
}
// if path.length > 2, we are nested - highlight subflow instances
for (var i=2;i<o._source.path.length;i++) {
config.messageMouseEnter(o._source.path[i]);
}
}
});
msg.on("mouseleave", function() {
@@ -419,6 +425,9 @@ RED.debug = (function() {
if (o._source._alias) {
config.messageMouseLeave(o._source._alias);
}
for (var i=2;i<o._source.path.length;i++) {
config.messageMouseLeave(o._source.path[i]);
}
}
});
var name = sanitize(((o.name?o.name:o.id)||"").toString());
@@ -452,7 +461,7 @@ RED.debug = (function() {
.appendTo(metaRow)
.on("click", function(evt) {
evt.preventDefault();
config.messageSourceClick(sourceNode.id);
config.messageSourceClick(sourceNode.id, sourceNode._alias, sourceNode.path);
});
} else if (name) {
$('<span class="red-ui-debug-msg-name">'+name+'</span>').appendTo(metaRow);

View File

@@ -7,8 +7,8 @@ $(function() {
messageMouseLeave: function(sourceId) {
window.opener.postMessage({event:"mouseLeave",id:sourceId},'*');
},
messageSourceClick: function(sourceId) {
window.opener.postMessage({event:"mouseClick",id:sourceId},'*');
messageSourceClick: function(sourceId, aliasId, path) {
window.opener.postMessage({event:"mouseClick",id:sourceId, _alias: aliasId, path: path},'*');
},
clear: function() {
window.opener.postMessage({event:"clear"},'*');