UNPKG

nodejs-event-driven

Version:

NodeJS agnostic event driven with EventEmitter support

149 lines 5.16 kB
import { EventEmitterBusService } from '@main/infra/event-bus/event-emitter/event-emitter-bus.service.js'; export var LogLevel; (function (LogLevel) { LogLevel[LogLevel["ERROR"] = 1] = "ERROR"; LogLevel[LogLevel["WARN"] = 2] = "WARN"; LogLevel[LogLevel["INFO"] = 4] = "INFO"; LogLevel[LogLevel["DEBUG"] = 5] = "DEBUG"; })(LogLevel || (LogLevel = {})); export const ERROR_MESSAGE_NOT_INITIALIZED = 'Kafka is not initialized!'; export class KafkaEventBusService extends EventEmitterBusService { static DEFAULT_CLIENT_ID; static DEFAULT_URL = 'localhost:9092'; #logger; #kafka = null; #consumers = new Map(); #kafkaConfig; #logCreator; constructor(config) { super(config); this.#logger = config.logger; this.#kafkaConfig = { ...config }; this.#logCreator = (logLevel) => (entry) => { const message = `kafka - ${entry.log.message}`; switch (logLevel) { case LogLevel.ERROR: this.#logger?.error(message); break; case LogLevel.WARN: this.#logger?.warn(message); break; case LogLevel.INFO: this.#logger?.info(message); break; case LogLevel.DEBUG: this.#logger?.debug(message); break; default: } }; } async #createTopic(eventName) { const kafka = this.#kafka; if (!kafka) { throw new Error(ERROR_MESSAGE_NOT_INITIALIZED); } const admin = kafka.admin(); await admin.connect(); await admin.createTopics({ topics: [ { topic: eventName, numPartitions: 1, replicationFactor: 1, }, ], }); await admin.disconnect(); } async #consume(eventName, listener, once = false) { const kafka = this.#kafka; if (!kafka) { throw new Error(ERROR_MESSAGE_NOT_INITIALIZED); } await this.#createTopic(eventName); const consumer = kafka.consumer({ groupId: eventName, allowAutoTopicCreation: true, }); await consumer.connect(); this.#consumers.set(eventName, consumer); await consumer.subscribe({ topic: this.getTopic(eventName), fromBeginning: true, }); await consumer.run({ eachMessage: async ({ message }) => { if (once) { this.off(eventName); } const value = message.value?.toString(); const data = value ? JSON.parse(value) : undefined; listener(data); }, }); } async #produce(eventName, data) { const kafka = this.#kafka; if (!kafka) { throw new Error(ERROR_MESSAGE_NOT_INITIALIZED); } await this.#createTopic(eventName); const producer = kafka.producer({ allowAutoTopicCreation: true }); await producer.connect(); try { await producer.send({ topic: this.getTopic(eventName), messages: [{ value: JSON.stringify(data) }], }); } finally { await producer.disconnect(); } } async #stopConsumer(consumer) { await consumer.stop(); await consumer.disconnect(); } getTopic(eventName) { return this.#kafkaConfig.topicPrefix ? `${this.#kafkaConfig.topicPrefix}-${eventName}` : `${eventName}`; } on(eventName, listener) { this.#logger?.debug(`register listener for topic: ${eventName}`); void this.#consume(eventName, listener); } once(eventName, listener) { this.#logger?.debug(`register once listener for channel: ${eventName}`); void this.#consume(eventName, listener, true); } off(eventName) { const consumer = this.#consumers.get(eventName); if (!consumer) { return; } this.#logger?.debug(`unregister listener for topic: ${eventName}`); this.#consumers.delete(eventName); void this.#stopConsumer(consumer); } send(eventName, data) { this.#logger?.debug(`sending ${String(data)} to topic ${eventName}`); void this.#produce(eventName, data); } async start() { const { Kafka } = await import('kafkajs'); this.#kafka = new Kafka({ logCreator: this.#logCreator, brokers: this.#kafkaConfig.brokers ?? [KafkaEventBusService.DEFAULT_URL], clientId: this.#kafkaConfig.clientId ?? KafkaEventBusService.DEFAULT_CLIENT_ID, }); } async stop() { await Promise.all(this.#consumers.keys().map((eventName) => { this.off(eventName); })); } } export const createKafkaEventBusService = (config) => new KafkaEventBusService(config); //# sourceMappingURL=kafka-event-bus.service.js.map