From 36850895cf63dd91f2a9ab8e68483aeea62a34ad Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Tue, 15 May 2018 15:56:15 -0400 Subject: [PATCH 1/9] add process wrapper to handle logs --- lib/core/processWrapper.js | 41 ++++++++++++++++++++++++++++++++++ lib/pipeline/webpackProcess.js | 39 ++------------------------------ 2 files changed, 43 insertions(+), 37 deletions(-) create mode 100644 lib/core/processWrapper.js diff --git a/lib/core/processWrapper.js b/lib/core/processWrapper.js new file mode 100644 index 00000000..e8c115f9 --- /dev/null +++ b/lib/core/processWrapper.js @@ -0,0 +1,41 @@ +const constants = require('../constants'); + +// Override process.chdir so that we have a partial-implementation PWD for Windows +const realChdir = process.chdir; +process.chdir = (...args) => { + if (!process.env.PWD) { + process.env.PWD = process.cwd(); + } + realChdir(...args); +}; + +class ProcessWrapper { + constructor(_options) { + this.interceptLogs(); + } + + interceptLogs() { + const context = {}; + context.console = console; + + context.console.log = this.log.bind(this, 'log'); + context.console.warn = this.log.bind(this, 'warn'); + context.console.info = this.log.bind(this, 'info'); + context.console.debug = this.log.bind(this, 'debug'); + context.console.trace = this.log.bind(this, 'trace'); + context.console.dir = this.log.bind(this, 'dir'); + } + + log(type, ...messages) { + if (messages[0].indexOf('hard-source')) { + return; + } + process.send({result: constants.pipeline.log, message: messages, type}); + } +} + +process.on('exit', () => { + process.exit(0); +}); + +module.exports = ProcessWrapper; diff --git a/lib/pipeline/webpackProcess.js b/lib/pipeline/webpackProcess.js index e6484791..26d557e2 100644 --- a/lib/pipeline/webpackProcess.js +++ b/lib/pipeline/webpackProcess.js @@ -4,42 +4,11 @@ const utils = require('../utils/utils'); const fs = require('../core/fs'); const constants = require('../constants'); const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); - -// Override process.chdir so that we have a partial-implementation PWD for Windows -const realChdir = process.chdir; -process.chdir = (...args) => { - if (!process.env.PWD) { - process.env.PWD = process.cwd(); - } - realChdir(...args); -}; +const ProcessWrapper = require('../core/processWrapper'); let webpackProcess; -class WebpackProcess { - constructor(_options) { - this.interceptLogs(); - } - - interceptLogs() { - const context = {}; - context.console = console; - - context.console.log = this.log.bind(this, 'log'); - context.console.warn = this.log.bind(this, 'warn'); - context.console.info = this.log.bind(this, 'info'); - context.console.debug = this.log.bind(this, 'debug'); - context.console.trace = this.log.bind(this, 'trace'); - context.console.dir = this.log.bind(this, 'dir'); - } - - log(type, ...messages) { - if (messages[0].indexOf('hard-source')) { - return; - } - process.send({result: constants.pipeline.log, message: messages, type}); - } - +class WebpackProcess extends ProcessWrapper { build(file, importsList, callback) { const self = this; let realCwd; @@ -150,7 +119,3 @@ process.on('message', (msg) => { }); } }); - -process.on('exit', () => { - process.exit(0); -}); From 3c0fda5a40e2ee106fc109493cc578813436a898 Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Tue, 15 May 2018 17:08:08 -0400 Subject: [PATCH 2/9] add eventWrapper not tested --- lib/constants.json | 11 ++++-- lib/pipeline/pipeline.js | 2 +- lib/pipeline/webpackProcess.js | 2 +- lib/process/eventsWrapper.js | 48 +++++++++++++++++++++++++ lib/{core => process}/processWrapper.js | 2 +- 5 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 lib/process/eventsWrapper.js rename lib/{core => process}/processWrapper.js (92%) diff --git a/lib/constants.json b/lib/constants.json index 362dbbc0..b99d1387 100644 --- a/lib/constants.json +++ b/lib/constants.json @@ -16,11 +16,18 @@ "contractFilesChanged": "contractFilesChanged", "contractConfigChanged": "contractConfigChanged" }, + "process": { + "log": "log", + "events": { + "on": "on", + "request": "request", + "response": "response" + } + }, "pipeline": { "init": "init", "build": "build", "initiated": "initiated", - "built": "built", - "log": "log" + "built": "built" } } diff --git a/lib/pipeline/pipeline.js b/lib/pipeline/pipeline.js index 6b20a890..579dd4cc 100644 --- a/lib/pipeline/pipeline.js +++ b/lib/pipeline/pipeline.js @@ -79,7 +79,7 @@ class Pipeline { return next(msg.error); } - if (msg.result === constants.pipeline.log) { + if (msg.result === constants.process.log) { if (self.logger[msg.type]) { return self.logger[msg.type](self.normalizeInput(msg.message)); } diff --git a/lib/pipeline/webpackProcess.js b/lib/pipeline/webpackProcess.js index 26d557e2..ca917d3c 100644 --- a/lib/pipeline/webpackProcess.js +++ b/lib/pipeline/webpackProcess.js @@ -4,7 +4,7 @@ const utils = require('../utils/utils'); const fs = require('../core/fs'); const constants = require('../constants'); const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); -const ProcessWrapper = require('../core/processWrapper'); +const ProcessWrapper = require('../process/processWrapper'); let webpackProcess; diff --git a/lib/process/eventsWrapper.js b/lib/process/eventsWrapper.js new file mode 100644 index 00000000..c4181490 --- /dev/null +++ b/lib/process/eventsWrapper.js @@ -0,0 +1,48 @@ +const constants = require('../constants'); + +class Events { + constructor() { + this.eventId = 0; + this.subscribedEvents = {}; + this.listenToParentProcess(); + } + + listenToParentProcess() { + process.on('message', (msg) => { + if (!msg.event || msg.event !== constants.process.events.response) { + return; + } + if (!this.subscribedEvents[msg.eventId]) { + return; + } + this.subscribedEvents[msg.eventId](msg.result); + }); + } + + sendEvent() { + const eventType = arguments[0]; + const requestName = arguments[1]; + + let args = [].slice.call(arguments, 2); + this.eventId++; + this.subscribedEvents[this.eventId] = args[args.length - 1]; + args = args.splice(0, args.length - 2); + + process.send({ + event: eventType, + requestName, + args, + eventId: this.eventId + }); + } + + on() { + this.sendEvent(constants.process.events.on, ...arguments); + } + + request() { + this.sendEvent(constants.process.events.request, ...arguments); + } +} + +module.exports = Events; diff --git a/lib/core/processWrapper.js b/lib/process/processWrapper.js similarity index 92% rename from lib/core/processWrapper.js rename to lib/process/processWrapper.js index e8c115f9..27691547 100644 --- a/lib/core/processWrapper.js +++ b/lib/process/processWrapper.js @@ -30,7 +30,7 @@ class ProcessWrapper { if (messages[0].indexOf('hard-source')) { return; } - process.send({result: constants.pipeline.log, message: messages, type}); + process.send({result: constants.process.log, message: messages, type}); } } From 1c125a019c34c42d539b99c3f7b9e077d43706cc Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Wed, 16 May 2018 16:41:15 -0400 Subject: [PATCH 3/9] conflict in en.json --- lib/i18n/locales/en.json | 1 + lib/pipeline/pipeline.js | 24 ++++---- lib/process/processLauncher.js | 103 +++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 lib/process/processLauncher.js diff --git a/lib/i18n/locales/en.json b/lib/i18n/locales/en.json index 988c3a2e..da47b60b 100644 --- a/lib/i18n/locales/en.json +++ b/lib/i18n/locales/en.json @@ -96,6 +96,7 @@ "help": "help", "quit": "quit", "Error Compiling/Building contracts: ": "Error Compiling/Building contracts: ", + "file not found, creating it...": "file not found, creating it..." "{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}": "{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}", "downloading {{packageName}} {{version}}....": "downloading {{packageName}} {{version}}....", "Swarm node is offline...": "Swarm node is offline...", diff --git a/lib/pipeline/pipeline.js b/lib/pipeline/pipeline.js index 579dd4cc..e7fad4f1 100644 --- a/lib/pipeline/pipeline.js +++ b/lib/pipeline/pipeline.js @@ -1,6 +1,6 @@ const fs = require('../core/fs.js'); const async = require('async'); -const child_process = require('child_process'); +const ProcessLauncher = require('../process/processLauncher'); const utils = require('../utils/utils.js'); const constants = require('../constants'); @@ -69,22 +69,18 @@ class Pipeline { // JS files async.waterfall([ function runWebpack(next) { - const webpackProcess = child_process.fork(utils.joinPath(__dirname, 'webpackProcess.js')); + const webpackProcess = new ProcessLauncher({ + modulePath: utils.joinPath(__dirname, 'webpackProcess.js'), + logger: self.logger, + normalizeInput: self.normalizeInput + }); + console.log('Starting process'); webpackProcess.send({action: constants.pipeline.init, options: {}}); webpackProcess.send({action: constants.pipeline.build, file, importsList}); - webpackProcess.on('message', function (msg) { - if (msg.result === constants.pipeline.built) { - webpackProcess.disconnect(); - return next(msg.error); - } - - if (msg.result === constants.process.log) { - if (self.logger[msg.type]) { - return self.logger[msg.type](self.normalizeInput(msg.message)); - } - self.logger.debug(self.normalizeInput(msg.message)); - } + webpackProcess.subscribeTo('result', constants.pipeline.built, (msg) => { + webpackProcess.disconnect(); + return next(msg.error); }); }, diff --git a/lib/process/processLauncher.js b/lib/process/processLauncher.js new file mode 100644 index 00000000..0e0550ee --- /dev/null +++ b/lib/process/processLauncher.js @@ -0,0 +1,103 @@ +const child_process = require('child_process'); +const constants = require('../constants'); + +class ProcessLauncher { + constructor(options) { + this.process = child_process.fork(options.modulePath); + this.logger = options.logger; + this.normalizeInput = options.normalizeInput; + + this.subscriptions = {}; + this._subscribeToMessages(); + } + + _subscribeToMessages() { + const self = this; + this.process.on('message', (msg) => { + console.log('Received message', msg); + if (msg.result === constants.process.log) { + if (self.logger[msg.type]) { + return self.logger[msg.type](self.normalizeInput(msg.message)); + } + self.logger.debug(self.normalizeInput(msg.message)); + return; + } + + const messageKeys = Object.keys(msg); + const subscriptionsKeys = Object.keys(self.subscriptions); + let subscriptions; + let messageKey; + // Find if the message contains a key that we are subscribed to + messageKeys.some(_messageKey => { + return subscriptionsKeys.some(subscriptionKey => { + if (_messageKey === subscriptionKey) { + subscriptions = self.subscriptions[subscriptionKey]; + messageKey = _messageKey; + return true; + } + return false; + }); + }); + + if (subscriptions) { + console.log('Found the subscriptions'); + let subscription; + // Find if we are subscribed to one of the values + subscriptions.some(sub => { + if (msg[messageKey] === sub.value) { + subscription = sub; + return true; + } + return false; + }); + + if (subscription) { + console.log('Found the sub and calling'); + // We are subscribed to that message, call the callback + subscription.callback(msg); + } + } + }); + } + + subscribeTo(key, value, callback) { + console.log('Subscribe to ', key, value); + if (this.subscriptions[key]) { + this.subscriptions[key].push(value); + return; + } + this.subscriptions[key] = [{value, callback}]; + console.log('Subs', this.subscriptions); + } + + unsubscribeTo(key, value) { + if (!value) { + this.subscriptions[key] = []; + } + if (this.subscriptions[key]) { + this.subscriptions[key].filter((val, index) => { + if (val.value === value) { + this.subscriptions[key] = this.subscriptions[key].splice(index, 1); + } + }); + } + } + + unsubsribeToAll() { + this.subscriptions = {}; + } + + send() { + this.process.send(...arguments); + } + + disconnect() { + this.process.disconnect(); + } + + kill() { + this.process.kill(...arguments); + } +} + +module.exports = ProcessLauncher; From 953ce6659e3b58bbc71d20578b494332915c28fd Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Wed, 16 May 2018 10:19:46 -0400 Subject: [PATCH 4/9] reorg subscribe to message method --- lib/pipeline/pipeline.js | 1 - lib/process/processLauncher.js | 92 ++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/lib/pipeline/pipeline.js b/lib/pipeline/pipeline.js index e7fad4f1..47173ab4 100644 --- a/lib/pipeline/pipeline.js +++ b/lib/pipeline/pipeline.js @@ -74,7 +74,6 @@ class Pipeline { logger: self.logger, normalizeInput: self.normalizeInput }); - console.log('Starting process'); webpackProcess.send({action: constants.pipeline.init, options: {}}); webpackProcess.send({action: constants.pipeline.build, file, importsList}); diff --git a/lib/process/processLauncher.js b/lib/process/processLauncher.js index 0e0550ee..c631fc13 100644 --- a/lib/process/processLauncher.js +++ b/lib/process/processLauncher.js @@ -14,60 +14,68 @@ class ProcessLauncher { _subscribeToMessages() { const self = this; this.process.on('message', (msg) => { - console.log('Received message', msg); if (msg.result === constants.process.log) { - if (self.logger[msg.type]) { - return self.logger[msg.type](self.normalizeInput(msg.message)); - } - self.logger.debug(self.normalizeInput(msg.message)); - return; + return self._handleLog(msg); } - - const messageKeys = Object.keys(msg); - const subscriptionsKeys = Object.keys(self.subscriptions); - let subscriptions; - let messageKey; - // Find if the message contains a key that we are subscribed to - messageKeys.some(_messageKey => { - return subscriptionsKeys.some(subscriptionKey => { - if (_messageKey === subscriptionKey) { - subscriptions = self.subscriptions[subscriptionKey]; - messageKey = _messageKey; - return true; - } - return false; - }); - }); - - if (subscriptions) { - console.log('Found the subscriptions'); - let subscription; - // Find if we are subscribed to one of the values - subscriptions.some(sub => { - if (msg[messageKey] === sub.value) { - subscription = sub; - return true; - } - return false; - }); - - if (subscription) { - console.log('Found the sub and calling'); - // We are subscribed to that message, call the callback - subscription.callback(msg); - } + if (msg.event) { + return this._handleEvent(msg); } + this._checkSubscriptions(msg); }); } + _handleLog(msg) { + if (this.logger[msg.type]) { + return this.logger[msg.type](this.normalizeInput(msg.message)); + } + this.logger.debug(this.normalizeInput(msg.message)); + } + + _handleEvent(msg) { + console.log(msg); + } + + _checkSubscriptions(msg) { + const messageKeys = Object.keys(msg); + const subscriptionsKeys = Object.keys(this.subscriptions); + let subscriptions; + let messageKey; + // Find if the message contains a key that we are subscribed to + messageKeys.some(_messageKey => { + return subscriptionsKeys.some(subscriptionKey => { + if (_messageKey === subscriptionKey) { + subscriptions = this.subscriptions[subscriptionKey]; + messageKey = _messageKey; + return true; + } + return false; + }); + }); + + if (subscriptions) { + let subscription; + // Find if we are subscribed to one of the values + subscriptions.some(sub => { + if (msg[messageKey] === sub.value) { + subscription = sub; + return true; + } + return false; + }); + + if (subscription) { + // We are subscribed to that message, call the callback + subscription.callback(msg); + } + } + } + subscribeTo(key, value, callback) { - console.log('Subscribe to ', key, value); if (this.subscriptions[key]) { this.subscriptions[key].push(value); return; } this.subscriptions[key] = [{value, callback}]; - console.log('Subs', this.subscriptions); } unsubscribeTo(key, value) { From 1ffc9ffe08db90549736a2a6bb218ffcecd73b3e Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Wed, 16 May 2018 11:25:06 -0400 Subject: [PATCH 5/9] enable events calling for request and on --- lib/pipeline/pipeline.js | 1 + lib/process/eventsWrapper.js | 8 ++++---- lib/process/processLauncher.js | 19 ++++++++++++++++++- lib/process/processWrapper.js | 2 ++ package-lock.json | 12 ++++++------ package.json | 1 + 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/lib/pipeline/pipeline.js b/lib/pipeline/pipeline.js index 47173ab4..a9f2ee2f 100644 --- a/lib/pipeline/pipeline.js +++ b/lib/pipeline/pipeline.js @@ -72,6 +72,7 @@ class Pipeline { const webpackProcess = new ProcessLauncher({ modulePath: utils.joinPath(__dirname, 'webpackProcess.js'), logger: self.logger, + events: self.events, normalizeInput: self.normalizeInput }); webpackProcess.send({action: constants.pipeline.init, options: {}}); diff --git a/lib/process/eventsWrapper.js b/lib/process/eventsWrapper.js index c4181490..95255413 100644 --- a/lib/process/eventsWrapper.js +++ b/lib/process/eventsWrapper.js @@ -1,8 +1,8 @@ +const uuid = require('uuid/v1'); const constants = require('../constants'); class Events { constructor() { - this.eventId = 0; this.subscribedEvents = {}; this.listenToParentProcess(); } @@ -24,15 +24,15 @@ class Events { const requestName = arguments[1]; let args = [].slice.call(arguments, 2); - this.eventId++; - this.subscribedEvents[this.eventId] = args[args.length - 1]; + const eventId = uuid(); + this.subscribedEvents[eventId] = args[args.length - 1]; args = args.splice(0, args.length - 2); process.send({ event: eventType, requestName, args, - eventId: this.eventId + eventId: eventId }); } diff --git a/lib/process/processLauncher.js b/lib/process/processLauncher.js index c631fc13..0cf8cdf0 100644 --- a/lib/process/processLauncher.js +++ b/lib/process/processLauncher.js @@ -6,6 +6,7 @@ class ProcessLauncher { this.process = child_process.fork(options.modulePath); this.logger = options.logger; this.normalizeInput = options.normalizeInput; + this.events = options.events; this.subscriptions = {}; this._subscribeToMessages(); @@ -32,7 +33,23 @@ class ProcessLauncher { } _handleEvent(msg) { - console.log(msg); + const self = this; + if (!self.events[msg.event]) { + self.logger.warn('Unknown event method called: ' + msg.event); + return; + } + if (!msg.args || !Array.isArray(msg.args)) { + msg.args = []; + } + // Add callback in the args + msg.args.push((result) => { + self.process.send({ + event: constants.process.events.response, + result, + eventId: msg.eventId + }); + }); + self.events[msg.event](msg.requestName, ...msg.args); } _checkSubscriptions(msg) { diff --git a/lib/process/processWrapper.js b/lib/process/processWrapper.js index 27691547..e2a705eb 100644 --- a/lib/process/processWrapper.js +++ b/lib/process/processWrapper.js @@ -1,4 +1,5 @@ const constants = require('../constants'); +const Events = require('./eventsWrapper'); // Override process.chdir so that we have a partial-implementation PWD for Windows const realChdir = process.chdir; @@ -12,6 +13,7 @@ process.chdir = (...args) => { class ProcessWrapper { constructor(_options) { this.interceptLogs(); + this.events = new Events(); } interceptLogs() { diff --git a/package-lock.json b/package-lock.json index 67df1eb6..2673f621 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.2.tgz", "integrity": "sha512-Q3FWsbdmkQd1ib11A4XNWQvRD//5KpPoGawA8aB2DR7pWKoW9XQv3+dGxD/Z1eVFze23Okdo27ZQytVFlweKvQ==", "requires": { - "@types/node": "10.0.9" + "@types/node": "10.1.0" } }, "@types/lockfile": { @@ -32,16 +32,16 @@ "integrity": "sha512-pD6JuijPmrfi84qF3/TzGQ7zi0QIX+d7ZdetD6jUA6cp+IsCzAquXZfi5viesew+pfpOTIdAVKuh1SHA7KeKzg==" }, "@types/node": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.0.9.tgz", - "integrity": "sha512-ekJ3mXJcJP+Nn5kC6eCmWPND/fHx/Ga12Lz0IJgTfGz1ge7OCIR5xcDY5tcYgnyM1kWsVDRH2bguxkGcNj39AQ==" + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.1.0.tgz", + "integrity": "sha512-sELcX/cJHwRp8kn4hYSvBxKGJ+ubl3MvS8VJQe5gz/sp7CifYxsiCxIJ35wMIYyGVMgfO2AzRa8UcVReAcJRlw==" }, "@types/node-fetch": { "version": "1.6.9", "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-1.6.9.tgz", "integrity": "sha512-n2r6WLoY7+uuPT7pnEtKJCmPUGyJ+cbyBR8Avnu4+m1nzz7DwBVuyIvvlBzCZ/nrpC7rIgb3D6pNavL7rFEa9g==", "requires": { - "@types/node": "10.0.9" + "@types/node": "10.1.0" } }, "@types/semver": { @@ -54,7 +54,7 @@ "resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.0.tgz", "integrity": "sha512-YybbEHNngcHlIWVCYsoj7Oo1JU9JqONuAlt1LlTH/lmL8BMhbzdFUgReY87a05rY1j8mfK47Del+TCkaLAXwLw==", "requires": { - "@types/node": "10.0.9" + "@types/node": "10.1.0" } }, "@types/url-join": { diff --git a/package.json b/package.json index bc14f1e9..5a50e19a 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "underscore": "^1.9.0", "underscore.string": "^3.3.4", "url-loader": "^0.6.2", + "uuid": "^3.2.1", "viz.js": "^1.8.1", "web3": "1.0.0-beta.34", "web3-provider-engine": "^14.0.5", From d17256dc40a530a42afe9032658e6a4bef62e405 Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Wed, 16 May 2018 11:49:38 -0400 Subject: [PATCH 6/9] add JS Doc for processLauncher --- lib/process/processLauncher.js | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lib/process/processLauncher.js b/lib/process/processLauncher.js index 0cf8cdf0..7086d696 100644 --- a/lib/process/processLauncher.js +++ b/lib/process/processLauncher.js @@ -1,7 +1,18 @@ const child_process = require('child_process'); const constants = require('../constants'); + class ProcessLauncher { + + /** + * Constructor of ProcessLauncher. Forks the module and sets up the message handling + * @param {Object} options Options tp start the process + * * modulePath {String} Absolute path to the module to fork + * * logger {Object} Logger + * * normalizeInput {Function} Function to normalize logs + * * events {Function} Events Emitter instance + * @return {ProcessLauncher} The ProcessLauncher instance + */ constructor(options) { this.process = child_process.fork(options.modulePath); this.logger = options.logger; @@ -12,6 +23,7 @@ class ProcessLauncher { this._subscribeToMessages(); } + // Subscribes to messages from the child process and delegates to the right methods _subscribeToMessages() { const self = this; this.process.on('message', (msg) => { @@ -25,6 +37,7 @@ class ProcessLauncher { }); } + // Translates logs from the child process to the logger _handleLog(msg) { if (this.logger[msg.type]) { return this.logger[msg.type](this.normalizeInput(msg.message)); @@ -32,6 +45,7 @@ class ProcessLauncher { this.logger.debug(this.normalizeInput(msg.message)); } + // Handle event calls from the child process _handleEvent(msg) { const self = this; if (!self.events[msg.event]) { @@ -52,6 +66,7 @@ class ProcessLauncher { self.events[msg.event](msg.requestName, ...msg.args); } + // Looks at the subscriptions to see if there is a callback to call _checkSubscriptions(msg) { const messageKeys = Object.keys(msg); const subscriptionsKeys = Object.keys(this.subscriptions); @@ -87,6 +102,13 @@ class ProcessLauncher { } } + /** + * Subscribe to a message using a key-value pair + * @param {String} key Message key to subscribe to + * @param {String} value Value that the above key must have for the callback to be called + * @param {Function} callback callback(response) + * @return {void} + */ subscribeTo(key, value, callback) { if (this.subscriptions[key]) { this.subscriptions[key].push(value); @@ -95,6 +117,13 @@ class ProcessLauncher { this.subscriptions[key] = [{value, callback}]; } + /** + * Unsubscribes from a previously subscribed key-value pair (or key if no value) + * @param {String} key Message key to unsubscribe + * @param {String} value [Optional] Value of the key to unsubscribe + * If there is no value, unsubscribes from all the values of that key + * @return {void} + */ unsubscribeTo(key, value) { if (!value) { this.subscriptions[key] = []; @@ -108,18 +137,38 @@ class ProcessLauncher { } } + /** + * Unsubscribes from all subscriptions + * @return {void} + */ unsubsribeToAll() { this.subscriptions = {}; } + /** + * Sends a message to the child process. Same as ChildProcess.send() + * @params {Object} message Message to send + * For other parameters, see: + * https://nodejs.org/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback + * @return {void} + */ send() { this.process.send(...arguments); } + /** + * Disconnects the child process. It will exit on its own + * @return {void} + */ disconnect() { this.process.disconnect(); } + /** + * Kills the child process + * https://nodejs.org/api/child_process.html#child_process_subprocess_kill_signal + * @return {void} + */ kill() { this.process.kill(...arguments); } From 80c382557f559b083f708de336e4cd2dbea55bdf Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Wed, 16 May 2018 11:58:18 -0400 Subject: [PATCH 7/9] add jsdoc to processWrapper --- lib/process/processWrapper.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/process/processWrapper.js b/lib/process/processWrapper.js index e2a705eb..6ec77409 100644 --- a/lib/process/processWrapper.js +++ b/lib/process/processWrapper.js @@ -11,6 +11,15 @@ process.chdir = (...args) => { }; class ProcessWrapper { + + /** + * Class from which process extend. Should not be instantiated alone. + * Manages the log interception so that all console.* get sent back to the parent process + * Also creates an Events instance. To use it, just do `this.event.[on|request]` + * + * @param {Options} _options Nothing for now + * @return {ProcessWrapper} Do not instantiate this alone. Use it to extend + */ constructor(_options) { this.interceptLogs(); this.events = new Events(); @@ -20,16 +29,19 @@ class ProcessWrapper { const context = {}; context.console = console; - context.console.log = this.log.bind(this, 'log'); - context.console.warn = this.log.bind(this, 'warn'); - context.console.info = this.log.bind(this, 'info'); - context.console.debug = this.log.bind(this, 'debug'); - context.console.trace = this.log.bind(this, 'trace'); - context.console.dir = this.log.bind(this, 'dir'); + context.console.log = this._log.bind(this, 'log'); + context.console.warn = this._log.bind(this, 'warn'); + context.console.info = this._log.bind(this, 'info'); + context.console.debug = this._log.bind(this, 'debug'); + context.console.trace = this._log.bind(this, 'trace'); + context.console.dir = this._log.bind(this, 'dir'); } - log(type, ...messages) { - if (messages[0].indexOf('hard-source')) { + _log(type, ...messages) { + const isHardSource = messages.some(message => { + return (typeof message === 'string' && message.indexOf('hard-source') > -1); + }); + if (isHardSource) { return; } process.send({result: constants.process.log, message: messages, type}); From 441063dd10ecc9c9b64cd80648f1fcbabe57ab37 Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Wed, 16 May 2018 12:03:39 -0400 Subject: [PATCH 8/9] add docs for eventWrapper --- lib/process/eventsWrapper.js | 7 +++++++ lib/process/processWrapper.js | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/process/eventsWrapper.js b/lib/process/eventsWrapper.js index 95255413..de102158 100644 --- a/lib/process/eventsWrapper.js +++ b/lib/process/eventsWrapper.js @@ -2,6 +2,13 @@ const uuid = require('uuid/v1'); const constants = require('../constants'); class Events { + + /** + * Constructs an event wrapper for processes. + * Handles sending an event message to the parent process and waiting for its response + * No need to create an instance of eventsWrapper for your own process, just extend ProcessWrapper + * Then, you an use `this.events.[on|request]` with the usual parameters you would use + */ constructor() { this.subscribedEvents = {}; this.listenToParentProcess(); diff --git a/lib/process/processWrapper.js b/lib/process/processWrapper.js index 6ec77409..a22f1556 100644 --- a/lib/process/processWrapper.js +++ b/lib/process/processWrapper.js @@ -15,10 +15,9 @@ class ProcessWrapper { /** * Class from which process extend. Should not be instantiated alone. * Manages the log interception so that all console.* get sent back to the parent process - * Also creates an Events instance. To use it, just do `this.event.[on|request]` + * Also creates an Events instance. To use it, just do `this.events.[on|request]` * * @param {Options} _options Nothing for now - * @return {ProcessWrapper} Do not instantiate this alone. Use it to extend */ constructor(_options) { this.interceptLogs(); From c1f72061e3dbc4bd10d74a26b201e6afc2520dae Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Wed, 16 May 2018 14:06:34 -0400 Subject: [PATCH 9/9] add tests for processLauncher --- lib/process/processLauncher.js | 6 +- test/processLauncher.js | 139 +++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 test/processLauncher.js diff --git a/lib/process/processLauncher.js b/lib/process/processLauncher.js index 7086d696..e58db305 100644 --- a/lib/process/processLauncher.js +++ b/lib/process/processLauncher.js @@ -111,7 +111,7 @@ class ProcessLauncher { */ subscribeTo(key, value, callback) { if (this.subscriptions[key]) { - this.subscriptions[key].push(value); + this.subscriptions[key].push({value, callback}); return; } this.subscriptions[key] = [{value, callback}]; @@ -131,7 +131,7 @@ class ProcessLauncher { if (this.subscriptions[key]) { this.subscriptions[key].filter((val, index) => { if (val.value === value) { - this.subscriptions[key] = this.subscriptions[key].splice(index, 1); + this.subscriptions[key].splice(index, 1); } }); } @@ -141,7 +141,7 @@ class ProcessLauncher { * Unsubscribes from all subscriptions * @return {void} */ - unsubsribeToAll() { + unsubscribeToAll() { this.subscriptions = {}; } diff --git a/test/processLauncher.js b/test/processLauncher.js new file mode 100644 index 00000000..e75d2b4e --- /dev/null +++ b/test/processLauncher.js @@ -0,0 +1,139 @@ +/*global describe, it, before, beforeEach*/ +const assert = require('assert'); +const sinon = require('sinon'); +const TestLogger = require('../lib/tests/test_logger.js'); +const ProcessLauncher = require('../lib/process/processLauncher'); + +describe('ProcessWrapper', () => { + let processLauncher; + + before(() => { + sinon.stub(ProcessLauncher.prototype, '_subscribeToMessages'); + processLauncher = new ProcessLauncher({ + logger: new TestLogger({}) + }); + }); + + describe('subscribeTo', () => { + + beforeEach(() => { + processLauncher.subscriptions = {}; + }); + + it('should create an array for the key value', function () { + processLauncher.subscribeTo('test', 'value', 'myCallback'); + assert.deepEqual(processLauncher.subscriptions, { + "test": [ + { + "callback": "myCallback", + "value": "value" + } + ] + }); + }); + + it('should add another value to the key', () => { + processLauncher.subscribeTo('test', 'value', 'myCallback'); + processLauncher.subscribeTo('test', 'value2', 'myCallback2'); + assert.deepEqual(processLauncher.subscriptions, { + "test": [ + { + "callback": "myCallback", + "value": "value" + }, + { + "callback": "myCallback2", + "value": "value2" + } + ] + }); + }); + }); + + describe('unsubscribeTo', () => { + it('should remove the value for the key', () => { + processLauncher.subscriptions = { + "test": [ + { + "callback": "myCallback", + "value": "value" + }, + { + "callback": "myCallback2", + "value": "value2" + } + ] + }; + + processLauncher.unsubscribeTo('test', 'value2'); + assert.deepEqual(processLauncher.subscriptions, { + "test": [ + { + "callback": "myCallback", + "value": "value" + } + ] + }); + }); + + it('should remove the whole key', () => { + processLauncher.subscriptions = { + "test": [ + { + "callback": "myCallback", + "value": "value" + } + ] + }; + + processLauncher.unsubscribeTo('test'); + assert.deepEqual(processLauncher.subscriptions, {test: []}); + }); + }); + + describe('unsubscribeToAll', () => { + it('clears every subscriptions', () => { + processLauncher.subscriptions = { + "test": [ + { + "callback": "myCallback", + "value": "value" + } + ] + }; + + processLauncher.unsubscribeToAll(); + assert.deepEqual(processLauncher.subscriptions, {}); + }); + }); + + describe('_checkSubscriptions', function () { + it('should not do anything if not in subscription', function () { + const callback = sinon.stub(); + processLauncher.subscriptions = { + "test": [ + { + "callback": callback, + "value": "value" + } + ] + }; + processLauncher._checkSubscriptions({does: 'nothing', for: 'real'}); + assert.strictEqual(callback.callCount, 0); + }); + + it('should call the callback', function () { + const callback = sinon.stub(); + processLauncher.subscriptions = { + "test": [ + { + "callback": callback, + "value": "value" + } + ] + }; + processLauncher._checkSubscriptions({test: 'value'}); + assert.strictEqual(callback.callCount, 1); + }); + }); +});