-
Notifications
You must be signed in to change notification settings - Fork 124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add pipes & hooks wildcard event #1305
Changes from 2 commits
59f13ad
bf7c5bd
3f9331f
9cb5393
488a774
0ab6275
97b50d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -831,8 +831,15 @@ class PluginsManager { | |||||||||
* @returns {Promise} | ||||||||||
*/ | ||||||||||
function triggerHooks(emitter, event, data) { | ||||||||||
const wildcardEvents = getWildcardEvents(event) | ||||||||||
// filter events like 'foo:*' because EventEmitter2 | ||||||||||
// already have this wildcard feature | ||||||||||
.filter(e => e[0] !== '*' && e.split(':')[1] !== '*'); | ||||||||||
|
||||||||||
emitter.emit(event, data); | ||||||||||
|
||||||||||
wildcardEvents.forEach(wildcardEvent => emitter.emit(wildcardEvent, data)); | ||||||||||
|
||||||||||
return Bluebird.resolve(data); | ||||||||||
} | ||||||||||
|
||||||||||
|
@@ -845,15 +852,19 @@ 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]); | ||||||||||
scottinet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
} | ||||||||||
|
||||||||||
if (wildcardEvent && pipes && pipes[wildcardEvent] && pipes[wildcardEvent].length) { | ||||||||||
preparedPipes = preparedPipes.concat(pipes[wildcardEvent]); | ||||||||||
if (wildcardEvents && pipes) { | ||||||||||
wildcardEvents.forEach(wildcardEvent => { | ||||||||||
if (pipes[wildcardEvent] && pipes[wildcardEvent].length) { | ||||||||||
preparedPipes.push(...pipes[wildcardEvent]); | ||||||||||
} | ||||||||||
}); | ||||||||||
} | ||||||||||
|
||||||||||
if (preparedPipes.length === 0) { | ||||||||||
|
@@ -876,19 +887,30 @@ function triggerPipes(pipes, event, data) { | |||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* For a specific event, return the corresponding wildcard | ||||||||||
* For a specific event, return the correspondings wildcards | ||||||||||
* @example | ||||||||||
* getWildcardEvent('data:create') // return 'data:*' | ||||||||||
* @param {string} event | ||||||||||
* @returns {String} wildcard event | ||||||||||
* getWildcardEvents('data:create') // return ['data:*'] | ||||||||||
* getWildcardEvents('data:beforeCreate') // return ['data:*', 'data:before*'] | ||||||||||
* @param {String} event | ||||||||||
* @returns {Array<String>} wildcard events | ||||||||||
*/ | ||||||||||
function getWildcardEvent (event) { | ||||||||||
const indexDelimiter = event.indexOf(':'); | ||||||||||
if (indexDelimiter !== 1) { | ||||||||||
return event.substring(0, indexDelimiter+1) + '*'; | ||||||||||
function getWildcardEvents (event) { | ||||||||||
const [scope, name] = event.split(':'); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This leaves events with 3 or more separator out of the picture. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ugly but I think it can help you to figure out how to handle more than 3 separators:
Suggested change
|
||||||||||
|
||||||||||
if (!name) { | ||||||||||
return null; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to prevent breaking code using the return value of this function without first checking that it didn't return null:
Suggested change
|
||||||||||
} | ||||||||||
|
||||||||||
return null; | ||||||||||
const wildcardEvents = ['*:*', `${scope}:*`]; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I disagree with the event
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As we discuss with @thomasarbona, it could be nice to handle There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Veto: regexp are slow, and the number and frequency of events triggered make them a critical part of the code. Why do we need regular expressions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right I didn't get that this action appends at funnel level. Ignore my previous comment 😅 |
||||||||||
|
||||||||||
['before', 'after'].forEach(prefix => { | ||||||||||
if (name.startsWith(prefix)) { | ||||||||||
wildcardEvents.push(`${scope}:${prefix}*`); | ||||||||||
wildcardEvents.push(`*:${prefix}*`); | ||||||||||
} | ||||||||||
}); | ||||||||||
|
||||||||||
return wildcardEvents; | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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' | ||
} | ||
}]; | ||
|
||
pluginsManager.run() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that promise must be returned, otherwise the test isn't awaited and it always passes |
||
.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' | ||
} | ||
}]; | ||
|
||
pluginsManager.run() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
.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' | ||
} | ||
}]; | ||
|
||
pluginsManager.run() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
.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: { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be simpler (and better) to completely get rid of EventEmitter2, since we're only using it for wildcards (this should be a light change since EventEmitter2 has the same interface than the native EventEmitter).
And instead manage wildcards ourselves entirely, because that kind of exception handling is a bit costly performances wide, and events triggering is a critical part of Kuzzle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this code is unsafe because
getWildcardEvents
can return null.