nodejs-event-driven
Version:
NodeJS agnostic event driven with EventEmitter support
101 lines • 3.9 kB
JavaScript
import { BaseEventBusService } from '../../../base-event-bus.service.js';
export default class RabbitmqEventBusService extends BaseEventBusService {
static DEFAULT_USERNAME = 'guest';
static DEFAULT_PASSWORD = 'guest';
static DEFAULT_URL = `amqp://${RabbitmqEventBusService.DEFAULT_USERNAME}:${RabbitmqEventBusService.DEFAULT_PASSWORD}@localhost:5672`;
#logger;
#connection = null;
#registeredChannel = {};
#url;
constructor(config) {
super();
this.#url = config.url ?? RabbitmqEventBusService.DEFAULT_URL;
this.#logger = config.logger;
}
#checkConnection() {
const connection = this.#connection;
if (!connection) {
throw new Error('Connection is not initialized');
}
return connection;
}
async #closeRegisteredChannels() {
await Promise.all(Object.keys(this.#registeredChannel).map(async (eventName) => {
const channel = this.#registeredChannel[eventName];
await channel.cancel(eventName);
this.off(eventName);
}));
}
async #createChannel(eventName) {
const connection = this.#checkConnection();
const channel = await connection.createChannel();
this.#registeredChannel[eventName] = channel;
await channel.assertQueue(eventName, { durable: false });
return channel;
}
async #consumeQueue(eventName, listener, once = false) {
if (once) {
this.#logger?.debug(`register once listener for channel: ${eventName}`);
}
else {
this.#logger?.debug(`register listener for channel: ${eventName}`);
}
const channel = await this.#createChannel(eventName);
const consumeMsg = async (msg) => {
if (once) {
await channel.cancel(eventName);
}
const data = JSON.parse(String(msg.content));
this.#logger?.debug(`received event ${eventName}: ${String(data)}`);
listener(data);
return Promise.resolve();
};
await channel.consume(eventName, (msg) => {
if (msg === null) {
return;
}
void consumeMsg(msg);
}, { noAck: true, consumerTag: eventName });
}
on(eventName, listener) {
void this.#consumeQueue(eventName, listener);
}
once(eventName, listener) {
void this.#consumeQueue(eventName, listener, true);
}
off(eventName) {
const channel = this.#registeredChannel[eventName];
if (!channel) {
return;
}
delete this.#registeredChannel[eventName];
this.#logger?.debug(`closing channel ${eventName}`);
void channel.close().catch((err) => {
const error = err;
this.#logger?.warn(`Error while trying to close channel for event: ${eventName}: ${error.message}`);
});
}
send(eventName, data) {
void this.#createChannel(eventName).then((channel) => {
this.#logger?.debug(`sending ${String(data)} to channel ${eventName}`);
channel.sendToQueue(eventName, Buffer.from(JSON.stringify(data)));
});
}
sendAndWait(sendEventName, successEventName, errorEventName, data) {
this.#logger?.debug(`sending event ${sendEventName} and waiting for event ${successEventName}…`);
return super.sendAndWait(sendEventName, successEventName, errorEventName, data);
}
async start() {
const { connect, } = await import('amqplib');
this.#connection = await connect(this.#url);
}
async stop() {
await this.#closeRegisteredChannels();
const connection = this.#connection;
if (connection) {
await connection.close();
this.#connection = null;
}
}
}
//# sourceMappingURL=rabbitmq-event-bus.service.js.map