Move exec and events components to util module

The exec and events components are common components that
are used by both runtime and registry. It makes sense to
move them into the util package.

This also adds some docs to the registry module
This commit is contained in:
Nick O'Leary 2020-12-02 09:25:10 +00:00
parent a1f565f756
commit 6fb96fa3c1
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
33 changed files with 491 additions and 319 deletions

2
API.md
View File

@ -10,6 +10,6 @@ Module | Description
[@node-red/editor-api](@node-red_editor-api.html) | an Express application that serves the Node-RED editor and provides the Admin HTTP API
[@node-red/runtime](@node-red_runtime.html) | the core runtime of Node-RED
[@node-red/util](@node-red_util.html) | common utilities for the Node-RED runtime and editor modules
@node-red/registry | the internal node registry
[@node-red/registry](@node-red_registry.html) | the internal node registry
@node-red/nodes | the default set of core nodes
@node-red/editor-client | the client-side resources of the Node-RED editor application

View File

@ -461,7 +461,8 @@ module.exports = function(grunt) {
'packages/node_modules/@node-red/runtime/lib/hooks.js',
'packages/node_modules/@node-red/util/**/*.js',
'packages/node_modules/@node-red/editor-api/lib/index.js',
'packages/node_modules/@node-red/editor-api/lib/auth/index.js'
'packages/node_modules/@node-red/editor-api/lib/auth/index.js',
'packages/node_modules/@node-red/registry/lib/index.js'
],
options: {
destination: 'docs',

View File

@ -1,4 +1,4 @@
/**
/*!
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -29,16 +29,27 @@ var loader = require("./loader");
var installer = require("./installer");
var library = require("./library");
var settings;
/**
* Initialise the registry with a reference to a runtime object
* @param {Object} runtime - a runtime object
* @memberof @node-red/registry
*/
function init(runtime) {
settings = runtime.settings;
installer.init(runtime);
installer.init(runtime.settings);
// Loader requires the full runtime object because it initialises
// the util module it. The Util module is responsible for constructing the
// RED object passed to node modules when they are loaded.
loader.init(runtime);
registry.init(settings,loader,runtime.events);
registry.init(runtime.settings,loader);
library.init();
}
/**
* Triggers the intial discovery and loading of all Node-RED node modules.
* found on the node path.
* @return {Promise} - resolves when the registry has finised discovering node modules.
* @memberof @node-red/registry
*/
function load() {
registry.load();
return installer.checkPrereq().then(loader.load);
@ -66,34 +77,221 @@ module.exports = {
init:init,
load:load,
clear: registry.clear,
/**
* Register a node constructor function.
*
* @param {Object} nodeSet - the Node Set object the constructor is for
* @param {String} type - the node type
* @param {Function} constructor - the node constructor function
* @function
* @memberof @node-red/registry
*/
registerType: registry.registerNodeConstructor,
/**
* Get a node constructor function.
*
* @param {String} type - the node type
* @return {Function} the node constructor function
* @function
* @memberof @node-red/registry
*/
get: registry.getNodeConstructor,
/**
* Get a node's set information.
*
* @param {String} type - the node type or set identifier
* @return {Object} the node set information
* @function
* @memberof @node-red/registry
*/
getNodeInfo: registry.getNodeInfo,
/**
* Get a list of all nodes in the registry.
*
* @return {Object} the node list
* @function
* @memberof @node-red/registry
*/
getNodeList: registry.getNodeList,
/**
* Get a modules's information.
*
* @param {String} type - the module identifier
* @return {Object} the module information
* @function
* @memberof @node-red/registry
*/
getModuleInfo: registry.getModuleInfo,
/**
* Get a list of all moduless in the registry.
*
* @return {Object} the module list
* @function
* @memberof @node-red/registry
*/
getModuleList: registry.getModuleList,
/**
* Get the HTML configs for all nodes in the registry.
*
* @param {String} lang - the language to return, default `en-US`
* @return {String} the node configs
* @function
* @memberof @node-red/registry
*/
getNodeConfigs: registry.getAllNodeConfigs,
/**
* Get the HTML config for a single node set.
*
* @param {String} id - the node identifier
* @param {String} lang - the language to return, default `en-US`
* @return {String} the node config
* @function
* @memberof @node-red/registry
*/
getNodeConfig: registry.getNodeConfig,
/**
* Get the local path to a node's icon file.
*
* @param {String} module - the module that provides the icon
* @param {String} icon - the name of the icon
* @return {String} the local path to the icon
* @function
* @memberof @node-red/registry
*/
getNodeIconPath: registry.getNodeIconPath,
/**
* Get the full list of all icons available.
*
* @return {String} the icon list
* @function
* @memberof @node-red/registry
*/
getNodeIcons: registry.getNodeIcons,
/**
* Enables a node set, making it available for use.
*
* @param {String} type - the node type or set identifier
* @return {Promise} A promise that resolves when the node set has been enabled
* @throws if the identifier is not recognised or runtime settings are unavailable
* @function
* @memberof @node-red/registry
*/
enableNode: enableNodeSet,
/**
* Disables a node set, making it unavailable for use.
*
* @param {String} type - the node type or set identifier
* @return {Promise} A promise that resolves when the node set has been disabled
* @throws if the identifier is not recognised or runtime settings are unavailable
* @function
* @memberof @node-red/registry
*/
disableNode: registry.disableNodeSet,
/**
* Loads a new module into the registry.
*
* This will rescan the node module paths looking for this module.
*
* @param {String} module - the name of the module to add
* @return {Promise<Object>} A promise that resolves with the module information once it has been added
* @throws if the module has already been added or the runtime settings are unavailable
* @function
* @memberof @node-red/registry
*/
addModule: addModule,
/**
* Removes a module from the registry.
*
* @param {String} module - the name of the module to remove
* @return {Promise<Array>} A promise that resolves with the list of removed node sets
* @throws if the module is not found or the runtime settings are unavailable
* @function
* @memberof @node-red/registry
*/
removeModule: registry.removeModule,
/**
* Installs a new node module using npm and then add to the registry
*
* @param {String|Buffer} module - the name of the module to install, or a Buffer containing a module tar file
* @param {String} version - the version of the module to install, default: `latest`
* @param {String} url - (optional) a url to install the module from
* @return {Promise<Array>} A promise that resolves with the module information once it has been installed
* @function
* @memberof @node-red/registry
*/
installModule: installer.installModule,
/**
* Uninstalls a module using npm
*
* @param {String} module - the name of the module to uninstall
* @return {Promise<Array>} A promise that resolves when the module has been removed
* @function
* @memberof @node-red/registry
*/
uninstallModule: installer.uninstallModule,
/**
* Update to internal list of available modules based on what has been actually
* loaded.
*
* The `autoInstallModules` runtime option means the runtime may try to install
* missing modules after the initial load is complete. If that flag is not set
* this function is used to remove the modules from the registry's saved list.
* @function
* @memberof @node-red/registry
*/
cleanModuleList: registry.cleanModuleList,
/**
* Check if the regisrty is able to install/remove modules.
*
* This is based on whether it has found `npm` on the command-line.
* @return {Boolean} whether the installer is enabled
*
* @function
* @memberof @node-red/registry
*/
paletteEditorEnabled: installer.paletteEditorEnabled,
/**
* Get a list of all example flows provided by nodes in the registry.
* @return {Object} an object, indexed by module, listing all example flows
*
* @function
* @memberof @node-red/registry
*/
getNodeExampleFlows: library.getExampleFlows,
/**
* Gets the full path to a node example
* @param {String} module - the name of the module providing the example
* @param {String} path - the relative path of the example
* @return {String} the full path to the example
*
* @function
* @memberof @node-red/registry
*/
getNodeExampleFlowPath: library.getExampleFlowPath,
deprecated: require("./deprecated")

View File

@ -22,11 +22,7 @@ var tar = require("tar");
var registry = require("./registry");
var library = require("./library");
var log;
var exec;
var events;
const {exec,log,events} = require("@node-red/util");
var child_process = require('child_process');
var npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
var paletteEditorEnabled = false;
@ -37,11 +33,8 @@ const slashRe = process.platform === "win32" ? /\\|[/]/ : /[/]/;
const pkgurlRe = /^(https?|git(|\+https?|\+ssh|\+file)):\/\//;
const localtgzRe = /^([a-zA-Z]:|\/).+tgz$/;
function init(runtime) {
events = runtime.events;
settings = runtime.settings;
log = runtime.log;
exec = runtime.exec;
function init(_settings) {
settings = _settings;
}
var activePromise = Promise.resolve();

View File

@ -14,7 +14,7 @@
* limitations under the License.
**/
var fs = require('fs');
var fs = require('fs-extra');
var fspath = require('path');
var runtime;
@ -22,38 +22,34 @@ var runtime;
var exampleRoots = {};
var exampleFlows = null;
function getFlowsFromPath(path) {
return new Promise(function(resolve,reject) {
var result = {};
fs.readdir(path,function(err,files) {
var promises = [];
var validFiles = [];
files.forEach(function(file) {
var fullPath = fspath.join(path,file);
var stats = fs.lstatSync(fullPath);
if (stats.isDirectory()) {
validFiles.push(file);
promises.push(getFlowsFromPath(fullPath));
} else if (/\.json$/.test(file)){
validFiles.push(file);
promises.push(Promise.resolve(file.split(".")[0]))
}
})
var i=0;
Promise.all(promises).then(function(results) {
results.forEach(function(r) {
if (typeof r === 'string') {
result.f = result.f||[];
result.f.push(r);
} else {
result.d = result.d||{};
result.d[validFiles[i]] = r;
}
i++;
})
resolve(result);
})
});
async function getFlowsFromPath(path) {
var result = {};
var validFiles = [];
return fs.readdir(path).then(files => {
var promises = [];
files.forEach(function(file) {
var fullPath = fspath.join(path,file);
var stats = fs.lstatSync(fullPath);
if (stats.isDirectory()) {
validFiles.push(file);
promises.push(getFlowsFromPath(fullPath));
} else if (/\.json$/.test(file)){
validFiles.push(file);
promises.push(Promise.resolve(file.split(".")[0]))
}
})
return Promise.all(promises)
}).then(results => {
results.forEach(function(r,i) {
if (typeof r === 'string') {
result.f = result.f||[];
result.f.push(r);
} else {
result.d = result.d||{};
result.d[validFiles[i]] = r;
}
})
return result;
})
}

View File

@ -22,15 +22,14 @@ var localfilesystem = require("./localfilesystem");
var registry = require("./registry");
var registryUtil = require("./util")
var i18n = require("@node-red/util").i18n;
var log = require("@node-red/util").log;
var settings;
var runtime;
function init(_runtime) {
runtime = _runtime;
settings = runtime.settings;
localfilesystem.init(runtime);
registryUtil.init(runtime);
settings = _runtime.settings;
localfilesystem.init(settings);
registryUtil.init(_runtime);
}
function load(disableNodePathScan) {
@ -38,7 +37,7 @@ function load(disableNodePathScan) {
// We should expose that as an option at some point, although the
// performance gains are minimal.
//return loadNodeFiles(registry.getModuleList());
runtime.log.info(runtime.log._("server.loading"));
log.info(log._("server.loading"));
var nodeFiles = localfilesystem.getNodeFiles(disableNodePathScan);
return loadNodeFiles(nodeFiles);
@ -51,9 +50,9 @@ function loadNodeFiles(nodeFiles) {
/* istanbul ignore else */
if (nodeFiles.hasOwnProperty(module)) {
if (nodeFiles[module].redVersion &&
!semver.satisfies(runtime.version().replace(/(\-[1-9A-Za-z-][0-9A-Za-z-\.]*)?(\+[0-9A-Za-z-\.]+)?$/,""), nodeFiles[module].redVersion)) {
!semver.satisfies((settings.version||"0.0.0").replace(/(\-[1-9A-Za-z-][0-9A-Za-z-\.]*)?(\+[0-9A-Za-z-\.]+)?$/,""), nodeFiles[module].redVersion)) {
//TODO: log it
runtime.log.warn("["+module+"] "+runtime.log._("server.node-version-mismatch",{version:nodeFiles[module].redVersion}));
log.warn("["+module+"] "+log._("server.node-version-mismatch",{version:nodeFiles[module].redVersion}));
nodeFiles[module].err = "version_mismatch";
continue;
}

View File

@ -16,9 +16,6 @@
var fs = require("fs");
var path = require("path");
var log;
var log = require("@node-red/util").log;
var i18n = require("@node-red/util").i18n;
@ -26,8 +23,8 @@ var settings;
var disableNodePathScan = false;
var iconFileExtensions = [".png", ".gif", ".svg"];
function init(runtime) {
settings = runtime.settings;
function init(_settings) {
settings = _settings;
}
function isIncluded(name) {

View File

@ -19,8 +19,7 @@ var path = require("path");
var fs = require("fs");
var library = require("./library");
var events;
const {events} = require("@node-red/util")
var settings;
var loader;
@ -31,10 +30,9 @@ var nodeConstructors = {};
var nodeTypeToId = {};
var moduleNodes = {};
function init(_settings,_loader, _events) {
function init(_settings,_loader) {
settings = _settings;
loader = _loader;
events = _events;
moduleNodes = {};
nodeTypeToId = {};
nodeConstructors = {};

View File

@ -14,9 +14,8 @@
* limitations under the License.
**/
var path = require("path");
var i18n = require("@node-red/util").i18n;
var registry;
const path = require("path");
const {events,i18n,log} = require("@node-red/util");
var runtime;
function copyObjectProperties(src,dst,copyList,blockList) {
@ -40,7 +39,7 @@ function copyObjectProperties(src,dst,copyList,blockList) {
}
}
function requireModule(name) {
var moduleInfo = registry.getModuleInfo(name);
var moduleInfo = require("./index").getModuleInfo(name);
if (moduleInfo && moduleInfo.path) {
var relPath = path.relative(__dirname, moduleInfo.path);
return require(relPath);
@ -56,14 +55,14 @@ function createNodeApi(node) {
nodes: {},
log: {},
settings: {},
events: runtime.events,
events: events,
hooks: runtime.hooks,
util: runtime.util,
version: runtime.version,
require: requireModule,
comms: {
publish: function(topic,data,retain) {
runtime.events.emit("comms",{
events.emit("comms",{
topic: topic,
data: data,
retain: retain
@ -83,7 +82,7 @@ function createNodeApi(node) {
red.nodes.registerType = function(type,constructor,opts) {
runtime.nodes.registerType(node.id,type,constructor,opts);
}
copyObjectProperties(runtime.log,red.log,null,["init"]);
copyObjectProperties(log,red.log,null,["init"]);
copyObjectProperties(runtime.settings,red.settings,null,["init","load","reset"]);
if (runtime.adminApi) {
red.auth = runtime.adminApi.auth;
@ -108,7 +107,6 @@ function createNodeApi(node) {
module.exports = {
init: function(_runtime) {
runtime = _runtime;
registry = require("@node-red/registry/lib");
},
createNodeApi: createNodeApi
}

View File

@ -33,6 +33,7 @@ var runtime;
var retained = {};
var connections = [];
const events = require("@node-red/util").events;
function handleCommsEvent(event) {
publish(event.topic,event.data,event.retain);
@ -88,14 +89,14 @@ var api = module.exports = {
runtime = _runtime;
connections = [];
retained = {};
runtime.events.removeListener("node-status",handleStatusEvent);
runtime.events.on("node-status",handleStatusEvent);
runtime.events.removeListener("runtime-event",handleRuntimeEvent);
runtime.events.on("runtime-event",handleRuntimeEvent);
runtime.events.removeListener("comms",handleCommsEvent);
runtime.events.on("comms",handleCommsEvent);
runtime.events.removeListener("event-log",handleEventLog);
runtime.events.on("event-log",handleEventLog);
events.removeListener("node-status",handleStatusEvent);
events.on("node-status",handleStatusEvent);
events.removeListener("runtime-event",handleRuntimeEvent);
events.on("runtime-event",handleRuntimeEvent);
events.removeListener("comms",handleCommsEvent);
events.on("comms",handleCommsEvent);
events.removeListener("event-log",handleEventLog);
events.on("event-log",handleEventLog);
},
/**

View File

@ -376,16 +376,18 @@ var api = module.exports = {
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 runtime.i18n.i.changeLanguage(lang, function(){
var nodeList = runtime.nodes.getNodeList();
var result = {};
nodeList.forEach(function(n) {
if (n.module !== "node-red") {
result[n.id] = runtime.i18n.i.getResourceBundle(lang, n.id)||{};
}
return new Promise( (resolve,reject) => {
runtime.i18n.i.changeLanguage(lang, function(){
var nodeList = runtime.nodes.getNodeList();
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);
});
runtime.i18n.i.changeLanguage(prevLang);
return result;
});
},

View File

@ -16,8 +16,8 @@
var clone = require("clone");
var redUtil = require("@node-red/util").util;
const events = require("@node-red/util").events;
var flowUtil = require("./util");
var events = require("../events");
const context = require('../nodes/context');
const hooks = require("../hooks");
@ -679,7 +679,6 @@ module.exports = {
asyncMessageDelivery = !runtime.settings.runtimeSyncDelivery
Log = runtime.log;
Subflow = require("./Subflow");
Subflow.init(runtime);
},
create: function(parent,global,conf) {
return new Flow(parent,global,conf);

View File

@ -18,16 +18,11 @@ const clone = require("clone");
const Flow = require('./Flow').Flow;
const context = require('../nodes/context');
const util = require("util");
const events = require("../events");
const redUtil = require("@node-red/util").util;
const events = require("@node-red/util").events;
const flowUtil = require("./util");
const credentials = require("../nodes/credentials");
var Log;
/**
* Create deep copy of object
*/
@ -509,8 +504,6 @@ function createSubflow(parent,globalFlow,subflowDef,subflowInstance) {
}
module.exports = {
init: function(runtime) {
Log = runtime.log;
},
init: function(runtime) {},
create: createSubflow
}

View File

@ -25,9 +25,8 @@ var context = require("../nodes/context")
var credentials = require("../nodes/credentials");
var flowUtil = require("./util");
var log;
var events = require("../events");
const events = require("@node-red/util").events;
var redUtil = require("@node-red/util").util;
const hooks = require("../hooks");
var storage = null;
var settings = null;
@ -712,7 +711,7 @@ module.exports = {
*/
load: load,
loadFlows: load,
get:getNode,
eachNode: eachNode,

View File

@ -20,19 +20,15 @@ var redNodes = require("./nodes");
var flows = require("./flows");
var storage = require("./storage");
var library = require("./library");
var events = require("./events");
var hooks = require("./hooks");
var settings = require("./settings");
var exec = require("./exec");
var express = require("express");
var path = require('path');
var fs = require("fs");
var os = require("os");
var redUtil = require("@node-red/util");
var log = redUtil.log;
var i18n = redUtil.i18n;
const {log,i18n,events,exec,util} = require("@node-red/util");
var runtimeMetricInterval = null;
@ -65,7 +61,7 @@ var server;
* better abstracted.
* @memberof @node-red/runtime
*/
function init(userSettings,httpServer,_adminApi,__util) {
function init(userSettings,httpServer,_adminApi) {
server = httpServer;
userSettings.version = getVersion();
settings.init(userSettings);
@ -79,14 +75,6 @@ function init(userSettings,httpServer,_adminApi,__util) {
redNodes.init(runtime);
library.init(runtime);
externalAPI.init(runtime);
exec.init(runtime);
if (__util) {
log = __util.log;
i18n = __util.i18n;
} else {
log = redUtil.log;
i18n = redUtil.i18n;
}
}
var version;
@ -246,6 +234,10 @@ function reportMetrics() {
/**
* Stops the runtime.
*
* Once called, Node-RED should not be restarted until the Node.JS process is
* restarted.
*
* @return {Promise} - resolves when the runtime is stopped.
* @memberof @node-red/runtime
*/
@ -266,17 +258,17 @@ function stop() {
// This is the internal api
var runtime = {
version: getVersion,
get log() { return log },
get i18n() { return i18n },
log: log,
i18n: i18n,
events: events,
settings: settings,
storage: storage,
events: events,
hooks: hooks,
nodes: redNodes,
flows: flows,
library: library,
exec: exec,
util: require("@node-red/util").util,
util: util,
get adminApi() { return adminApi },
get adminApp() { return adminApp },
get nodeApp() { return nodeApp },

View File

@ -28,7 +28,7 @@ var context = require("./context");
var Node = require("./Node");
var log;
var events = require("../events");
const events = require("@node-red/util").events;
var settings;

View File

@ -17,7 +17,7 @@
var Path = require('path');
var crypto = require('crypto');
var log = require("@node-red/util").log; // TODO: separate module
var log = require("@node-red/util").log;
var runtime;
var storageModule;

View File

@ -26,6 +26,7 @@ var sshKeys = require("./ssh");
var settings;
var runtime;
var log = require("@node-red/util").log;
const events = require("@node-red/util").events;
var projectsDir;
@ -532,7 +533,7 @@ Project.prototype.status = function(user, includeRemote) {
result.merging = true;
if (!self.merging) {
self.merging = true;
runtime.events.emit("runtime-event",{
events.emit("runtime-event",{
id:"runtime-state",
payload:{
type:"warning",
@ -556,7 +557,7 @@ Project.prototype.status = function(user, includeRemote) {
}
if (result.commits.total === 0 && Object.keys(result.files).length === 0) {
if (!self.empty) {
runtime.events.emit("runtime-event",{
events.emit("runtime-event",{
id:"runtime-state",
payload:{
type:"warning",
@ -570,9 +571,9 @@ Project.prototype.status = function(user, includeRemote) {
} else {
if (self.empty) {
if (self.paths.flowFile) {
runtime.events.emit("runtime-event",{id:"runtime-state",retain:true});
events.emit("runtime-event",{id:"runtime-state",retain:true});
} else {
runtime.events.emit("runtime-event",{
events.emit("runtime-event",{
id:"runtime-state",
payload:{
type:"warning",

View File

@ -14,8 +14,6 @@
* limitations under the License.
**/
var exec = require("../../../../exec");
var authResponseServer = require('./authServer').ResponseServer;
var sshResponseServer = require('./authServer').ResponseSSHServer;
var clone = require('clone');
@ -23,7 +21,7 @@ var path = require("path");
var gitCommand = "git";
var gitVersion;
var log = require("@node-red/util").log;
const {log,exec} = require("@node-red/util");
function runGitCommand(args,cwd,env,emit) {
log.trace(gitCommand + JSON.stringify(args));

View File

@ -28,6 +28,7 @@ var Projects = require("./Project");
var settings;
var runtime;
var log = require("@node-red/util").log;
const events = require("@node-red/util").events;
var projectsEnabled = false;
var projectLogMessages = [];
@ -355,11 +356,11 @@ function getActiveProject(user) {
function reloadActiveProject(action) {
return runtime.nodes.stopFlows().then(function() {
return runtime.nodes.loadFlows(true).then(function() {
runtime.events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
}).catch(function(err) {
// We're committed to the project change now, so notify editors
// that it has changed.
runtime.events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
throw err;
});
});

View File

@ -17,6 +17,8 @@
const log = require("./lib/log");
const i18n = require("./lib/i18n");
const util = require("./lib/util");
const events = require("./lib/events");
const exec = require("./lib/exec");
/**
* This module provides common utilities for the Node-RED runtime and editor
@ -54,4 +56,18 @@ module.exports = {
* @memberof @node-red/util
*/
util: util,
/**
* Runtime events
* @mixes @node-red/util_event
* @memberof @node-red/util
*/
events: events,
/**
* Run system commands with event-log integration
* @mixes @node-red/util_exec
* @memberof @node-red/util
*/
exec: exec
}

View File

@ -14,6 +14,11 @@
* limitations under the License.
**/
/**
* Runtime events
* @mixin @node-red/util_events
*/
const events = new (require("events")).EventEmitter();
@ -45,14 +50,14 @@ module.exports = events;
/**
* Runtime events emitter
* @mixin @node-red/runtime_events
* @mixin @node-red/util_events
*/
/**
* Register an event listener for a runtime event
* @name on
* @function
* @memberof @node-red/runtime_events
* @memberof @node-red/util_events
* @param {String} eventName - the name of the event to listen to
* @param {Function} listener - the callback function for the event
*/
@ -61,7 +66,7 @@ module.exports = events;
* Emit an event to all of its registered listeners
* @name emit
* @function
* @memberof @node-red/runtime_events
* @memberof @node-red/util_events
* @param {String} eventName - the name of the event to emit
* @param {any} ...args - the arguments to pass in the event
* @return {Boolean} - whether the event had listeners or not

View File

@ -1,4 +1,4 @@
/**
/*!
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -14,19 +14,41 @@
* limitations under the License.
**/
const child_process = require('child_process');
const { util } = require('@node-red/util');
/**
* Run system commands with event-log integration
* @mixin @node-red/util_exec
*/
var events;
const child_process = require('child_process');
const events = require("./events");
const util = require('./util');
function logLines(id,type,data) {
events.emit("event-log", {id:id,payload:{ts: Date.now(),data:data,type:type}});
}
module.exports = {
init: function(_runtime) {
events = _runtime.events;
},
/**
* Run a system command with stdout/err being emitted as 'event-log' events
* on the @node-red/util/events handler.
*
* The main arguments to this function are the same as passed to `child_process.spawn`
*
* @param {String} command - the command to run
* @param {Array} args - arguments for the command
* @param {Object} options - options to pass child_process.spawn
* @param {Boolean} emit - whether to emit events to the event-log for each line of stdout/err
* @return {Promise} A promise that resolves (rc=0) or rejects (rc!=0) when the command completes. The value
* of the promise is an object of the form:
*
* {
* code: <exit code>,
* stdout: <standard output from the command>,
* stderr: <standard error from the command>
* }
* @memberof @node-red/util_exec
*/
run: function(command,args,options,emit) {
var invocationId = util.generateId();

View File

@ -1,4 +1,4 @@
/**
/*!
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -12,7 +12,6 @@
* 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.
* @ignore
**/
/**
@ -91,7 +90,7 @@ var consoleLogger = function(msg) {
} catch(e){
message = 'Exception trying to log: '+util.inspect(message);
}
util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+message);
}
}

View File

@ -121,6 +121,10 @@ module.exports = {
},
/**
* Stop the Node-RED application.
*
* Once called, Node-RED should not be restarted until the Node.JS process is
* restarted.
*
* @return {Promise} - resolves when complete
* @memberof node-red
*/
@ -161,10 +165,10 @@ module.exports = {
/**
* Runtime events emitter
* @see @node-red/runtime_events
* @see @node-red/util_events
* @memberof node-red
*/
events: runtime.events,
events: redUtil.events,
/**
* Runtime hooks engine

View File

@ -25,6 +25,7 @@ var NR_TEST_UTILS = require("nr-test-utils");
var installer = NR_TEST_UTILS.require("@node-red/registry/lib/installer");
var registry = NR_TEST_UTILS.require("@node-red/registry/lib/index");
var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry");
const { events, exec, log } = NR_TEST_UTILS.require("@node-red/util");
describe('nodes/registry/installer', function() {
@ -38,21 +39,15 @@ describe('nodes/registry/installer', function() {
_: function(msg) { return msg }
}
var execResponse;
beforeEach(function() {
installer.init({log:mockLog, settings:{}, events: new EventEmitter(), exec: {
run: function() {
return Promise.resolve("");
}
}});
sinon.stub(exec,"run", () => execResponse || Promise.resolve(""))
installer.init({})
});
function initInstaller(execResult) {
installer.init({log:mockLog, settings:{}, events: new EventEmitter(), exec: {
run: function() {
return execResult;
}
}});
}
afterEach(function() {
execResponse = null;
if (registry.addModule.restore) {
registry.addModule.restore();
}
@ -72,7 +67,7 @@ describe('nodes/registry/installer', function() {
if (fs.statSync.restore) {
fs.statSync.restore();
}
exec.run.restore();
});
describe("installs module", function() {
@ -108,7 +103,7 @@ describe('nodes/registry/installer', function() {
}
var p = Promise.reject(res);
p.catch((err)=>{});
initInstaller(p)
execResponse = p;
installer.installModule("this_wont_exist").catch(function(err) {
err.should.have.property("code",404);
done();
@ -122,7 +117,7 @@ describe('nodes/registry/installer', function() {
}
var p = Promise.reject(res);
p.catch((err)=>{});
initInstaller(p)
execResponse = p;
sinon.stub(typeRegistry,"getModuleInfo", function() {
return {
version: "0.1.1"
@ -163,7 +158,7 @@ describe('nodes/registry/installer', function() {
}
var p = Promise.reject(res);
p.catch((err)=>{});
initInstaller(p)
execResponse = p;
installer.installModule("this_wont_exist").then(function() {
done(new Error("Unexpected success"));
}).catch(err => {
@ -181,7 +176,7 @@ describe('nodes/registry/installer', function() {
}
var p = Promise.resolve(res);
p.catch((err)=>{});
initInstaller(p)
execResponse = p;
var addModule = sinon.stub(registry,"addModule",function(md) {
return Promise.resolve(nodeInfo);
@ -226,7 +221,7 @@ describe('nodes/registry/installer', function() {
}
var p = Promise.resolve(res);
p.catch((err)=>{});
initInstaller(p)
execResponse = p;
installer.installModule(resourcesDir).then(function(info) {
info.should.eql(nodeInfo);
done();
@ -242,7 +237,7 @@ describe('nodes/registry/installer', function() {
}
var p = Promise.resolve(res);
p.catch((err)=>{});
initInstaller(p)
execResponse = p;
var addModule = sinon.stub(registry,"addModule",function(md) {
return Promise.resolve(nodeInfo);
@ -280,7 +275,7 @@ describe('nodes/registry/installer', function() {
}
var p = Promise.reject(res);
p.catch((err)=>{});
initInstaller(p)
execResponse = p;
installer.uninstallModule("this_wont_exist").then(function() {
done(new Error("Unexpected success"));
@ -304,7 +299,7 @@ describe('nodes/registry/installer', function() {
}
var p = Promise.resolve(res);
p.catch((err)=>{});
initInstaller(p)
execResponse = p;
sinon.stub(fs,"statSync", function(fn) { return {}; });

View File

@ -53,7 +53,7 @@ describe("red/nodes/registry/localfilesystem",function() {
}
describe("#getNodeFiles",function() {
it("Finds all the node files in the resources tree",function(done) {
localfilesystem.init({settings:{coreNodesDir:resourcesDir}});
localfilesystem.init({coreNodesDir:resourcesDir});
var nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
var nm = nodeList['node-red'];
@ -68,7 +68,7 @@ describe("red/nodes/registry/localfilesystem",function() {
done();
});
it("Includes node files from settings",function(done) {
localfilesystem.init({settings:{nodesIncludes:['TestNode1.js'],coreNodesDir:resourcesDir}});
localfilesystem.init({nodesIncludes:['TestNode1.js'],coreNodesDir:resourcesDir});
var nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
var nm = nodeList['node-red'];
@ -78,7 +78,7 @@ describe("red/nodes/registry/localfilesystem",function() {
done();
});
it("Excludes node files from settings",function(done) {
localfilesystem.init({settings:{nodesExcludes:['TestNode1.js'],coreNodesDir:resourcesDir}});
localfilesystem.init({nodesExcludes:['TestNode1.js'],coreNodesDir:resourcesDir});
var nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
var nm = nodeList['node-red'];
@ -88,7 +88,7 @@ describe("red/nodes/registry/localfilesystem",function() {
done();
});
it("Finds nodes in userDir/nodes",function(done) {
localfilesystem.init({settings:{userDir:userDir}});
localfilesystem.init({userDir:userDir});
var nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
var nm = nodeList['node-red'];
@ -99,7 +99,7 @@ describe("red/nodes/registry/localfilesystem",function() {
});
it("Finds nodes in settings.nodesDir (string)",function(done) {
localfilesystem.init({settings:{nodesDir:userDir}});
localfilesystem.init({nodesDir:userDir});
var nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
var nm = nodeList['node-red'];
@ -110,7 +110,7 @@ describe("red/nodes/registry/localfilesystem",function() {
});
it("Finds nodes in settings.nodesDir (string,relative path)",function(done) {
var relativeUserDir = path.join("test","unit","@node-red","registry","lib","resources","userDir");
localfilesystem.init({settings:{nodesDir:relativeUserDir}});
localfilesystem.init({nodesDir:relativeUserDir});
var nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
var nm = nodeList['node-red'];
@ -120,7 +120,7 @@ describe("red/nodes/registry/localfilesystem",function() {
done();
});
it("Finds nodes in settings.nodesDir (array)",function(done) {
localfilesystem.init({settings:{nodesDir:[userDir]}});
localfilesystem.init({nodesDir:[userDir]});
var nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
var nm = nodeList['node-red'];
@ -139,7 +139,7 @@ describe("red/nodes/registry/localfilesystem",function() {
}
return _join.apply(null,arguments);
}));
localfilesystem.init({settings:{coreNodesDir:moduleDir}});
localfilesystem.init({coreNodesDir:moduleDir});
var nodeList = localfilesystem.getNodeFiles();
nodeList.should.have.a.property("node-red");
var nm = nodeList['node-red'];
@ -175,18 +175,7 @@ describe("red/nodes/registry/localfilesystem",function() {
it("scans icon files in the resources tree",function(done) {
var count = 0;
localfilesystem.init({
// events:{emit:function(eventName,dir){
// if (count === 0) {
// eventName.should.equal("node-icon-dir");
// dir.name.should.equal("node-red");
// dir.icons.should.be.an.Array();
// count = 1;
// } else if (count === 1) {
// done();
// }
// }},
settings:{coreNodesDir:resourcesDir}
coreNodesDir: resourcesDir
});
var list = localfilesystem.getNodeFiles(true);
list.should.have.property("node-red");
@ -201,22 +190,7 @@ describe("red/nodes/registry/localfilesystem",function() {
it("scans icons dir in library",function(done) {
var count = 0;
localfilesystem.init({
//
// events:{emit:function(eventName,dir){
// eventName.should.equal("node-icon-dir");
// if (count === 0) {
// dir.name.should.equal("node-red");
// dir.icons.should.be.an.Array();
// count = 1;
// } else if (count === 1) {
// dir.name.should.equal("Library");
// dir.icons.should.be.an.Array();
// dir.icons.length.should.equal(1);
// dir.icons[0].should.be.equal("test_icon.png");
// done();
// }
// }},
settings:{userDir:userDir}
userDir: userDir
});
var list = localfilesystem.getNodeFiles(true);
list.should.have.property("node-red");
@ -240,7 +214,7 @@ describe("red/nodes/registry/localfilesystem",function() {
}
return _join.apply(null,arguments);
}));
localfilesystem.init({settings:{coreNodesDir:moduleDir}});
localfilesystem.init({coreNodesDir:moduleDir});
var nodeModule = localfilesystem.getModuleFiles('TestNodeModule');
nodeModule.should.have.a.property('TestNodeModule');
nodeModule['TestNodeModule'].should.have.a.property('name','TestNodeModule');
@ -266,7 +240,7 @@ describe("red/nodes/registry/localfilesystem",function() {
}
return _join.apply(null,arguments);
}));
localfilesystem.init({settings:{coreNodesDir:moduleDir}});
localfilesystem.init({coreNodesDir:moduleDir});
/*jshint immed: false */
(function(){
localfilesystem.getModuleFiles('WontExistModule');
@ -286,14 +260,7 @@ describe("red/nodes/registry/localfilesystem",function() {
return _join.apply(null,arguments);
}));
localfilesystem.init({
// events:{emit:function(eventName,dir){
// eventName.should.equal("node-icon-dir");
// dir.name.should.equal("TestNodeModule");
// dir.icons.should.be.an.Array();
// done();
// }},
settings:{coreNodesDir:moduleDir}
coreNodesDir: moduleDir
});
var nodeModule = localfilesystem.getModuleFiles('TestNodeModule');
nodeModule.should.have.property("TestNodeModule");

View File

@ -21,9 +21,7 @@ var path = require("path");
var NR_TEST_UTILS = require("nr-test-utils");
var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry");
var EventEmitter = require('events');
var events = new EventEmitter();
const { events } = NR_TEST_UTILS.require("@node-red/util");
describe("red/nodes/registry/registry",function() {
@ -84,7 +82,7 @@ describe("red/nodes/registry/registry",function() {
describe('#init/load', function() {
it('loads initial config', function(done) {
typeRegistry.init(settingsWithStorageAndInitialConfig,null,events);
typeRegistry.init(settingsWithStorageAndInitialConfig,null);
typeRegistry.getNodeList().should.have.lengthOf(0);
typeRegistry.load();
typeRegistry.getNodeList().should.have.lengthOf(1);
@ -121,7 +119,7 @@ describe("red/nodes/registry/registry",function() {
}}
};
var expected = JSON.parse('{"node-red":{"name":"node-red","nodes":{"sentiment":{"name":"sentiment","types":["sentiment"],"enabled":true,"module":"node-red"},"inject":{"name":"inject","types":["inject"],"enabled":true,"module":"node-red"}}},"testModule":{"name":"testModule","nodes":{"a-module.js":{"name":"a-module.js","types":["example"],"enabled":true,"module":"testModule"}}}}');
typeRegistry.init(legacySettings,null,events);
typeRegistry.init(legacySettings,null);
typeRegistry.load();
legacySettings.set.calledOnce.should.be.true();
legacySettings.set.args[0][1].should.eql(expected);
@ -133,7 +131,7 @@ describe("red/nodes/registry/registry",function() {
describe.skip('#addNodeSet', function() {
it('adds a node set for an unknown module', function() {
typeRegistry.init(settings,null,events);
typeRegistry.init(settings,null);
typeRegistry.getNodeList().should.have.lengthOf(0);
typeRegistry.getModuleList().should.eql({});
@ -162,7 +160,7 @@ describe("red/nodes/registry/registry",function() {
it('adds a node set to an existing module', function() {
typeRegistry.init(settings,null,events);
typeRegistry.init(settings,null);
typeRegistry.getNodeList().should.have.lengthOf(0);
typeRegistry.getModuleList().should.eql({});
@ -191,7 +189,7 @@ describe("red/nodes/registry/registry",function() {
});
it('doesnt add node set types if node set has an error', function() {
typeRegistry.init(settings,null,events);
typeRegistry.init(settings,null);
typeRegistry.getNodeList().should.have.lengthOf(0);
typeRegistry.getModuleList().should.eql({});
@ -207,7 +205,7 @@ describe("red/nodes/registry/registry",function() {
});
it('doesnt add node set if type already exists', function() {
typeRegistry.init(settings,null,events);
typeRegistry.init(settings,null);
typeRegistry.getNodeList().should.have.lengthOf(0);
typeRegistry.getModuleList().should.eql({});
@ -241,7 +239,7 @@ describe("red/nodes/registry/registry",function() {
describe("#enableNodeSet", function() {
it('throws error if settings unavailable', function() {
typeRegistry.init(settings,null,events);
typeRegistry.init(settings,null);
/*jshint immed: false */
(function(){
typeRegistry.enableNodeSet("test-module/test-name");
@ -249,7 +247,7 @@ describe("red/nodes/registry/registry",function() {
});
it('throws error if module unknown', function() {
typeRegistry.init(settingsWithStorageAndInitialConfig,null,events);
typeRegistry.init(settingsWithStorageAndInitialConfig,null);
/*jshint immed: false */
(function(){
typeRegistry.enableNodeSet("test-module/unknown");
@ -260,7 +258,7 @@ describe("red/nodes/registry/registry",function() {
});
describe("#disableNodeSet", function() {
it('throws error if settings unavailable', function() {
typeRegistry.init(settings,null,events);
typeRegistry.init(settings,null);
/*jshint immed: false */
(function(){
typeRegistry.disableNodeSet("test-module/test-name");
@ -268,7 +266,7 @@ describe("red/nodes/registry/registry",function() {
});
it('throws error if module unknown', function() {
typeRegistry.init(settingsWithStorageAndInitialConfig,null,events);
typeRegistry.init(settingsWithStorageAndInitialConfig,null);
/*jshint immed: false */
(function(){
typeRegistry.disableNodeSet("test-module/unknown");
@ -279,7 +277,7 @@ describe("red/nodes/registry/registry",function() {
describe('#getNodeConfig', function() {
it('returns nothing for an unregistered type config', function(done) {
typeRegistry.init(settings,null,events);
typeRegistry.init(settings,null);
var config = typeRegistry.getNodeConfig("imaginary-shark");
(config === null).should.be.true();
done();
@ -288,7 +286,7 @@ describe("red/nodes/registry/registry",function() {
describe('#saveNodeList',function() {
it('rejects when settings unavailable',function(done) {
typeRegistry.init(stubSettings({},false,{}),null,events);
typeRegistry.init(stubSettings({},false,{}),null);
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {"test-name":{module:"test-module",name:"test-name",types:[]}}});
typeRegistry.saveNodeList().catch(function(err) {
done();
@ -296,7 +294,7 @@ describe("red/nodes/registry/registry",function() {
});
it('saves the list',function(done) {
var s = stubSettings({},true,{});
typeRegistry.init(s,null,events);
typeRegistry.init(s,null);
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":testNodeSet1,
@ -325,7 +323,7 @@ describe("red/nodes/registry/registry",function() {
describe('#removeModule',function() {
it('throws error for unknown module', function() {
var s = stubSettings({},true,{});
typeRegistry.init(s,null,events);
typeRegistry.init(s,null);
/*jshint immed: false */
(function(){
typeRegistry.removeModule("test-module/unknown");
@ -333,7 +331,7 @@ describe("red/nodes/registry/registry",function() {
});
it('throws error for unavaiable settings', function() {
var s = stubSettings({},false,{});
typeRegistry.init(s,null,events);
typeRegistry.init(s,null);
/*jshint immed: false */
(function(){
typeRegistry.removeModule("test-module/unknown");
@ -341,7 +339,7 @@ describe("red/nodes/registry/registry",function() {
});
it('removes a known module', function() {
var s = stubSettings({},true,{});
typeRegistry.init(s,null,events);
typeRegistry.init(s,null);
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":testNodeSet1
}});
@ -360,7 +358,7 @@ describe("red/nodes/registry/registry",function() {
it('returns node config', function() {
typeRegistry.init(settings,{
getNodeHelp: function(config) { return "HE"+config.name+"LP" }
},events);
});
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":{
@ -389,7 +387,7 @@ describe("red/nodes/registry/registry",function() {
});
describe('#getModuleInfo', function() {
it('returns module info', function() {
typeRegistry.init(settings,{},events);
typeRegistry.init(settings,{});
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":{
id: "test-module/test-name",
@ -413,7 +411,7 @@ describe("red/nodes/registry/registry",function() {
});
describe('#getNodeInfo', function() {
it('returns node info', function() {
typeRegistry.init(settings,{},events);
typeRegistry.init(settings,{});
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":{
id: "test-module/test-name",
@ -434,7 +432,7 @@ describe("red/nodes/registry/registry",function() {
});
describe('#getFullNodeInfo', function() {
it('returns node info', function() {
typeRegistry.init(settings,{},events);
typeRegistry.init(settings,{});
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":{
id: "test-module/test-name",
@ -459,7 +457,7 @@ describe("red/nodes/registry/registry",function() {
});
describe('#getNodeList', function() {
it("returns a filtered list", function() {
typeRegistry.init(settings,{},events);
typeRegistry.init(settings,{});
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":{
id: "test-module/test-name",
@ -526,7 +524,7 @@ describe("red/nodes/registry/registry",function() {
it('returns a registered icon' , function() {
var testIcon = path.resolve(__dirname+'/resources/userDir/lib/icons/');
typeRegistry.init(settings,{},events);
typeRegistry.init(settings,{});
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":{
id: "test-module/test-name",
@ -558,7 +556,7 @@ describe("red/nodes/registry/registry",function() {
it('returns an icon list of registered node module', function() {
var testIcon = path.resolve(__dirname+'/resources/userDir/lib/icons/');
typeRegistry.init(settings,{},events);
typeRegistry.init(settings,{});
typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {
"test-name":{
id: "test-module/test-name",

View File

@ -19,6 +19,7 @@ var sinon = require("sinon");
var NR_TEST_UTILS = require("nr-test-utils");
var comms = NR_TEST_UTILS.require("@node-red/runtime/lib/api/comms");
var events = NR_TEST_UTILS.require("@node-red/util/lib/events");
describe("runtime-api/comms", function() {
describe("listens for events", function() {
@ -30,21 +31,19 @@ describe("runtime-api/comms", function() {
}
var eventHandlers = {};
before(function(done) {
sinon.stub(events,"removeListener", function() {})
sinon.stub(events,"on", function(evt,handler) { eventHandlers[evt] = handler })
comms.init({
log: {
trace: function(){}
},
events: {
removeListener: function() {},
on: function(evt,handler) {
eventHandlers[evt] = handler;
}
}
})
comms.addConnection({client: clientConnection}).then(done);
})
after(function(done) {
comms.removeConnection({client: clientConnection}).then(done);
events.removeListener.restore();
events.on.restore();
})
afterEach(function() {
messages = [];
@ -98,18 +97,18 @@ describe("runtime-api/comms", function() {
}
}
before(function() {
sinon.stub(events,"removeListener", function() {})
sinon.stub(events,"on", function(evt,handler) { eventHandlers[evt] = handler })
comms.init({
log: {
trace: function(){}
},
events: {
removeListener: function() {},
on: function(evt,handler) {
eventHandlers[evt] = handler;
}
}
})
})
after(function() {
events.removeListener.restore();
events.on.restore();
})
afterEach(function(done) {
comms.removeConnection({client: clientConnection1}).then(function() {
comms.removeConnection({client: clientConnection2}).then(done);
@ -178,18 +177,18 @@ describe("runtime-api/comms", function() {
}
var eventHandlers = {};
before(function() {
sinon.stub(events,"removeListener", function() {})
sinon.stub(events,"on", function(evt,handler) { eventHandlers[evt] = handler })
comms.init({
log: {
trace: function(){}
},
events: {
removeListener: function() {},
on: function(evt,handler) {
eventHandlers[evt] = handler;
}
}
})
})
after(function() {
events.removeListener.restore();
events.on.restore();
})
afterEach(function(done) {
messages = [];
comms.removeConnection({client: clientConnection}).then(done);

View File

@ -22,7 +22,7 @@ var NR_TEST_UTILS = require("nr-test-utils");
var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows");
var RedNode = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node");
var RED = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes");
var events = NR_TEST_UTILS.require("@node-red/runtime/lib/events");
var events = NR_TEST_UTILS.require("@node-red/util/lib/events");
var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials");
var typeRegistry = NR_TEST_UTILS.require("@node-red/registry")
var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow");

View File

@ -28,6 +28,7 @@ var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/settings");
var util = NR_TEST_UTILS.require("@node-red/util");
var log = NR_TEST_UTILS.require("@node-red/util").log;
var i18n = NR_TEST_UTILS.require("@node-red/util").i18n;
describe("runtime", function() {
afterEach(function() {
@ -43,43 +44,45 @@ describe("runtime", function() {
delete process.env.NODE_RED_HOME;
});
function mockUtil(metrics) {
return {
log:{
log: sinon.stub(),
warn: sinon.stub(),
info: sinon.stub(),
trace: sinon.stub(),
metric: sinon.stub().returns(!!metrics),
_: function() { return "abc"}
},
i18n: {
registerMessageCatalog: function(){
return Promise.resolve();
}
}
}
sinon.stub(log,"log",function(){})
sinon.stub(log,"warn",function(){})
sinon.stub(log,"info",function(){})
sinon.stub(log,"trace",function(){})
sinon.stub(log,"metric",function(){ return !!metrics })
sinon.stub(log,"_",function(){ return "abc"})
sinon.stub(i18n,"registerMessageCatalog",function(){ return Promise.resolve()})
}
function unmockUtil() {
log.log.restore && log.log.restore();
log.warn.restore && log.warn.restore();
log.info.restore && log.info.restore();
log.trace.restore && log.trace.restore();
log.metric.restore && log.metric.restore();
log._.restore && log._.restore();
i18n.registerMessageCatalog.restore && i18n.registerMessageCatalog.restore();
}
describe("init", function() {
beforeEach(function() {
sinon.stub(log,"init",function() {});
sinon.stub(settings,"init",function() {});
sinon.stub(redNodes,"init",function() {})
mockUtil();
});
afterEach(function() {
log.init.restore();
settings.init.restore();
redNodes.init.restore();
unmockUtil();
})
it("initialises components", function() {
runtime.init({testSettings: true, httpAdminRoot:"/"},mockUtil());
runtime.init({testSettings: true, httpAdminRoot:"/"});
settings.init.called.should.be.true();
redNodes.init.called.should.be.true();
});
it("returns version", function() {
runtime.init({testSettings: true, httpAdminRoot:"/"},mockUtil());
runtime.init({testSettings: true, httpAdminRoot:"/"});
return runtime.version().then(version => {
/^\d+\.\d+\.\d+(-.*)?$/.test(version).should.be.true();
});
@ -98,7 +101,6 @@ describe("runtime", function() {
var redNodesLoadFlows;
var redNodesStartFlows;
var redNodesLoadContextsPlugin;
var i18nRegisterMessageCatalog;
beforeEach(function() {
storageInit = sinon.stub(storage,"init",function(settings) {return Promise.resolve();});
@ -108,7 +110,7 @@ describe("runtime", function() {
redNodesLoadFlows = sinon.stub(redNodes,"loadFlows",function() {return Promise.resolve()});
redNodesStartFlows = sinon.stub(redNodes,"startFlows",function() {});
redNodesLoadContextsPlugin = sinon.stub(redNodes,"loadContextsPlugin",function() {return Promise.resolve()});
i18nRegisterMessageCatalog = sinon.stub(util.i18n,"registerMessageCatalog",function() {return Promise.resolve()});
mockUtil();
});
afterEach(function() {
storageInit.restore();
@ -119,7 +121,7 @@ describe("runtime", function() {
redNodesLoadFlows.restore();
redNodesStartFlows.restore();
redNodesLoadContextsPlugin.restore();
i18nRegisterMessageCatalog.restore();
unmockUtil();
});
it("reports errored/missing modules",function(done) {
redNodesGetNodeList = sinon.stub(redNodes,"getNodeList", function(cb) {
@ -128,8 +130,7 @@ describe("runtime", function() {
{ module:"module",enabled:true,loaded:false,types:["typeA","typeB"]} // missing
].filter(cb);
});
var util = mockUtil();
runtime.init({testSettings: true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
runtime.init({testSettings: true, httpAdminRoot:"/", load:function() { return Promise.resolve();}});
// sinon.stub(console,"log");
runtime.start().then(function() {
// console.log.restore();
@ -139,9 +140,9 @@ describe("runtime", function() {
redNodesLoad.calledOnce.should.be.true();
redNodesLoadFlows.calledOnce.should.be.true();
util.log.warn.calledWithMatch("Failed to register 1 node type");
util.log.warn.calledWithMatch("Missing node modules");
util.log.warn.calledWithMatch(" - module: typeA, typeB");
log.warn.calledWithMatch("Failed to register 1 node type");
log.warn.calledWithMatch("Missing node modules");
log.warn.calledWithMatch(" - module: typeA, typeB");
redNodesCleanModuleList.calledOnce.should.be.true();
done();
} catch(err) {
@ -159,16 +160,15 @@ describe("runtime", function() {
].filter(cb);
});
var serverInstallModule = sinon.stub(redNodes,"installModule",function(name) { return Promise.resolve({nodes:[]});});
var util = mockUtil();
runtime.init({testSettings: true, autoInstallModules:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
runtime.init({testSettings: true, autoInstallModules:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}});
sinon.stub(console,"log");
runtime.start().then(function() {
console.log.restore();
try {
util.log.warn.calledWithMatch("Failed to register 2 node types");
util.log.warn.calledWithMatch("Missing node modules");
util.log.warn.calledWithMatch(" - module: typeA, typeB");
util.log.warn.calledWithMatch(" - node-red: typeC, typeD");
log.warn.calledWithMatch("Failed to register 2 node types");
log.warn.calledWithMatch("Missing node modules");
log.warn.calledWithMatch(" - module: typeA, typeB");
log.warn.calledWithMatch(" - node-red: typeC, typeD");
redNodesCleanModuleList.calledOnce.should.be.false();
serverInstallModule.calledOnce.should.be.true();
serverInstallModule.calledWithMatch("module");
@ -186,14 +186,13 @@ describe("runtime", function() {
{ err:"errored",name:"errName" } // error
].filter(cb);
});
var util = mockUtil();
runtime.init({testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
runtime.init({testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}});
sinon.stub(console,"log");
runtime.start().then(function() {
console.log.restore();
try {
util.log.warn.neverCalledWithMatch("Failed to register 1 node type");
util.log.warn.calledWithMatch("[errName] errored");
log.warn.neverCalledWithMatch("Failed to register 1 node type");
log.warn.calledWithMatch("[errName] errored");
done();
} catch(err) {
done(err);
@ -204,21 +203,21 @@ describe("runtime", function() {
it("reports runtime metrics",function(done) {
var stopFlows = sinon.stub(redNodes,"stopFlows",function() { return Promise.resolve();} );
redNodesGetNodeList = sinon.stub(redNodes,"getNodeList", function() {return []});
var util = mockUtil(true);
unmockUtil();
mockUtil(true);
runtime.init(
{testSettings: true, runtimeMetricInterval:200, httpAdminRoot:"/", load:function() { return Promise.resolve();}},
{},
undefined,
util);
undefined);
// sinon.stub(console,"log");
runtime.start().then(function() {
// console.log.restore();
setTimeout(function() {
try {
util.log.log.args.should.have.lengthOf(3);
util.log.log.args[0][0].should.have.property("event","runtime.memory.rss");
util.log.log.args[1][0].should.have.property("event","runtime.memory.heapTotal");
util.log.log.args[2][0].should.have.property("event","runtime.memory.heapUsed");
log.log.args.should.have.lengthOf(3);
log.log.args[0][0].should.have.property("event","runtime.memory.rss");
log.log.args[1][0].should.have.property("event","runtime.memory.heapTotal");
log.log.args[2][0].should.have.property("event","runtime.memory.heapUsed");
done();
} catch(err) {
done(err);

View File

@ -17,9 +17,9 @@ var should = require("should");
var NR_TEST_UTILS = require("nr-test-utils");
describe("runtime/events", function() {
describe("@node-red/util/events", function() {
it('can be required without errors', function() {
NR_TEST_UTILS.require("@node-red/runtime/lib/events");
NR_TEST_UTILS.require("@node-red/util/lib/events");
});
it.skip('more tests needed', function(){})
});

View File

@ -16,30 +16,31 @@
var should = require("should");
var sinon = require("sinon");
var path = require("path");
var events = require("events");
var EventEmitter = require("events").EventEmitter;
var child_process = require('child_process');
var NR_TEST_UTILS = require("nr-test-utils");
var exec = NR_TEST_UTILS.require("@node-red/runtime/lib/exec");
var events = NR_TEST_UTILS.require("@node-red/util/lib/events");
var exec = NR_TEST_UTILS.require("@node-red/util/lib/exec");
describe("runtime/exec", function() {
var logEvents;
var mockProcess;
const eventLogHandler = function(ev) {
logEvents.push(ev);
}
beforeEach(function() {
var logEventHandler = new events.EventEmitter();
logEvents = [];
logEventHandler.on('event-log', function(ev) {
logEvents.push(ev);
});
exec.init({events:logEventHandler});
mockProcess = new events.EventEmitter();
mockProcess.stdout = new events.EventEmitter();
mockProcess.stderr = new events.EventEmitter();
logEvents = [];
events.on("event-log", eventLogHandler);
mockProcess = new EventEmitter();
mockProcess.stdout = new EventEmitter();
mockProcess.stderr = new EventEmitter();
sinon.stub(child_process,'spawn',function(command,args,options) {
mockProcess._args = {command,args,options};
return mockProcess;
@ -47,6 +48,7 @@ describe("runtime/exec", function() {
});
afterEach(function() {
events.removeListener("event-log", eventLogHandler);
if (child_process.spawn.restore) {
child_process.spawn.restore();
}