UNPKG

@aws-lambda-powertools/parser

Version:
135 lines (134 loc) 5.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SnsSqsEnvelope = void 0; const zod_1 = require("zod"); const errors_js_1 = require("../errors.js"); const sns_js_1 = require("../schemas/sns.js"); const sqs_js_1 = require("../schemas/sqs.js"); const envelope_js_1 = require("./envelope.js"); const createError = (index, issues) => ({ issues: issues.map((issue) => ({ ...issue, path: ['Records', index, 'body', ...issue.path], })), }); const parseStep = (parser, data, index) => { const result = parser(data); return result.success ? { success: true, data: result.data } : { success: false, error: createError(index, result.error.issues), }; }; /** * SNS plus SQS Envelope to extract array of Records * * Published messages from SNS to SQS has a slightly different payload structure * than regular SNS messages, and when sent to SQS, they are stringified into the * `body` field of each SQS record. * * To parse the `Message` field of the SNS notification, we need to: * 1. Parse SQS schema with incoming data * 2. `JSON.parse()` the SNS payload and parse against SNS Notification schema * 3. Finally, parse the payload against the provided schema */ exports.SnsSqsEnvelope = { /** * This is a discriminator to differentiate whether an envelope returns an array or an object * @hidden */ [envelope_js_1.envelopeDiscriminator]: 'array', parse(data, schema) { let parsedEnvelope; try { parsedEnvelope = sqs_js_1.SqsSchema.parse(data); } catch (error) { throw new errors_js_1.ParseError('Failed to parse SQS Envelope', { cause: error, }); } return parsedEnvelope.Records.map((record, recordIndex) => { try { return schema.parse(sns_js_1.SnsSqsNotificationSchema.parse(JSON.parse(record.body)).Message); } catch (error) { throw new errors_js_1.ParseError(`Failed to parse SQS Record at index ${recordIndex}`, { cause: new zod_1.ZodError(error instanceof zod_1.ZodError ? error.issues.map((issue) => ({ ...issue, path: ['Records', recordIndex, 'body', ...issue.path], })) : [ { code: 'custom', input: record.body, message: `Invalid JSON - ${error.message}`, path: ['Records', recordIndex, 'body'], }, ]), }); } }); }, safeParse(data, schema) { const parsedEnvelope = sqs_js_1.SqsSchema.safeParse(data); if (!parsedEnvelope.success) { return { success: false, error: new errors_js_1.ParseError('Failed to parse SQS envelope', { cause: parsedEnvelope.error, }), originalEvent: data, }; } const parseRecord = (record, index) => { try { const body = JSON.parse(record.body); const notification = parseStep((data) => sns_js_1.SnsSqsNotificationSchema.safeParse(data), body, index); if (!notification.success) return notification; return parseStep((data) => schema.safeParse(data), notification.data.Message, index); } catch (error) { return { success: false, error: createError(index, [ { code: 'custom', message: `Invalid JSON - ${error.message}`, input: record.body, path: [], }, ]), }; } }; const result = parsedEnvelope.data.Records.reduce((acc, record, index) => { const parsed = parseRecord(record, index); if (!parsed.success) { acc.success = false; acc.errors[index] = parsed.error; } else { acc.records.push(parsed.data); } return acc; }, { success: true, records: [], errors: {} }); if (result.success) { return { success: true, data: result.records }; } const indexes = Object.keys(result.errors); const errorMessage = indexes.length > 1 ? `Failed to parse SQS Records at indexes ${indexes.join(', ')}` : `Failed to parse SQS Record at index ${indexes[0]}`; return { success: false, error: new errors_js_1.ParseError(errorMessage, { cause: new zod_1.ZodError(Object.values(result.errors).flatMap((e) => e.issues)), }), originalEvent: data, }; }, };