@cumulus/message
Version:
Utilities for building and parsing Cumulus messages
158 lines • 6.81 kB
JavaScript
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
;