From 876a7a464689fd10b52a8724a8ac84130ee526ba Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 27 May 2020 12:20:23 +0100 Subject: [PATCH] Add bulk-activate actions for debug node Adds the actions: - core:activate-all-debug-nodes - core:activate-all-flow-debug-nodes to match the deactivate* actions. Also adds: - core:activate-selected-debug-nodes - core:deactivate-selected-debug-nodes Adds a new httpAdmin route - /debug/(enable/disable) - that can be use to bulk enable/disable nodes via HTTP Post. --- package.json | 2 +- .../@node-red/nodes/core/common/21-debug.html | 112 ++++++++++++------ .../@node-red/nodes/core/common/21-debug.js | 73 ++++++++---- test/nodes/core/common/21-debug_spec.js | 24 ++++ 4 files changed, 151 insertions(+), 60 deletions(-) diff --git a/package.json b/package.json index 5bf4e5ee0..abb22e118 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "minami": "1.2.3", "mocha": "^5.2.0", "mosca": "^2.8.3", - "node-red-node-test-helper": "^0.2.3", + "node-red-node-test-helper": "^0.2.5", "node-sass": "^4.13.1", "should": "^8.4.0", "sinon": "1.17.7", diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.html b/packages/node_modules/@node-red/nodes/core/common/21-debug.html index 17c45861e..d12279c74 100644 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.html +++ b/packages/node_modules/@node-red/nodes/core/common/21-debug.html @@ -43,9 +43,20 @@ var subWindow = null; function activateAjaxCall(node, active, successCallback) { + var url; + var body; + + if (Array.isArray(node)) { + url = "debug/"+(active?"enable":"disable"); + body = {nodes: node.map(function(n) { return n.id})} + node = node[0]; + } else { + url = "debug/"+node.id+"/"+(active?"enable":"disable"); + } $.ajax({ - url: "debug/"+node.id+"/"+(active?"enable":"disable"), + url: url, type: "POST", + data: body, success: successCallback, error: function(jqXHR,textStatus,errorThrown) { if (jqXHR.status == 404) { @@ -126,7 +137,6 @@ } }, onpaletteadd: function() { - console.log(RED); var options = { messageMouseEnter: function(sourceId) { if (sourceId) { @@ -283,44 +293,76 @@ RED.events.on("project:change", this.clearMessageList); RED.actions.add("core:clear-debug-messages", function() { RED.debug.clearMessageList(true) }); + RED.actions.add("core:activate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(true), true); }); + RED.actions.add("core:activate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, true),true); }); + RED.actions.add("core:activate-all-flow-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, false),true); }); - RED.actions.add("core:deactivate-all-debug-nodes", function() { deactivateAllDebugNodes(true); }); - RED.actions.add("core:deactivate-all-flow-debug-nodes", function() { deactivateAllDebugNodes(false); }); + RED.actions.add("core:deactivate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(false), false); }); + RED.actions.add("core:deactivate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(false, true),false); }); + RED.actions.add("core:deactivate-all-flow-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(false, false),false); }); - function deactivateAllDebugNodes(globally) { - var historyEvents = []; - RED.nodes.eachNode(function(n) { - if (n.type === "debug" && n.active === true) { - if (globally === true || n.z == RED.workspaces.active()) { - if(RED.nodes.subflow(n.z) === undefined) { - historyEvents.push({ - t: "edit", - node: n, - changed: n.changed, - changes: { - active: n.active - }, - callback: function() { - activateAjaxCall(n, n.active); - } - }); - n.active = false; - n.changed = true; - n.dirty = true; - activateAjaxCall(n, n.active); - } + function getSelectedDebugNodes(state) { + var nodes = []; + var selection = RED.view.selection(); + if (selection.nodes) { + selection.nodes.forEach(function(n) { + if (RED.nodes.subflow(n.z)) { + return; + } + if (n.type === 'debug' && n.active !== state) { + nodes.push(n); + } else if (n.type === 'group') { + nodes = nodes.concat( RED.group.getNodes(n,true).filter(function(n) { + return n.type === 'debug' && n.active !== state + })); } - } - }); - if (historyEvents.length > 0) { - RED.history.push({ - t: "multi", - events: historyEvents, - dirty: RED.nodes.dirty() }); - RED.nodes.dirty(true); } - RED.view.redraw(); + return nodes; + + } + function getMatchingDebugNodes(state,globally) { + var nodes = []; + var filter = {type:"debug"}; + if (!globally) { + filter.z = RED.workspaces.active(); + } + var candidateNodes = RED.nodes.filterNodes(filter); + nodes = candidateNodes.filter(function(n) { + return n.active !== state && !RED.nodes.subflow(n.z) + }) + return nodes; + } + + function setDebugNodeState(nodes,state) { + var historyEvents = []; + if (nodes.length > 0) { + activateAjaxCall(nodes,false, function(resp, textStatus, xhr) { + nodes.forEach(function(n) { + historyEvents.push({ + t: "edit", + node: n, + changed: n.changed, + changes: { + active: n.active + } + }); + n.active = state; + n.changed = true; + n.dirty = true; + }) + RED.history.push({ + t: "multi", + events: historyEvents, + dirty: RED.nodes.dirty(), + callback: function() { + activateAjaxCall(nodes,nodes[0].active); + } + }); + RED.nodes.dirty(true); + RED.view.redraw(); + }); + } } $("#red-ui-sidebar-debug-open").on("click", function(e) { diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.js b/packages/node_modules/@node-red/nodes/core/common/21-debug.js index c3566faa6..2a76eb401 100644 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.js +++ b/packages/node_modules/@node-red/nodes/core/common/21-debug.js @@ -52,7 +52,7 @@ module.exports = function(RED) { // Either apply the jsonata expression or... if (preparedEditExpression) { RED.util.evaluateJSONataExpression(preparedEditExpression, msg, (err, value) => { - if (err) { done(RED._("debug.invalid-exp", {error: editExpression})); } + if (err) { done(RED._("debug.invalid-exp", {error: editExpression})); } else { done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value}); } }); } else { @@ -61,7 +61,7 @@ module.exports = function(RED) { var output = msg[property]; if (node.complete !== "false" && typeof node.complete !== "undefined") { property = node.complete; - try { output = RED.util.getMessageProperty(msg,node.complete); } + try { output = RED.util.getMessageProperty(msg,node.complete); } catch(err) { output = undefined; } } 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}); @@ -84,10 +84,10 @@ module.exports = function(RED) { // Either apply the jsonata expression or... if (preparedStatExpression) { RED.util.evaluateJSONataExpression(preparedStatExpression, msg, (err, value) => { - if (err) { done(RED._("debug.invalid-exp", {error:editExpression})); } + if (err) { done(RED._("debug.invalid-exp", {error:editExpression})); } else { done(null,{msg:value}); } }); - } + } else { // Extract the required message property var output; @@ -107,8 +107,8 @@ module.exports = function(RED) { var fill = "grey"; var shape = "dot"; if (node.statusType === "auto") { - if (msg.hasOwnProperty("error")) { - fill = "red"; + if (msg.hasOwnProperty("error")) { + fill = "red"; st = msg.error.message; } if (msg.hasOwnProperty("status")) { @@ -131,7 +131,7 @@ module.exports = function(RED) { sendDebug({id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:msg}); } done(); - } + } else { prepareValue(msg,function(err,debugMsg) { if (err) { @@ -185,24 +185,49 @@ module.exports = function(RED) { }); RED.log.addHandler(DebugNode.logHandler); - RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) { - var node = RED.nodes.getNode(req.params.id); - var state = req.params.state; - if (node !== null && typeof node !== "undefined" ) { - if (state === "enable") { - node.active = true; - res.sendStatus(200); - if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); } - } else if (state === "disable") { - node.active = false; - res.sendStatus(201); - if (node.tostatus && node.hasOwnProperty("oldStatus")) { - node.oldStatus.shape = "dot"; - node.status(node.oldStatus); - } - } else { - res.sendStatus(404); + function setNodeState(node,state) { + if (state) { + node.active = true; + if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); } + } else { + node.active = false; + if (node.tostatus && node.hasOwnProperty("oldStatus")) { + node.oldStatus.shape = "dot"; + node.status(node.oldStatus); } + } + } + + RED.httpAdmin.post("/debug/:state", RED.auth.needsPermission("debug.write"), function(req,res) { + var state = req.params.state; + if (state !== 'enable' && state !== 'disable') { + res.sendStatus(404); + return; + } + var nodes = req.body && req.body.nodes; + if (Array.isArray(nodes)) { + nodes.forEach(function(id) { + var node = RED.nodes.getNode(id); + if (node !== null && typeof node !== "undefined" ) { + setNodeState(node, state === "enable"); + } + }) + res.sendStatus(state === "enable" ? 200 : 201); + } else { + res.sendStatus(400); + } + }) + + RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) { + var state = req.params.state; + if (state !== 'enable' && state !== 'disable') { + res.sendStatus(404); + return; + } + var node = RED.nodes.getNode(req.params.id); + if (node !== null && typeof node !== "undefined" ) { + setNodeState(node,state === "enable"); + res.sendStatus(state === "enable" ? 200 : 201); } else { res.sendStatus(404); } diff --git a/test/nodes/core/common/21-debug_spec.js b/test/nodes/core/common/21-debug_spec.js index bdff7976c..990797ef3 100644 --- a/test/nodes/core/common/21-debug_spec.js +++ b/test/nodes/core/common/21-debug_spec.js @@ -603,6 +603,30 @@ describe('debug node', function() { .post('/debug/n99/enable') .expect(404).end(done); }); + + it('should return 400 for invalid bulk disable', function(done) { + var flow = [{id:"n1", type:"debug", active: true }]; + helper.load(debugNode, flow, function() { + helper.request() + .post('/debug/disable') + .send({}) + .set('Content-type', 'application/json') + .expect(400).end(done); + }); + + }) + + it('should return success for bulk disable', function(done) { + var flow = [{id:"n1", type:"debug", active: true }]; + helper.load(debugNode, flow, function() { + helper.request() + .post('/debug/disable') + .send({nodes:['n1']}) + .set('Content-type', 'application/json') + .expect(201).end(done); + }); + + }) }); describe('get', function() {