UNPKG

coffee-core

Version:

Coffee IT API core library

178 lines 6.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RabbitMQSubscriber = void 0; const common_1 = require("@nestjs/common"); const amqplib_1 = require("amqplib"); const consume_handler_1 = require("./consume-handler"); class RabbitMQSubscriber { config; static instance; channel; server; createdQueue; consumeHandlers = []; usingRandomQueueName; queueName; logger = new common_1.Logger(RabbitMQSubscriber.name); eventRetryTimeoutSeconds = 2 * 1000; consumeTimeoutSeconds = 5 * 1000; autoAcknowledge; isExclusive; autoDeleteQueue; isConsuming = false; constructor(config) { this.config = config; this.queueName = config.QUEUE_NAME; this.usingRandomQueueName = this.queueName === ''; this.autoDeleteQueue = this.queueName === ''; this.autoAcknowledge = config.AUTO_ACKNOWLEDGE === undefined ? false : config.AUTO_ACKNOWLEDGE; this.isExclusive = config.EXCLUSIVE_QUEUES === undefined ? true : config.EXCLUSIVE_QUEUES; } async subscribe(bindingKey, onConsume, messageClass, enableLogging = true) { let channel; try { channel = await this.getChannel(); } catch (err) { this.logger.error(`Got an error when subscribing for key ${bindingKey}`); setTimeout(() => { this.logger.debug(`Retrying subscribe after channel error for key ${bindingKey}`); this.subscribe(bindingKey, onConsume, messageClass); }, this.eventRetryTimeoutSeconds); return; } channel.on('close', () => { this.logger.debug(`Channel closed for key ${bindingKey}`); this.subscribe(bindingKey, onConsume, messageClass); }); try { await this.startEventSubscribe(channel, bindingKey, onConsume, messageClass, enableLogging); } catch (err) { this.logger.error(`Got an error when consuming for key ${bindingKey}`); setTimeout(() => { this.logger.debug(`Retrying subscribe after consume error for key ${bindingKey}`); this.subscribe(bindingKey, onConsume, messageClass, enableLogging); }, this.eventRetryTimeoutSeconds); } } async startEventSubscribe(channel, bindingKey, onConsume, messageClass, enableLogging) { this.consumeHandlers.push(new consume_handler_1.ConsumeHandler(bindingKey, onConsume, messageClass, enableLogging)); const exchangeName = this.config.EXCHANGE_NAME; await channel.assertExchange(exchangeName, this.config.EXCHANGE_TYPE, { durable: this.config.DURABLE_EXCHANGE, }); const queueOptions = { durable: this.config.DURABLE_QUEUES, autoDelete: this.autoDeleteQueue, }; if (this.usingRandomQueueName && !this.createdQueue) { this.createdQueue = await channel.assertQueue(this.queueName, queueOptions); } if (!this.usingRandomQueueName) { this.createdQueue = await channel.assertQueue(this.queueName, queueOptions); } await channel.bindQueue(this.createdQueue.queue, exchangeName, bindingKey); this.logger.debug(`Subscriber is now consuming on ${bindingKey}`); } async startReceivingMessages() { if (this.isConsuming) { return; } let channel; try { channel = await this.getChannel(); } catch (err) { this.logger.error('Got an error while getting the channel to receive messages.'); setTimeout(() => { this.startReceivingMessages(); }, this.consumeTimeoutSeconds); return; } this.isConsuming = true; await channel.consume(this.queueName, async (msg) => { const { content } = msg; if (!content) { return; } try { const { routingKey } = msg.fields; const consumeHandler = this.findConsumeHandler(routingKey); if (!consumeHandler) { this.logger.debug(`Could not find consume handler for routingKey: ${routingKey}. Message lost.`); const requeue = false; channel.nack(msg, false, requeue); return; } const messageObject = JSON.parse(content.toString()); await consumeHandler.consume(messageObject); channel.ack(msg); } catch (err) { this.logger.error(`Got an error when consuming event from queue ${this.queueName}: ${err}`); } }, { noAck: this.autoAcknowledge, exclusive: this.isExclusive }); } findConsumeHandler(routingKey) { return this.consumeHandlers.find((handler) => handler.bindingKey === routingKey); } async close() { if (this.channel) { await this.channel.close(); } } setValidationMiddleware(middleware) { this.consumeHandlers.forEach((handler) => handler.setValidationMiddleware(middleware)); } async getChannel() { if (this.channel) { return this.channel; } const server = await this.getServer(); this.channel = await server.createChannel(); this.channel.on('close', () => { this.logger.debug('Channel closed'); this.channel = undefined; if (this.isConsuming) { this.isConsuming = false; this.startReceivingMessages(); } this.isConsuming = false; }); this.channel.on('err', () => { this.logger.debug('Channel got an error'); this.channel = undefined; this.isConsuming = false; }); return this.channel; } async getServer() { if (this.server) { return this.server; } this.server = await this.connect(); this.server.on('close', () => { this.logger.debug('RabbitMQ server closed'); this.server = undefined; }); this.server.on('err', () => { this.logger.debug('RabbitMQ server got an error'); this.server = undefined; }); return this.server; } async connect() { return (0, amqplib_1.connect)(this.config.AMQP_CONNECTION_URI); } static getInstance(config) { if (!RabbitMQSubscriber.instance) { RabbitMQSubscriber.instance = new RabbitMQSubscriber(config); } return RabbitMQSubscriber.instance; } } exports.RabbitMQSubscriber = RabbitMQSubscriber; //# sourceMappingURL=rabbitmq-subscriber.js.map