mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Initial plugin runtime api implementation
This commit is contained in:
parent
bebebaa3dd
commit
a006b52052
@ -22,6 +22,7 @@ var flow = require("./flow");
|
|||||||
var context = require("./context");
|
var context = require("./context");
|
||||||
var auth = require("../auth");
|
var auth = require("../auth");
|
||||||
var info = require("./settings");
|
var info = require("./settings");
|
||||||
|
var plugins = require("./plugins");
|
||||||
|
|
||||||
var apiUtil = require("../util");
|
var apiUtil = require("../util");
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ module.exports = {
|
|||||||
nodes.init(runtimeAPI);
|
nodes.init(runtimeAPI);
|
||||||
context.init(runtimeAPI);
|
context.init(runtimeAPI);
|
||||||
info.init(settings,runtimeAPI);
|
info.init(settings,runtimeAPI);
|
||||||
|
plugins.init(runtimeAPI);
|
||||||
|
|
||||||
var needsPermission = auth.needsPermission;
|
var needsPermission = auth.needsPermission;
|
||||||
|
|
||||||
@ -80,6 +82,10 @@ module.exports = {
|
|||||||
|
|
||||||
adminApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler);
|
adminApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler);
|
||||||
|
|
||||||
|
// Plugins
|
||||||
|
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
|
||||||
|
adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, apiUtil.errorHandler);
|
||||||
|
|
||||||
return adminApp;
|
return adminApp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
packages/node_modules/@node-red/editor-api/lib/admin/plugins.js
vendored
Normal file
38
packages/node_modules/@node-red/editor-api/lib/admin/plugins.js
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
var apiUtils = require("../util");
|
||||||
|
|
||||||
|
var runtimeAPI;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
init: function(_runtimeAPI) {
|
||||||
|
runtimeAPI = _runtimeAPI;
|
||||||
|
},
|
||||||
|
getAll: function(req,res) {
|
||||||
|
var opts = {
|
||||||
|
user: req.user,
|
||||||
|
req: apiUtils.getRequestLogObject(req)
|
||||||
|
}
|
||||||
|
if (req.get("accept") == "application/json") {
|
||||||
|
runtimeAPI.plugins.getPluginList(opts).then(function(list) {
|
||||||
|
res.json(list);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
|
||||||
|
runtimeAPI.plugins.getPluginConfigs(opts).then(function(configs) {
|
||||||
|
res.send(configs);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getCatalogs: function(req,res) {
|
||||||
|
var opts = {
|
||||||
|
user: req.user,
|
||||||
|
lang: req.query.lng,
|
||||||
|
req: apiUtils.getRequestLogObject(req)
|
||||||
|
}
|
||||||
|
runtimeAPI.plugins.getPluginCatalogs(opts).then(function(result) {
|
||||||
|
res.json(result);
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
apiUtils.rejectHandler(req,res,err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
@ -38,6 +38,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event": {
|
"event": {
|
||||||
|
"loadPlugins": "Loading Plugins",
|
||||||
"loadPalette": "Loading Palette",
|
"loadPalette": "Loading Palette",
|
||||||
"loadNodeCatalogs": "Loading Node catalogs",
|
"loadNodeCatalogs": "Loading Node catalogs",
|
||||||
"loadNodes": "Loading Nodes __count__",
|
"loadNodes": "Loading Nodes __count__",
|
||||||
|
@ -108,6 +108,31 @@ RED.i18n = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
loadPluginCatalogs: function(done) {
|
||||||
|
var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
|
||||||
|
var toLoad = languageList.length;
|
||||||
|
|
||||||
|
languageList.forEach(function(lang) {
|
||||||
|
$.ajax({
|
||||||
|
headers: {
|
||||||
|
"Accept":"application/json"
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
url: apiRootUrl+'plugins/messages?lng='+lang,
|
||||||
|
success: function(data) {
|
||||||
|
var namespaces = Object.keys(data);
|
||||||
|
namespaces.forEach(function(ns) {
|
||||||
|
i18n.addResourceBundle(lang,ns,data[ns]);
|
||||||
|
});
|
||||||
|
toLoad--;
|
||||||
|
if (toLoad === 0) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -15,19 +15,66 @@
|
|||||||
**/
|
**/
|
||||||
var RED = (function() {
|
var RED = (function() {
|
||||||
|
|
||||||
function appendNodeConfig(nodeConfig,done) {
|
|
||||||
|
function loadPluginList() {
|
||||||
|
loader.reportProgress(RED._("event.loadPlugins"), 10)
|
||||||
|
$.ajax({
|
||||||
|
headers: {
|
||||||
|
"Accept":"application/json"
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
url: 'plugins',
|
||||||
|
success: function(data) {
|
||||||
|
console.log(data);
|
||||||
|
loader.reportProgress(RED._("event.loadPlugins"), 13)
|
||||||
|
RED.i18n.loadPluginCatalogs(function() {
|
||||||
|
loadPlugins(function() {
|
||||||
|
loadNodeList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function loadPlugins(done) {
|
||||||
|
loader.reportProgress(RED._("event.loadPlugins",{count:""}), 17)
|
||||||
|
var lang = localStorage.getItem("editor-language")||i18n.detectLanguage();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
headers: {
|
||||||
|
"Accept":"text/html",
|
||||||
|
"Accept-Language": lang
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
url: 'plugins',
|
||||||
|
success: function(data) {
|
||||||
|
var configs = data.trim().split(/(?=<!-- --- \[red-plugin:\S+\] --- -->)/);
|
||||||
|
var totalCount = configs.length;
|
||||||
|
var stepConfig = function() {
|
||||||
|
// loader.reportProgress(RED._("event.loadNodes",{count:(totalCount-configs.length)+"/"+totalCount}), 30 + ((totalCount-configs.length)/totalCount)*40 )
|
||||||
|
if (configs.length === 0) {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
var config = configs.shift();
|
||||||
|
appendPluginConfig(config,stepConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stepConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendConfig(config, moduleIdMatch, targetContainer, done) {
|
||||||
done = done || function(){};
|
done = done || function(){};
|
||||||
var m = /<!-- --- \[red-module:(\S+)\] --- -->/.exec(nodeConfig.trim());
|
|
||||||
var moduleId;
|
var moduleId;
|
||||||
if (m) {
|
if (moduleIdMatch) {
|
||||||
moduleId = m[1];
|
moduleId = moduleIdMatch[1];
|
||||||
} else {
|
} else {
|
||||||
moduleId = "unknown";
|
moduleId = "unknown";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var hasDeferred = false;
|
|
||||||
|
|
||||||
var nodeConfigEls = $("<div>"+nodeConfig+"</div>");
|
var hasDeferred = false;
|
||||||
|
var nodeConfigEls = $("<div>"+config+"</div>");
|
||||||
var scripts = nodeConfigEls.find("script");
|
var scripts = nodeConfigEls.find("script");
|
||||||
var scriptCount = scripts.length;
|
var scriptCount = scripts.length;
|
||||||
scripts.each(function(i,el) {
|
scripts.each(function(i,el) {
|
||||||
@ -38,14 +85,14 @@ var RED = (function() {
|
|||||||
newScript.onload = function() {
|
newScript.onload = function() {
|
||||||
scriptCount--;
|
scriptCount--;
|
||||||
if (scriptCount === 0) {
|
if (scriptCount === 0) {
|
||||||
$("#red-ui-editor-node-configs").append(nodeConfigEls);
|
$(targetContainer).append(nodeConfigEls);
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($(el).attr('type') === "module") {
|
if ($(el).attr('type') === "module") {
|
||||||
newScript.type = "module";
|
newScript.type = "module";
|
||||||
}
|
}
|
||||||
$("#red-ui-editor-node-configs").append(newScript);
|
$(targetContainer).append(newScript);
|
||||||
newScript.src = RED.settings.apiRootUrl+srcUrl;
|
newScript.src = RED.settings.apiRootUrl+srcUrl;
|
||||||
hasDeferred = true;
|
hasDeferred = true;
|
||||||
} else {
|
} else {
|
||||||
@ -61,7 +108,7 @@ var RED = (function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!hasDeferred) {
|
if (!hasDeferred) {
|
||||||
$("#red-ui-editor-node-configs").append(nodeConfigEls);
|
$(targetContainer).append(nodeConfigEls);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
@ -73,6 +120,23 @@ var RED = (function() {
|
|||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function appendPluginConfig(pluginConfig,done) {
|
||||||
|
appendConfig(
|
||||||
|
pluginConfig,
|
||||||
|
/<!-- --- \[red-plugin:(\S+)\] --- -->/.exec(pluginConfig.trim()),
|
||||||
|
"#red-ui-editor-plugin-configs",
|
||||||
|
done
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendNodeConfig(nodeConfig,done) {
|
||||||
|
appendConfig(
|
||||||
|
nodeConfig,
|
||||||
|
/<!-- --- \[red-module:(\S+)\] --- -->/.exec(nodeConfig.trim()),
|
||||||
|
"#red-ui-editor-node-configs",
|
||||||
|
done
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function loadNodeList() {
|
function loadNodeList() {
|
||||||
loader.reportProgress(RED._("event.loadPalette"), 20)
|
loader.reportProgress(RED._("event.loadPalette"), 20)
|
||||||
@ -580,7 +644,7 @@ var RED = (function() {
|
|||||||
|
|
||||||
RED.actions.add("core:show-about", showAbout);
|
RED.actions.add("core:show-about", showAbout);
|
||||||
|
|
||||||
loadNodeList();
|
loadPluginList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -596,6 +660,7 @@ var RED = (function() {
|
|||||||
'<div id="red-ui-sidebar"></div>'+
|
'<div id="red-ui-sidebar"></div>'+
|
||||||
'<div id="red-ui-sidebar-separator"></div>'+
|
'<div id="red-ui-sidebar-separator"></div>'+
|
||||||
'</div>').appendTo(options.target);
|
'</div>').appendTo(options.target);
|
||||||
|
$('<div id="red-ui-editor-plugin-configs"></div>').appendTo(options.target);
|
||||||
$('<div id="red-ui-editor-node-configs"></div>').appendTo(options.target);
|
$('<div id="red-ui-editor-node-configs"></div>').appendTo(options.target);
|
||||||
$('<div id="red-ui-full-shade" class="hide"></div>').appendTo(options.target);
|
$('<div id="red-ui-full-shade" class="hide"></div>').appendTo(options.target);
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ var registry = require("./registry");
|
|||||||
var loader = require("./loader");
|
var loader = require("./loader");
|
||||||
var installer = require("./installer");
|
var installer = require("./installer");
|
||||||
var library = require("./library");
|
var library = require("./library");
|
||||||
|
var plugins = require("./plugins");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise the registry with a reference to a runtime object
|
* Initialise the registry with a reference to a runtime object
|
||||||
@ -40,6 +41,7 @@ function init(runtime) {
|
|||||||
// the util module it. The Util module is responsible for constructing the
|
// the util module it. The Util module is responsible for constructing the
|
||||||
// RED object passed to node modules when they are loaded.
|
// RED object passed to node modules when they are loaded.
|
||||||
loader.init(runtime);
|
loader.init(runtime);
|
||||||
|
plugins.init(runtime.settings);
|
||||||
registry.init(runtime.settings,loader);
|
registry.init(runtime.settings,loader);
|
||||||
library.init();
|
library.init();
|
||||||
}
|
}
|
||||||
@ -297,6 +299,12 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
getNodeExampleFlowPath: library.getExampleFlowPath,
|
getNodeExampleFlowPath: library.getExampleFlowPath,
|
||||||
|
|
||||||
|
registerPlugin: plugins.registerPlugin,
|
||||||
|
getPlugin: plugins.getPlugin,
|
||||||
|
getPluginsByType: plugins.getPluginsByType,
|
||||||
|
getPluginList: plugins.getPluginList,
|
||||||
|
getPluginConfigs: plugins.getPluginConfigs,
|
||||||
|
|
||||||
deprecated: require("./deprecated")
|
deprecated: require("./deprecated")
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -36,78 +36,140 @@ function load(disableNodePathScan) {
|
|||||||
// To skip node scan, the following line will use the stored node list.
|
// 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
|
// We should expose that as an option at some point, although the
|
||||||
// performance gains are minimal.
|
// performance gains are minimal.
|
||||||
//return loadNodeFiles(registry.getModuleList());
|
//return loadModuleFiles(registry.getModuleList());
|
||||||
log.info(log._("server.loading"));
|
log.info(log._("server.loading"));
|
||||||
|
|
||||||
var nodeFiles = localfilesystem.getNodeFiles(disableNodePathScan);
|
var modules = localfilesystem.getNodeFiles(disableNodePathScan);
|
||||||
return loadNodeFiles(nodeFiles);
|
return loadModuleFiles(modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadNodeFiles(nodeFiles) {
|
|
||||||
|
function loadModuleTypeFiles(module, type) {
|
||||||
|
const things = module[type];
|
||||||
|
var first = true;
|
||||||
var promises = [];
|
var promises = [];
|
||||||
var nodes = [];
|
for (var thingName in things) {
|
||||||
for (var module in nodeFiles) {
|
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (nodeFiles.hasOwnProperty(module)) {
|
if (things.hasOwnProperty(thingName)) {
|
||||||
if (nodeFiles[module].redVersion &&
|
if (module.name != "node-red" && first) {
|
||||||
!semver.satisfies((settings.version||"0.0.0").replace(/(\-[1-9A-Za-z-][0-9A-Za-z-\.]*)?(\+[0-9A-Za-z-\.]+)?$/,""), nodeFiles[module].redVersion)) {
|
// Check the module directory exists
|
||||||
|
first = false;
|
||||||
|
var fn = things[thingName].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 {
|
||||||
|
var promise;
|
||||||
|
if (type === "nodes") {
|
||||||
|
promise = loadNodeConfig(things[thingName]);
|
||||||
|
} else if (type === "plugins") {
|
||||||
|
promise = loadPluginConfig(things[thingName]);
|
||||||
|
}
|
||||||
|
promises.push(
|
||||||
|
promise.then(
|
||||||
|
(function() {
|
||||||
|
var m = module.name;
|
||||||
|
var n = thingName;
|
||||||
|
return function(nodeSet) {
|
||||||
|
things[n] = nodeSet;
|
||||||
|
return nodeSet;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
).catch(err => {console.log(err)})
|
||||||
|
);
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err)
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return promises;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadModuleFiles(modules) {
|
||||||
|
var pluginPromises = [];
|
||||||
|
var nodePromises = [];
|
||||||
|
for (var module in modules) {
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (modules.hasOwnProperty(module)) {
|
||||||
|
if (modules[module].redVersion &&
|
||||||
|
!semver.satisfies((settings.version||"0.0.0").replace(/(\-[1-9A-Za-z-][0-9A-Za-z-\.]*)?(\+[0-9A-Za-z-\.]+)?$/,""), modules[module].redVersion)) {
|
||||||
//TODO: log it
|
//TODO: log it
|
||||||
log.warn("["+module+"] "+log._("server.node-version-mismatch",{version:nodeFiles[module].redVersion}));
|
log.warn("["+module+"] "+log._("server.node-version-mismatch",{version:modules[module].redVersion}));
|
||||||
nodeFiles[module].err = "version_mismatch";
|
modules[module].err = "version_mismatch";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (module == "node-red" || !registry.getModuleInfo(module)) {
|
if (module == "node-red" || !registry.getModuleInfo(module)) {
|
||||||
var first = true;
|
if (modules[module].nodes) {
|
||||||
for (var node in nodeFiles[module].nodes) {
|
nodePromises = nodePromises.concat(loadModuleTypeFiles(modules[module], "nodes"));
|
||||||
/* istanbul ignore else */
|
}
|
||||||
if (nodeFiles[module].nodes.hasOwnProperty(node)) {
|
if (modules[module].plugins) {
|
||||||
if (module != "node-red" && first) {
|
pluginPromises = pluginPromises.concat(loadModuleTypeFiles(modules[module], "plugins"));
|
||||||
// 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]).then((function() {
|
|
||||||
var m = module;
|
|
||||||
var n = node;
|
|
||||||
return function(nodeSet) {
|
|
||||||
nodeFiles[m].nodes[n] = nodeSet;
|
|
||||||
nodes.push(nodeSet);
|
|
||||||
}
|
|
||||||
})()).catch(err => {}));
|
|
||||||
} catch(err) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.all(promises).then(function(results) {
|
var pluginList;
|
||||||
for (var module in nodeFiles) {
|
var nodeList;
|
||||||
if (nodeFiles.hasOwnProperty(module)) {
|
|
||||||
if (!nodeFiles[module].err) {
|
return Promise.all(pluginPromises).then(function(results) {
|
||||||
registry.addModule(nodeFiles[module]);
|
pluginList = results.filter(r => !!r);
|
||||||
|
// Initial plugin load has happened. Ensure modules that provide
|
||||||
|
// plugins are in the registry now.
|
||||||
|
for (var module in modules) {
|
||||||
|
if (modules.hasOwnProperty(module)) {
|
||||||
|
if (modules[module].plugins && Object.keys(modules[module].plugins).length > 0) {
|
||||||
|
// Add the modules for plugins
|
||||||
|
if (!modules[module].err) {
|
||||||
|
registry.addModule(modules[module]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loadNodeSetList(nodes);
|
return loadNodeSetList(pluginList);
|
||||||
|
}).then(function() {
|
||||||
|
return Promise.all(nodePromises);
|
||||||
|
}).then(function(results) {
|
||||||
|
nodeList = results.filter(r => !!r);
|
||||||
|
// Initial node load has happened. Ensure remaining modules are in the registry
|
||||||
|
for (var module in modules) {
|
||||||
|
if (modules.hasOwnProperty(module)) {
|
||||||
|
if (!modules[module].plugins || Object.keys(modules[module].plugins).length === 0) {
|
||||||
|
if (!modules[module].err) {
|
||||||
|
registry.addModule(modules[module]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loadNodeSetList(nodeList);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPluginTemplate(plugin) {
|
||||||
|
return fs.readFile(plugin.template,'utf8').then(content => {
|
||||||
|
plugin.config = content;
|
||||||
|
return plugin;
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
plugin.err = "Error: "+plugin.template+" does not exist";
|
||||||
|
} else {
|
||||||
|
plugin.err = err.toString();
|
||||||
|
}
|
||||||
|
return plugin;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,11 +237,12 @@ async function loadNodeLocales(node) {
|
|||||||
node.namespace = node.module;
|
node.namespace = node.module;
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
return fs.stat(path.join(path.dirname(node.file),"locales")).then(stat => {
|
const baseFile = node.file||node.template;
|
||||||
|
return fs.stat(path.join(path.dirname(baseFile),"locales")).then(stat => {
|
||||||
node.namespace = node.id;
|
node.namespace = node.id;
|
||||||
return i18n.registerMessageCatalog(node.id,
|
return i18n.registerMessageCatalog(node.id,
|
||||||
path.join(path.dirname(node.file),"locales"),
|
path.join(path.dirname(baseFile),"locales"),
|
||||||
path.basename(node.file,".js")+".json")
|
path.basename(baseFile).replace(/\.[^.]+$/,".json"))
|
||||||
.then(() => node);
|
.then(() => node);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
node.namespace = node.module;
|
node.namespace = node.module;
|
||||||
@ -204,6 +267,7 @@ async function loadNodeConfig(fileInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var node = {
|
var node = {
|
||||||
|
type: "node",
|
||||||
id: id,
|
id: id,
|
||||||
module: module,
|
module: module,
|
||||||
name: name,
|
name: name,
|
||||||
@ -227,6 +291,58 @@ async function loadNodeConfig(fileInfo) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadPluginConfig(fileInfo) {
|
||||||
|
var file = fileInfo.file;
|
||||||
|
var module = fileInfo.module;
|
||||||
|
var name = fileInfo.name;
|
||||||
|
var version = fileInfo.version;
|
||||||
|
|
||||||
|
var id = module + "/" + name;
|
||||||
|
var isEnabled = true;
|
||||||
|
|
||||||
|
// TODO: registry.getPluginInfo
|
||||||
|
|
||||||
|
// var info = registry.getPluginInfo(id);
|
||||||
|
// if (info) {
|
||||||
|
// if (info.hasOwnProperty("loaded")) {
|
||||||
|
// throw new Error(file+" already loaded");
|
||||||
|
// }
|
||||||
|
// isEnabled = info.enabled;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
if (!fs.existsSync(jsFile)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
var plugin = {
|
||||||
|
type: "plugin",
|
||||||
|
id: id,
|
||||||
|
module: module,
|
||||||
|
name: name,
|
||||||
|
enabled: isEnabled,
|
||||||
|
loaded:false,
|
||||||
|
version: version,
|
||||||
|
local: fileInfo.local,
|
||||||
|
plugins: [],
|
||||||
|
config: "",
|
||||||
|
help: {}
|
||||||
|
};
|
||||||
|
var jsFile = file.replace(/\.[^.]+$/,".js");
|
||||||
|
var htmlFile = file.replace(/\.[^.]+$/,".html");
|
||||||
|
if (fs.existsSync(jsFile)) {
|
||||||
|
plugin.file = jsFile;
|
||||||
|
}
|
||||||
|
if (fs.existsSync(htmlFile)) {
|
||||||
|
plugin.template = htmlFile;
|
||||||
|
}
|
||||||
|
await loadNodeLocales(plugin)
|
||||||
|
|
||||||
|
if (plugin.template && !settings.disableEditor) {
|
||||||
|
return loadPluginTemplate(plugin);
|
||||||
|
}
|
||||||
|
return plugin
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the specified node into the runtime
|
* Loads the specified node into the runtime
|
||||||
* @param node a node info object - see loadNodeConfig
|
* @param node a node info object - see loadNodeConfig
|
||||||
@ -236,8 +352,6 @@ async function loadNodeConfig(fileInfo) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function loadNodeSet(node) {
|
function loadNodeSet(node) {
|
||||||
var nodeDir = path.dirname(node.file);
|
|
||||||
var nodeFn = path.basename(node.file);
|
|
||||||
if (!node.enabled) {
|
if (!node.enabled) {
|
||||||
return Promise.resolve(node);
|
return Promise.resolve(node);
|
||||||
} else {
|
} else {
|
||||||
@ -284,11 +398,59 @@ function loadNodeSet(node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadPlugin(plugin) {
|
||||||
|
if (!plugin.file) {
|
||||||
|
// No runtime component - nothing to load
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var r = require(plugin.file);
|
||||||
|
if (typeof r === "function") {
|
||||||
|
|
||||||
|
var red = registryUtil.createNodeApi(plugin);
|
||||||
|
var promise = r(red);
|
||||||
|
if (promise != null && typeof promise.then === "function") {
|
||||||
|
return promise.then(function() {
|
||||||
|
plugin.enabled = true;
|
||||||
|
plugin.loaded = true;
|
||||||
|
return plugin;
|
||||||
|
}).catch(function(err) {
|
||||||
|
plugin.err = err;
|
||||||
|
return plugin;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plugin.enabled = true;
|
||||||
|
plugin.loaded = true;
|
||||||
|
return plugin;
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err);
|
||||||
|
plugin.err = err;
|
||||||
|
var stack = err.stack;
|
||||||
|
var message;
|
||||||
|
if (stack) {
|
||||||
|
var i = stack.indexOf(plugin.file);
|
||||||
|
if (i > -1) {
|
||||||
|
var excerpt = stack.substring(i+node.file.length+1,i+plugin.file.length+20);
|
||||||
|
var m = /^(\d+):(\d+)/.exec(excerpt);
|
||||||
|
if (m) {
|
||||||
|
plugin.err = err+" (line:"+m[1]+")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadNodeSetList(nodes) {
|
function loadNodeSetList(nodes) {
|
||||||
var promises = [];
|
var promises = [];
|
||||||
nodes.forEach(function(node) {
|
nodes.forEach(function(node) {
|
||||||
if (!node.err) {
|
if (!node.err) {
|
||||||
promises.push(loadNodeSet(node).catch(err => {}));
|
if (node.type === "plugin") {
|
||||||
|
promises.push(loadPlugin(node).catch(err => {}));
|
||||||
|
} else {
|
||||||
|
promises.push(loadNodeSet(node).catch(err => {}));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
promises.push(node);
|
promises.push(node);
|
||||||
}
|
}
|
||||||
@ -316,6 +478,7 @@ function addModule(module) {
|
|||||||
return Promise.reject(e);
|
return Promise.reject(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
<<<<<<< HEAD
|
||||||
var moduleFiles = {};
|
var moduleFiles = {};
|
||||||
var moduleStack = [module];
|
var moduleStack = [module];
|
||||||
while(moduleStack.length > 0) {
|
while(moduleStack.length > 0) {
|
||||||
@ -339,6 +502,10 @@ function addModule(module) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loadNodeFiles(moduleFiles).then(() => module)
|
return loadNodeFiles(moduleFiles).then(() => module)
|
||||||
|
=======
|
||||||
|
var moduleFiles = localfilesystem.getModuleFiles(module);
|
||||||
|
return loadModuleFiles(moduleFiles);
|
||||||
|
>>>>>>> Initial plugin runtime api implementation
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
|
@ -220,33 +220,41 @@ function getModuleNodeFiles(module) {
|
|||||||
var moduleDir = module.dir;
|
var moduleDir = module.dir;
|
||||||
var pkg = module.package;
|
var pkg = module.package;
|
||||||
|
|
||||||
var nodes = pkg['node-red'].nodes||{};
|
|
||||||
var results = [];
|
|
||||||
var iconDirs = [];
|
var iconDirs = [];
|
||||||
var iconList = [];
|
var iconList = [];
|
||||||
for (var n in nodes) {
|
|
||||||
/* istanbul ignore else */
|
function scanTypes(types) {
|
||||||
if (nodes.hasOwnProperty(n)) {
|
const files = [];
|
||||||
var file = path.join(moduleDir,nodes[n]);
|
for (var n in types) {
|
||||||
results.push({
|
/* istanbul ignore else */
|
||||||
file: file,
|
if (types.hasOwnProperty(n)) {
|
||||||
module: pkg.name,
|
var file = path.join(moduleDir,types[n]);
|
||||||
name: n,
|
files.push({
|
||||||
version: pkg.version
|
file: file,
|
||||||
});
|
module: pkg.name,
|
||||||
var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons");
|
name: n,
|
||||||
if (iconDirs.indexOf(iconDir) == -1) {
|
version: pkg.version
|
||||||
try {
|
});
|
||||||
fs.statSync(iconDir);
|
var iconDir = path.join(moduleDir,path.dirname(types[n]),"icons");
|
||||||
var icons = scanIconDir(iconDir);
|
if (iconDirs.indexOf(iconDir) == -1) {
|
||||||
iconList.push({path:iconDir,icons:icons});
|
try {
|
||||||
iconDirs.push(iconDir);
|
fs.statSync(iconDir);
|
||||||
} catch(err) {
|
var icons = scanIconDir(iconDir);
|
||||||
|
iconList.push({path:iconDir,icons:icons});
|
||||||
|
iconDirs.push(iconDir);
|
||||||
|
} catch(err) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
var result = {files:results,icons:iconList};
|
|
||||||
|
var result = {
|
||||||
|
nodeFiles:scanTypes(pkg['node-red'].nodes||{}),
|
||||||
|
pluginFiles:scanTypes(pkg['node-red'].plugins||{}),
|
||||||
|
icons:iconList
|
||||||
|
};
|
||||||
|
|
||||||
var examplesDir = path.join(moduleDir,"examples");
|
var examplesDir = path.join(moduleDir,"examples");
|
||||||
try {
|
try {
|
||||||
@ -396,6 +404,7 @@ function convertModuleFileListToObject(moduleFiles) {
|
|||||||
local: moduleFile.local||false,
|
local: moduleFile.local||false,
|
||||||
user: moduleFile.user||false,
|
user: moduleFile.user||false,
|
||||||
nodes: {},
|
nodes: {},
|
||||||
|
plugins: {},
|
||||||
icons: nodeModuleFiles.icons,
|
icons: nodeModuleFiles.icons,
|
||||||
examples: nodeModuleFiles.examples
|
examples: nodeModuleFiles.examples
|
||||||
};
|
};
|
||||||
@ -408,11 +417,14 @@ function convertModuleFileListToObject(moduleFiles) {
|
|||||||
if (moduleFile.usedBy) {
|
if (moduleFile.usedBy) {
|
||||||
nodeList[moduleFile.package.name].usedBy = moduleFile.usedBy;
|
nodeList[moduleFile.package.name].usedBy = moduleFile.usedBy;
|
||||||
}
|
}
|
||||||
nodeModuleFiles.files.forEach(function(node) {
|
nodeModuleFiles.nodeFiles.forEach(function(node) {
|
||||||
node.local = moduleFile.local||false;
|
|
||||||
nodeList[moduleFile.package.name].nodes[node.name] = node;
|
nodeList[moduleFile.package.name].nodes[node.name] = node;
|
||||||
nodeList[moduleFile.package.name].nodes[node.name].local = moduleFile.local || false;
|
nodeList[moduleFile.package.name].nodes[node.name].local = moduleFile.local || false;
|
||||||
});
|
});
|
||||||
|
nodeModuleFiles.pluginFiles.forEach(function(plugin) {
|
||||||
|
nodeList[moduleFile.package.name].plugins[plugin.name] = plugin;
|
||||||
|
nodeList[moduleFile.package.name].plugins[plugin.name].local = moduleFile.local || false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return nodeList;
|
return nodeList;
|
||||||
}
|
}
|
||||||
|
100
packages/node_modules/@node-red/registry/lib/plugins.js
vendored
Normal file
100
packages/node_modules/@node-red/registry/lib/plugins.js
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
const registry = require("./registry");
|
||||||
|
const {events} = require("@node-red/util")
|
||||||
|
|
||||||
|
var pluginConfigCache = {};
|
||||||
|
var pluginToId = {};
|
||||||
|
var plugins = {};
|
||||||
|
var pluginsByType = {};
|
||||||
|
var settings;
|
||||||
|
|
||||||
|
function init(_settings) {
|
||||||
|
settings = _settings;
|
||||||
|
plugins = {};
|
||||||
|
pluginConfigCache = {};
|
||||||
|
pluginToId = {};
|
||||||
|
pluginsByType = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerPlugin(nodeSetId,id,definition) {
|
||||||
|
var moduleId = registry.getModuleFromSetId(nodeSetId);
|
||||||
|
var pluginId = registry.getNodeFromSetId(nodeSetId);
|
||||||
|
|
||||||
|
definition.id = id;
|
||||||
|
definition.module = moduleId;
|
||||||
|
pluginToId[id] = nodeSetId;
|
||||||
|
plugins[id] = definition;
|
||||||
|
var module = registry.getModule(moduleId);
|
||||||
|
module.plugins[pluginId].plugins.push(definition);
|
||||||
|
if (definition.type) {
|
||||||
|
pluginsByType[definition.type] = pluginsByType[definition.type] || [];
|
||||||
|
pluginsByType[definition.type].push(definition);
|
||||||
|
}
|
||||||
|
if (definition.onadd && typeof definition.onadd === 'function') {
|
||||||
|
definition.onadd();
|
||||||
|
}
|
||||||
|
events.emit("registry:plugin-added",id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlugin(id) {
|
||||||
|
return plugins[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPluginsByType(type) {
|
||||||
|
return pluginsByType[type] || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPluginConfigs(lang) {
|
||||||
|
if (!pluginConfigCache[lang]) {
|
||||||
|
var result = "";
|
||||||
|
var script = "";
|
||||||
|
var moduleConfigs = registry.getModuleList();
|
||||||
|
for (var module in moduleConfigs) {
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (moduleConfigs.hasOwnProperty(module)) {
|
||||||
|
var plugins = moduleConfigs[module].plugins;
|
||||||
|
for (var plugin in plugins) {
|
||||||
|
if (plugins.hasOwnProperty(plugin)) {
|
||||||
|
var config = plugins[plugin];
|
||||||
|
if (config.enabled && !config.err && config.config) {
|
||||||
|
result += "\n<!-- --- [red-plugin:"+config.id+"] --- -->\n";
|
||||||
|
result += config.config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pluginConfigCache[lang] = result;
|
||||||
|
}
|
||||||
|
return pluginConfigCache[lang];
|
||||||
|
}
|
||||||
|
function getPluginList() {
|
||||||
|
var list = [];
|
||||||
|
var moduleConfigs = registry.getModuleList();
|
||||||
|
for (var module in moduleConfigs) {
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (moduleConfigs.hasOwnProperty(module)) {
|
||||||
|
var plugins = moduleConfigs[module].plugins;
|
||||||
|
for (var plugin in plugins) {
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (plugins.hasOwnProperty(plugin)) {
|
||||||
|
var pluginInfo = registry.filterNodeInfo(plugins[plugin]);
|
||||||
|
pluginInfo.version = moduleConfigs[module].version;
|
||||||
|
// if (moduleConfigs[module].pending_version) {
|
||||||
|
// nodeInfo.pending_version = moduleConfigs[module].pending_version;
|
||||||
|
// }
|
||||||
|
list.push(pluginInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
init,
|
||||||
|
registerPlugin,
|
||||||
|
getPlugin,
|
||||||
|
getPluginsByType,
|
||||||
|
getPluginConfigs,
|
||||||
|
getPluginList
|
||||||
|
}
|
@ -67,17 +67,24 @@ function filterNodeInfo(n) {
|
|||||||
if (n.hasOwnProperty("err")) {
|
if (n.hasOwnProperty("err")) {
|
||||||
r.err = n.err;
|
r.err = n.err;
|
||||||
}
|
}
|
||||||
|
if (n.hasOwnProperty("plugins")) {
|
||||||
|
r.plugins = n.plugins;
|
||||||
|
}
|
||||||
|
if (n.type === "plugin") {
|
||||||
|
r.editor = !!n.template;
|
||||||
|
r.runtime = !!n.file;
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function getModule(id) {
|
function getModuleFromSetId(id) {
|
||||||
var parts = id.split("/");
|
var parts = id.split("/");
|
||||||
return parts.slice(0,parts.length-1).join("/");
|
return parts.slice(0,parts.length-1).join("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNode(id) {
|
function getNodeFromSetId(id) {
|
||||||
var parts = id.split("/");
|
var parts = id.split("/");
|
||||||
return parts[parts.length-1];
|
return parts[parts.length-1];
|
||||||
}
|
}
|
||||||
@ -220,11 +227,11 @@ function addModule(module) {
|
|||||||
|
|
||||||
|
|
||||||
function removeNode(id) {
|
function removeNode(id) {
|
||||||
var config = moduleConfigs[getModule(id)].nodes[getNode(id)];
|
var config = moduleConfigs[getModuleFromSetId(id)].nodes[getNodeFromSetId(id)];
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new Error("Unrecognised id: "+id);
|
throw new Error("Unrecognised id: "+id);
|
||||||
}
|
}
|
||||||
delete moduleConfigs[getModule(id)].nodes[getNode(id)];
|
delete moduleConfigs[getModuleFromSetId(id)].nodes[getNodeFromSetId(id)];
|
||||||
var i = nodeList.indexOf(id);
|
var i = nodeList.indexOf(id);
|
||||||
if (i > -1) {
|
if (i > -1) {
|
||||||
nodeList.splice(i,1);
|
nodeList.splice(i,1);
|
||||||
@ -294,9 +301,9 @@ function getNodeInfo(typeOrId) {
|
|||||||
}
|
}
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (id) {
|
if (id) {
|
||||||
var module = moduleConfigs[getModule(id)];
|
var module = moduleConfigs[getModuleFromSetId(id)];
|
||||||
if (module) {
|
if (module) {
|
||||||
var config = module.nodes[getNode(id)];
|
var config = module.nodes[getNodeFromSetId(id)];
|
||||||
if (config) {
|
if (config) {
|
||||||
var info = filterNodeInfo(config);
|
var info = filterNodeInfo(config);
|
||||||
if (config.hasOwnProperty("loaded")) {
|
if (config.hasOwnProperty("loaded")) {
|
||||||
@ -323,9 +330,9 @@ function getFullNodeInfo(typeOrId) {
|
|||||||
}
|
}
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (id) {
|
if (id) {
|
||||||
var module = moduleConfigs[getModule(id)];
|
var module = moduleConfigs[getModuleFromSetId(id)];
|
||||||
if (module) {
|
if (module) {
|
||||||
return module.nodes[getNode(id)];
|
return module.nodes[getNodeFromSetId(id)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -359,16 +366,10 @@ function getNodeList(filter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getModuleList() {
|
function getModuleList() {
|
||||||
//var list = [];
|
|
||||||
//for (var module in moduleNodes) {
|
|
||||||
// /* istanbul ignore else */
|
|
||||||
// if (moduleNodes.hasOwnProperty(module)) {
|
|
||||||
// list.push(registry.getModuleInfo(module));
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//return list;
|
|
||||||
return moduleConfigs;
|
return moduleConfigs;
|
||||||
|
}
|
||||||
|
function getModule(id) {
|
||||||
|
return moduleConfigs[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getModuleInfo(module) {
|
function getModuleInfo(module) {
|
||||||
@ -461,13 +462,11 @@ function getAllNodeConfigs(lang) {
|
|||||||
var script = "";
|
var script = "";
|
||||||
for (var i=0;i<nodeList.length;i++) {
|
for (var i=0;i<nodeList.length;i++) {
|
||||||
var id = nodeList[i];
|
var id = nodeList[i];
|
||||||
|
var module = moduleConfigs[getModuleFromSetId(id)]
|
||||||
var module = moduleConfigs[getModule(id)]
|
|
||||||
if (!module.user && (module.usedBy && module.usedBy.length > 0)) {
|
if (!module.user && (module.usedBy && module.usedBy.length > 0)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
var config = module.nodes[getNodeFromSetId(id)];
|
||||||
var config = module.nodes[getNode(id)];
|
|
||||||
if (config.enabled && !config.err) {
|
if (config.enabled && !config.err) {
|
||||||
result += "\n<!-- --- [red-module:"+id+"] --- -->\n";
|
result += "\n<!-- --- [red-module:"+id+"] --- -->\n";
|
||||||
result += config.config;
|
result += config.config;
|
||||||
@ -486,11 +485,11 @@ function getAllNodeConfigs(lang) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNodeConfig(id,lang) {
|
function getNodeConfig(id,lang) {
|
||||||
var config = moduleConfigs[getModule(id)];
|
var config = moduleConfigs[getModuleFromSetId(id)];
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
config = config.nodes[getNode(id)];
|
config = config.nodes[getNodeFromSetId(id)];
|
||||||
if (config) {
|
if (config) {
|
||||||
var result = "<!-- --- [red-module:"+id+"] --- -->\n"+config.config;
|
var result = "<!-- --- [red-module:"+id+"] --- -->\n"+config.config;
|
||||||
result += loader.getNodeHelp(config,lang||"en-US")
|
result += loader.getNodeHelp(config,lang||"en-US")
|
||||||
@ -511,7 +510,7 @@ function getNodeConstructor(type) {
|
|||||||
if (typeof id === "undefined") {
|
if (typeof id === "undefined") {
|
||||||
config = undefined;
|
config = undefined;
|
||||||
} else {
|
} else {
|
||||||
config = moduleConfigs[getModule(id)].nodes[getNode(id)];
|
config = moduleConfigs[getModuleFromSetId(id)].nodes[getNodeFromSetId(id)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config || (config.enabled && !config.err)) {
|
if (!config || (config.enabled && !config.err)) {
|
||||||
@ -548,7 +547,7 @@ function enableNodeSet(typeOrId) {
|
|||||||
}
|
}
|
||||||
var config;
|
var config;
|
||||||
try {
|
try {
|
||||||
config = moduleConfigs[getModule(id)].nodes[getNode(id)];
|
config = moduleConfigs[getModuleFromSetId(id)].nodes[getNodeFromSetId(id)];
|
||||||
delete config.err;
|
delete config.err;
|
||||||
config.enabled = true;
|
config.enabled = true;
|
||||||
nodeConfigCache = {};
|
nodeConfigCache = {};
|
||||||
@ -571,7 +570,7 @@ function disableNodeSet(typeOrId) {
|
|||||||
}
|
}
|
||||||
var config;
|
var config;
|
||||||
try {
|
try {
|
||||||
config = moduleConfigs[getModule(id)].nodes[getNode(id)];
|
config = moduleConfigs[getModuleFromSetId(id)].nodes[getNodeFromSetId(id)];
|
||||||
// TODO: persist setting
|
// TODO: persist setting
|
||||||
config.enabled = false;
|
config.enabled = false;
|
||||||
nodeConfigCache = {};
|
nodeConfigCache = {};
|
||||||
@ -710,6 +709,7 @@ var registry = module.exports = {
|
|||||||
getFullNodeInfo: getFullNodeInfo,
|
getFullNodeInfo: getFullNodeInfo,
|
||||||
getNodeList: getNodeList,
|
getNodeList: getNodeList,
|
||||||
getModuleList: getModuleList,
|
getModuleList: getModuleList,
|
||||||
|
getModule: getModule,
|
||||||
getModuleInfo: getModuleInfo,
|
getModuleInfo: getModuleInfo,
|
||||||
|
|
||||||
getNodeIconPath: getNodeIconPath,
|
getNodeIconPath: getNodeIconPath,
|
||||||
@ -725,5 +725,8 @@ var registry = module.exports = {
|
|||||||
|
|
||||||
saveNodeList: saveNodeList,
|
saveNodeList: saveNodeList,
|
||||||
|
|
||||||
cleanModuleList: cleanModuleList
|
cleanModuleList: cleanModuleList,
|
||||||
|
getModuleFromSetId: getModuleFromSetId,
|
||||||
|
getNodeFromSetId: getNodeFromSetId,
|
||||||
|
filterNodeInfo: filterNodeInfo
|
||||||
};
|
};
|
||||||
|
@ -70,6 +70,17 @@ function createNodeApi(node) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
plugins: {
|
||||||
|
registerPlugin: function(id,definition) {
|
||||||
|
return runtime.plugins.registerPlugin(node.id,id,definition);
|
||||||
|
},
|
||||||
|
get: function(id) {
|
||||||
|
return runtime.plugins.getPlugin(id);
|
||||||
|
},
|
||||||
|
getByType: function(type) {
|
||||||
|
return runtime.plugins.getPluginsByType(type);
|
||||||
|
}
|
||||||
|
},
|
||||||
library: {
|
library: {
|
||||||
register: function(type) {
|
register: function(type) {
|
||||||
return runtime.library.register(node.id,type);
|
return runtime.library.register(node.id,type);
|
||||||
|
@ -28,6 +28,7 @@ var api = module.exports = {
|
|||||||
api.library.init(runtime);
|
api.library.init(runtime);
|
||||||
api.projects.init(runtime);
|
api.projects.init(runtime);
|
||||||
api.context.init(runtime);
|
api.context.init(runtime);
|
||||||
|
api.plugins.init(runtime);
|
||||||
},
|
},
|
||||||
|
|
||||||
comms: require("./comms"),
|
comms: require("./comms"),
|
||||||
@ -37,6 +38,7 @@ var api = module.exports = {
|
|||||||
settings: require("./settings"),
|
settings: require("./settings"),
|
||||||
projects: require("./projects"),
|
projects: require("./projects"),
|
||||||
context: require("./context"),
|
context: require("./context"),
|
||||||
|
plugins: require("./plugins"),
|
||||||
|
|
||||||
isStarted: async function(opts) {
|
isStarted: async function(opts) {
|
||||||
return runtime.isStarted();
|
return runtime.isStarted();
|
||||||
|
57
packages/node_modules/@node-red/runtime/lib/api/plugins.js
vendored
Normal file
57
packages/node_modules/@node-red/runtime/lib/api/plugins.js
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* @mixin @node-red/runtime_plugins
|
||||||
|
*/
|
||||||
|
|
||||||
|
var runtime;
|
||||||
|
|
||||||
|
var api = module.exports = {
|
||||||
|
init: function(_runtime) {
|
||||||
|
runtime = _runtime;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the editor content for an individual plugin
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
|
* @return {Promise<NodeInfo>} - the node information
|
||||||
|
* @memberof @node-red/runtime_nodes
|
||||||
|
*/
|
||||||
|
getPluginList: async function(opts) {
|
||||||
|
runtime.log.audit({event: "plugins.list.get"}, opts.req);
|
||||||
|
return runtime.plugins.getPluginList();
|
||||||
|
},
|
||||||
|
|
||||||
|
getPluginConfigs: async function(opts) {
|
||||||
|
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
||||||
|
return runtime.plugins.getPluginConfigs(opts.lang);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Gets all registered module message catalogs
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {User} opts.lang - the i18n language to return. If not set, uses runtime default (en-US)
|
||||||
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
|
* @return {Promise<Object>} - the message catalogs
|
||||||
|
* @memberof @node-red/runtime_nodes
|
||||||
|
*/
|
||||||
|
getPluginCatalogs: async function(opts) {
|
||||||
|
var lang = opts.lang;
|
||||||
|
var prevLang = runtime.i18n.i.language;
|
||||||
|
// Trigger a load from disk of the language if it is not the default
|
||||||
|
return new Promise( (resolve,reject) => {
|
||||||
|
runtime.i18n.i.changeLanguage(lang, function(){
|
||||||
|
var nodeList = runtime.plugins.getPluginList();
|
||||||
|
var result = {};
|
||||||
|
nodeList.forEach(function(n) {
|
||||||
|
if (n.module !== "node-red") {
|
||||||
|
result[n.id] = runtime.i18n.i.getResourceBundle(lang, n.id)||{};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
runtime.i18n.i.changeLanguage(prevLang);
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
@ -21,6 +21,7 @@ var flows = require("./flows");
|
|||||||
var storage = require("./storage");
|
var storage = require("./storage");
|
||||||
var library = require("./library");
|
var library = require("./library");
|
||||||
var hooks = require("./hooks");
|
var hooks = require("./hooks");
|
||||||
|
var plugins = require("./plugins");
|
||||||
var settings = require("./settings");
|
var settings = require("./settings");
|
||||||
|
|
||||||
var express = require("express");
|
var express = require("express");
|
||||||
@ -280,6 +281,7 @@ var runtime = {
|
|||||||
storage: storage,
|
storage: storage,
|
||||||
hooks: hooks,
|
hooks: hooks,
|
||||||
nodes: redNodes,
|
nodes: redNodes,
|
||||||
|
plugins: plugins,
|
||||||
flows: flows,
|
flows: flows,
|
||||||
library: library,
|
library: library,
|
||||||
exec: exec,
|
exec: exec,
|
||||||
@ -341,6 +343,12 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
context: externalAPI.context,
|
context: externalAPI.context,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @memberof @node-red/runtime
|
||||||
|
* @mixes @node-red/runtime_plugins
|
||||||
|
*/
|
||||||
|
plugins: externalAPI.plugins,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the runtime is started
|
* Returns whether the runtime is started
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
|
10
packages/node_modules/@node-red/runtime/lib/plugins.js
vendored
Normal file
10
packages/node_modules/@node-red/runtime/lib/plugins.js
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const registry = require("@node-red/registry");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
init: function() {},
|
||||||
|
registerPlugin: registry.registerPlugin,
|
||||||
|
getPlugin: registry.getPlugin,
|
||||||
|
getPluginsByType: registry.getPluginsByType,
|
||||||
|
getPluginList: registry.getPluginList,
|
||||||
|
getPluginConfigs: registry.getPluginConfigs,
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugin": "winning"
|
||||||
|
}
|
12
test/resources/plugin/test-plugin/package.json
Normal file
12
test/resources/plugin/test-plugin/package.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "test-plugin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"node-red": {
|
||||||
|
"plugins": {
|
||||||
|
"test": "test.js",
|
||||||
|
"test-editor-plugin": "test-editor-plugin.html",
|
||||||
|
"test-runtime-plugin": "test-runtime-plugin.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
console.log("Loaded test-plugin/test-editor-plugin")
|
||||||
|
// RED.plugins.registerPlugin("")
|
||||||
|
//
|
||||||
|
</script>
|
10
test/resources/plugin/test-plugin/test-runtime-plugin.js
Normal file
10
test/resources/plugin/test-plugin/test-runtime-plugin.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
console.log("Loaded test-plugin/test-runtime-plugin")
|
||||||
|
|
||||||
|
RED.plugins.registerPlugin("my-test-runtime-only-plugin", {
|
||||||
|
type: "bar",
|
||||||
|
onadd: function() {
|
||||||
|
console.log("my-test-runtime-only-plugin.onadd called")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
4
test/resources/plugin/test-plugin/test.html
Normal file
4
test/resources/plugin/test-plugin/test.html
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
console.log("Loaded test-plugin/test")
|
||||||
|
// RED.plugins.registerPlugin("")
|
||||||
|
</script>
|
13
test/resources/plugin/test-plugin/test.js
Normal file
13
test/resources/plugin/test-plugin/test.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
console.log("Loaded test-plugin/test")
|
||||||
|
|
||||||
|
RED.plugins.registerPlugin("my-test-plugin", {
|
||||||
|
type: "foo",
|
||||||
|
onadd: function() {
|
||||||
|
console.log("my-test-plugin.onadd called")
|
||||||
|
RED.events.on("registry:plugin-added", function(id) {
|
||||||
|
console.log(`my-test-plugin: plugin-added event "${id}"`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
111
test/unit/@node-red/editor-api/lib/admin/plugins_spec.js
Normal file
111
test/unit/@node-red/editor-api/lib/admin/plugins_spec.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
const should = require("should");
|
||||||
|
const request = require('supertest');
|
||||||
|
const express = require('express');
|
||||||
|
const bodyParser = require("body-parser");
|
||||||
|
|
||||||
|
var app;
|
||||||
|
|
||||||
|
var NR_TEST_UTILS = require("nr-test-utils");
|
||||||
|
|
||||||
|
var plugins = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/plugins");
|
||||||
|
|
||||||
|
describe("api/editor/plugins", function() {
|
||||||
|
const pluginList = [
|
||||||
|
{
|
||||||
|
"id": "test-module/test-set",
|
||||||
|
"enabled": true,
|
||||||
|
"local": false,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "foo",
|
||||||
|
"id": "a-plugin",
|
||||||
|
"module": "test-module"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bar",
|
||||||
|
"id": "a-plugin2",
|
||||||
|
"module": "test-module"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "foo",
|
||||||
|
"id": "a-plugin3",
|
||||||
|
"module": "test-module"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "test-module/test-disabled-set",
|
||||||
|
"enabled": false,
|
||||||
|
"local": false,
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const pluginConfigs = `
|
||||||
|
<!-- --- [red-plugin:test-module/test-set] --- -->
|
||||||
|
test-module-config`;
|
||||||
|
|
||||||
|
const pluginCatalogs = { "test-module": {"foo": "bar"}};
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
app = express();
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.get("/plugins",plugins.getAll);
|
||||||
|
app.get("/plugins/messages",plugins.getCatalogs);
|
||||||
|
|
||||||
|
plugins.init({
|
||||||
|
plugins: {
|
||||||
|
getPluginList: async function() { return pluginList },
|
||||||
|
getPluginConfigs: async function() { return pluginConfigs },
|
||||||
|
getPluginCatalogs: async function() { return pluginCatalogs }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the list of plugins', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/plugins")
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JSON.stringify(res.body).should.eql(JSON.stringify(pluginList));
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns the plugin configs', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/plugins")
|
||||||
|
.set('Accept', 'text/html')
|
||||||
|
.expect(200)
|
||||||
|
.expect(pluginConfigs)
|
||||||
|
.end(function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns the plugin catalogs', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/plugins/messages")
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JSON.stringify(res.body).should.eql(JSON.stringify(pluginCatalogs));
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
153
test/unit/@node-red/registry/lib/plugins_spec.js
Normal file
153
test/unit/@node-red/registry/lib/plugins_spec.js
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
|
||||||
|
const should = require("should");
|
||||||
|
const sinon = require("sinon");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const NR_TEST_UTILS = require("nr-test-utils");
|
||||||
|
|
||||||
|
const plugins = NR_TEST_UTILS.require("@node-red/registry/lib/plugins");
|
||||||
|
const registry = NR_TEST_UTILS.require("@node-red/registry/lib/registry");
|
||||||
|
const { events } = NR_TEST_UTILS.require("@node-red/util");
|
||||||
|
|
||||||
|
describe("red/nodes/registry/plugins",function() {
|
||||||
|
let receivedEvents = [];
|
||||||
|
let modules;
|
||||||
|
function handleEvent(evnt) {
|
||||||
|
receivedEvents.push(evnt);
|
||||||
|
}
|
||||||
|
beforeEach(function() {
|
||||||
|
plugins.init({});
|
||||||
|
receivedEvents = [];
|
||||||
|
modules = {
|
||||||
|
"test-module": {
|
||||||
|
plugins: {
|
||||||
|
"test-set": {
|
||||||
|
id: "test-module/test-set",
|
||||||
|
enabled: true,
|
||||||
|
config: "test-module-config",
|
||||||
|
plugins: []
|
||||||
|
},
|
||||||
|
"test-disabled-set": {
|
||||||
|
id: "test-module/test-disabled-set",
|
||||||
|
enabled: false,
|
||||||
|
config: "disabled-plugin-config",
|
||||||
|
plugins: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events.on("registry:plugin-added",handleEvent);
|
||||||
|
sinon.stub(registry,"getModule", moduleId => modules[moduleId]);
|
||||||
|
sinon.stub(registry,"getModuleList", () => modules)
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
events.removeListener("registry:plugin-added",handleEvent);
|
||||||
|
registry.getModule.restore();
|
||||||
|
registry.getModuleList.restore();
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("registerPlugin", function() {
|
||||||
|
it("registers a plugin", function() {
|
||||||
|
let pluginDef = {}
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
|
||||||
|
receivedEvents.length.should.eql(1);
|
||||||
|
receivedEvents[0].should.eql("a-plugin");
|
||||||
|
should.exist(modules['test-module'].plugins['test-set'].plugins[0])
|
||||||
|
modules['test-module'].plugins['test-set'].plugins[0].should.equal(pluginDef)
|
||||||
|
})
|
||||||
|
it("calls a plugins onadd function", function() {
|
||||||
|
let pluginDef = { onadd: sinon.stub() }
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
|
||||||
|
pluginDef.onadd.called.should.be.true();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("getPlugin", function() {
|
||||||
|
it("returns a registered plugin", function() {
|
||||||
|
let pluginDef = {}
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
|
||||||
|
pluginDef.should.equal(plugins.getPlugin("a-plugin"));
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("getPluginsByType", function() {
|
||||||
|
it("returns a plugins of a given type", function() {
|
||||||
|
let pluginDef = {type: "foo"}
|
||||||
|
let pluginDef2 = {type: "bar"}
|
||||||
|
let pluginDef3 = {type: "foo"}
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin2",pluginDef2);
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin3",pluginDef3);
|
||||||
|
|
||||||
|
let fooPlugins = plugins.getPluginsByType("foo");
|
||||||
|
let barPlugins = plugins.getPluginsByType("bar");
|
||||||
|
let noPlugins = plugins.getPluginsByType("none");
|
||||||
|
|
||||||
|
noPlugins.should.be.of.length(0);
|
||||||
|
|
||||||
|
fooPlugins.should.be.of.length(2);
|
||||||
|
fooPlugins.should.containEql(pluginDef);
|
||||||
|
fooPlugins.should.containEql(pluginDef3);
|
||||||
|
|
||||||
|
barPlugins.should.be.of.length(1);
|
||||||
|
barPlugins.should.containEql(pluginDef2);
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("getPluginConfigs", function() {
|
||||||
|
it("gets all plugin configs", function() {
|
||||||
|
let configs = plugins.getPluginConfigs("en-US");
|
||||||
|
configs.should.eql(`
|
||||||
|
<!-- --- [red-plugin:test-module/test-set] --- -->
|
||||||
|
test-module-config`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
describe("getPluginList", function() {
|
||||||
|
it("returns a plugins of a given type", function() {
|
||||||
|
let pluginDef = {type: "foo"}
|
||||||
|
let pluginDef2 = {type: "bar"}
|
||||||
|
let pluginDef3 = {type: "foo"}
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin2",pluginDef2);
|
||||||
|
plugins.registerPlugin("test-module/test-set","a-plugin3",pluginDef3);
|
||||||
|
|
||||||
|
let pluginList = plugins.getPluginList();
|
||||||
|
JSON.stringify(pluginList).should.eql(JSON.stringify(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "test-module/test-set",
|
||||||
|
"enabled": true,
|
||||||
|
"local": false,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"type": "foo",
|
||||||
|
"id": "a-plugin",
|
||||||
|
"module": "test-module"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bar",
|
||||||
|
"id": "a-plugin2",
|
||||||
|
"module": "test-module"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "foo",
|
||||||
|
"id": "a-plugin3",
|
||||||
|
"module": "test-module"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "test-module/test-disabled-set",
|
||||||
|
"enabled": false,
|
||||||
|
"local": false,
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
});
|
68
test/unit/@node-red/runtime/lib/api/plugins_spec.js
Normal file
68
test/unit/@node-red/runtime/lib/api/plugins_spec.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
const should = require("should");
|
||||||
|
const sinon = require("sinon");
|
||||||
|
|
||||||
|
const NR_TEST_UTILS = require("nr-test-utils");
|
||||||
|
const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/api/plugins")
|
||||||
|
|
||||||
|
const mockLog = () => ({
|
||||||
|
log: sinon.stub(),
|
||||||
|
debug: sinon.stub(),
|
||||||
|
trace: sinon.stub(),
|
||||||
|
warn: sinon.stub(),
|
||||||
|
info: sinon.stub(),
|
||||||
|
metric: sinon.stub(),
|
||||||
|
audit: sinon.stub(),
|
||||||
|
_: function() { return "abc"}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("runtime-api/plugins", function() {
|
||||||
|
const pluginList = [{id:"one",module:'test-module'},{id:"two",module:"node-red"}];
|
||||||
|
const pluginConfigs = "123";
|
||||||
|
|
||||||
|
describe("getPluginList", function() {
|
||||||
|
it("gets the plugin list", function() {
|
||||||
|
plugins.init({
|
||||||
|
log: mockLog(),
|
||||||
|
plugins: {
|
||||||
|
getPluginList: function() { return pluginList}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return plugins.getPluginList({}).then(function(result) {
|
||||||
|
result.should.eql(pluginList);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("getPluginConfigs", function() {
|
||||||
|
it("gets the plugin configs", function() {
|
||||||
|
plugins.init({
|
||||||
|
log: mockLog(),
|
||||||
|
plugins: {
|
||||||
|
getPluginConfigs: function() { return pluginConfigs}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return plugins.getPluginConfigs({}).then(function(result) {
|
||||||
|
result.should.eql(pluginConfigs);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("getPluginCatalogs", function() {
|
||||||
|
it("gets the plugin catalogs", function() {
|
||||||
|
plugins.init({
|
||||||
|
log: mockLog(),
|
||||||
|
plugins: {
|
||||||
|
getPluginList: function() { return pluginList}
|
||||||
|
},
|
||||||
|
i18n: {
|
||||||
|
i: {
|
||||||
|
changeLanguage: function(lang,done) { done && done() },
|
||||||
|
getResourceBundle: function(lang, id) { return {lang,id}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return plugins.getPluginCatalogs({lang: "en-US"}).then(function(result) {
|
||||||
|
JSON.stringify(result).should.eql(JSON.stringify({ one: { lang: "en-US", id: "one" } }))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
13
test/unit/@node-red/runtime/lib/plugins_spec.js
Normal file
13
test/unit/@node-red/runtime/lib/plugins_spec.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const should = require("should");
|
||||||
|
const sinon = require("sinon");
|
||||||
|
const NR_TEST_UTILS = require("nr-test-utils");
|
||||||
|
|
||||||
|
const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/plugins");
|
||||||
|
|
||||||
|
describe("runtime/plugins",function() {
|
||||||
|
|
||||||
|
it.skip("delegates all functions to registry module", function() {
|
||||||
|
// There's no easy way to test this as we can't stub the registry functions
|
||||||
|
// before the plugin module gets a reference to them
|
||||||
|
})
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user