From e1dd8cf2abaf4d8daae16c1a6682e6227fa19b93 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 8 Apr 2015 20:17:24 +0100 Subject: [PATCH] Restore node order in palette following async changes The move to async loading of node files led to them appearing out of order in the palette. --- red/nodes/index.js | 1 - red/nodes/registry/loader.js | 43 ++++++++++++++++++++-- red/nodes/registry/registry.js | 37 ++++++++++--------- red/server.js | 4 +-- test/red/nodes/registry/index_spec.js | 46 +++++++++++++++--------- test/red/nodes/registry/registry_spec.js | 40 ++++++++++++++++++--- 6 files changed, 127 insertions(+), 44 deletions(-) diff --git a/red/nodes/index.js b/red/nodes/index.js index c63572aa0..118d5cac4 100644 --- a/red/nodes/index.js +++ b/red/nodes/index.js @@ -127,7 +127,6 @@ module.exports = { getNodeList: registry.getNodeList, getModuleInfo: registry.getModuleInfo, - getModuleList: registry.getModuleList, getNodeConfigs: registry.getNodeConfigs, getNodeConfig: registry.getNodeConfig, diff --git a/red/nodes/registry/loader.js b/red/nodes/registry/loader.js index 9a9ae4d91..738f49622 100644 --- a/red/nodes/registry/loader.js +++ b/red/nodes/registry/loader.js @@ -31,6 +31,11 @@ function init(_settings) { } function load(defaultNodesDir,disableNodePathScan) { + // To skip node scan, the following line will use the stored node list. + // We should expose that as an option at some point, although the + // performance gains are minimal. + //return loadNodeFiles(registry.getModuleList()); + var nodeFiles = localfilesystem.getNodeFiles(defaultNodesDir,disableNodePathScan); return loadNodeFiles(nodeFiles); } @@ -41,9 +46,31 @@ function loadNodeFiles(nodeFiles) { /* istanbul ignore else */ if (nodeFiles.hasOwnProperty(module)) { if (!registry.getModuleInfo(module)) { + var first = true; for (var node in nodeFiles[module].nodes) { /* istanbul ignore else */ if (nodeFiles[module].nodes.hasOwnProperty(node)) { + if (module != "node-red" && first) { + // Check the module directory exists + first = false; + var fn = nodeFiles[module].nodes[node].file; + var parts = fn.split("/"); + var i = parts.length-1; + for (;i>=0;i--) { + if (parts[i] == "node_modules") { + break; + } + } + var moduleFn = parts.slice(0,i+2).join("/"); + + try { + var stat = fs.statSync(moduleFn); + } catch(err) { + // Module not found, don't attempt to load its nodes + break; + } + } + try { promises.push(loadNodeConfig(nodeFiles[module].nodes[node])) } catch(err) { @@ -55,7 +82,10 @@ function loadNodeFiles(nodeFiles) { } } return when.settle(promises).then(function(results) { - var nodes = results.map(function(r) { return r.value; }); + var nodes = results.map(function(r) { + registry.addNodeSet(r.value.id,r.value,r.value.version); + return r.value; + }); return loadNodeSetList(nodes); }); } @@ -84,15 +114,23 @@ function loadNodeConfig(fileInfo) { file: file, template: file.replace(/\.js$/,".html"), enabled: isEnabled, - loaded:false + loaded:false, + version: version }; + if (fileInfo.hasOwnProperty("types")) { + node.types = fileInfo.types; + } fs.readFile(node.template,'utf8', function(err,content) { if (err) { node.types = []; if (err.code === 'ENOENT') { + if (!node.types) { + node.types = []; + } node.err = "Error: "+file+" does not exist"; } else { + node.types = []; node.err = err.toString(); } } else { @@ -116,7 +154,6 @@ function loadNodeConfig(fileInfo) { } } } - registry.addNodeSet(id,node,version); resolve(node); }); }); diff --git a/red/nodes/registry/registry.js b/red/nodes/registry/registry.js index 7f801f30d..cf2c3f70c 100644 --- a/red/nodes/registry/registry.js +++ b/red/nodes/registry/registry.js @@ -158,7 +158,6 @@ function addNodeSet(id,set,version) { nodeTypeToId[t] = id; }); } - moduleNodes[set.module] = moduleNodes[set.module]||[]; moduleNodes[set.module].push(set.name); @@ -262,14 +261,16 @@ function getNodeList(filter) { } function getModuleList() { - var list = []; - for (var module in moduleNodes) { - /* istanbul ignore else */ - if (moduleNodes.hasOwnProperty(module)) { - list.push(registry.getModuleInfo(module)); - } - } - return list; + //var list = []; + //for (var module in moduleNodes) { + // /* istanbul ignore else */ + // if (moduleNodes.hasOwnProperty(module)) { + // list.push(registry.getModuleInfo(module)); + // } + //} + //return list; + return moduleConfigs; + } function getModuleInfo(module) { @@ -437,16 +438,18 @@ function cleanModuleList() { } } } - } else if (moduleConfigs[mod] && !moduleNodes[mod]) { - // For node modules, look for missing ones - for (node in nodes) { - /* istanbul ignore else */ - if (nodes.hasOwnProperty(node)) { - removeNode(mod+"/"+node); - removed = true; + } else { + if (moduleConfigs[mod] && !moduleNodes[mod]) { + // For node modules, look for missing ones + for (node in nodes) { + /* istanbul ignore else */ + if (nodes.hasOwnProperty(node)) { + removeNode(mod+"/"+node); + removed = true; + } } + delete moduleConfigs[mod]; } - delete moduleConfigs[mod]; } } } diff --git a/red/server.js b/red/server.js index cbdaf3af6..667778658 100644 --- a/red/server.js +++ b/red/server.js @@ -63,7 +63,7 @@ function start() { log.info("Node.js version: "+process.version); log.info("Loading palette nodes"); redNodes.init(settings,storage,app); - redNodes.load().then(function() { + return redNodes.load().then(function() { var i; var nodeErrors = redNodes.getNodeList(function(n) { return n.err!=null;}); @@ -106,10 +106,10 @@ function start() { } log.info("Settings file : "+settings.settingsFile); redNodes.loadFlows(); + comms.start(); }).otherwise(function(err) { console.log(err); }); - comms.start(); }); } diff --git a/test/red/nodes/registry/index_spec.js b/test/red/nodes/registry/index_spec.js index 9be323a39..c06cb9a4c 100644 --- a/test/red/nodes/registry/index_spec.js +++ b/test/red/nodes/registry/index_spec.js @@ -307,22 +307,34 @@ describe('red/nodes/registry/index', function() { typeRegistry.load("wontexist",true).then(function() { var nodeList = typeRegistry.getNodeList(); var moduleList = typeRegistry.getModuleList(); + Object.keys(moduleList).should.have.length(1); + moduleList.should.have.a.property("node-red"); + Object.keys(moduleList["node-red"].nodes).should.have.length(3); + nodeList.should.be.Array.and.have.length(3); - moduleList.should.be.Array.and.have.length(1); settingsSave.callCount.should.equal(1); settingsSave.firstCall.args[0].should.be.equal("nodes"); var savedList = settingsSave.firstCall.args[1]; + + moduleList['node-red'].nodes['TestNode1'].should.have.a.property("id","node-red/TestNode1"); + moduleList['node-red'].nodes['TestNode1'].should.have.a.property("name","TestNode1"); + moduleList['node-red'].nodes['TestNode1'].should.have.a.property("module","node-red"); + moduleList['node-red'].nodes['TestNode1'].should.have.a.property("file"); + moduleList['node-red'].nodes['TestNode1'].should.have.a.property("enabled",true); + moduleList['node-red'].nodes['TestNode1'].should.have.a.property("types"); + moduleList['node-red'].nodes['TestNode1'].should.have.a.property("config"); + moduleList['node-red'].nodes['TestNode1'].should.have.a.property("template"); - savedList[moduleList[0].name].name.should.equal(moduleList[0].name); - - savedList[moduleList[0].name].nodes[moduleList[0].nodes[0].name].name.should.equal(moduleList[0].nodes[0].name); - savedList[moduleList[0].name].nodes[moduleList[0].nodes[1].name].name.should.equal(moduleList[0].nodes[1].name); - savedList[moduleList[0].name].nodes[moduleList[0].nodes[2].name].name.should.equal(moduleList[0].nodes[2].name); - - savedList[moduleList[0].name].nodes[moduleList[0].nodes[0].name].should.not.have.property("err"); - savedList[moduleList[0].name].nodes[moduleList[0].nodes[1].name].should.not.have.property("err"); - savedList[moduleList[0].name].nodes[moduleList[0].nodes[2].name].should.not.have.property("err"); + savedList['node-red'].nodes['TestNode1'].should.not.have.a.property("id"); + savedList['node-red'].nodes['TestNode1'].should.have.a.property("name",moduleList['node-red'].nodes['TestNode1'].name); + savedList['node-red'].nodes['TestNode1'].should.have.a.property("module",moduleList['node-red'].nodes['TestNode1'].module); + savedList['node-red'].nodes['TestNode1'].should.have.a.property("file",moduleList['node-red'].nodes['TestNode1'].file); + savedList['node-red'].nodes['TestNode1'].should.have.a.property("enabled",moduleList['node-red'].nodes['TestNode1'].enabled); + savedList['node-red'].nodes['TestNode1'].should.have.a.property("types",moduleList['node-red'].nodes['TestNode1'].types); + savedList['node-red'].nodes['TestNode1'].should.not.have.a.property("config"); + savedList['node-red'].nodes['TestNode1'].should.not.have.a.property("template"); + done(); }).catch(function(e) { @@ -415,10 +427,12 @@ describe('red/nodes/registry/index', function() { typeRegistry.addModule("TestNodeModule").then(function() { var list = typeRegistry.getModuleList(); - 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); + Object.keys(list).should.have.length(1); + list.should.have.a.property("TestNodeModule"); + Object.keys(list["TestNodeModule"].nodes).should.have.length(2); + + list["TestNodeModule"].nodes["TestNodeMod1"].should.have.property("name", "TestNodeMod1"); + list["TestNodeModule"].nodes["TestNodeMod2"].should.have.property("name", "TestNodeMod2"); done(); }).catch(function(e) { @@ -656,8 +670,8 @@ describe('red/nodes/registry/index', function() { var modules = typeRegistry.getModuleList(); - modules[0].should.have.property("name","TestNodeModule"); - modules[0].should.have.property("version","0.0.1"); + modules.should.have.property("TestNodeModule"); + modules["TestNodeModule"].should.have.property("version","0.0.1"); done(); }).catch(function(e) { diff --git a/test/red/nodes/registry/registry_spec.js b/test/red/nodes/registry/registry_spec.js index 1aac149c2..1669b54e4 100644 --- a/test/red/nodes/registry/registry_spec.js +++ b/test/red/nodes/registry/registry_spec.js @@ -117,12 +117,26 @@ describe("red/nodes/registry/registry",function() { typeRegistry.init(settings); typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.have.lengthOf(0); + typeRegistry.getModuleList().should.eql({}); typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); typeRegistry.getNodeList().should.have.lengthOf(1); - typeRegistry.getModuleList().should.have.lengthOf(1); + var moduleList = typeRegistry.getModuleList(); + moduleList.should.have.a.property("test-module"); + moduleList["test-module"].should.have.a.property("name","test-module"); + moduleList["test-module"].should.have.a.property("version","0.0.1"); + moduleList["test-module"].should.have.a.property("nodes"); + moduleList["test-module"].nodes.should.have.a.property("test-name"); + + moduleList["test-module"].nodes["test-name"].should.eql({ + id: 'test-module/test-name', + module: 'test-module', + name: 'test-name', + enabled: true, + loaded: false, + types: [ 'test-a', 'test-b' ] + }); }); @@ -130,20 +144,36 @@ describe("red/nodes/registry/registry",function() { typeRegistry.init(settings); typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.have.lengthOf(0); + typeRegistry.getModuleList().should.eql({}); typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); + typeRegistry.getNodeList().should.have.lengthOf(1); + var moduleList = typeRegistry.getModuleList(); + Object.keys(moduleList).should.have.a.lengthOf(1); + moduleList.should.have.a.property("test-module"); + moduleList["test-module"].should.have.a.property("name","test-module"); + moduleList["test-module"].should.have.a.property("version","0.0.1"); + moduleList["test-module"].should.have.a.property("nodes"); + + Object.keys(moduleList["test-module"].nodes).should.have.a.lengthOf(1); + moduleList["test-module"].nodes.should.have.a.property("test-name"); + + typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2); typeRegistry.getNodeList().should.have.lengthOf(2); - typeRegistry.getModuleList().should.have.lengthOf(1); + moduleList = typeRegistry.getModuleList(); + Object.keys(moduleList).should.have.a.lengthOf(1); + Object.keys(moduleList["test-module"].nodes).should.have.a.lengthOf(2); + moduleList["test-module"].nodes.should.have.a.property("test-name"); + moduleList["test-module"].nodes.should.have.a.property("test-name-2"); }); it('doesnt add node set types if node set has an error', function() { typeRegistry.init(settings); typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.have.lengthOf(0); + typeRegistry.getModuleList().should.eql({}); typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1");