@diy0r/nestjs-rabbitmq
Version:
Nestjs rabbitMQ module
311 lines • 13.8 kB
JavaScript
"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