UNPKG

@diy0r/nestjs-rabbitmq

Version:
311 lines 13.8 kB
"use strict"; 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 __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RmqService = void 0; const common_1 = require("@nestjs/common"); const interfaces_1 = require("./interfaces"); const constants_1 = require("./constants"); const common_2 = require("./common"); const rmq_connect_service_1 = require("./rmq-connect.service"); const get_uniqId_1 = require("./common/get-uniqId"); const stream_1 = require("stream"); const logger_1 = require("./common/logger"); const core_1 = require("@nestjs/core"); const class_validator_1 = require("class-validator"); let RmqService = class RmqService { constructor(moduleRef, rmqNestjsConnectService, metaTagsScannerService, rmqErrorService, RMQOptions, options, serDes, interceptors, middlewares, moduleToken) { this.moduleRef = moduleRef; this.rmqNestjsConnectService = rmqNestjsConnectService; this.metaTagsScannerService = metaTagsScannerService; this.rmqErrorService = rmqErrorService; this.RMQOptions = RMQOptions; this.options = options; this.serDes = serDes; this.interceptors = interceptors; this.middlewares = middlewares; this.moduleToken = moduleToken; this.sendResponseEmitter = new stream_1.EventEmitter(); this.extendedOptions = null; this.rmqMessageTags = null; this.replyToQueue = null; this.exchange = null; this.isInitialized = false; this.connected = false; this.extendedOptions = RMQOptions.extendedOptions ?? {}; this.logger = RMQOptions.extendedOptions?.appOptions?.logger ? RMQOptions.extendedOptions.appOptions?.logger : new logger_1.RQMColorLogger(this.extendedOptions.appOptions?.logMessages); } async onModuleInit() { await this.init(); this.isInitialized = true; } healthCheck() { return this.rmqNestjsConnectService.isConnected; } async init() { this.exchange = await this.rmqNestjsConnectService.assertExchange(this.options.exchange); if (this.options?.replyTo) await this.assertReplyQueue(); if (this.options?.queue) { this.scanMetaTags(); await this.bindQueueExchange(); } } async send(topic, message, options) { if (!this.replyToQueue) throw Error(constants_1.INDICATE_REPLY_QUEUE); await this.initializationCheck(); const correlationId = (0, get_uniqId_1.getUniqId)(); const timeout = options?.timeout ?? this.options.messageTimeout ?? constants_1.DEFAULT_TIMEOUT; return new Promise(async (resolve, reject) => { const timerId = setTimeout(() => { reject(new common_2.RMQError(constants_1.TIMEOUT_ERROR)); }, timeout); this.sendResponseEmitter.once(correlationId, (msg) => { clearTimeout(timerId); if (msg.properties?.headers?.['-x-error']) { return reject(this.rmqErrorService.errorHandler(msg)); } const content = msg.content; if (content.toString()) resolve(this.serDes.deserialize(content)); }); const confirmationFunction = (err, ok) => { if (err) { clearTimeout(timerId); reject(new common_2.RMQError(constants_1.NACKED)); } }; await this.rmqNestjsConnectService.publish({ exchange: this.options.exchange.exchange, routingKey: topic, content: this.serDes.serialize(message), options: { replyTo: this.replyToQueue.queue, appId: this.options.serviceName, correlationId, timestamp: new Date().getTime(), ...options, }, }, confirmationFunction); }); } async notify(topic, message, options) { await this.initializationCheck(); return new Promise(async (resolve, reject) => { const confirmationFunction = (err, ok) => { if (err !== null) { this.logger.error(`Publish failed: ${err.message}`); return reject(constants_1.NACKED); } resolve({ status: 'ok' }); }; await this.rmqNestjsConnectService.publish({ exchange: this.options.exchange.exchange, routingKey: topic, content: this.serDes.serialize(message), options: { appId: this.options.serviceName, timestamp: new Date().getTime(), ...options, }, }, confirmationFunction); if (this.extendedOptions?.typeChannel !== interfaces_1.TypeChannel.CONFIRM_CHANNEL) { resolve({ status: 'ok' }); } }); } async listenQueue(message) { try { const route = this.getRouteByTopic(message.fields.routingKey); const consumer = this.getConsumer(route); const messageParse = this.deserializeMessage(message.content, consumer); const result = await this.handle(message, messageParse, consumer); if (message.properties.replyTo) { await this.sendReply(message.properties.replyTo, consumer, result, message.properties.correlationId); } } catch (error) { this.logger.error('Error processing message', { error, message }); this.rmqNestjsConnectService.nack(message, false, false); } } async handle(message, messageParse, consumer) { if (!consumer) return this.buildErrorResponse(constants_1.NON_ROUTE); const errorMessages = await this.validate(messageParse, consumer.validate); if (errorMessages) return this.buildErrorResponse(errorMessages); const middlewareResult = await this.executeMiddlewares(consumer, message, messageParse); const interceptorsReversed = await this.interceptorsReverse(consumer, message, messageParse); if (middlewareResult.content != null) return middlewareResult; if (consumer.handler) { const result = await this.handleMessage(consumer.handler, messageParse, message); await this.reverseInterceptors(interceptorsReversed, result.content, message); return result; } } async reverseInterceptors(interceptorsReversed, result, message) { for (const revers of interceptorsReversed.reverse()) await revers(result, message); } async executeMiddlewares(consumer, message, messageParse) { const middlewares = this.getMiddlewares(consumer); const result = { content: null, headers: {} }; try { for (const middleware of middlewares) { const middlewareResult = await middleware.use(message, messageParse); if (middlewareResult != undefined) result.content = middlewareResult; } } catch (error) { result.headers = this.rmqErrorService.buildError(error); } return result; } async interceptorsReverse(consumer, message, messageParse) { const interceptors = this.getInterceptors(consumer.interceptors); const interceptorsReversed = []; for (const intercept of interceptors) { const fnReversed = await intercept(message, messageParse); interceptorsReversed.push(fnReversed); } return interceptorsReversed; } getInterceptors(consumerInterceptors) { const moduleInterceptors = this.interceptors.map(token => { const instance = this.moduleRef.get(token); return instance.intercept.bind(instance); }); return moduleInterceptors.concat(consumerInterceptors); } getConsumer(route) { return this.rmqMessageTags.get(route) || this.rmqMessageTags.get(constants_1.NON_ROUTE); } deserializeMessage(content, consumer) { return consumer?.serdes?.deserialize ? consumer.serdes.deserialize(content) : this.serDes.deserialize(content); } getMiddlewares(consumer) { return this.middlewares.concat(consumer.middlewares).map((middleware) => new middleware()); } async validate(messageParse, paramType) { if (!paramType) return; const object = Object.assign(new paramType(), messageParse); const errors = await (0, class_validator_1.validate)(object); if (errors.length) { const errorMessages = errors.flatMap(error => Object.values(error.constraints)).join('; '); return errorMessages; } } async handleMessage(handler, messageParse, message) { const result = { content: {}, headers: {} }; try { result.content = (await handler(messageParse, message)) || {}; } catch (error) { result.headers = this.rmqErrorService.buildError(error); } return result; } async sendReply(replyTo, consumer, result, correlationId) { const serializedResult = consumer.serdes?.serialize(result.content) || this.serDes.serialize(result.content); await this.rmqNestjsConnectService.sendToReplyQueue({ replyTo, content: serializedResult, headers: result.headers, correlationId, }); } async listenReplyQueue(message) { if (message.properties.correlationId) { this.sendResponseEmitter.emit(message.properties.correlationId, message); } } async bindQueueExchange() { const { queue: queueName, consumeOptions } = this.options.queue; if (!this.rmqMessageTags?.size) return this.logger.warn(constants_1.NON_DECLARED_ROUTE); const queue = await this.rmqNestjsConnectService.assertQueue(interfaces_1.TypeQueue.QUEUE, this.options.queue); this.rmqMessageTags.forEach(async (_, key) => { await this.rmqNestjsConnectService.bindQueue({ queue: queue.queue, source: this.exchange.exchange, pattern: key.toString(), }); }); await this.rmqNestjsConnectService.listenQueue(queueName, this.listenQueue.bind(this), consumeOptions); } async assertReplyQueue() { const { queue, options, consumeOptions } = this.options.replyTo; this.replyToQueue = await this.rmqNestjsConnectService.assertQueue(interfaces_1.TypeQueue.REPLY_QUEUE, { queue, options, }); await this.rmqNestjsConnectService.listenReplyQueue(this.replyToQueue.queue, this.listenReplyQueue.bind(this), consumeOptions); } buildErrorResponse(errorMessage) { return { content: {}, headers: this.rmqErrorService.buildError(new common_2.RMQError(errorMessage, this.options.serviceName)), }; } ack(...params) { return this.rmqNestjsConnectService.ack(...params); } nack(...params) { return this.rmqNestjsConnectService.nack(...params); } async initializationCheck() { if (this.isInitialized) return; await new Promise(resolve => setTimeout(resolve, constants_1.INITIALIZATION_STEP_DELAY)); await this.initializationCheck(); } getRouteByTopic(topic) { for (const route of this.rmqMessageTags.keys()) { const regex = (0, common_2.toRegex)(route); const isMatch = regex.test(topic); if (isMatch) return route; } return ''; } scanMetaTags() { this.rmqMessageTags = this.metaTagsScannerService.scan(constants_1.RMQ_MESSAGE_META_TAG, this.moduleToken); } async onModuleDestroy() { this.sendResponseEmitter.removeAllListeners(); } }; exports.RmqService = RmqService; exports.RmqService = RmqService = __decorate([ (0, common_1.Injectable)(), __param(4, (0, common_1.Inject)(constants_1.RMQ_OPTIONS)), __param(5, (0, common_1.Inject)(constants_1.RMQ_BROKER_OPTIONS)), __param(6, (0, common_1.Inject)(constants_1.SERDES)), __param(7, (0, common_1.Inject)(constants_1.INTERCEPTORS)), __param(8, (0, common_1.Inject)(constants_1.MIDDLEWARES)), __param(9, (0, common_1.Inject)(constants_1.MODULE_TOKEN)), __metadata("design:paramtypes", [core_1.ModuleRef, rmq_connect_service_1.RmqNestjsConnectService, common_2.MetaTagsScannerService, common_2.RmqErrorService, Object, Object, Object, Array, Array, String]) ], RmqService); //# sourceMappingURL=rmq.service.js.map