From 6fb96fa3c157e6f34e2a99a2fd87c52e2fad0a2d Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 2 Dec 2020 09:25:10 +0000 Subject: [PATCH] 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 --- API.md | 2 +- Gruntfile.js | 3 +- .../@node-red/registry/lib/index.js | 210 +++++++++++++++++- .../@node-red/registry/lib/installer.js | 13 +- .../@node-red/registry/lib/library.js | 62 +++--- .../@node-red/registry/lib/loader.js | 15 +- .../@node-red/registry/lib/localfilesystem.js | 7 +- .../@node-red/registry/lib/registry.js | 6 +- .../@node-red/registry/lib/util.js | 14 +- .../@node-red/runtime/lib/api/comms.js | 17 +- .../@node-red/runtime/lib/api/nodes.js | 20 +- .../@node-red/runtime/lib/flows/Flow.js | 3 +- .../@node-red/runtime/lib/flows/Subflow.js | 11 +- .../@node-red/runtime/lib/flows/index.js | 5 +- .../@node-red/runtime/lib/index.js | 28 +-- .../@node-red/runtime/lib/nodes/index.js | 2 +- .../@node-red/runtime/lib/storage/index.js | 2 +- .../localfilesystem/projects/Project.js | 9 +- .../localfilesystem/projects/git/index.js | 4 +- .../storage/localfilesystem/projects/index.js | 5 +- packages/node_modules/@node-red/util/index.js | 16 ++ .../@node-red/{runtime => util}/lib/events.js | 11 +- .../@node-red/{runtime => util}/lib/exec.js | 36 ++- .../node_modules/@node-red/util/lib/log.js | 5 +- packages/node_modules/node-red/lib/red.js | 8 +- .../@node-red/registry/lib/installer_spec.js | 37 ++- .../registry/lib/localfilesystem_spec.js | 59 ++--- .../@node-red/registry/lib/registry_spec.js | 50 ++--- .../@node-red/runtime/lib/api/comms_spec.js | 35 ++- .../@node-red/runtime/lib/flows/index_spec.js | 2 +- test/unit/@node-red/runtime/lib/index_spec.js | 85 ++++--- .../{runtime => util}/lib/events_spec.js | 4 +- .../{runtime => util}/lib/exec_spec.js | 24 +- 33 files changed, 491 insertions(+), 319 deletions(-) rename packages/node_modules/@node-red/{runtime => util}/lib/events.js (92%) rename packages/node_modules/@node-red/{runtime => util}/lib/exec.js (67%) rename test/unit/@node-red/{runtime => util}/lib/events_spec.js (88%) rename test/unit/@node-red/{runtime => util}/lib/exec_spec.js (90%) diff --git a/API.md b/API.md index f349ea8fe..692a34723 100644 --- a/API.md +++ b/API.md @@ -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 diff --git a/Gruntfile.js b/Gruntfile.js index b01763ab0..2bdc2e3aa 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -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', diff --git a/packages/node_modules/@node-red/registry/lib/index.js b/packages/node_modules/@node-red/registry/lib/index.js index 2805534a2..ebcde8cea 100644 --- a/packages/node_modules/@node-red/registry/lib/index.js +++ b/packages/node_modules/@node-red/registry/lib/index.js @@ -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} 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} 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} 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} 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") diff --git a/packages/node_modules/@node-red/registry/lib/installer.js b/packages/node_modules/@node-red/registry/lib/installer.js index 03f9eb11c..9735aa8a1 100644 --- a/packages/node_modules/@node-red/registry/lib/installer.js +++ b/packages/node_modules/@node-red/registry/lib/installer.js @@ -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(); diff --git a/packages/node_modules/@node-red/registry/lib/library.js b/packages/node_modules/@node-red/registry/lib/library.js index a8ca8a775..90b0fda82 100644 --- a/packages/node_modules/@node-red/registry/lib/library.js +++ b/packages/node_modules/@node-red/registry/lib/library.js @@ -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; }) } diff --git a/packages/node_modules/@node-red/registry/lib/loader.js b/packages/node_modules/@node-red/registry/lib/loader.js index 3a960c73c..18d202420 100644 --- a/packages/node_modules/@node-red/registry/lib/loader.js +++ b/packages/node_modules/@node-red/registry/lib/loader.js @@ -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; } diff --git a/packages/node_modules/@node-red/registry/lib/localfilesystem.js b/packages/node_modules/@node-red/registry/lib/localfilesystem.js index d5bef63cd..7aea3f57b 100644 --- a/packages/node_modules/@node-red/registry/lib/localfilesystem.js +++ b/packages/node_modules/@node-red/registry/lib/localfilesystem.js @@ -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) { diff --git a/packages/node_modules/@node-red/registry/lib/registry.js b/packages/node_modules/@node-red/registry/lib/registry.js index 89f04eacc..a91735071 100644 --- a/packages/node_modules/@node-red/registry/lib/registry.js +++ b/packages/node_modules/@node-red/registry/lib/registry.js @@ -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 = {}; diff --git a/packages/node_modules/@node-red/registry/lib/util.js b/packages/node_modules/@node-red/registry/lib/util.js index 5a6d3da38..dbb6c6fc7 100644 --- a/packages/node_modules/@node-red/registry/lib/util.js +++ b/packages/node_modules/@node-red/registry/lib/util.js @@ -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 } diff --git a/packages/node_modules/@node-red/runtime/lib/api/comms.js b/packages/node_modules/@node-red/runtime/lib/api/comms.js index 42e727412..90f1603cd 100644 --- a/packages/node_modules/@node-red/runtime/lib/api/comms.js +++ b/packages/node_modules/@node-red/runtime/lib/api/comms.js @@ -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); }, /** diff --git a/packages/node_modules/@node-red/runtime/lib/api/nodes.js b/packages/node_modules/@node-red/runtime/lib/api/nodes.js index 6a05b4c17..9ac83c814 100644 --- a/packages/node_modules/@node-red/runtime/lib/api/nodes.js +++ b/packages/node_modules/@node-red/runtime/lib/api/nodes.js @@ -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; }); }, diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index a7e00bfbc..a4791e084 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -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); diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js b/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js index 78ec9ee75..da15ecfe4 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js @@ -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 } diff --git a/packages/node_modules/@node-red/runtime/lib/flows/index.js b/packages/node_modules/@node-red/runtime/lib/flows/index.js index a207b8f05..350115f81 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/index.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/index.js @@ -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, diff --git a/packages/node_modules/@node-red/runtime/lib/index.js b/packages/node_modules/@node-red/runtime/lib/index.js index 89b0dcfd6..dd1594467 100644 --- a/packages/node_modules/@node-red/runtime/lib/index.js +++ b/packages/node_modules/@node-red/runtime/lib/index.js @@ -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 }, diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/index.js b/packages/node_modules/@node-red/runtime/lib/nodes/index.js index be31df2f7..858274be2 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/index.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/index.js @@ -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; diff --git a/packages/node_modules/@node-red/runtime/lib/storage/index.js b/packages/node_modules/@node-red/runtime/lib/storage/index.js index e7f09c20f..f5e07e254 100644 --- a/packages/node_modules/@node-red/runtime/lib/storage/index.js +++ b/packages/node_modules/@node-red/runtime/lib/storage/index.js @@ -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; diff --git a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js index 55bb9d078..d87001f1f 100644 --- a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js +++ b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js @@ -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", diff --git a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js index dbcb5fb61..e1ded4337 100644 --- a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js +++ b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js @@ -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)); diff --git a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/index.js b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/index.js index 80a3f31e4..3bbeb823b 100644 --- a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/index.js +++ b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/index.js @@ -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; }); }); diff --git a/packages/node_modules/@node-red/util/index.js b/packages/node_modules/@node-red/util/index.js index 66ac91bd3..29e1a480c 100644 --- a/packages/node_modules/@node-red/util/index.js +++ b/packages/node_modules/@node-red/util/index.js @@ -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 } diff --git a/packages/node_modules/@node-red/runtime/lib/events.js b/packages/node_modules/@node-red/util/lib/events.js similarity index 92% rename from packages/node_modules/@node-red/runtime/lib/events.js rename to packages/node_modules/@node-red/util/lib/events.js index 8e6935222..df45b4d16 100644 --- a/packages/node_modules/@node-red/runtime/lib/events.js +++ b/packages/node_modules/@node-red/util/lib/events.js @@ -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 diff --git a/packages/node_modules/@node-red/runtime/lib/exec.js b/packages/node_modules/@node-red/util/lib/exec.js similarity index 67% rename from packages/node_modules/@node-red/runtime/lib/exec.js rename to packages/node_modules/@node-red/util/lib/exec.js index 0ef3c069c..c7197ef65 100644 --- a/packages/node_modules/@node-red/runtime/lib/exec.js +++ b/packages/node_modules/@node-red/util/lib/exec.js @@ -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: , + * stdout: , + * stderr: + * } + + * @memberof @node-red/util_exec + */ run: function(command,args,options,emit) { var invocationId = util.generateId(); diff --git a/packages/node_modules/@node-red/util/lib/log.js b/packages/node_modules/@node-red/util/lib/log.js index 8d7b84966..341019080 100644 --- a/packages/node_modules/@node-red/util/lib/log.js +++ b/packages/node_modules/@node-red/util/lib/log.js @@ -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); } } diff --git a/packages/node_modules/node-red/lib/red.js b/packages/node_modules/node-red/lib/red.js index 86fa513d1..e46e40e26 100644 --- a/packages/node_modules/node-red/lib/red.js +++ b/packages/node_modules/node-red/lib/red.js @@ -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 diff --git a/test/unit/@node-red/registry/lib/installer_spec.js b/test/unit/@node-red/registry/lib/installer_spec.js index b344dbbea..e0e7380f6 100644 --- a/test/unit/@node-red/registry/lib/installer_spec.js +++ b/test/unit/@node-red/registry/lib/installer_spec.js @@ -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 {}; }); diff --git a/test/unit/@node-red/registry/lib/localfilesystem_spec.js b/test/unit/@node-red/registry/lib/localfilesystem_spec.js index d86a08adb..5cb3c180d 100644 --- a/test/unit/@node-red/registry/lib/localfilesystem_spec.js +++ b/test/unit/@node-red/registry/lib/localfilesystem_spec.js @@ -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"); diff --git a/test/unit/@node-red/registry/lib/registry_spec.js b/test/unit/@node-red/registry/lib/registry_spec.js index d493ed688..60b48938d 100644 --- a/test/unit/@node-red/registry/lib/registry_spec.js +++ b/test/unit/@node-red/registry/lib/registry_spec.js @@ -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", diff --git a/test/unit/@node-red/runtime/lib/api/comms_spec.js b/test/unit/@node-red/runtime/lib/api/comms_spec.js index e1359cb7d..2f2e35a54 100644 --- a/test/unit/@node-red/runtime/lib/api/comms_spec.js +++ b/test/unit/@node-red/runtime/lib/api/comms_spec.js @@ -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); diff --git a/test/unit/@node-red/runtime/lib/flows/index_spec.js b/test/unit/@node-red/runtime/lib/flows/index_spec.js index 6f6066100..e230d2407 100644 --- a/test/unit/@node-red/runtime/lib/flows/index_spec.js +++ b/test/unit/@node-red/runtime/lib/flows/index_spec.js @@ -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"); diff --git a/test/unit/@node-red/runtime/lib/index_spec.js b/test/unit/@node-red/runtime/lib/index_spec.js index f6a506de1..60bdc286b 100644 --- a/test/unit/@node-red/runtime/lib/index_spec.js +++ b/test/unit/@node-red/runtime/lib/index_spec.js @@ -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); diff --git a/test/unit/@node-red/runtime/lib/events_spec.js b/test/unit/@node-red/util/lib/events_spec.js similarity index 88% rename from test/unit/@node-red/runtime/lib/events_spec.js rename to test/unit/@node-red/util/lib/events_spec.js index 09b47693d..09f5d2ae0 100644 --- a/test/unit/@node-red/runtime/lib/events_spec.js +++ b/test/unit/@node-red/util/lib/events_spec.js @@ -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(){}) }); diff --git a/test/unit/@node-red/runtime/lib/exec_spec.js b/test/unit/@node-red/util/lib/exec_spec.js similarity index 90% rename from test/unit/@node-red/runtime/lib/exec_spec.js rename to test/unit/@node-red/util/lib/exec_spec.js index 1e37b7cc1..887938d16 100644 --- a/test/unit/@node-red/runtime/lib/exec_spec.js +++ b/test/unit/@node-red/util/lib/exec_spec.js @@ -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(); }