diff --git a/integration/rabbitmq/e2e/cancel-and-resume.e2e-spec.ts b/integration/rabbitmq/e2e/cancel-and-resume.e2e-spec.ts index 78b671567..fd958a98b 100644 --- a/integration/rabbitmq/e2e/cancel-and-resume.e2e-spec.ts +++ b/integration/rabbitmq/e2e/cancel-and-resume.e2e-spec.ts @@ -24,7 +24,7 @@ describe('Rabbit Cancel and Resume', () => { const moduleFixture = await Test.createTestingModule({ providers: [], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/e2e/configuration.e2e-spec.ts b/integration/rabbitmq/e2e/configuration.e2e-spec.ts index f1a7b222f..6db67bee4 100644 --- a/integration/rabbitmq/e2e/configuration.e2e-spec.ts +++ b/integration/rabbitmq/e2e/configuration.e2e-spec.ts @@ -4,7 +4,8 @@ import { RabbitMQModule, AmqpConnectionManager, } from '@golevelup/nestjs-rabbitmq'; -import { ConsoleLogger } from '@nestjs/common'; +import { createMock } from '@golevelup/ts-jest'; +import { Logger, Provider } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import * as amqplib from 'amqplib'; @@ -14,7 +15,11 @@ const rabbitPort = process.env.NODE_ENV === 'ci' ? process.env.RABBITMQ_PORT : '5672'; const uri = `amqp://rabbitmq:rabbitmq@${rabbitHost}:${rabbitPort}`; const amqplibUri = `${uri}?heartbeat=5`; -const logger = new ConsoleLogger('Custom logger'); +const customLogger = createMock({ + log: jest.fn(), + error: jest.fn(), + warn: jest.fn(), +}); const nonExistingExchange = 'non-existing-exchange'; const nonExistingQueue = 'non-existing-queue'; @@ -22,7 +27,7 @@ const nonExistingQueue = 'non-existing-queue'; const testRoutingKey = 'test'; class RabbitConfig { - createModuleConfig(): RabbitMQConfig { + create(): RabbitMQConfig { return { uri, connectionManagerOptions: { heartbeatIntervalInSeconds: 5 }, @@ -30,6 +35,14 @@ class RabbitConfig { }; } } +const silentLoggerProvider: Provider = { + provide: Logger, + useValue: { + log: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + }, +}; describe('Module Configuration', () => { let app: TestingModule; @@ -43,14 +56,14 @@ describe('Module Configuration', () => { describe('forRoot', () => { it('should configure RabbitMQ', async () => { const spy = jest.spyOn(amqplib, 'connect'); - const logSpy = jest.spyOn(logger, 'log'); + const logSpy = jest.spyOn(customLogger, 'log'); app = await Test.createTestingModule({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ uri, connectionInitOptions: { wait: true, reject: true, timeout: 3000 }, - logger, + logger: customLogger, }), ], }).compile(); @@ -63,24 +76,11 @@ describe('Module Configuration', () => { expect(logSpy).toHaveBeenCalled(); }); - it('should be able to configure RabbitMQ when config is not provided', async () => { - const spy = jest.spyOn(amqplib, 'connect'); - app = await Test.createTestingModule({ - imports: [RabbitMQModule.forRoot(RabbitMQModule)], - }).compile(); - const connectionManager = app.get(AmqpConnectionManager); - const connection = app.get(AmqpConnection); - - expect(spy).not.toHaveBeenCalled(); - expect(app).toBeDefined(); - expect(connectionManager).toBeDefined(); - expect(connection).toBeUndefined(); - }); - it('should be able to add connection latter when rmq config is not provided through module', async () => { const spy = jest.spyOn(amqplib, 'connect'); app = await Test.createTestingModule({ - imports: [RabbitMQModule.forRoot(RabbitMQModule)], + providers: [silentLoggerProvider], + imports: [RabbitMQModule.forRoot({ uri })], }).compile(); const connectionManager = app.get(AmqpConnectionManager); const connection = new AmqpConnection({ @@ -97,7 +97,7 @@ describe('Module Configuration', () => { try { app = await Test.createTestingModule({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: nonExistingExchange, @@ -111,7 +111,7 @@ describe('Module Configuration', () => { reject: true, timeout: 3000, }, - logger, + logger: customLogger, }), ], }).compile(); @@ -129,7 +129,7 @@ describe('Module Configuration', () => { app = await Test.createTestingModule({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: nonExistingExchange, @@ -143,7 +143,7 @@ describe('Module Configuration', () => { reject: true, timeout: 3000, }, - logger, + logger: customLogger, }), ], }).compile(); @@ -166,7 +166,7 @@ describe('Module Configuration', () => { app = await Test.createTestingModule({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: nonExistingExchange, @@ -180,7 +180,7 @@ describe('Module Configuration', () => { reject: true, timeout: 3000, }, - logger, + logger: customLogger, }), ], }).compile(); @@ -199,7 +199,7 @@ describe('Module Configuration', () => { try { app = await Test.createTestingModule({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ queues: [ { name: nonExistingQueue, @@ -212,7 +212,7 @@ describe('Module Configuration', () => { reject: true, timeout: 3000, }, - logger, + logger: customLogger, }), ], }).compile(); @@ -230,7 +230,7 @@ describe('Module Configuration', () => { app = await Test.createTestingModule({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ queues: [ { name: nonExistingQueue, @@ -243,7 +243,7 @@ describe('Module Configuration', () => { reject: true, timeout: 3000, }, - logger, + logger: customLogger, }), ], }).compile(); @@ -285,8 +285,9 @@ describe('Module Configuration', () => { }); app = await Test.createTestingModule({ + providers: [silentLoggerProvider], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: async () => { return { queues: [ @@ -330,8 +331,9 @@ describe('Module Configuration', () => { const spy = jest.spyOn(amqplib, 'connect'); app = await Test.createTestingModule({ + providers: [silentLoggerProvider], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: async () => { return { uri, @@ -356,8 +358,9 @@ describe('Module Configuration', () => { const spy = jest.spyOn(amqplib, 'connect'); app = await Test.createTestingModule({ + providers: [silentLoggerProvider], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useClass: RabbitConfig, }), ], @@ -369,55 +372,13 @@ describe('Module Configuration', () => { expect(spy).toHaveBeenCalledWith(amqplibUri, undefined); }); - it('should configure RabbitMQ with useExisting explicit provide', async () => { - const spy = jest.spyOn(amqplib, 'connect'); - - const instance = new RabbitConfig(); - - app = await Test.createTestingModule({ - imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { - useExisting: { - provide: RabbitConfig, - value: instance, - }, - }), - ], - }).compile(); - - expect(app).toBeDefined(); - - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith(amqplibUri, undefined); - }); - - it('should configure RabbitMQ with useExisting implicit provide', async () => { - const spy = jest.spyOn(amqplib, 'connect'); - - const instance = new RabbitConfig(); - - app = await Test.createTestingModule({ - imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { - useExisting: { - value: instance, - }, - }), - ], - }).compile(); - - expect(app).toBeDefined(); - - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith(amqplibUri, undefined); - }); - describe('should use `createExchangeIfNotExists` flag correctly', () => { it("should throw an error if exchange doesn't exist and `createExchangeIfNotExists` is false", async () => { try { app = await Test.createTestingModule({ + providers: [silentLoggerProvider], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: async () => { return { exchanges: [ @@ -451,8 +412,9 @@ describe('Module Configuration', () => { const spy = jest.spyOn(amqplib, 'connect'); app = await Test.createTestingModule({ + providers: [silentLoggerProvider], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: async () => { return { exchanges: [ @@ -491,8 +453,9 @@ describe('Module Configuration', () => { const spy = jest.spyOn(amqplib, 'connect'); app = await Test.createTestingModule({ + providers: [silentLoggerProvider], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: async () => { return { exchanges: [ @@ -568,8 +531,9 @@ describe('Module Configuration', () => { }); app = await Test.createTestingModule({ + providers: [silentLoggerProvider], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: async () => { return { exchanges: [ @@ -638,8 +602,9 @@ describe('Module Configuration', () => { const otherExchangeName = 'otherExchange'; app = await Test.createTestingModule({ + providers: [silentLoggerProvider], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: async () => { return { exchanges: [ diff --git a/integration/rabbitmq/e2e/connection-failover.e2e-spec1.ts b/integration/rabbitmq/e2e/connection-failover.e2e-spec1.ts index 017159e89..9ea435dc4 100644 --- a/integration/rabbitmq/e2e/connection-failover.e2e-spec1.ts +++ b/integration/rabbitmq/e2e/connection-failover.e2e-spec1.ts @@ -63,7 +63,7 @@ export class RestController { @Module({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/e2e/execution-context.e2e-spec.ts b/integration/rabbitmq/e2e/execution-context.e2e-spec.ts index 3b61aa70d..3f277d639 100644 --- a/integration/rabbitmq/e2e/execution-context.e2e-spec.ts +++ b/integration/rabbitmq/e2e/execution-context.e2e-spec.ts @@ -52,7 +52,7 @@ describe('Rabbit Subscribe Without Register Handlers', () => { const moduleFixture = await Test.createTestingModule({ providers: [SubscribeService, TestInterceptor], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/e2e/graceful-shutdown.e2e-spec.ts b/integration/rabbitmq/e2e/graceful-shutdown.e2e-spec.ts index affe98b70..c03f3ee82 100644 --- a/integration/rabbitmq/e2e/graceful-shutdown.e2e-spec.ts +++ b/integration/rabbitmq/e2e/graceful-shutdown.e2e-spec.ts @@ -54,7 +54,7 @@ describe('Rabbit Graceful Shutdown', () => { const moduleFixture = await Test.createTestingModule({ providers: [SubscribeService], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ uri, connectionInitOptions: { wait: true, reject: true, timeout: 3000 }, }), diff --git a/integration/rabbitmq/e2e/multiple-channels.e2e-spec.ts b/integration/rabbitmq/e2e/multiple-channels.e2e-spec.ts index 2883bc395..c8976e555 100644 --- a/integration/rabbitmq/e2e/multiple-channels.e2e-spec.ts +++ b/integration/rabbitmq/e2e/multiple-channels.e2e-spec.ts @@ -150,7 +150,7 @@ describe('Rabbit Multiple Channels', () => { const moduleFixture = await Test.createTestingModule({ providers: [SubscribeToMultipleChannelsService], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/e2e/nack-and-requeue.e2e-spec.ts b/integration/rabbitmq/e2e/nack-and-requeue.e2e-spec.ts index 68f7399f7..843149aae 100644 --- a/integration/rabbitmq/e2e/nack-and-requeue.e2e-spec.ts +++ b/integration/rabbitmq/e2e/nack-and-requeue.e2e-spec.ts @@ -60,7 +60,7 @@ describe('Nack and Requeue', () => { const moduleFixture = await Test.createTestingModule({ providers: [SubscribeService], imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: () => ({ exchanges: [ { diff --git a/integration/rabbitmq/e2e/rmq-context.e2e-spec.ts b/integration/rabbitmq/e2e/rmq-context.e2e-spec.ts index 53e1b19e7..140b3ad89 100644 --- a/integration/rabbitmq/e2e/rmq-context.e2e-spec.ts +++ b/integration/rabbitmq/e2e/rmq-context.e2e-spec.ts @@ -50,7 +50,7 @@ describe('RMQ Context in Global interceptor', () => { const moduleFixture = await Test.createTestingModule({ providers: [SubscribeService, TestInterceptor], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/e2e/rpc-no-direct-reply.e2e-spec.ts b/integration/rabbitmq/e2e/rpc-no-direct-reply.e2e-spec.ts index 7f728b74a..c1d54976d 100644 --- a/integration/rabbitmq/e2e/rpc-no-direct-reply.e2e-spec.ts +++ b/integration/rabbitmq/e2e/rpc-no-direct-reply.e2e-spec.ts @@ -39,7 +39,7 @@ describe('Rabbit Direct Reply To', () => { const moduleFixture = await Test.createTestingModule({ providers: [RpcService], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/e2e/serialization.e2e-spec.ts b/integration/rabbitmq/e2e/serialization.e2e-spec.ts index e163373eb..99d457e48 100644 --- a/integration/rabbitmq/e2e/serialization.e2e-spec.ts +++ b/integration/rabbitmq/e2e/serialization.e2e-spec.ts @@ -77,7 +77,7 @@ describe('Rabbit Subscribe', () => { SubscribeHandlerSerializationService, ], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/e2e/subscribe-no-handlers.e2e-spec.ts b/integration/rabbitmq/e2e/subscribe-no-handlers.e2e-spec.ts index 22f4e96d3..786ffe0cd 100644 --- a/integration/rabbitmq/e2e/subscribe-no-handlers.e2e-spec.ts +++ b/integration/rabbitmq/e2e/subscribe-no-handlers.e2e-spec.ts @@ -38,7 +38,7 @@ describe('Rabbit Subscribe Without Register Handlers', () => { const moduleFixture = await Test.createTestingModule({ providers: [SubscribeService], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/e2e/subscribe.e2e-spec.ts b/integration/rabbitmq/e2e/subscribe.e2e-spec.ts index cb5b30701..2e21113e7 100644 --- a/integration/rabbitmq/e2e/subscribe.e2e-spec.ts +++ b/integration/rabbitmq/e2e/subscribe.e2e-spec.ts @@ -195,7 +195,7 @@ describe('Rabbit Subscribe', () => { const moduleFixture = await Test.createTestingModule({ providers: [SubscribeService], imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: exchange, diff --git a/integration/rabbitmq/src/app.module.ts b/integration/rabbitmq/src/app.module.ts index fef823ac2..f8c5312ac 100644 --- a/integration/rabbitmq/src/app.module.ts +++ b/integration/rabbitmq/src/app.module.ts @@ -13,7 +13,7 @@ const uri = `amqp://rabbitmq:rabbitmq@${rabbitHost}:${rabbitPort}`; @Module({ imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: () => ({ exchanges: [ { diff --git a/integration/rabbitmq/src/controller-discovery/controller-discovery.module.ts b/integration/rabbitmq/src/controller-discovery/controller-discovery.module.ts index 89d3d50f6..06331ece3 100644 --- a/integration/rabbitmq/src/controller-discovery/controller-discovery.module.ts +++ b/integration/rabbitmq/src/controller-discovery/controller-discovery.module.ts @@ -11,7 +11,7 @@ const uri = `amqp://rabbitmq:rabbitmq@${rabbitHost}:${rabbitPort}`; @Module({ imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: () => ({ exchanges: [ { diff --git a/integration/rabbitmq/src/named-connection/named-connection.module.ts b/integration/rabbitmq/src/named-connection/named-connection.module.ts index 4faf72cb7..87296b10c 100644 --- a/integration/rabbitmq/src/named-connection/named-connection.module.ts +++ b/integration/rabbitmq/src/named-connection/named-connection.module.ts @@ -11,7 +11,7 @@ const uri = `amqp://rabbitmq:rabbitmq@${rabbitHost}:${rabbitPort}`; @Module({ imports: [ - RabbitMQModule.forRootAsync(RabbitMQModule, { + RabbitMQModule.forRootAsync({ useFactory: () => ({ name: CONNECTION_NAME, exchanges: [ diff --git a/packages/rabbitmq/README.md b/packages/rabbitmq/README.md index 657d225ba..9abab2bf7 100644 --- a/packages/rabbitmq/README.md +++ b/packages/rabbitmq/README.md @@ -71,7 +71,7 @@ import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; @Module({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: 'exchange1', @@ -116,7 +116,7 @@ import { MessagingService } from './messaging/messaging.service'; @Module({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: 'exchange1', @@ -149,13 +149,16 @@ This library is built using an underlying NestJS concept called `External Contex You can identify RabbitMQ contexts by their context type, `'rmq'`: ```typescript +import { RABBIT_CONTEXT_TYPE_KEY } from '@golevelup/nestjs-rabbitmq'; @Injectable() class ExampleInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler) { - const contextType = context.getType<'http' | 'rmq'>(); + const contextType = context.getType< + 'http' | typeof RABBIT_CONTEXT_TYPE_KEY + >(); // Do nothing if this is a RabbitMQ event - if (contextType === 'rmq') { + if (contextType === RABBIT_CONTEXT_TYPE_KEY) { return next.handle(); } @@ -199,7 +202,7 @@ import { MessagingService } from './messaging/messaging.service'; @Module({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ exchanges: [ { name: 'exchange1', @@ -312,12 +315,12 @@ import { ConsumeMessage } from 'amqplib'; @Module({ imports: [ - RabbitMQModule.forRoot(RabbitMQModule, { + RabbitMQModule.forRoot({ // ... deserializer: (message: Buffer, msg: ConsumeMessage) => { const decodedMessage = myCustomDeserializer( msg.toString(), - msg.properties.headers + msg.properties.headers, ); return decodedMessage; }, @@ -654,7 +657,7 @@ import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; uri: 'amqp://rabbitmq:rabbitmq@localhost:5672', connectionInitOptions: { wait: false }, } - : undefined + : undefined, ), ], }) diff --git a/packages/rabbitmq/package.json b/packages/rabbitmq/package.json index c3cd01b01..856177c25 100644 --- a/packages/rabbitmq/package.json +++ b/packages/rabbitmq/package.json @@ -34,9 +34,7 @@ "url": "https://github.com/golevelup/nestjs/issues" }, "dependencies": { - "@golevelup/nestjs-common": "workspace:^", "@golevelup/nestjs-discovery": "workspace:^", - "@golevelup/nestjs-modules": "workspace:^", "amqp-connection-manager": "^4.1.14", "amqplib": "^0.10.5", "lodash": "^4.17.21" @@ -47,7 +45,7 @@ "peerDependencies": { "@nestjs/common": "^10.x", "@nestjs/core": "^10.x", - "reflect-metadata": "^0.1.0 || ^0.2.0", + "reflect-metadata": "^0.2.2", "rxjs": "^7.x" }, "publishConfig": { diff --git a/packages/rabbitmq/src/rabbitmq-module-definition.ts b/packages/rabbitmq/src/rabbitmq-module-definition.ts new file mode 100644 index 000000000..3d9307a6d --- /dev/null +++ b/packages/rabbitmq/src/rabbitmq-module-definition.ts @@ -0,0 +1,7 @@ +import { ConfigurableModuleBuilder } from '@nestjs/common'; +import { RabbitMQConfig } from './rabbitmq.interfaces'; + +export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = + new ConfigurableModuleBuilder() + .setClassMethodName('forRoot') + .build(); diff --git a/packages/rabbitmq/src/rabbitmq.constants.ts b/packages/rabbitmq/src/rabbitmq.constants.ts index bb172f5d4..5afeca881 100644 --- a/packages/rabbitmq/src/rabbitmq.constants.ts +++ b/packages/rabbitmq/src/rabbitmq.constants.ts @@ -1,5 +1,7 @@ +import { MODULE_OPTIONS_TOKEN } from './rabbitmq-module-definition'; + export const RABBIT_HANDLER = Symbol('RABBIT_HANDLER'); -export const RABBIT_CONFIG_TOKEN = Symbol('RABBIT_CONFIG'); +export const RABBIT_CONFIG_TOKEN = MODULE_OPTIONS_TOKEN; export const RABBIT_PARAM_TYPE = 3; export const RABBIT_HEADER_TYPE = 4; export const RABBIT_REQUEST_TYPE = 5; diff --git a/packages/rabbitmq/src/rabbitmq.decorators.ts b/packages/rabbitmq/src/rabbitmq.decorators.ts index 036535ad9..0989b633b 100644 --- a/packages/rabbitmq/src/rabbitmq.decorators.ts +++ b/packages/rabbitmq/src/rabbitmq.decorators.ts @@ -1,4 +1,3 @@ -import { makeInjectableDecorator } from '@golevelup/nestjs-common'; import 'reflect-metadata'; import { applyDecorators, @@ -6,6 +5,7 @@ import { PipeTransform, Type, assignMetadata, + Inject, } from '@nestjs/common'; import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants'; import { isString } from 'lodash'; @@ -36,8 +36,7 @@ export const RabbitSubscribe = makeRabbitDecorator({ type: 'subscribe' }); export const RabbitRPC = makeRabbitDecorator({ type: 'rpc' }); -export const InjectRabbitMQConfig = - makeInjectableDecorator(RABBIT_CONFIG_TOKEN); +export const InjectRabbitMQConfig = () => Inject(RABBIT_CONFIG_TOKEN); export const createPipesRpcParamDecorator = ( diff --git a/packages/rabbitmq/src/rabbitmq.module.ts b/packages/rabbitmq/src/rabbitmq.module.ts index 737c57730..c866dfa9e 100644 --- a/packages/rabbitmq/src/rabbitmq.module.ts +++ b/packages/rabbitmq/src/rabbitmq.module.ts @@ -3,14 +3,12 @@ import { DiscoveryModule, DiscoveryService, } from '@golevelup/nestjs-discovery'; -import { createConfigurableDynamicRootModule } from '@golevelup/nestjs-modules'; import { DynamicModule, Module, Logger, OnApplicationBootstrap, OnApplicationShutdown, - Inject, LoggerService, } from '@nestjs/common'; import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants'; @@ -19,49 +17,47 @@ import { groupBy } from 'lodash'; import { AmqpConnection } from './amqp/connection'; import { AmqpConnectionManager } from './amqp/connectionManager'; import { assertRabbitMqUri } from './amqp/utils'; +import { ConfigurableModuleClass } from './rabbitmq-module-definition'; import { RABBIT_CONFIG_TOKEN, RABBIT_CONTEXT_TYPE_KEY, RABBIT_HANDLER, } from './rabbitmq.constants'; +import { InjectRabbitMQConfig } from './rabbitmq.decorators'; import { RabbitRpcParamsFactory } from './rabbitmq.factory'; import { RabbitHandlerConfig, RabbitMQConfig } from './rabbitmq.interfaces'; @Module({ imports: [DiscoveryModule], -}) -export class RabbitMQModule - extends createConfigurableDynamicRootModule( - RABBIT_CONFIG_TOKEN, + providers: [ { - providers: [ - { - provide: AmqpConnectionManager, - useFactory: async ( - config: RabbitMQConfig, - ): Promise => { - await RabbitMQModule.AmqpConnectionFactory(config); - return RabbitMQModule.connectionManager; - }, - inject: [RABBIT_CONFIG_TOKEN], - }, - { - provide: AmqpConnection, - useFactory: async ( - config: RabbitMQConfig, - connectionManager: AmqpConnectionManager, - ): Promise => { - return connectionManager.getConnection( - config?.name || 'default', - ) as AmqpConnection; - }, - inject: [RABBIT_CONFIG_TOKEN, AmqpConnectionManager], - }, - RabbitRpcParamsFactory, - ], - exports: [AmqpConnectionManager, AmqpConnection], + provide: AmqpConnectionManager, + useFactory: async ( + config: RabbitMQConfig, + ): Promise => { + await RabbitMQModule.AmqpConnectionFactory(config); + return RabbitMQModule.connectionManager; + }, + inject: [RABBIT_CONFIG_TOKEN], }, - ) + { + provide: AmqpConnection, + useFactory: async ( + config: RabbitMQConfig, + connectionManager: AmqpConnectionManager, + ): Promise => { + return connectionManager.getConnection( + config?.name || 'default', + ) as AmqpConnection; + }, + inject: [RABBIT_CONFIG_TOKEN, AmqpConnectionManager], + }, + RabbitRpcParamsFactory, + ], + exports: [AmqpConnectionManager, AmqpConnection], +}) +export class RabbitMQModule + extends ConfigurableModuleClass implements OnApplicationBootstrap, OnApplicationShutdown { private readonly logger: LoggerService; @@ -74,7 +70,7 @@ export class RabbitMQModule private readonly externalContextCreator: ExternalContextCreator, private readonly rpcParamsFactory: RabbitRpcParamsFactory, private readonly connectionManager: AmqpConnectionManager, - @Inject(RABBIT_CONFIG_TOKEN) config: RabbitMQConfig, + @InjectRabbitMQConfig() config: RabbitMQConfig, ) { super(); this.logger = config?.logger || new Logger(RabbitMQModule.name); diff --git a/packages/rabbitmq/src/tests/rabbitmq-module.spec.ts b/packages/rabbitmq/src/tests/rabbitmq-module.spec.ts new file mode 100644 index 000000000..9642a115b --- /dev/null +++ b/packages/rabbitmq/src/tests/rabbitmq-module.spec.ts @@ -0,0 +1,31 @@ +import { Test } from '@nestjs/testing'; +import { AmqpConnectionManager } from '../amqp/connectionManager'; +import { RABBIT_CONFIG_TOKEN } from '../rabbitmq.constants'; +import { RabbitMQConfig } from '../rabbitmq.interfaces'; +import { RabbitMQModule } from '../rabbitmq.module'; + +const uri = 'amqp://rabbitmq:rabbitmq@test:5555'; + +describe(RabbitMQModule.name, () => { + it('should create the module successfully', async () => { + const moduleFixture = await Test.createTestingModule({ + imports: [ + RabbitMQModule.forRoot({ + uri, + }), + ], + }) + .overrideProvider(AmqpConnectionManager) + .useValue(new AmqpConnectionManager()) + .compile(); + + const app = moduleFixture.createNestApplication(); + const connectionManager = app.get( + AmqpConnectionManager, + ); + const config = app.get(RABBIT_CONFIG_TOKEN); + + expect(connectionManager).toBeInstanceOf(AmqpConnectionManager); + expect(config).toEqual(expect.objectContaining({ uri })); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5216362e..85a245a3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -252,15 +252,9 @@ importers: packages/rabbitmq: dependencies: - '@golevelup/nestjs-common': - specifier: workspace:^ - version: link:../common '@golevelup/nestjs-discovery': specifier: workspace:^ version: link:../discovery - '@golevelup/nestjs-modules': - specifier: workspace:^ - version: link:../modules '@nestjs/common': specifier: ^10.x version: 10.4.4(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -277,7 +271,7 @@ importers: specifier: ^4.17.21 version: 4.17.21 reflect-metadata: - specifier: ^0.1.0 || ^0.2.0 + specifier: ^0.2.2 version: 0.2.2 rxjs: specifier: ^7.x