diff --git a/lib/event-subscribers.loader.ts b/lib/event-subscribers.loader.ts index e7d1303b..daae9ebc 100644 --- a/lib/event-subscribers.loader.ts +++ b/lib/event-subscribers.loader.ts @@ -95,7 +95,7 @@ export class EventSubscribersLoader listenerMethod( event, (...args: unknown[]) => - this.wrapFunctionInTryCatchBlocks(instance, methodKey, args), + this.wrapFunctionInTryCatchBlocks(instance, methodKey, args, options), options, ); } @@ -142,6 +142,7 @@ export class EventSubscribersLoader contextInstance, listenerMethodKey, args, + options, ); }, options, @@ -177,11 +178,16 @@ export class EventSubscribersLoader instance: Record, methodKey: string, args: unknown[], + options?: OnEventOptions, ) { try { return await instance[methodKey].call(instance, ...args); } catch (e) { - this.logger.error(e); + if (options?.suppressErrors ?? true) { + this.logger.error(e); + } else { + throw e; + } } } } diff --git a/lib/interfaces/on-event-options.interface.ts b/lib/interfaces/on-event-options.interface.ts index 6a7437ca..9e9ef360 100644 --- a/lib/interfaces/on-event-options.interface.ts +++ b/lib/interfaces/on-event-options.interface.ts @@ -9,4 +9,11 @@ export type OnEventOptions = OnOptions & { * @default false */ prependListener?: boolean; + + /** + * If "true", the onEvent callback will not throw an error while handling the event. Otherwise, if "false" it will throw an error. + * + * @default true + */ + suppressErrors?: boolean; }; diff --git a/tests/e2e/module-e2e.spec.ts b/tests/e2e/module-e2e.spec.ts index 5cd6ef3b..a6d78e2c 100644 --- a/tests/e2e/module-e2e.spec.ts +++ b/tests/e2e/module-e2e.spec.ts @@ -133,6 +133,17 @@ describe('EventEmitterModule - e2e', () => { expect(result).toBeTruthy(); }); + it('should be able to gracefully recover when an unexpected error occurs from provider and suppressErrors is true', async () => { + const eventsConsumerRef = app.get(EventsProviderConsumer); + await app.init(); + + const emitter = app.get(EventEmitter2); + const result = emitter.emit('error-handling-suppressed.provider'); + + expect(eventsConsumerRef.errorHandlingCalls).toEqual(1); + expect(result).toBeTruthy(); + }); + it('should be able to gracefully recover when an unexpected error occurs from request scoped', async () => { await app.init(); @@ -142,6 +153,29 @@ describe('EventEmitterModule - e2e', () => { expect(result).toBeTruthy(); }); + it('should be able to gracefully recover when an unexpected error occurs from request scoped and suppressErrors is true', async () => { + await app.init(); + + const eventEmitter = app.get(EventEmitter2); + const result = eventEmitter.emit('error-handling-suppressed.request-scoped'); + + expect(result).toBeTruthy(); + }); + + it('should throw when an unexpected error occurs from provider and suppressErrors is false', async () => { + await app.init(); + + const eventEmitter = app.get(EventEmitter2); + expect(eventEmitter.emitAsync('error-throwing.provider')).rejects.toThrow("This is a test error"); + }); + + it('should throw when an unexpected error occurs from request scoped and suppressErrors is false', async () => { + await app.init(); + + const eventEmitter = app.get(EventEmitter2); + expect(eventEmitter.emitAsync('error-throwing.request-scoped')).rejects.toThrow("This is a test error"); + }); + afterEach(async () => { await app.close(); }); diff --git a/tests/src/events-provider.consumer.ts b/tests/src/events-provider.consumer.ts index b0f51a24..35eee72e 100644 --- a/tests/src/events-provider.consumer.ts +++ b/tests/src/events-provider.consumer.ts @@ -23,4 +23,15 @@ export class EventsProviderConsumer { this.errorHandlingCalls++; throw new Error('This is a test error'); } + + @OnEvent('error-handling-suppressed.provider', { suppressErrors: true }) + onErrorHandlingSuppressedEvent() { + this.errorHandlingCalls++; + throw new Error('This is a test error'); + } + + @OnEvent('error-throwing.provider', { suppressErrors: false }) + onErrorThrowingEvent() { + throw new Error('This is a test error'); + } } diff --git a/tests/src/events-provider.request-scoped.consumer.ts b/tests/src/events-provider.request-scoped.consumer.ts index 0477aa47..f08b38cd 100644 --- a/tests/src/events-provider.request-scoped.consumer.ts +++ b/tests/src/events-provider.request-scoped.consumer.ts @@ -26,4 +26,14 @@ export class EventsProviderRequestScopedConsumer { onErrorHandlingEvent() { throw new Error('This is a test error'); } + + @OnEvent('error-handling-suppressed.request-scoped', { suppressErrors: true }) + onErrorHandlingSuppressedEvent() { + throw new Error('This is a test error'); + } + + @OnEvent('error-throwing.request-scoped', { suppressErrors: false }) + onErrorThrowingEvent() { + throw new Error('This is a test error'); + } }