UNPKG

@vtexlab/planner-message-bus

Version:

A Message Bus that uses AWS SNS, AWS SQS, and AWS EventBridge

163 lines (162 loc) 9.23 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createConsumerMessages = exports.sendMessageQueue = exports.redeliveryMessageQueue = void 0; const sqs_consumer_1 = require("sqs-consumer"); const constants_1 = require("../utils/constants"); const client_sqs_1 = require("@aws-sdk/client-sqs"); const api_1 = require("@opentelemetry/api"); const observability_1 = require("../utils/opentelemetry/observability"); const eventBridge_service_1 = require("./eventBridge.service"); const client = new client_sqs_1.SQSClient(); function redeliveryMessageQueue(queueName, delaySeconds, message, sendParams, stoppedCondition, callback, errorCallback) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f; const span = (0, observability_1.startSpan)(redeliveryMessageQueue.name, api_1.SpanKind.PRODUCER, { content: message.Content, endpoint: queueName }); const startsAt = (new Date((_c = (_b = (_a = message === null || message === void 0 ? void 0 : message.MessageAttributes) === null || _a === void 0 ? void 0 : _a['RedeliveryStartsAt']) === null || _b === void 0 ? void 0 : _b.StringValue) !== null && _c !== void 0 ? _c : new Date())); const currentAt = new Date(); const attempt = (parseInt((_f = (_e = (_d = message === null || message === void 0 ? void 0 : message.MessageAttributes) === null || _d === void 0 ? void 0 : _d['RedeliveryAttempt']) === null || _e === void 0 ? void 0 : _e.StringValue) !== null && _f !== void 0 ? _f : '0') + 1); if (stoppedCondition && stoppedCondition(startsAt, currentAt, attempt)) { console.warn('stopped queue1 at ' + attempt); return; } try { const params = Object.assign({ QueueUrl: (0, constants_1.QUEUE_URL_TEMPLATE)(queueName), MessageBody: message.Body, DelaySeconds: delaySeconds, MessageAttributes: { RedeliveryStartsAt: { DataType: 'String', StringValue: startsAt.toString() }, RedeliveryCurrentAt: { DataType: 'String', StringValue: currentAt.toString() }, RedeliveryAttempt: { DataType: 'Number', StringValue: attempt.toString() } } }, sendParams); span === null || span === void 0 ? void 0 : span.setAttributes({ delaySeconds: params.DelaySeconds, startsAt: startsAt.toString(), currentAt: currentAt.toString(), attempt }); yield ackMessage(queueName, message); const output = yield client.send(new client_sqs_1.SendMessageCommand(Object.assign({}, params))); span === null || span === void 0 ? void 0 : span.addEvent('messageReledivered', { messageId: output.MessageId }); callback === null || callback === void 0 ? void 0 : callback(output, span); return output.MessageId; } catch (error) { span === null || span === void 0 ? void 0 : span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message }); span === null || span === void 0 ? void 0 : span.recordException(error); errorCallback === null || errorCallback === void 0 ? void 0 : errorCallback(error, span); throw error; } finally { span === null || span === void 0 ? void 0 : span.end(); } }); } exports.redeliveryMessageQueue = redeliveryMessageQueue; function sendMessageQueue(queueName, contentMessage, sendParams, callback, errorCallback) { return __awaiter(this, void 0, void 0, function* () { const span = (0, observability_1.startSpan)(sendMessageQueue.name, api_1.SpanKind.PRODUCER, { content: contentMessage, endpoint: queueName }); try { const params = Object.assign({ MessageBody: JSON.stringify(contentMessage), QueueUrl: (0, constants_1.QUEUE_URL_TEMPLATE)(queueName) }, sendParams); const output = yield client.send(new client_sqs_1.SendMessageCommand(params)); span === null || span === void 0 ? void 0 : span.addEvent('messageSent', { messageId: output.MessageId }); callback === null || callback === void 0 ? void 0 : callback(output, span); return output.MessageId; } catch (error) { span === null || span === void 0 ? void 0 : span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message }); span === null || span === void 0 ? void 0 : span.recordException(error); errorCallback === null || errorCallback === void 0 ? void 0 : errorCallback(error, span); throw error; } finally { span === null || span === void 0 ? void 0 : span.end(); } }); } exports.sendMessageQueue = sendMessageQueue; function createConsumerMessages(params) { const consumer = sqs_consumer_1.Consumer.create({ messageAttributeNames: ['All'], queueUrl: (0, constants_1.QUEUE_URL_TEMPLATE)(params.endpoint), handleMessage: (message) => __awaiter(this, void 0, void 0, function* () { var _a, _b; const span = (0, observability_1.startSpan)('handleConsumerMessage', api_1.SpanKind.CONSUMER); if (message.Body) { let messageContent = JSON.parse(message.Body); if (messageContent.Type === 'Notification') { messageContent = JSON.parse(messageContent.Message); } message.Content = messageContent; } span === null || span === void 0 ? void 0 : span.setAttributes(Object.assign({}, (0, observability_1.setDefaultAttributes)(params.endpoint, message.Content))); try { yield params.handle(message, span); span === null || span === void 0 ? void 0 : span.addEvent('messageConsumed', { messageId: message.MessageId }); } catch (error) { span === null || span === void 0 ? void 0 : span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message }); span === null || span === void 0 ? void 0 : span.recordException(error); yield executeSecondLevelResilience(params.endpoint, message, params.maxRetryCount, params.delaySeconds); span === null || span === void 0 ? void 0 : span.addEvent('secondLevelResilienceExecuted'); } finally { if ((_a = message.Content) === null || _a === void 0 ? void 0 : _a.RuleId) { try { yield new eventBridge_service_1.EBScheduler().deleteSchedule((_b = message.Content) === null || _b === void 0 ? void 0 : _b.RuleId); } catch (_c) { } } span === null || span === void 0 ? void 0 : span.end(); } }), sqs: client, batchSize: params.batchSize }); consumer.start(); return consumer; } exports.createConsumerMessages = createConsumerMessages; function executeSecondLevelResilience(queueName, message, maxRetryCount, delaySeconds) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; let retryCount = parseInt((_c = (_b = (_a = message === null || message === void 0 ? void 0 : message.MessageAttributes) === null || _a === void 0 ? void 0 : _a['RetryCount']) === null || _b === void 0 ? void 0 : _b.StringValue) !== null && _c !== void 0 ? _c : '0'); yield sendMessageQueue(retryCount <= maxRetryCount ? queueName : `${queueName}-dlq`, message.Body, { DelaySeconds: retryCount <= maxRetryCount ? delaySeconds : 0, MessageAttributes: { 'RetryCount': { DataType: 'Number', StringValue: (retryCount <= maxRetryCount ? (retryCount + 1) : maxRetryCount).toString() } } }); }); } function ackMessage(queueName, message) { return __awaiter(this, void 0, void 0, function* () { yield client.send(new client_sqs_1.DeleteMessageCommand({ ReceiptHandle: message.ReceiptHandle, QueueUrl: (0, constants_1.QUEUE_URL_TEMPLATE)(queueName) })); }); }