From 56cb985de9cf5be99f69c8a066e95be88241d6d2 Mon Sep 17 00:00:00 2001 From: Anna Thomas Date: Fri, 21 Nov 2014 10:36:32 +0000 Subject: [PATCH] Separated put response into /nodes/:mod and /nodes/:mod/:set Updated put tests --- red/api/index.js | 3 +- red/api/nodes.js | 51 +++++++- red/nodes/registry.js | 24 ++-- test/red/api/nodes_spec.js | 241 +++++++++++++++++++++++++++++++++++-- 4 files changed, 299 insertions(+), 20 deletions(-) diff --git a/red/api/index.js b/red/api/index.js index 6795ceb0e..4ed91af4e 100644 --- a/red/api/index.js +++ b/red/api/index.js @@ -53,10 +53,11 @@ function init(adminApp) { adminApp.post("/nodes",nodes.post); adminApp.get("/nodes/:mod",nodes.getModule); - adminApp.put("/nodes/:id",nodes.put); + adminApp.put("/nodes/:mod",nodes.putModule); adminApp.delete("/nodes/:id",nodes.delete); adminApp.get("/nodes/:mod/:set",nodes.getSet); + adminApp.put("/nodes/:mod/:set",nodes.putSet); // Plugins adminApp.get("/plugins",plugins.getAll); diff --git a/red/api/nodes.js b/red/api/nodes.js index 249db7298..0b1c87dd4 100644 --- a/red/api/nodes.js +++ b/red/api/nodes.js @@ -122,7 +122,7 @@ module.exports = { } }, - put: function(req,res) { + putSet: function(req,res) { if (!settings.available()) { res.send(400,new Error("Settings unavailable").toString()); return; @@ -133,9 +133,9 @@ module.exports = { return; } try { - var info; - var id = req.params.id; + var id = req.params.mod+"/"+req.params.set; var node = redNodes.getNodeInfo(id); + var info; if (!node) { res.send(404); } else if (!node.err && node.enabled === body.enabled) { @@ -161,5 +161,50 @@ module.exports = { } catch(err) { res.send(400,err.toString()); } + }, + + putModule: function(req,res) { + if (!settings.available()) { + res.send(400,new Error("Settings unavailable").toString()); + return; + } + var body = req.body; + if (!body.hasOwnProperty("enabled")) { + res.send(400,"Invalid request"); + return; + } + try { + var mod = req.params.mod; + var module = redNodes.getModuleInfo(mod); + if (!module) { + res.send(404); + return; + } + var nodes = module.nodes; + for (var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + var info; + if (node.err || node.enabled !== body.enabled) { + if (body.enabled) { + info = redNodes.enableNode(node.id); + } else { + info = redNodes.disableNode(node.id); + } + if (info.enabled === body.enabled && !info.err) { + comms.publish("node/"+(body.enabled?"enabled":"disabled"),info,false); + util.log("[red] "+(body.enabled?"Enabled":"Disabled")+" node types:"); + for (var j = 0; j < info.types.length; j++) { + util.log("[red] - " + info.types[j]); + } + } else if (body.enabled && info.err) { + util.log("[red] Failed to enable node:"); + util.log("[red] - "+info.name+" : "+info.err); + } + } + } + res.json(module); + } catch(err) { + res.send(400,err.toString()); + } } }; diff --git a/red/nodes/registry.js b/red/nodes/registry.js index 00af2237c..983c774fc 100644 --- a/red/nodes/registry.js +++ b/red/nodes/registry.js @@ -149,8 +149,9 @@ var registry = (function() { return filterNodeInfo(nodeConfigs[nodeTypeToId[typeOrId]]); } else if (nodeConfigs[typeOrId]) { return filterNodeInfo(nodeConfigs[typeOrId]); + } else { + return null; } - return null; }, getNodeList: function() { var list = []; @@ -179,15 +180,20 @@ var registry = (function() { return list; }, getModuleInfo: function(module) { - var nodes = nodeModules[module].nodes; - var m = { - name: module, - nodes: [] - }; - for (var i = 0; i < nodes.length; ++i) { - m.nodes.push(filterNodeInfo(nodeConfigs[module+"/"+nodes[i]])); + if (nodeModules[module]) { + var nodes = nodeModules[module].nodes; + var m = { + name: module, + version: "TODO", + nodes: [] + }; + for (var i = 0; i < nodes.length; ++i) { + m.nodes.push(filterNodeInfo(nodeConfigs[module+"/"+nodes[i]])); + } + return m; + } else { + return null; } - return m; }, registerNodeConstructor: function(type,constructor) { if (nodeConstructors[type]) { diff --git a/test/red/api/nodes_spec.js b/test/red/api/nodes_spec.js index b3468ad26..681b353ff 100644 --- a/test/red/api/nodes_spec.js +++ b/test/red/api/nodes_spec.js @@ -38,7 +38,8 @@ describe("nodes api", function() { app.post("/nodes",nodes.post); app.get("/nodes/:mod",nodes.getModule); app.get("/nodes/:mod/:set",nodes.getSet); - app.put("/nodes/:id",nodes.put); + app.put("/nodes/:mod",nodes.putModule); + app.put("/nodes/:mod/:set",nodes.putSet); app.delete("/nodes/:id",nodes.delete); }); @@ -433,7 +434,27 @@ describe("nodes api", function() { }); }); - it('returns 400 for invalid payload', function(done) { + it('returns 400 for invalid node payload', function(done) { + var settingsAvailable = sinon.stub(settings,'available', function() { + return true; + }); + + request(app) + .put('/nodes/node-red/foo') + .send({}) + .expect(400) + .end(function(err,res) { + settingsAvailable.restore(); + if (err) { + throw err; + } + res.text.should.equal("Invalid request"); + + done(); + }); + }); + + it('returns 400 for invalid module payload', function(done) { var settingsAvailable = sinon.stub(settings,'available', function() { return true; }); @@ -452,6 +473,7 @@ describe("nodes api", function() { done(); }); }); + it('returns 404 for unknown node', function(done) { var settingsAvailable = sinon.stub(settings,'available', function() { return true; @@ -461,7 +483,7 @@ describe("nodes api", function() { }); request(app) - .put('/nodes/foo') + .put('/nodes/node-red/foo') .send({enabled:false}) .expect(404) .end(function(err,res) { @@ -474,6 +496,28 @@ describe("nodes api", function() { }); }); + it('returns 404 for unknown module', function(done) { + var settingsAvailable = sinon.stub(settings,'available', function() { + return true; + }); + var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) { + return null; + }); + + request(app) + .put('/nodes/node-blue') + .send({enabled:false}) + .expect(404) + .end(function(err,res) { + settingsAvailable.restore(); + getModuleInfo.restore(); + if (err) { + throw err; + } + done(); + }); + }); + it('enables disabled node', function(done) { var settingsAvailable = sinon.stub(settings,'available', function() { return true; @@ -486,7 +530,7 @@ describe("nodes api", function() { }); request(app) - .put('/nodes/foo') + .put('/nodes/node-red/foo') .send({enabled:true}) .expect(200) .end(function(err,res) { @@ -502,6 +546,7 @@ describe("nodes api", function() { done(); }); }); + it('disables enabled node', function(done) { var settingsAvailable = sinon.stub(settings,'available', function() { return true; @@ -514,7 +559,7 @@ describe("nodes api", function() { }); request(app) - .put('/nodes/foo') + .put('/nodes/node-red/foo') .send({enabled:false}) .expect(200) .end(function(err,res) { @@ -530,6 +575,7 @@ describe("nodes api", function() { done(); }); }); + describe('no-ops if already in the right state', function() { function run(state,done) { var settingsAvailable = sinon.stub(settings,'available', function() { @@ -547,7 +593,7 @@ describe("nodes api", function() { }); request(app) - .put('/nodes/foo') + .put('/nodes/node-red/foo') .send({enabled:state}) .expect(200) .end(function(err,res) { @@ -575,6 +621,7 @@ describe("nodes api", function() { run(false,done); }); }); + describe('does not no-op if err on node', function() { function run(state,done) { var settingsAvailable = sinon.stub(settings,'available', function() { @@ -592,7 +639,7 @@ describe("nodes api", function() { }); request(app) - .put('/nodes/foo') + .put('/nodes/node-red/foo') .send({enabled:state}) .expect(200) .end(function(err,res) { @@ -620,6 +667,186 @@ describe("nodes api", function() { run(false,done); }); }); + + it('enables disabled module', function(done) { + var n1 = {id:"123",enabled:false,types:['a']}; + var n2 = {id:"456",enabled:false,types:['b']}; + var settingsAvailable = sinon.stub(settings,'available', function() { + return true; + }); + var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(name) { + return {name:"node-red", nodes:[n1, n2]}; + }); + + var enableNode = sinon.stub(redNodes,'enableNode'); + enableNode.onFirstCall().returns((function() { + n1.enabled = true; + return n1; + })()); + enableNode.onSecondCall().returns((function() { + n2.enabled = true; + return n2; + })()); + enableNode.returns(null); + + request(app) + .put('/nodes/node-red') + .send({enabled:true}) + .expect(200) + .end(function(err,res) { + settingsAvailable.restore(); + getModuleInfo.restore(); + enableNode.restore(); + if (err) { + throw err; + } + res.body.should.have.property("name","node-red"); + res.body.should.have.property("nodes"); + res.body.nodes[0].should.have.property("enabled",true); + res.body.nodes[1].should.have.property("enabled",true); + + done(); + }); + }); + + it('disables enabled module', function(done) { + var n1 = {id:"123",enabled:true,types:['a']}; + var n2 = {id:"456",enabled:true,types:['b']}; + var settingsAvailable = sinon.stub(settings,'available', function() { + return true; + }); + var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(name) { + return {name:"node-red", nodes:[n1, n2]}; + }); + + var disableNode = sinon.stub(redNodes,'disableNode'); + disableNode.onFirstCall().returns((function() { + n1.enabled = false; + return n1; + })()); + disableNode.onSecondCall().returns((function() { + n2.enabled = false; + return n2; + })()); + disableNode.returns(null); + + request(app) + .put('/nodes/node-red') + .send({enabled:false}) + .expect(200) + .end(function(err,res) { + settingsAvailable.restore(); + getModuleInfo.restore(); + disableNode.restore(); + if (err) { + throw err; + } + res.body.should.have.property("name","node-red"); + res.body.should.have.property("nodes"); + res.body.nodes[0].should.have.property("enabled",false); + res.body.nodes[1].should.have.property("enabled",false); + + done(); + }); + }); + + describe('no-ops if a node in module already in the right state', function() { + function run(state,done) { + var node = {id:"123",enabled:state,types:['a']}; + var settingsAvailable = sinon.stub(settings,'available', function() { + return true; + }); + var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) { + return {name:"node-red", nodes:[node]}; + }); + var enableNode = sinon.stub(redNodes,'enableNode',function(id) { + node.enabled = true; + return node; + }); + var disableNode = sinon.stub(redNodes,'disableNode',function(id) { + node.enabled = false; + return node; + }); + + request(app) + .put('/nodes/node-red') + .send({enabled:state}) + .expect(200) + .end(function(err,res) { + settingsAvailable.restore(); + getModuleInfo.restore(); + var enableNodeCalled = enableNode.called; + var disableNodeCalled = disableNode.called; + enableNode.restore(); + disableNode.restore(); + if (err) { + throw err; + } + enableNodeCalled.should.be.false; + disableNodeCalled.should.be.false; + res.body.should.have.property("name","node-red"); + res.body.should.have.property("nodes"); + res.body.nodes[0].should.have.property("enabled",state); + + done(); + }); + } + it('already enabled', function(done) { + run(true,done); + }); + it('already disabled', function(done) { + run(false,done); + }); + }); + + describe('does not no-op if err on a node in module', function() { + function run(state,done) { + var node = {id:"123",enabled:state,types:['a'],err:"foo"}; + var settingsAvailable = sinon.stub(settings,'available', function() { + return true; + }); + var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) { + return {name:"node-red", nodes:[node]}; + }); + var enableNode = sinon.stub(redNodes,'enableNode',function(id) { + node.enabled = true; + return node; + }); + var disableNode = sinon.stub(redNodes,'disableNode',function(id) { + node.enabled = false; + return node; + }); + + request(app) + .put('/nodes/node-red') + .send({enabled:state}) + .expect(200) + .end(function(err,res) { + settingsAvailable.restore(); + getModuleInfo.restore(); + var enableNodeCalled = enableNode.called; + var disableNodeCalled = disableNode.called; + enableNode.restore(); + disableNode.restore(); + if (err) { + throw err; + } + enableNodeCalled.should.be.equal(state); + disableNodeCalled.should.be.equal(!state); + res.body.should.have.property("name","node-red"); + res.body.should.have.property("nodes"); + res.body.nodes[0].should.have.property("enabled",state); + + done(); + }); + } + it('already enabled', function(done) { + run(true,done); + }); + it('already disabled', function(done) { + run(false,done); + }); + }); });