UNPKG

@nam088/nestjs-rabbitmq

Version:

A comprehensive RabbitMQ module for NestJS with decorator-based message handling

338 lines 16.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var RabbitMQCoreModule_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.RabbitMQCoreModule = void 0; const core_1 = require("@nestjs/core"); const common_1 = require("@nestjs/common"); const metadata_scanner_1 = require("@nestjs/core/metadata-scanner"); const amqp = __importStar(require("amqp-connection-manager")); const rabbitmq_service_1 = require("./services/rabbitmq.service"); const service_discovery_service_1 = require("./services/service-discovery.service"); const rabbit_controller_decorator_1 = require("./decorators/rabbit-controller.decorator"); const constants_1 = require("./constants"); let RabbitMQCoreModule = RabbitMQCoreModule_1 = class RabbitMQCoreModule { constructor(moduleRef, modulesContainer) { this.moduleRef = moduleRef; this.modulesContainer = modulesContainer; this.logger = new common_1.Logger(RabbitMQCoreModule_1.name); this.metadataScanner = new metadata_scanner_1.MetadataScanner(); } static forRoot(options) { const connectionName = options.connectionName || constants_1.DEFAULT_CONNECTION_NAME; const connectionManagerProvider = { provide: `${constants_1.RABBITMQ_CONNECTION_MANAGER}_${connectionName}`, useFactory: () => amqp.connect([options.uri], { heartbeatIntervalInSeconds: options.connectionOptions?.heartbeatIntervalInSeconds || 5, reconnectTimeInSeconds: options.connectionOptions?.reconnectTimeInSeconds || 10, }), }; const serviceProvider = { inject: [`${constants_1.RABBITMQ_CONNECTION_MANAGER}_${connectionName}`], provide: `${constants_1.RABBITMQ_SERVICE}_${connectionName}`, useFactory: async (connectionManager) => { const service = new rabbitmq_service_1.RabbitMQService(connectionManager, connectionName, options.logLevel ?? 'error'); await service.initialize(); if (options.exchanges) { for (const exchange of options.exchanges) { await service.assertExchange(exchange.name, exchange.type, exchange.options); } } if (options.queues) { for (const queue of options.queues) { await service.assertQueue(queue.name, queue.options); } } return service; }, }; const optionsProvider = { provide: `${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`, useValue: options, }; const providers = [connectionManagerProvider, serviceProvider, optionsProvider]; const exports = [serviceProvider]; if (options.serviceDiscovery?.enabled) { const discoveryProvider = { inject: [`${constants_1.RABBITMQ_SERVICE}_${connectionName}`], provide: `${constants_1.RABBITMQ_SERVICE_DISCOVERY}_${connectionName}`, useFactory: (rabbitService) => new service_discovery_service_1.ServiceDiscoveryService(rabbitService, options.serviceDiscovery), }; providers.push(discoveryProvider); exports.push(discoveryProvider); } return { providers, exports, module: RabbitMQCoreModule_1, }; } static forRootAsync(options) { const connectionName = options.connectionName || constants_1.DEFAULT_CONNECTION_NAME; const connectionManagerProvider = { inject: [`${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`], provide: `${constants_1.RABBITMQ_CONNECTION_MANAGER}_${connectionName}`, useFactory: (moduleOptions) => amqp.connect([moduleOptions.uri], { heartbeatIntervalInSeconds: moduleOptions.connectionOptions?.heartbeatIntervalInSeconds || 5, reconnectTimeInSeconds: moduleOptions.connectionOptions?.reconnectTimeInSeconds || 10, }), }; const serviceProvider = { inject: [ `${constants_1.RABBITMQ_CONNECTION_MANAGER}_${connectionName}`, `${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`, ], provide: `${constants_1.RABBITMQ_SERVICE}_${connectionName}`, useFactory: async (connectionManager, moduleOptions) => { const service = new rabbitmq_service_1.RabbitMQService(connectionManager, connectionName, moduleOptions.logLevel ?? 'error'); await service.initialize(); if (moduleOptions.exchanges) { for (const exchange of moduleOptions.exchanges) { await service.assertExchange(exchange.name, exchange.type, exchange.options); } } if (moduleOptions.queues) { for (const queue of moduleOptions.queues) { await service.assertQueue(queue.name, queue.options); } } return service; }, }; const asyncProviders = this.createAsyncProviders(options, connectionName); const providers = [...asyncProviders, connectionManagerProvider, serviceProvider]; const exports = [serviceProvider]; return { imports: options.imports || [], providers, exports, module: RabbitMQCoreModule_1, }; } async onApplicationBootstrap() { const moduleOptions = this.moduleRef.get(`${constants_1.RABBITMQ_MODULE_OPTIONS}_${constants_1.DEFAULT_CONNECTION_NAME}`, { strict: false }); const autoDiscover = moduleOptions?.autoDiscover !== false; if (!autoDiscover) { return; } for (const moduleRef of this.modulesContainer.values()) { if (!this.shouldScanModule(moduleRef, moduleOptions)) continue; await this.processModuleProviders(moduleRef, moduleOptions); } } static createAsyncOptionsProvider(options, connectionName) { if (options.useFactory) { return { inject: options.inject || [], provide: `${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`, useFactory: options.useFactory, }; } const inject = options.useExisting || options.useClass; return { inject: inject ? [inject] : [], provide: `${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`, useFactory: async (optionsFactory) => await optionsFactory.createRabbitMQOptions(), }; } static createAsyncProviders(options, connectionName) { if (options.useExisting || options.useFactory) { return [this.createAsyncOptionsProvider(options, connectionName)]; } if (options.useClass) { return [ this.createAsyncOptionsProvider(options, connectionName), { provide: options.useClass, useClass: options.useClass, }, ]; } return []; } async processModuleProviders(moduleRef, options) { for (const provider of moduleRef.providers.values()) { if (!this.shouldScanProvider(provider, options)) continue; const instance = provider.instance; const prototype = instance && Object.getPrototypeOf(instance); if (!instance || !prototype) { continue; } this.processProviderMethods(instance, prototype); } } processProviderMethods(instance, prototype) { const SUBSCRIBE_KEY = 'RABBITMQ_SUBSCRIBE_METADATA'; const RPC_KEY = 'RABBIT_RPC_METADATA'; const methodNames = this.metadataScanner.getAllMethodNames(prototype); for (const methodName of methodNames) { const methodDescriptor = prototype[methodName]; const subOptions = methodDescriptor ? Reflect.getMetadata(SUBSCRIBE_KEY, methodDescriptor) : null; const rpcOptions = methodDescriptor ? Reflect.getMetadata(RPC_KEY, methodDescriptor) : null; if (subOptions) void this.registerDiscoveredHandler(instance, methodName, subOptions); if (rpcOptions) void this.registerDiscoveredRpcHandler(instance, methodName, rpcOptions); } } async registerDiscoveredHandler(instance, methodName, options) { const connectionName = options.connectionName || constants_1.DEFAULT_CONNECTION_NAME; const rabbitService = this.moduleRef.get(`${constants_1.RABBITMQ_SERVICE}_${connectionName}`, { strict: false, }); if (!rabbitService) { this.logger.error(`RabbitMQ service not found for connection: ${connectionName}`); return; } try { if (options.queue) { await rabbitService.assertQueue(options.queue, options.queueOptions); } if (options.exchange && options.routingKey && options.queue) { await rabbitService.bindQueue(options.queue, options.exchange, options.routingKey); } const handler = instance[methodName].bind(instance); if (options.queue) { await rabbitService.consume(options.queue, async (message) => handler(message), options.consumeOptions); this.logger.log(`Registered subscriber: ${instance.constructor?.name}.${methodName} -> ${options.queue}`); } } catch (error) { this.logger.error(`Failed to register subscriber ${(instance.constructor && instance.constructor.name) || 'Unknown'}.${methodName}`, error?.stack); } } async registerDiscoveredRpcHandler(instance, methodName, options) { const connectionName = options.connectionName || constants_1.DEFAULT_CONNECTION_NAME; const rabbitService = this.moduleRef.get(`${constants_1.RABBITMQ_SERVICE}_${connectionName}`, { strict: false, }); if (!rabbitService) { this.logger.error(`RabbitMQ service not found for connection: ${connectionName}`); return; } if (options.queue) { await rabbitService.assertQueue(options.queue, options.queueOptions); } const handler = instance[methodName].bind(instance); const channel = rabbitService.getChannel(); await channel.consume(options.queue, async (msg) => { if (!msg) { return; } try { const payloadStr = msg.content?.toString(); let payload = payloadStr; try { payload = JSON.parse(payloadStr); } catch { this.logger.error(`Failed to parse payload as JSON, using raw string: ${payloadStr}`); } const response = await handler(payload); const responseBuffer = Buffer.isBuffer(response) ? response : Buffer.from(JSON.stringify(response ?? null)); if (msg.properties?.replyTo) { await channel.sendToQueue(msg.properties.replyTo, responseBuffer, { persistent: false, correlationId: msg.properties.correlationId, }); } channel.ack?.(msg); } catch (error) { this.logger.error(`Failed to process RPC ${(instance.constructor && instance.constructor.name) || 'Unknown'}.${methodName}`, error?.stack); channel.nack?.(msg, false, false); } }, { noAck: false, prefetch: options.prefetchCount || 1, ...options.consumeOptions, }); this.logger.log(`Registered RPC handler: ${instance.constructor?.name}.${methodName} -> ${options.queue}`); } shouldScanModule(moduleRef, options) { const scope = options?.scanScope ?? 'all'; if (scope === 'all') return true; if (scope === 'modules' || scope === 'annotated' || scope === 'providers') { const include = options?.includeModules; if (!include || include.length === 0) return true; const name = moduleRef?.metatype?.name; return include.some((m) => (typeof m === 'string' ? m === name : m === moduleRef?.metatype)); } return true; } shouldScanProvider(provider, options) { const scope = options?.scanScope ?? 'all'; const token = provider?.name || provider?.metatype?.name || provider?.token; if (options?.excludeProviders && options.excludeProviders.length > 0) { if (options.excludeProviders.some((p) => typeof p === 'string' ? p === token : p === provider?.metatype)) { return false; } } if (options?.includeProviders && options.includeProviders.length > 0) { const included = options.includeProviders.some((p) => typeof p === 'string' ? p === token : p === provider?.metatype); if (!included) return false; } if (scope === 'annotated') { const instance = provider.instance; const ctor = instance?.constructor ?? provider?.metatype; return !!(ctor && Reflect.getMetadata && Reflect.getMetadata(rabbit_controller_decorator_1.RABBIT_CONTROLLER_KEY, ctor)); } return true; } }; exports.RabbitMQCoreModule = RabbitMQCoreModule; exports.RabbitMQCoreModule = RabbitMQCoreModule = RabbitMQCoreModule_1 = __decorate([ (0, common_1.Global)(), (0, common_1.Module)({}), __metadata("design:paramtypes", [core_1.ModuleRef, core_1.ModulesContainer]) ], RabbitMQCoreModule); //# sourceMappingURL=rabbitmq-core.module.js.map