UNPKG

@goparrot/pubsub-event-bus

Version:
158 lines 7.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Consumer = void 0; const tslib_1 = require("tslib"); const common_1 = require("@nestjs/common"); const lodash_1 = require("lodash"); const interface_1 = require("../interface"); const lifecycle_event_1 = require("../lifecycle-event"); const provider_1 = require("../provider"); const utils_1 = require("../utils"); const configuration_1 = require("../utils/configuration"); const EventBus_1 = require("./EventBus"); const PubsubManager_1 = require("./PubsubManager"); let Consumer = class Consumer extends PubsubManager_1.PubsubManager { constructor(eventBus, retryStrategies, options, rootRetryOptions, bindingQueueOptions, prepareHandlerStrategies) { super(); this.eventBus = eventBus; this.retryStrategies = retryStrategies; this.options = options; this.rootRetryOptions = rootRetryOptions; this.bindingQueueOptions = bindingQueueOptions; this.prepareHandlerStrategies = prepareHandlerStrategies; this.exchanges = new Set(); } async setupChannel(channel) { if (this.options.prefetchPerConsumer) { await channel.prefetch(this.options.prefetchPerConsumer, false); } if (this.options.prefetchPerChannel) { await channel.prefetch(this.options.prefetchPerChannel, true); } } async consume(handlerWrapper, onMessage) { if ((0, utils_1.appInTestingMode)()) { return; } const { handler, eventWrappers, options, queue } = handlerWrapper; this.initConnectionIfRequired(); this.initChannelIfRequired(); const handlerExchanges = eventWrappers.map((event) => event.options.exchange); const exchangesToAssert = (0, lodash_1.chain)(handlerExchanges) .filter((exchange) => !this.exchanges.has(exchange)) .uniq() .value(); exchangesToAssert.forEach((exchange) => this.exchanges.add(exchange)); const bindingPatterns = eventWrappers.map((event) => this.extractBindingPattern(event)); await this.channelWrapper.addSetup(async (channel) => { await Promise.all([ ...exchangesToAssert.map(async (exchange) => channel.assertExchange(exchange, 'topic', this.assertExchangeOptions)), channel.assertQueue(queue, this.bindingOptions(options.bindingQueueOptions)), ...this.bindEvents(channel, queue, eventWrappers), channel.consume(queue, (msg) => { try { onMessage(msg); } catch (e) { this.logger().warn(`Message execution/acknowledge error: [${e.message}]`); } }), ]); this.logger().log(`Listening for "${bindingPatterns.join(', ')}" events from [${handlerExchanges.join(', ')} <- ${queue}]`); await this.eventBus.publish(new lifecycle_event_1.HandlerBound(handler.name)); }); } extractBindingPattern(mappedEvent) { var _a; const { event, options: { customBindingPattern, customRoutingKey }, } = mappedEvent; return (_a = customBindingPattern !== null && customBindingPattern !== void 0 ? customBindingPattern : customRoutingKey) !== null && _a !== void 0 ? _a : (0, utils_1.toEventName)(event.name); } configureAutoAck(wrapper) { var _a; this.prepareHandlerStrategies[(_a = wrapper.options.autoAck) !== null && _a !== void 0 ? _a : interface_1.AutoAckEnum.ALWAYS_ACK].process(wrapper, this); } addHandleCatch(handlerWrapper) { const { handler } = handlerWrapper; const originalMethod = handler.prototype.handle; const logger = this.logger(); Reflect.defineProperty(handler.prototype, 'handle', { ...Reflect.getOwnPropertyDescriptor(handler.prototype, 'handle'), async value(event) { try { await originalMethod.apply(this, [event]); } catch (error) { logger.error(JSON.stringify({ error, event, }), error instanceof Error ? error.stack : undefined, handler.name); } }, }); } ack(message) { if ((0, utils_1.appInTestingMode)()) { return; } this.channelWrapper.ack(message); } nack(message) { if ((0, utils_1.appInTestingMode)()) { return; } this.channelWrapper.nack(message); } async publish(exchange, routingKey, content, options) { await this.channelWrapper.publish(exchange, routingKey, content, options); } async configureRetryInfrastructure(wrappers) { var _a, _b; const wrappersWithRetryStrategy = wrappers.filter((wrapper) => wrapper.options.autoAck === interface_1.AutoAckEnum.AUTO_RETRY); const maxRetryAttempts = Math.max(this.rootRetryOptions.maxRetryAttempts, ...wrappersWithRetryStrategy.map((wrapper) => { var _a, _b; return (_b = (_a = wrapper.options.retryOptions) === null || _a === void 0 ? void 0 : _a.maxRetryAttempts) !== null && _b !== void 0 ? _b : 0; })); if (!maxRetryAttempts) { (_b = (_a = this.logger()).debug) === null || _b === void 0 ? void 0 : _b.call(_a, 'Retry infrastructure configuration skipped because no handlers with auto retry enabled found'); return; } if (maxRetryAttempts < 0) { throw new Error(`Invalid max retry count value (${maxRetryAttempts})`); } if (maxRetryAttempts > 100) { throw new Error(`Too great value for max retry count (${maxRetryAttempts})`); } await Promise.all((0, lodash_1.chain)(wrappersWithRetryStrategy) .groupBy((wrapper) => { var _a, _b; return (_b = (_a = wrapper.options.retryOptions) === null || _a === void 0 ? void 0 : _a.strategy) !== null && _b !== void 0 ? _b : interface_1.RetryStrategyEnum.DEAD_LETTER_TTL; }) .entries() .map(async ([strategy, wrappers]) => this.retryStrategies[strategy].setupInfrastructure(this.channelWrapper, wrappers)) .value()); } queue(handlerWrapper) { const pckName = process.env.npm_package_name.split('/').pop(); const platform = pckName.replace(/[_-]/gi, '.'); return [platform, (0, utils_1.toSnakeCase)(handlerWrapper.handler.name)].join(':'); } bindingOptions(extra = {}) { return { ...this.consumerConfiguration(), ...extra, }; } consumerConfiguration() { return this.bindingQueueOptions; } bindEvents(channel, queueName, eventWrappers) { return eventWrappers.map(async (event) => { return channel.bindQueue(queueName, event.options.exchange, this.extractBindingPattern(event)); }); } }; exports.Consumer = Consumer; exports.Consumer = Consumer = tslib_1.__decorate([ (0, common_1.Injectable)(), tslib_1.__param(1, (0, common_1.Inject)(provider_1.CQRS_RETRY_STRATEGIES)), tslib_1.__param(2, (0, common_1.Inject)(configuration_1.CQRS_MODULE_CONSUMER_OPTIONS)), tslib_1.__param(3, (0, common_1.Inject)(configuration_1.CQRS_RETRY_OPTIONS)), tslib_1.__param(4, (0, common_1.Inject)(configuration_1.CQRS_BINDING_QUEUE_CONFIG)), tslib_1.__param(5, (0, common_1.Inject)(provider_1.CQRS_PREPARE_HANDLER_STRATEGIES)), tslib_1.__metadata("design:paramtypes", [EventBus_1.EventBus, Object, Object, Object, Object, Object]) ], Consumer); //# sourceMappingURL=Consumer.js.map