UNPKG

@cumulus/message

Version:

Utilities for building and parsing Cumulus messages

158 lines 6.81 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDLAFailureKey = exports.getDLAKey = exports.extractFileName = exports.extractDateString = exports.getDLARootKey = exports.hoistCumulusMessageDetails = exports.unwrapDeadLetterCumulusMessage = exports.isDLQRecordLike = void 0; const moment_1 = __importDefault(require("moment")); const uuidv4_1 = require("uuidv4"); const Lambda_1 = require("@cumulus/aws-client/Lambda"); const SQS_1 = require("@cumulus/aws-client/SQS"); const logger_1 = __importDefault(require("@cumulus/logger")); const Providers_1 = require("./Providers"); const CumulusMessage_1 = require("./CumulusMessage"); const StepFunctions_1 = require("./StepFunctions"); const Collections_1 = require("./Collections"); const log = new logger_1.default({ sender: '@cumulus/DeadLetterMessage' }); /** * Bare check for SQS message Shape */ const isDLQRecordLike = (message) => ((0, SQS_1.isSQSRecordLike)(message) && 'error' in message); exports.isDLQRecordLike = isDLQRecordLike; /** * Unwrap dead letter Cumulus message, which may be wrapped in a * States cloudwatch event, which is wrapped in an SQS message. */ const unwrapDeadLetterCumulusMessage = async (messageBody) => { try { if ((0, SQS_1.isSQSRecordLike)(messageBody)) { // AWS.SQS.Message/SQS.Record case const unwrappedMessageBody = (0, SQS_1.parseSQSMessageBody)(messageBody); return await (0, exports.unwrapDeadLetterCumulusMessage)(unwrappedMessageBody); } if ((0, Lambda_1.isEventBridgeEvent)(messageBody)) { return await (0, StepFunctions_1.getCumulusMessageFromExecutionEvent)(messageBody); } if ((0, CumulusMessage_1.isCumulusMessageLike)(messageBody)) { return messageBody; } throw new TypeError('DeadLetter CumulusMessage in unrecognized format'); } catch (error) { log.error('Falling back to storing wrapped message after encountering unwrap error', error, JSON.stringify(messageBody)); return messageBody; } }; exports.unwrapDeadLetterCumulusMessage = unwrapDeadLetterCumulusMessage; const payloadHasGranules = (payload) => (payload instanceof Object && 'granules' in payload && Array.isArray(payload.granules)); const extractCollectionId = (message) => { const collectionName = message?.meta?.collection?.name || null; const collectionVersion = message?.meta?.collection?.version || null; if (collectionName && collectionVersion) { return (0, Collections_1.constructCollectionId)(collectionName, collectionVersion); } return null; }; const extractGranules = (message) => { if (payloadHasGranules(message.payload)) { return message.payload.granules.map((granule) => granule?.granuleId || null); } return null; }; /** * peel out metadata from an SQS(/DLQ)record * @param message DLQ or SQS message * @returns the given message without its body */ const extractSQSMetadata = (message) => { const metadata = { ...message }; delete metadata.body; delete metadata.Body; return metadata; }; /** * Reformat object with key attributes at top level. * */ const hoistCumulusMessageDetails = async (dlqRecord) => { let executionArn = null; let stateMachineArn = null; let status = null; let time = null; let collectionId = null; let granules = null; let providerId = null; let messageBody; messageBody = dlqRecord; let metadata = extractSQSMetadata(messageBody); /* de-nest sqs records of unknown depth */ while ((0, SQS_1.isSQSRecordLike)(messageBody)) { /* prefer outermost recorded metadata */ metadata = { ...extractSQSMetadata(messageBody), ...metadata }; messageBody = (0, SQS_1.parseSQSMessageBody)(messageBody); } const error = 'error' in metadata ? metadata.error : null; if ((0, Lambda_1.isEventBridgeEvent)(messageBody)) { executionArn = messageBody?.detail?.executionArn || null; stateMachineArn = messageBody?.detail?.stateMachineArn || null; status = messageBody?.detail?.status || null; time = messageBody?.time || null; let cumulusMessage; try { cumulusMessage = await (0, StepFunctions_1.getCumulusMessageFromExecutionEvent)(messageBody); } catch (error_) { cumulusMessage = undefined; log.error('could not parse details from DLQ message body', error_, messageBody); } if (cumulusMessage) { collectionId = extractCollectionId(cumulusMessage); granules = extractGranules(cumulusMessage); if ((0, Providers_1.isMessageWithProvider)(cumulusMessage)) { providerId = (0, Providers_1.getMessageProviderId)(cumulusMessage) || null; } } } else { log.error('could not parse details from DLQ message body', messageBody, 'expected EventBridgeEvent'); } return { ...metadata, body: JSON.stringify(messageBody), collectionId, providerId, granules, executionArn, stateMachineArn, status, time, error, }; // cast to DLARecord: ts is confused by explicit 'undefined' fields in metadata }; exports.hoistCumulusMessageDetails = hoistCumulusMessageDetails; const getDLARootKey = (stackName) => (`${stackName}/dead-letter-archive/sqs/`); exports.getDLARootKey = getDLARootKey; const extractDateString = (message) => (message.time && moment_1.default.utc(message.time).isValid() ? moment_1.default.utc(message.time).format('YYYY-MM-DD') : moment_1.default.utc().format('YYYY-MM-DD')); exports.extractDateString = extractDateString; const extractFileName = (message) => { // get token after the last / or : const executionName = message.executionArn ? message.executionArn.split(/[/:]/).pop() : 'unknown'; return `${executionName}-${(0, uuidv4_1.uuid)()}`; }; exports.extractFileName = extractFileName; const getDLAKey = (stackName, message) => { const dateString = (0, exports.extractDateString)(message); const fileName = (0, exports.extractFileName)(message); return `${(0, exports.getDLARootKey)(stackName)}${dateString}/${fileName}`; }; exports.getDLAKey = getDLAKey; const getDLAFailureKey = (stackName, message) => { const dateString = (0, exports.extractDateString)(message); const fileName = (0, exports.extractFileName)(message); return `${stackName}/dead-letter-archive/failed-sqs/${dateString}/${fileName}`; }; exports.getDLAFailureKey = getDLAFailureKey; //# sourceMappingURL=DeadLetterMessage.js.map