mirror of https://github.com/node-red/node-red.git
Merge 5085558cb2 into ce5fe9079e
commit
9c526e23b2
|
|
@ -12,6 +12,14 @@
|
|||
<input type="checkbox" id="node-input-uncaught" style="display: inline-block; width: auto; vertical-align: top; margin-left: 30px; margin-right: 5px;">
|
||||
<label for="node-input-uncaught" style="width: auto" data-i18n="catch.label.uncaught"></label>
|
||||
</div>
|
||||
<div class="form-row node-input-anyError-row">
|
||||
<input type="checkbox" id="node-input-anyError" style="display: inline-block; width: auto; vertical-align: top; margin-left: 30px; margin-right: 5px;">
|
||||
<label for="node-input-anyError" style="width: auto" data-i18n="catch.label.anyError"></label>
|
||||
</div>
|
||||
<div class="form-row node-input-includeConfig-row">
|
||||
<input type="checkbox" id="node-input-includeConfig" style="display: inline-block; width: auto; vertical-align: top; margin-left: 30px; margin-right: 5px;">
|
||||
<label for="node-input-includeConfig" style="width: auto" data-i18n="catch.label.includeConfig"></label>
|
||||
</div>
|
||||
<div class="form-row node-input-target-row">
|
||||
<button type="button" id="node-input-catch-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
|
||||
</div>
|
||||
|
|
@ -30,9 +38,11 @@
|
|||
category: 'common',
|
||||
color:"#e49191",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
scope: {value:null, type:"*[]"},
|
||||
uncaught: {value:false}
|
||||
name: { value: "" },
|
||||
scope: { value: null, type: "*[]" },
|
||||
uncaught: { value: false },
|
||||
anyError: { value: false },
|
||||
includeConfig: { value: false }
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
|
|
@ -80,29 +90,64 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
const sidebar = $("#red-ui-sidebar-config-category-global");
|
||||
var dirList = $("#node-input-catch-target-container-div").css({width: "100%", height: "100%"})
|
||||
.treeList({multi:true}).on("treelistitemmouseover", function(e, item) {
|
||||
item.node.highlighted = true;
|
||||
item.node.dirty = true;
|
||||
RED.view.redraw();
|
||||
if (item.node) {
|
||||
if (!item.node.x && !item.node.y) {
|
||||
sidebar.find(".red-ui-palette-node_id_" + item.node.id).children("div").addClass("highlighted");
|
||||
} else {
|
||||
item.node.highlighted = true;
|
||||
item.node.dirty = true;
|
||||
RED.view.redraw();
|
||||
}
|
||||
}
|
||||
}).on("treelistitemmouseout", function(e, item) {
|
||||
item.node.highlighted = false;
|
||||
item.node.dirty = true;
|
||||
RED.view.redraw();
|
||||
if (item.node) {
|
||||
if (!item.node.x && !item.node.y) {
|
||||
sidebar.find(".red-ui-palette-node_id_" + item.node.id).children("div").removeClass("highlighted");
|
||||
} else {
|
||||
item.node.highlighted = false;
|
||||
item.node.dirty = true;
|
||||
RED.view.redraw();
|
||||
}
|
||||
}
|
||||
})
|
||||
var candidateNodes = RED.nodes.filterNodes({z:node.z});
|
||||
var allChecked = true;
|
||||
var items = [];
|
||||
var nodeItemMap = {};
|
||||
|
||||
const flowItems = [];
|
||||
const flowItemMap = {};
|
||||
let activeWorkspace = RED.nodes.workspace(node.z);
|
||||
activeWorkspace = activeWorkspace || RED.nodes.subflow(node.z);
|
||||
flowItemMap[activeWorkspace.id] = {
|
||||
id: activeWorkspace.id,
|
||||
class: "red-ui-palette-header",
|
||||
label: activeWorkspace.label || activeWorkspace.name || activeWorkspace.id,
|
||||
expanded: true,
|
||||
children: []
|
||||
};
|
||||
|
||||
flowItemMap.global = {
|
||||
id: "global",
|
||||
class: "red-ui-palette-header",
|
||||
label: this._("catch.label.globalConfig"),
|
||||
expanded: true,
|
||||
children: []
|
||||
};
|
||||
|
||||
const nodeItemMap = {};
|
||||
const configNodeSet = new Set();
|
||||
const candidateNodes = RED.nodes.filterNodes({ z: node.z });
|
||||
RED.nodes.eachConfig((cn) => {
|
||||
// Add config nodes with the same or global scope
|
||||
if (cn.z === node.z || !cn.z) {
|
||||
configNodeSet.add(cn);
|
||||
candidateNodes.push(cn);
|
||||
}
|
||||
});
|
||||
candidateNodes.forEach(function(n) {
|
||||
if (n.id === node.id) {
|
||||
if (n.id === node.id || n.type === "global-config") {
|
||||
return;
|
||||
}
|
||||
var isChecked = scope.indexOf(n.id) !== -1;
|
||||
|
||||
allChecked = allChecked && isChecked;
|
||||
|
||||
var nodeDef = RED.nodes.getType(n.type);
|
||||
var label;
|
||||
var sublabel;
|
||||
|
|
@ -119,21 +164,42 @@
|
|||
if (!nodeDef || !label) {
|
||||
label = n.type;
|
||||
}
|
||||
const isChecked = scope.indexOf(n.id) !== -1;
|
||||
const isConfigNode = (nodeDef && nodeDef.category === "config") || (!nodeDef && !n.x && !n.y);
|
||||
nodeItemMap[n.id] = {
|
||||
node: n,
|
||||
label: label,
|
||||
sublabel: sublabel,
|
||||
selected: isChecked,
|
||||
checkbox: true
|
||||
checkbox: true,
|
||||
icon: isConfigNode ? "fa fa-cog" : ""
|
||||
};
|
||||
items.push(nodeItemMap[n.id]);
|
||||
if (!n.z && isConfigNode) {
|
||||
flowItemMap.global.children.push(nodeItemMap[n.id]);
|
||||
} else {
|
||||
flowItemMap[activeWorkspace.id].children.push(nodeItemMap[n.id]);
|
||||
}
|
||||
});
|
||||
|
||||
flowItems.push(flowItemMap[activeWorkspace.id], flowItemMap.global);
|
||||
|
||||
$("#node-input-includeConfig").on("change", function () {
|
||||
const checked = $(this).prop("checked");
|
||||
if (checked) {
|
||||
dirList.treeList('data', flowItems);
|
||||
} else {
|
||||
// Remove config nodes from the list
|
||||
const filteredItems = flowItemMap[activeWorkspace.id].children
|
||||
.filter((e) => !configNodeSet.has(e.node));
|
||||
dirList.treeList('data', filteredItems);
|
||||
}
|
||||
});
|
||||
dirList.treeList('data',items);
|
||||
|
||||
$("#node-input-catch-target-select").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
var preselected = dirList.treeList('selected').map(function(n) {return n.node.id});
|
||||
RED.tray.hide();
|
||||
// TODO: extend selection to config nodes too
|
||||
RED.view.selectNodes({
|
||||
selected: preselected,
|
||||
onselect: function(selection) {
|
||||
|
|
@ -169,6 +235,13 @@
|
|||
$(".node-input-target-row").hide();
|
||||
$(".node-input-uncaught-row").show();
|
||||
}
|
||||
if (scope === "group") {
|
||||
$(".node-input-anyError-row").hide();
|
||||
$(".node-input-includeConfig-row").hide();
|
||||
} else {
|
||||
$(".node-input-anyError-row").show();
|
||||
$(".node-input-includeConfig-row").show();
|
||||
}
|
||||
node._resize();
|
||||
});
|
||||
if (this.scope === null) {
|
||||
|
|
|
|||
|
|
@ -14,19 +14,20 @@
|
|||
* limitations under the License.
|
||||
**/
|
||||
|
||||
module.exports = function(RED) {
|
||||
module.exports = function (RED) {
|
||||
"use strict";
|
||||
|
||||
function CatchNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
RED.nodes.createNode(this, n);
|
||||
this.scope = n.scope;
|
||||
this.uncaught = n.uncaught;
|
||||
this.on("input",function(msg, send, done) {
|
||||
this.anyError = n.anyError;
|
||||
this.includeConfig = n.includeConfig;
|
||||
this.on("input", function (msg, send, done) {
|
||||
send(msg);
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("catch",CatchNode);
|
||||
RED.nodes.registerType("catch", CatchNode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,10 @@
|
|||
"label": {
|
||||
"source": "Catch errors from",
|
||||
"selectAll": "select all",
|
||||
"uncaught": "Ignore errors handled by other Catch nodes"
|
||||
"uncaught": "Ignore errors handled by other Catch nodes",
|
||||
"anyError": "Allow any error other than message processing",
|
||||
"includeConfig": "Catch errors from config nodes too",
|
||||
"globalConfig": "Global config nodes"
|
||||
},
|
||||
"scope": {
|
||||
"all": "all nodes",
|
||||
|
|
|
|||
|
|
@ -98,14 +98,17 @@
|
|||
"catchNodes": "catch : __number__",
|
||||
"catchUncaught": "catch : non capturé",
|
||||
"label": {
|
||||
"source": "Détecter les erreurs de",
|
||||
"source": "Capturer les erreurs ",
|
||||
"selectAll": "Tout sélectionner",
|
||||
"uncaught": "Ignorer les erreurs gérées par les autres noeuds Catch"
|
||||
"uncaught": "Ignorer les erreurs gérées par les autres noeuds Catch",
|
||||
"anyError": "Autoriser toute erreur autre que le traitement des messages",
|
||||
"includeConfig": "Capturer également les erreurs des noeuds de configuration",
|
||||
"globalConfig": "Noeuds de configuration globale"
|
||||
},
|
||||
"scope": {
|
||||
"all": "tous les noeuds",
|
||||
"all": "de tous les noeuds",
|
||||
"group": "dans le même groupe",
|
||||
"selected": "noeuds sélectionnés"
|
||||
"selected": "des noeuds sélectionnés"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ let Subflow;
|
|||
let Log;
|
||||
let Group;
|
||||
|
||||
/** @type {Record<string, Flow>} */
|
||||
let activeFlows;
|
||||
|
||||
let nodeCloseTimeout = 15000;
|
||||
let asyncMessageDelivery = true;
|
||||
|
||||
|
|
@ -620,9 +623,25 @@ class Flow {
|
|||
handled = node.users[userNode]._flow.handleError(node,logMessage,msg,node.users[userNode]) || handled;
|
||||
}
|
||||
}
|
||||
} else if (this.id === 'global') {
|
||||
for (const flow of Object.values(activeFlows)) {
|
||||
if (flow.id === 'global') {
|
||||
continue;
|
||||
}
|
||||
handled = flow.handleError(node, logMessage, msg) || handled;
|
||||
}
|
||||
} else {
|
||||
const candidateNodes = [];
|
||||
const isConfigNode = !!node._flow?.flow.configs[node.id];
|
||||
this.catchNodes.forEach(targetCatchNode => {
|
||||
if (msg === null && !targetCatchNode.anyError) {
|
||||
// Skip if the catch node only accepts errors produced by message processing
|
||||
return;
|
||||
}
|
||||
if (isConfigNode && !targetCatchNode.includeConfig) {
|
||||
// Skip if the catch node only accepts errors produced by non config nodes
|
||||
return;
|
||||
}
|
||||
if (targetCatchNode.g && targetCatchNode.scope === 'group' && !reportingNode.g) {
|
||||
// Catch node inside a group, reporting node not in a group - skip it
|
||||
return
|
||||
|
|
@ -860,6 +879,9 @@ module.exports = {
|
|||
Subflow = require("./Subflow");
|
||||
Group = require("./Group").Group
|
||||
},
|
||||
setActiveFlows: function (flows) {
|
||||
activeFlows = flows;
|
||||
},
|
||||
create: function(parent,global,conf) {
|
||||
return new Flow(parent,global,conf)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ function init(runtime) {
|
|||
typeEventRegistered = true;
|
||||
}
|
||||
Flow.init(runtime);
|
||||
Flow.setActiveFlows(activeFlows);
|
||||
flowUtil.init(runtime);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -566,9 +566,10 @@ Node.prototype.error = function(logMessage,msg) {
|
|||
if (typeof logMessage != 'boolean') {
|
||||
logMessage = logMessage || "";
|
||||
}
|
||||
var handled = false;
|
||||
if (this._flow && msg && typeof msg === 'object') {
|
||||
handled = this._flow.handleError(this,logMessage,msg);
|
||||
let handled = false;
|
||||
const shouldHandle = (msg && typeof msg === 'object') || msg === undefined;
|
||||
if (this._flow && shouldHandle) {
|
||||
handled = this._flow.handleError(this, logMessage, msg || null);
|
||||
}
|
||||
if (!handled) {
|
||||
log_helper(this, Log.ERROR, logMessage);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ describe('function node', function() {
|
|||
});
|
||||
|
||||
it('should do something with the catch node', function(done) {
|
||||
var flow = [{"id":"funcNode","type":"function","wires":[["goodNode"]],"func":"node.error('This is an error', msg);"},{"id":"goodNode","type":"helper"},{"id":"badNode","type":"helper"},{"id":"catchNode","type":"catch","scope":null,"uncaught":false,"wires":[["badNode"]]}];
|
||||
var flow = [{"id":"flow","type":"tab"},{"id":"funcNode","type":"function","wires":[["goodNode"]],"func":"node.error('This is an error', msg);","x":0,"y":0,"z":"flow"},{"id":"goodNode","type":"helper","z":"flow"},{"id":"badNode","type":"helper","z":"flow"},{"id":"catchNode","type":"catch","scope":null,"uncaught":false,"wires":[["badNode"]],"z":"flow"}];
|
||||
var catchNodeModule = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js")
|
||||
helper.load([catchNodeModule, functionNode], flow, function() {
|
||||
var funcNode = helper.getNode("funcNode");
|
||||
|
|
|
|||
Loading…
Reference in New Issue