diff --git a/red/api/index.js b/red/api/index.js index f0958b3b0..14aabc881 100644 --- a/red/api/index.js +++ b/red/api/index.js @@ -19,6 +19,7 @@ var util = require('util'); var ui = require("./ui"); var nodes = require("./nodes"); +var plugins = require("./plugins"); var flows = require("./flows"); var library = require("./library"); @@ -27,12 +28,12 @@ var settings = require("../settings"); var errorHandler = function(err,req,res,next) { //TODO: standardize json response res.send(400,err.toString()); -} +}; function init(adminApp) { - + adminApp.use(express.json()); - + library.init(adminApp); // Editor @@ -42,11 +43,11 @@ function init(adminApp) { adminApp.get("/settings",ui.settings); adminApp.use("/",ui.editor); } - + // Flows adminApp.get("/flows",flows.get); adminApp.post("/flows",flows.post); - + // Nodes adminApp.get("/nodes",nodes.getAll); adminApp.post("/nodes",nodes.post); @@ -54,17 +55,21 @@ function init(adminApp) { adminApp.get("/nodes/:id",nodes.get); adminApp.put("/nodes/:id",nodes.put); adminApp.delete("/nodes/:id",nodes.delete); - + + // Plugins + adminApp.get("/plugins",plugins.getAll); + adminApp.get("/plugins/:id",plugins.get); + // Library adminApp.post(new RegExp("/library/flows\/(.*)"),library.post); adminApp.get("/library/flows",library.getAll); adminApp.get(new RegExp("/library/flows\/(.*)"),library.get); - - + + // Error Handler adminApp.use(errorHandler); } module.exports = { init: init -} +}; diff --git a/red/api/plugins.js b/red/api/plugins.js new file mode 100644 index 000000000..bac6cfb4c --- /dev/null +++ b/red/api/plugins.js @@ -0,0 +1,32 @@ +/** + * Copyright 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var redNodes = require("../nodes"); + +module.exports = { + getAll: function(req,res) { + res.json(redNodes.getPluginList()); + }, + get: function(req,res) { + var id = req.params.id; + var result = redNodes.getPluginInfo(id); + if (result) { + res.send(result); + } else { + res.send(404); + } + } +}; diff --git a/red/nodes/index.js b/red/nodes/index.js index 36c63ce3f..a5ad0b6bd 100644 --- a/red/nodes/index.js +++ b/red/nodes/index.js @@ -28,14 +28,14 @@ function registerType(type,constructor,opts) { if (opts && opts.credentials) { credentials.register(type,opts.credentials); } - registry.registerType(type,constructor); + registry.registerType(type,constructor); } /** * Called from a Node's constructor function, invokes the super-class * constructor and attaches any credentials to the node. * @param node the node object being created - * @param def the instance definition for the node + * @param def the instance definition for the node */ function createNode(node,def) { Node.call(node,def); @@ -95,40 +95,42 @@ module.exports = { // Lifecycle init: init, load: registry.load, - + // Node registry createNode: createNode, getNode: flows.get, - + addNode: registry.addNode, removeNode: removeNode, - + addModule: registry.addModule, removeModule: removeModule, - + enableNode: registry.enableNode, disableNode: disableNode, - + // Node type registry registerType: registerType, getType: registry.get, getNodeInfo: registry.getNodeInfo, getNodeModuleInfo: registry.getNodeModuleInfo, + getPluginInfo: registry.getPluginInfo, getNodeList: registry.getNodeList, + getPluginList: registry.getPluginList, getNodeConfigs: registry.getNodeConfigs, getNodeConfig: registry.getNodeConfig, clearRegistry: registry.clear, cleanNodeList: registry.cleanNodeList, - + // Flow handling loadFlows: flows.load, stopFlows: flows.stopFlows, setFlows: flows.setFlows, getFlows: flows.getFlows, - + // Credentials addCredentials: credentials.add, getCredentials: credentials.get, deleteCredentials: credentials.delete -} +}; diff --git a/red/nodes/registry.js b/red/nodes/registry.js index f2073aff8..a6b3b4ba9 100644 --- a/red/nodes/registry.js +++ b/red/nodes/registry.js @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - + var util = require("util"); var when = require("when"); var whenNode = require('when/node'); var fs = require("fs"); var path = require("path"); -var crypto = require("crypto"); +var crypto = require("crypto"); var UglifyJS = require("uglify-js"); var events = require("../events"); @@ -33,7 +33,7 @@ function filterNodeInfo(n) { name: n.name, types: n.types, enabled: n.enabled - } + }; if (n.hasOwnProperty("loaded")) { r.loaded = n.loaded; } @@ -53,10 +53,10 @@ var registry = (function() { var nodeConstructors = {}; var nodeTypeToId = {}; var nodeModules = {}; - + function saveNodeList() { var nodeList = {}; - + for (var i in nodeConfigs) { if (nodeConfigs.hasOwnProperty(i)) { var nodeConfig = nodeConfigs[i]; @@ -75,7 +75,7 @@ var registry = (function() { return when.reject("Settings unavailable"); } } - + return { init: function() { if (settings.available()) { @@ -95,19 +95,19 @@ var registry = (function() { nodeList = []; nodeConfigCache = null; }, - + addNodeSet: function(id,set) { if (!set.err) { set.types.forEach(function(t) { nodeTypeToId[t] = id; }); } - + if (set.module) { nodeModules[set.module] = nodeModules[set.module]||{nodes:[]}; nodeModules[set.module].nodes.push(id); } - + nodeConfigs[id] = set; nodeList.push(id); nodeConfigCache = null; @@ -159,11 +159,39 @@ var registry = (function() { var list = []; for (var id in nodeConfigs) { if (nodeConfigs.hasOwnProperty(id)) { - list.push(filterNodeInfo(nodeConfigs[id])) + list.push(filterNodeInfo(nodeConfigs[id])); } } return list; }, + getPluginList: function() { + var list = []; + for (var plugin in nodeModules) { + if (nodeModules.hasOwnProperty(plugin)) { + var nodes = nodeModules[plugin].nodes; + var m = { + name: plugin, + nodes: [] + }; + for (var i = 0; i < nodes.length; ++i) { + m.nodes.push(filterNodeInfo(nodeConfigs[nodes[i]])); + } + list.push(m); + } + } + return list; + }, + getPluginInfo: function(plugin) { + var nodes = nodeModules[plugin].nodes; + var m = { + name: plugin, + nodes: [] + }; + for (var i = 0; i < nodes.length; ++i) { + m.nodes.push(filterNodeInfo(nodeConfigs[nodes[i]])); + } + return m; + }, registerNodeConstructor: function(type,constructor) { if (nodeConstructors[type]) { throw new Error(type+" already registered"); @@ -175,8 +203,8 @@ var registry = (function() { nodeConstructors[type] = constructor; events.emit("type-registered",type); }, - - + + /** * Gets all of the node template configs * @return all of the node templates in a single string @@ -201,7 +229,7 @@ var registry = (function() { } return nodeConfigCache; }, - + getNodeConfig: function(id) { var config = nodeConfigs[id]; if (config) { @@ -214,7 +242,7 @@ var registry = (function() { return null; } }, - + getNodeConstructor: function(type) { var config = nodeConfigs[nodeTypeToId[type]]; if (!config || (config.enabled && !config.err)) { @@ -222,7 +250,7 @@ var registry = (function() { } return null; }, - + clear: function() { nodeConfigCache = null; nodeConfigs = {}; @@ -230,20 +258,25 @@ var registry = (function() { nodeConstructors = {}; nodeTypeToId = {}; }, - + getTypeId: function(type) { return nodeTypeToId[type]; }, - + getModuleInfo: function(type) { return nodeModules[type]; }, - + enableNodeSet: function(id) { if (!settings.available()) { throw new Error("Settings unavailable"); } - var config = nodeConfigs[id]; + var config; + if (nodeTypeToId[id]) { + config = nodeConfigs[nodeTypeToId[id]]; + } else { + config = nodeConfigs[id]; + } if (config) { delete config.err; config.enabled = true; @@ -258,12 +291,17 @@ var registry = (function() { } return filterNodeInfo(config); }, - + disableNodeSet: function(id) { if (!settings.available()) { throw new Error("Settings unavailable"); } - var config = nodeConfigs[id]; + var config; + if (nodeTypeToId[id]) { + config = nodeConfigs[nodeTypeToId[id]]; + } else { + config = nodeConfigs[id]; + } if (config) { // TODO: persist setting config.enabled = false; @@ -274,9 +312,9 @@ var registry = (function() { } return filterNodeInfo(config); }, - + saveNodeList: saveNodeList, - + cleanNodeList: function() { var removed = false; for (var id in nodeConfigs) { @@ -291,7 +329,7 @@ var registry = (function() { saveNodeList(); } } - } + }; })(); @@ -330,14 +368,14 @@ function getNodeFiles(dir) { } } } - valid = valid && fs.existsSync(path.join(dir,fn.replace(/\.js$/,".html"))) - + valid = valid && fs.existsSync(path.join(dir,fn.replace(/\.js$/,".html"))); + if (valid) { result.push(path.join(dir,fn)); } } } else if (stats.isDirectory()) { - // Ignore /.dirs/, /lib/ /node_modules/ + // Ignore /.dirs/, /lib/ /node_modules/ if (!/^(\..*|lib|icons|node_modules|test)$/.test(fn)) { result = result.concat(getNodeFiles(path.join(dir,fn))); } else if (fn === "icons") { @@ -385,7 +423,7 @@ function scanTreeForNodesModules(moduleName) { } } catch(err) { } - + dir = up; up = path.resolve(path.join(dir,"..")); } @@ -451,7 +489,7 @@ function loadNodeConfig(file,module,name) { } var info = registry.getNodeInfo(id); - + var isEnabled = true; if (info) { @@ -460,38 +498,38 @@ function loadNodeConfig(file,module,name) { } isEnabled = info.enabled; } - + var node = { id: id, file: file, template: file.replace(/\.js$/,".html"), enabled: isEnabled, loaded:false - } - + }; + if (module) { node.name = module+":"+name; node.module = module; } else { - node.name = path.basename(file) + node.name = path.basename(file); } try { var content = fs.readFileSync(node.template,'utf8'); - + var types = []; - + var regExp = /\n\n\n\n

this should be filtered out

\n\n\n\n\n"); - + var nodeId = list[0].id; var nodeConfig = typeRegistry.getNodeConfig(nodeId); nodeConfig.should.equal("\n\n\n\n

this should be filtered out

\n"); @@ -290,7 +290,7 @@ describe('NodeRegistry', function() { done(e); }); }); - + it('stores the node list', function(done) { var settings = { nodesDir:[resourcesDir + "TestNode1",resourcesDir + "TestNode2",resourcesDir + "TestNode3"], @@ -303,39 +303,39 @@ describe('NodeRegistry', function() { typeRegistry.load("wontexist",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.Array.and.have.length(3); - + settingsSave.callCount.should.equal(1); settingsSave.firstCall.args[0].should.be.equal("nodes"); var savedList = settingsSave.firstCall.args[1]; - + savedList[list[0].id].name == list[0].name; savedList[list[1].id].name == list[1].name; savedList[list[2].id].name == list[2].name; - + savedList[list[0].id].should.not.have.property("err"); savedList[list[1].id].should.not.have.property("err"); savedList[list[2].id].should.not.have.property("err"); - + done(); }).catch(function(e) { done(e); }).finally(function() { settingsSave.restore(); }); - + }); - + it('allows nodes to be added by filename', function(done) { var settings = { available: function() { return true; }, set: function(s,v) {return when.resolve();}, get: function(s) { return null;} - } + } typeRegistry.init(settings); typeRegistry.load("wontexist",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; - + typeRegistry.addNode(resourcesDir + "TestNode1/TestNode1.js").then(function(node) { list = typeRegistry.getNodeList(); list[0].should.have.property("id"); @@ -343,20 +343,20 @@ describe('NodeRegistry', function() { list[0].should.have.property("types",["test-node-1"]); list[0].should.have.property("enabled",true); list[0].should.not.have.property("err"); - + node.should.be.an.Array.and.have.lengthOf(1); node.should.eql(list); - + done(); }).catch(function(e) { done(e); }); - + }).catch(function(e) { done(e); }); }); - + it('fails to add non-existent filename', function(done) { typeRegistry.init(settingsWithStorage); typeRegistry.load("wontexist",true).then(function(){ @@ -371,47 +371,149 @@ describe('NodeRegistry', function() { }).otherwise(function(e) { done(e); }); - + }).catch(function(e) { done(e); }); }); - + it('returns node info by type or id', function(done) { typeRegistry.init(settings); typeRegistry.load(resourcesDir + "TestNode1",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); - + var id = list[0].id; var type = list[0].types[0]; - + list[0].should.have.property("id"); list[0].should.have.property("name","TestNode1.js"); list[0].should.have.property("types",["test-node-1"]); list[0].should.have.property("enabled",true); list[0].should.not.have.property("err"); - + var info = typeRegistry.getNodeInfo(id); list[0].should.eql(info); var info2 = typeRegistry.getNodeInfo(type); list[0].should.eql(info2); - + done(); }).catch(function(e) { done(e); }); - + }); - - + + it('returns plugins list', function(done) { + var fs = require("fs"); + var path = require("path"); + + var pathJoin = (function() { + var _join = path.join; + return sinon.stub(path,"join",function() { + if (arguments.length == 3 && arguments[2] == "package.json") { + return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1],arguments[2]); + } + if (arguments.length == 2 && arguments[1] == "TestNodeModule") { + return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1]); + } + return _join.apply(this,arguments); + }); + })(); + + var readdirSync = (function() { + var originalReaddirSync = fs.readdirSync; + var callCount = 0; + return sinon.stub(fs,"readdirSync",function(dir) { + var result = []; + if (callCount == 1) { + result = originalReaddirSync(resourcesDir + "TestNodeModule" + path.sep + "node_modules"); + } + callCount++; + return result; + }); + })(); + typeRegistry.init(settingsWithStorage); + typeRegistry.load("wontexist",true).then(function(){ + + typeRegistry.addModule("TestNodeModule").then(function() { + var list = typeRegistry.getPluginList(); + list.should.be.an.Array.and.have.lengthOf(1); + list[0].should.have.property("name", "TestNodeModule"); + list[0].should.have.property("nodes"); + list[0].nodes.should.be.an.Array.and.have.lengthOf(2); + + done(); + }).catch(function(e) { + done(e); + }); + + }).catch(function(e) { + done(e); + }).finally(function() { + readdirSync.restore(); + pathJoin.restore(); + }); + }); + + it('returns plugin info', function(done) { + var fs = require("fs"); + var path = require("path"); + + var pathJoin = (function() { + var _join = path.join; + return sinon.stub(path,"join",function() { + if (arguments.length == 3 && arguments[2] == "package.json") { + return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1],arguments[2]); + } + if (arguments.length == 2 && arguments[1] == "TestNodeModule") { + return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1]); + } + return _join.apply(this,arguments); + }); + })(); + + var readdirSync = (function() { + var originalReaddirSync = fs.readdirSync; + var callCount = 0; + return sinon.stub(fs,"readdirSync",function(dir) { + var result = []; + if (callCount == 1) { + result = originalReaddirSync(resourcesDir + "TestNodeModule" + path.sep + "node_modules"); + } + callCount++; + return result; + }); + })(); + typeRegistry.init(settingsWithStorage); + typeRegistry.load("wontexist",true).then(function(){ + + typeRegistry.addModule("TestNodeModule").then(function(nodes) { + var list = typeRegistry.getPluginList(); + + var plugin = typeRegistry.getPluginInfo(list[0].name); + plugin.should.have.property("name", list[0].name); + plugin.should.have.property("nodes", nodes); + done(); + }).catch(function(e) { + done(e); + }); + + }).catch(function(e) { + done(e); + }).finally(function() { + readdirSync.restore(); + pathJoin.restore(); + }); + }); + it('rejects adding duplicate nodes', function(done) { typeRegistry.init(settingsWithStorage); typeRegistry.load(resourcesDir + "TestNode1",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); - + typeRegistry.addNode({file:resourcesDir + "TestNode1" + path.sep + "TestNode1.js"}).then(function(node) { done(new Error("duplicate node loaded")); }).otherwise(function(e) { @@ -419,12 +521,12 @@ describe('NodeRegistry', function() { list.should.be.an.Array.and.have.lengthOf(1); done(); }); - + }).catch(function(e) { done(e); }); }); - + it('removes nodes from the registry', function(done) { typeRegistry.init(settingsWithStorage); typeRegistry.load(resourcesDir + "TestNode1",true).then(function() { @@ -437,33 +539,33 @@ describe('NodeRegistry', function() { list[0].should.have.property("loaded",true); typeRegistry.getNodeConfigs().length.should.be.greaterThan(0); - + var info = typeRegistry.removeNode(list[0].id); - + info.should.have.property("id",list[0].id); info.should.have.property("enabled",false); info.should.have.property("loaded",false); - + typeRegistry.getNodeList().should.be.an.Array.and.be.empty; typeRegistry.getNodeConfigs().length.should.equal(0); - + var nodeConstructor = typeRegistry.get("test-node-1"); (typeof nodeConstructor).should.be.equal("undefined"); - - + + done(); }).catch(function(e) { done(e); }); }); - + it('rejects removing unknown nodes from the registry', function(done) { typeRegistry.init(settings); typeRegistry.load("wontexist",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; - + /*jshint immed: false */ (function() { typeRegistry.removeNode("1234"); @@ -474,11 +576,11 @@ describe('NodeRegistry', function() { done(e); }); }); - + it('scans the node_modules path for node files', function(done) { var fs = require("fs"); var path = require("path"); - + var eventEmitSpy = sinon.spy(events,"emit"); var pathJoin = (function() { var _join = path.join; @@ -492,7 +594,7 @@ describe('NodeRegistry', function() { return _join.apply(this,arguments); }); })(); - + var readdirSync = (function() { var originalReaddirSync = fs.readdirSync; var callCount = 0; @@ -505,7 +607,7 @@ describe('NodeRegistry', function() { return result; }); })(); - + typeRegistry.init(settings); typeRegistry.load("wontexist",false).then(function(){ var list = typeRegistry.getNodeList(); @@ -521,17 +623,17 @@ describe('NodeRegistry', function() { list[1].should.have.property("types",["test-node-mod-2"]); list[1].should.have.property("enabled",true); list[1].should.have.property("err"); - - + + eventEmitSpy.callCount.should.equal(2); - + eventEmitSpy.firstCall.args[0].should.be.equal("node-icon-dir"); eventEmitSpy.firstCall.args[1].should.be.equal( resourcesDir + "TestNodeModule" + path.sep+ "node_modules" + path.sep + "TestNodeModule" + path.sep + "icons"); eventEmitSpy.secondCall.args[0].should.be.equal("type-registered"); eventEmitSpy.secondCall.args[1].should.be.equal("test-node-mod-1"); - + done(); }).catch(function(e) { done(e); @@ -541,7 +643,7 @@ describe('NodeRegistry', function() { eventEmitSpy.restore(); }); }); - + it('allows nodes to be added by module name', function(done) { var fs = require("fs"); var path = require("path"); @@ -558,7 +660,7 @@ describe('NodeRegistry', function() { return _join.apply(this,arguments); }); })(); - + var readdirSync = (function() { var originalReaddirSync = fs.readdirSync; var callCount = 0; @@ -575,7 +677,7 @@ describe('NodeRegistry', function() { typeRegistry.load("wontexist",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; - + typeRegistry.addModule("TestNodeModule").then(function(node) { list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(2); @@ -590,14 +692,14 @@ describe('NodeRegistry', function() { list[1].should.have.property("types",["test-node-mod-2"]); list[1].should.have.property("enabled",true); list[1].should.have.property("err"); - + node.should.eql(list); - + done(); }).catch(function(e) { done(e); }); - + }).catch(function(e) { done(e); }).finally(function() { @@ -605,8 +707,8 @@ describe('NodeRegistry', function() { pathJoin.restore(); }); }); - - + + it('rejects adding duplicate node modules', function(done) { var fs = require("fs"); var path = require("path"); @@ -623,7 +725,7 @@ describe('NodeRegistry', function() { return _join.apply(this,arguments); }); })(); - + var readdirSync = (function() { var originalReaddirSync = fs.readdirSync; var callCount = 0; @@ -653,26 +755,26 @@ describe('NodeRegistry', function() { pathJoin.restore(); }); }); - - + + it('fails to add non-existent module name', function(done) { typeRegistry.init(settingsWithStorage); typeRegistry.load("wontexist",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; - + typeRegistry.addModule("DoesNotExistModule").then(function(node) { done(new Error("ENOENT not thrown")); }).otherwise(function(e) { e.code.should.eql("MODULE_NOT_FOUND"); done(); }); - + }).catch(function(e) { done(e); }); }); - + it('removes nodes from the registry by module', function(done) { var fs = require("fs"); var path = require("path"); @@ -689,7 +791,7 @@ describe('NodeRegistry', function() { return _join.apply(this,arguments); }); })(); - + var readdirSync = (function() { var originalReaddirSync = fs.readdirSync; var callCount = 0; @@ -708,14 +810,14 @@ describe('NodeRegistry', function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(2); var res = typeRegistry.removeModule("TestNodeModule"); - + res.should.be.an.Array.and.have.lengthOf(2); res[0].should.have.a.property("id",list[0].id); res[1].should.have.a.property("id",list[1].id); - + list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; - + done(); }).catch(function(e) { done(e); @@ -723,29 +825,29 @@ describe('NodeRegistry', function() { readdirSync.restore(); pathJoin.restore(); }); - + }); - + it('fails to remove non-existent module name', function(done) { typeRegistry.init(settings); typeRegistry.load("wontexist",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; - + /*jshint immed: false */ (function() { typeRegistry.removeModule("DoesNotExistModule"); }).should.throw(); - + done(); - + }).catch(function(e) { done(e); }); }); - - - it('allows nodes to be enabled and disabled', function(done) { + + + it('allows nodes to be enabled and disabled by hex-id', function(done) { typeRegistry.init(settingsWithStorage); typeRegistry.load(resourcesDir+path.sep+"TestNode1",true).then(function() { var list = typeRegistry.getNodeList(); @@ -753,28 +855,28 @@ describe('NodeRegistry', function() { list[0].should.have.property("id"); list[0].should.have.property("name","TestNode1.js"); list[0].should.have.property("enabled",true); - + var nodeConfig = typeRegistry.getNodeConfigs(); nodeConfig.length.should.be.greaterThan(0); - + var info = typeRegistry.disableNode(list[0].id); info.should.have.property("id",list[0].id); info.should.have.property("enabled",false); - + var list2 = typeRegistry.getNodeList(); list2.should.be.an.Array.and.have.lengthOf(1); list2[0].should.have.property("enabled",false); - + typeRegistry.getNodeConfigs().length.should.equal(0); - + var info2 = typeRegistry.enableNode(list[0].id); info2.should.have.property("id",list[0].id); info2.should.have.property("enabled",true); - + var list3 = typeRegistry.getNodeList(); list3.should.be.an.Array.and.have.lengthOf(1); list3[0].should.have.property("enabled",true); - + var nodeConfig2 = typeRegistry.getNodeConfigs(); nodeConfig2.should.eql(nodeConfig); @@ -783,23 +885,66 @@ describe('NodeRegistry', function() { done(e); }); }); - + + it('allows nodes to be enabled and disabled by node-type', function(done) { + typeRegistry.init(settingsWithStorage); + typeRegistry.load(resourcesDir+path.sep+"TestNode1",true).then(function() { + var list = typeRegistry.getNodeList(); + + list.should.be.an.Array.and.have.lengthOf(1); + list[0].should.have.property("id"); + list[0].should.have.property("name","TestNode1.js"); + list[0].should.have.property("types",["test-node-1"]); + list[0].should.have.property("enabled",true); + + var nodeConfig = typeRegistry.getNodeConfigs(); + nodeConfig.length.should.be.greaterThan(0); + + var info = typeRegistry.disableNode(list[0].types[0]); + info.should.have.property("id",list[0].id); + info.should.have.property("types",list[0].types); + info.should.have.property("enabled",false); + + var list2 = typeRegistry.getNodeList(); + list2.should.be.an.Array.and.have.lengthOf(1); + list2[0].should.have.property("enabled",false); + + typeRegistry.getNodeConfigs().length.should.equal(0); + + var info2 = typeRegistry.enableNode(list[0].types[0]); + info2.should.have.property("id",list[0].id); + info2.should.have.property("types",list[0].types); + info2.should.have.property("enabled",true); + + var list3 = typeRegistry.getNodeList(); + list3.should.be.an.Array.and.have.lengthOf(1); + list3[0].should.have.property("enabled",true); + + var nodeConfig2 = typeRegistry.getNodeConfigs(); + nodeConfig2.should.eql(nodeConfig); + + done(); + }).catch(function(e) { + done(e); + }); + }); + it('fails to enable/disable non-existent nodes', function(done) { typeRegistry.init(settings); typeRegistry.load("wontexist",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; - + /*jshint immed: false */ (function() { typeRegistry.disableNode("123"); }).should.throw(); - + /*jshint immed: false */ (function() { typeRegistry.enableNode("123"); }).should.throw(); - + done(); }).catch(function(e) { done(e);