I can't help but wonder if anyone has encountered this issue before.
Prior to this, my EventHandler structure looked like:
export interface EventHandler {
name: string;
canHandleEvent(event: EventEntity): boolean;
handleEvent(event: EventEntity): Promise<void>;
}
Initially, everything was working smoothly with my filter function and tests passing while filtering events using:
messages.forEach(message => {
const event: EventEntity = JSON.parse(message.Body);
this.handlers
.filter(handler => handler.canHandleEvent(event)) // Everything was working fine
.forEach(handler => {
// Logic
});
However, we recently had to make a change to the canHandleEvent
method to either return Boolean or Promise. This modification was necessary because certain promises needed resolution in order to determine whether the event could be handled or not.
export interface EventHandler {
// ...
canHandleEvent(event: EventEntity): boolean | Promise<boolean>;
}
To address this shift, I employed Promise.resolve
and Promise.all
, but unfortunately:
messages.forEach(async message => {
const event: EventEntity = JSON.parse(message.Body);
const handlersResolved = await Promise.all(this.handlers);
handlersResolved
.filter(handler => handler.canHandleEvent(event))
.forEach(handler => {
Currently, the tests are now passing for the Promise-based canHandleEvent
, but they are failing for Boolean returns. Here is an example of one such failing test scenario:
class HandlerB implements EventHandler {
name = HandlerB.name;
numRuns = 0;
canHandleEvent(event: EventEntity): boolean {
console.log('event', event)
return event.eventType === EventType.ONE_EVENT || event.eventType === EventType.SECOND_EVENT;
}
async handleEvent(event: EventEntity): Promise<void> {
return new Promise(resolve => {
setTimeout(() => {
this.numRuns += 1;
resolve();
}, 25);
});
}
}
Some previously passing tests that are now failing include:
it('Should process handlers that match, including canHandleEvent that returns Promise<boolean> TRUE', async () => {
setHandlers([handlerA, handlerB, handlerC]);
const event = await createEvent(EventType.SECOND_EVENT);
await sleep(1000);
expect(handlerA.numRuns, 'handleA').to.eql(0);
expect(handlerB.numRuns, 'handleB').to.eql(1);
expect(handlerC.numRuns, 'handleC').to.eql(1); // handlerC is Promise<boolean>, it works fine
expect(errorHandler.numRuns).to.eql(0);
handlerC.numRuns = 0;
});
it('Should allow handlers to pass even if one has an error', async () => {
setHandlers([handlerA, handlerB, errorHandler]);
const event = await createEvent(EventType.USER_REGISTRATION_STATUS);
await sleep(1000);
expect(handlerA.numRuns, 'handlerA').to.eql(1);
expect(handlerB.numRuns, 'handlerB').to.eql(1);
expect(errorHandler.numRuns, 'errorHandler').to.eql(1);
});
Any suggestions on how to tackle this challenge? I've attempted to differentiate between promises and booleans within the .filter
function with no success:
this.handlers
.filter(async handler => {
if(typeof handler.canHandleEvent(event).then == 'function') {
const result = await Promise.resolve(handler.canHandleEvent(event))
console.log('IS PROMISE!!', result);
return result
}
console.log('IT IS NOT PROMISE', handler.canHandleEvent(event))
return handler.canHandleEvent(event)
})