diff --git a/lib/api/core/plugins/pluginsManager.js b/lib/api/core/plugins/pluginsManager.js index 117d1f3d75..b5e2b72ed7 100644 --- a/lib/api/core/plugins/pluginsManager.js +++ b/lib/api/core/plugins/pluginsManager.js @@ -822,6 +822,35 @@ class PluginsManager { } } +/** + * For a specific event, return the correspondings wildcards + * @example + * getWildcardEvents('data:create') // return ['data:*'] + * getWildcardEvents('data:beforeCreate') // return ['data:*', 'data:before*'] + * @param {String} event + * @returns {Array} wildcard events + */ +const getWildcardEvents = _.memoize(event => { + const delimIndex = event.lastIndexOf(':'); + + if (delimIndex === -1) { + return []; + } + + const scope = event.slice(0, delimIndex); + const name = event.slice(delimIndex + 1); + const wildcardEvents = ['*']; + + ['before', 'after', 'error'].forEach(prefix => { + if (!name.startsWith(prefix)) { + return; + } + wildcardEvents.push(`${prefix}*`); + }); + + return wildcardEvents.map(e => `${scope}:${e}`); +}); + /** * Emit event * @@ -831,8 +860,12 @@ class PluginsManager { * @returns {Promise} */ function triggerHooks(emitter, event, data) { + const wildcardEvents = getWildcardEvents(event); + emitter.emit(event, data); + wildcardEvents.forEach(wildcardEvent => emitter.emit(wildcardEvent, data)); + return Bluebird.resolve(data); } @@ -845,16 +878,18 @@ function triggerHooks(emitter, event, data) { * @returns {Promise} */ function triggerPipes(pipes, event, data) { - let preparedPipes = []; - const wildcardEvent = getWildcardEvent(event); + const preparedPipes = []; + const wildcardEvents = getWildcardEvents(event); if (pipes && pipes[event] && pipes[event].length) { - preparedPipes = pipes[event]; + preparedPipes.push(...pipes[event]); } - if (wildcardEvent && pipes && pipes[wildcardEvent] && pipes[wildcardEvent].length) { - preparedPipes = preparedPipes.concat(pipes[wildcardEvent]); - } + wildcardEvents.forEach(wildcardEvent => { + if (pipes[wildcardEvent] && pipes[wildcardEvent].length) { + preparedPipes.push(...pipes[wildcardEvent]); + } + }); if (preparedPipes.length === 0) { return Bluebird.resolve(data); @@ -875,22 +910,6 @@ function triggerPipes(pipes, event, data) { }); } -/** - * For a specific event, return the corresponding wildcard - * @example - * getWildcardEvent('data:create') // return 'data:*' - * @param {string} event - * @returns {String} wildcard event - */ -function getWildcardEvent (event) { - const indexDelimiter = event.indexOf(':'); - if (indexDelimiter !== 1) { - return event.substring(0, indexDelimiter+1) + '*'; - } - - return null; -} - /** * Test if the provided argument is a constructor or not * diff --git a/lib/api/kuzzle.js b/lib/api/kuzzle.js index 5be1657f36..f1ae5ebc70 100644 --- a/lib/api/kuzzle.js +++ b/lib/api/kuzzle.js @@ -22,7 +22,7 @@ 'use strict'; const - EventEmitter = require('eventemitter2').EventEmitter2, + EventEmitter = require('eventemitter3'), config = require('../config'), path = require('path'), murmur = require('murmurhash-native').murmurHash128, @@ -55,12 +55,7 @@ const */ class Kuzzle extends EventEmitter { constructor() { - super({ - verboseMemoryLeak: true, - wildcard: true, - maxListeners: 30, - delimiter: ':' - }); + super(); /** @type {KuzzleConfiguration} */ this.config = config; diff --git a/package.json b/package.json index 2bff1df9e2..1aca927263 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dumpme": "^1.0.2", "easy-circular-list": "^1.0.13", "elasticsearch": "^15.4.1", - "eventemitter2": "^5.0.1", + "eventemitter3": "^3.1.2", "fs-extra": "^7.0.1", "glob": "^7.1.3", "ioredis": "^4.9.0", diff --git a/test/api/core/pluginsManager/run.test.js b/test/api/core/pluginsManager/run.test.js index 6f1a6d6639..db83cfe9fe 100644 --- a/test/api/core/pluginsManager/run.test.js +++ b/test/api/core/pluginsManager/run.test.js @@ -61,10 +61,8 @@ describe('PluginsManager.run', () => { pluginMock.expects('bar').never(); return pluginsManager.run() - .then(() => { - kuzzle.emit('foo:bar'); - pluginMock.verify(); - }); + .then(() => pluginsManager.trigger('foo:bar')) + .then(() => pluginMock.verify()); }); it('should attach event hook with function', () => { @@ -78,9 +76,8 @@ describe('PluginsManager.run', () => { }; return pluginsManager.run() + .then(() => pluginsManager.trigger('foo:bar')) .then(() => { - kuzzle.emit('foo:bar'); - should(bar).be.calledOnce(); should(foo).not.be.called(); }); @@ -101,10 +98,8 @@ describe('PluginsManager.run', () => { pluginMock.expects('baz').never(); return pluginsManager.run() - .then(() => { - kuzzle.emit('foo:bar'); - pluginMock.verify(); - }); + .then(() => pluginsManager.trigger('foo:bar')) + .then(() => pluginMock.verify()); }); it('should attach multi-target hook with function', () => { @@ -119,9 +114,8 @@ describe('PluginsManager.run', () => { }; return pluginsManager.run() + .then(() => pluginsManager.trigger('foo:bar')) .then(() => { - kuzzle.emit('foo:bar'); - should(bar).be.calledOnce(); should(foo).be.calledOnce(); should(baz).not.be.called(); @@ -142,10 +136,8 @@ describe('PluginsManager.run', () => { pluginMock.expects('bar').never(); return pluginsManager.run() - .then(() => { - kuzzle.emit('foo:bar'); - pluginMock.verify(); - }); + .then(() => pluginsManager.trigger('foo:bar')) + .then(() => pluginMock.verify()); }); it('should throw if a hook target is not a function and not a method name', () => { diff --git a/test/api/core/pluginsManager/trigger.test.js b/test/api/core/pluginsManager/trigger.test.js index cded7e6cdf..438e765d3e 100644 --- a/test/api/core/pluginsManager/trigger.test.js +++ b/test/api/core/pluginsManager/trigger.test.js @@ -1,12 +1,14 @@ const mockrequire = require('mock-require'), should = require('should'), + sinon = require('sinon'), ElasticsearchClientMock = require('../../../mocks/services/elasticsearchClient.mock'), KuzzleMock = require('../../../mocks/kuzzle.mock'), { errors: { PluginImplementationError - } + }, + Request: KuzzleRequest } = require('kuzzle-common-objects'); describe('Test plugins manager trigger', () => { @@ -44,6 +46,119 @@ describe('Test plugins manager trigger', () => { }); }); + it('should trigger hooks with before wildcard event', done => { + pluginsManager.plugins = [{ + object: { + init: () => {}, + hooks: { + 'foo:before*': 'myFunc' + }, + myFunc: done + }, + config: {}, + activated: true, + manifest: { + name: 'foo' + } + }]; + + pluginsManager.run() + .then(() => { + pluginsManager.trigger('foo:beforeBar'); + }); + }); + + it('should trigger hooks with after wildcard event', done => { + pluginsManager.plugins = [{ + object: { + init: () => {}, + hooks: { + 'foo:after*': 'myFunc' + }, + myFunc: done + }, + config: {}, + activated: true, + manifest: { + name: 'foo' + } + }]; + + pluginsManager.run() + .then(() => { + pluginsManager.trigger('foo:afterBar'); + }); + }); + + it('should trigger pipes with wildcard event', () => { + pluginsManager.plugins = [{ + object: { + init: () => {}, + pipes: { + 'foo:*': 'myFunc' + }, + myFunc: sinon.stub().callsArgWith(1, null, new KuzzleRequest({})) + }, + config: {}, + activated: true, + manifest: { + name: 'foo' + } + }]; + + return pluginsManager.run() + .then(() => pluginsManager.trigger('foo:bar')) + .then(() => { + should(pluginsManager.plugins[0].object.myFunc).be.calledOnce(); + }); + }); + + it('should trigger pipes with before wildcard event', () => { + pluginsManager.plugins = [{ + object: { + init: () => {}, + pipes: { + 'foo:before*': 'myFunc' + }, + myFunc: sinon.stub().callsArgWith(1, null, new KuzzleRequest({})) + }, + config: {}, + activated: true, + manifest: { + name: 'foo' + } + }]; + + return pluginsManager.run() + .then(() => pluginsManager.trigger('foo:beforeBar')) + .then(() => { + should(pluginsManager.plugins[0].object.myFunc).be.calledOnce(); + }); + }); + + it('should trigger pipes with after wildcard event', () => { + pluginsManager.plugins = [{ + object: { + init: () => {}, + pipes: { + 'foo:after*': 'myFunc' + }, + myFunc: sinon.stub().callsArgWith(1, null, new KuzzleRequest({})) + }, + config: {}, + activated: true, + manifest: { + name: 'foo' + } + }]; + + return pluginsManager.run() + .then(() => pluginsManager.trigger('foo:afterBar')) + .then(() => { + should(pluginsManager.plugins[0].object.myFunc).be.calledOnce(); + }); + }); + it('should reject a pipe returned value if it is not a Request instance', () => { const pluginMock = { object: { diff --git a/test/mocks/services/redisClient.mock.js b/test/mocks/services/redisClient.mock.js index a04ed7b494..e29b53ff61 100644 --- a/test/mocks/services/redisClient.mock.js +++ b/test/mocks/services/redisClient.mock.js @@ -1,5 +1,5 @@ const - EventEmitter = require('eventemitter2').EventEmitter2, + EventEmitter = require('eventemitter3'), IORedis = require('ioredis'), getBuiltinCommands = (new IORedis({lazyConnect: true})).getBuiltinCommands, Bluebird = require('bluebird'); @@ -10,7 +10,7 @@ const */ class RedisClientMock extends EventEmitter { constructor (err) { - super({verboseMemoryLeak: true}); + super(); this.getBuiltinCommands = getBuiltinCommands; @@ -46,7 +46,7 @@ class RedisClientMock extends EventEmitter { this.emit('end', true); }, 50); }; - Stream.prototype = new EventEmitter({verboseMemoryLeak: true}); + Stream.prototype = new EventEmitter(); return new Stream(); }