Skip to content

Commit

Permalink
Merge pull request #1304 from nestjs/feat/readiness-watcher
Browse files Browse the repository at this point in the history
feat: add event emitter readiness watcher class #1063
  • Loading branch information
kamilmysliwiec authored Oct 21, 2024
2 parents 63dc888 + eb6402e commit 9713732
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 7 deletions.
19 changes: 19 additions & 0 deletions lib/event-emitter-readiness.watcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Injectable } from '@nestjs/common';
import { promiseWithResolvers } from './utils/promise-with-resolvers';

@Injectable()
export class EventEmitterReadinessWatcher {
private readonly readyPromise = promiseWithResolvers();

waitUntilReady() {
return this.readyPromise.promise;
}

setReady() {
this.readyPromise.resolve();
}

setErrored(error: Error) {
this.readyPromise.reject(error);
}
}
4 changes: 3 additions & 1 deletion lib/event-emitter.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DynamicModule, Module } from '@nestjs/common';
import { DiscoveryModule } from '@nestjs/core';
import { EventEmitter2 } from 'eventemitter2';
import { EventEmitterReadinessWatcher } from './event-emitter-readiness.watcher';
import { EventSubscribersLoader } from './event-subscribers.loader';
import { EventsMetadataAccessor } from './events-metadata.accessor';
import { EventEmitterModuleOptions } from './interfaces';
Expand All @@ -15,12 +16,13 @@ export class EventEmitterModule {
providers: [
EventSubscribersLoader,
EventsMetadataAccessor,
EventEmitterReadinessWatcher,
{
provide: EventEmitter2,
useValue: new EventEmitter2(options),
},
],
exports: [EventEmitter2],
exports: [EventEmitter2, EventEmitterReadinessWatcher],
};
}
}
16 changes: 14 additions & 2 deletions lib/event-subscribers.loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@nestjs/core/injector/instance-wrapper';
import { Module } from '@nestjs/core/injector/module';
import { EventEmitter2 } from 'eventemitter2';
import { EventEmitterReadinessWatcher } from './event-emitter-readiness.watcher';
import { EventsMetadataAccessor } from './events-metadata.accessor';
import { OnEventOptions } from './interfaces';

Expand All @@ -33,10 +34,16 @@ export class EventSubscribersLoader
private readonly metadataAccessor: EventsMetadataAccessor,
private readonly metadataScanner: MetadataScanner,
private readonly moduleRef: ModuleRef,
private readonly eventEmitterReadinessWatcher: EventEmitterReadinessWatcher,
) {}

onApplicationBootstrap() {
this.loadEventListeners();
try {
this.loadEventListeners();
this.eventEmitterReadinessWatcher.setReady();
} catch (e) {
this.eventEmitterReadinessWatcher.setErrored(e as Error);
}
}

onApplicationShutdown() {
Expand Down Expand Up @@ -95,7 +102,12 @@ export class EventSubscribersLoader
listenerMethod(
event,
(...args: unknown[]) =>
this.wrapFunctionInTryCatchBlocks(instance, methodKey, args, options),
this.wrapFunctionInTryCatchBlocks(
instance,
methodKey,
args,
options,
),
options,
);
}
Expand Down
3 changes: 2 additions & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { EventEmitter2 } from 'eventemitter2';
export { EVENT_PAYLOAD } from './constants';
export * from './decorators';
export * from './event-emitter-readiness.watcher';
export * from './event-emitter.module';
export { EVENT_PAYLOAD } from './constants';
15 changes: 15 additions & 0 deletions lib/utils/promise-with-resolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* A polyfill for the Promise.withResolvers method that is not available in older versions of Node.js
* @returns A promise and its resolve and reject functions
*/
export function promiseWithResolvers() {
let resolve: () => void;
let reject: (reason?: any) => void;
const promise = new Promise<void>((res, rej) => {
resolve = res;
reject = rej;
});

// @ts-expect-error "resolve" and "reject" are assigned in the promise constructor
return { promise, resolve, reject };
}
22 changes: 19 additions & 3 deletions tests/e2e/module-e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { EventEmitter2 } from 'eventemitter2';
import { EventEmitterReadinessWatcher } from '../../lib';
import { AppModule } from '../src/app.module';
import {
TEST_EVENT_MULTIPLE_PAYLOAD,
Expand Down Expand Up @@ -157,7 +158,9 @@ describe('EventEmitterModule - e2e', () => {
await app.init();

const eventEmitter = app.get(EventEmitter2);
const result = eventEmitter.emit('error-handling-suppressed.request-scoped');
const result = eventEmitter.emit(
'error-handling-suppressed.request-scoped',
);

expect(result).toBeTruthy();
});
Expand All @@ -166,14 +169,27 @@ describe('EventEmitterModule - e2e', () => {
await app.init();

const eventEmitter = app.get(EventEmitter2);
expect(eventEmitter.emitAsync('error-throwing.provider')).rejects.toThrow("This is a test error");
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");
expect(
eventEmitter.emitAsync('error-throwing.request-scoped'),
).rejects.toThrow('This is a test error');
});

it('should be able to wait until the event emitter is ready', async () => {
const eventsConsumerRef = app.get(EventsControllerConsumer);
await app.init();

const eventEmitterWatcher = app.get(EventEmitterReadinessWatcher);
await expect(eventEmitterWatcher.waitUntilReady()).resolves.toBeUndefined();
expect(eventsConsumerRef.eventPayload).toEqual(TEST_EVENT_PAYLOAD);
});

afterEach(async () => {
Expand Down

0 comments on commit 9713732

Please sign in to comment.