Skip to content

Commit e5db5e6

Browse files
authored
fix: Pass complete SQS event to offline queue consumer (#1208)
Our current offline SQS event payload contains only the message body. This is insufficient in applications where we need to inspect the metadata of received messages, and leads to `TypeError` in the consumer during testing. This PR adds the missing fields to the event, trying to be relatively accurate. For example, the source queue ARN includes the correct queue name and AWS account ID. In terms of overall structure, an `SQSEvent` type is helpfully provided in the `@types/aws-lambda` package, which I've used to ensure our mocked event meets expectations. Complete event samples can be found here for reference: https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html Jira: [ENG-3437] [ENG-3437]: https://comicrelief.atlassian.net/browse/ENG-3437
1 parent 5e70d50 commit e5db5e6

1 file changed

Lines changed: 37 additions & 10 deletions

File tree

src/services/SQSService.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { createHash } from 'crypto';
2+
13
import {
24
InvokeCommand,
35
LambdaClient,
@@ -13,6 +15,7 @@ import {
1315
} from '@aws-sdk/client-sqs';
1416
import { NodeHttpHandler } from '@smithy/node-http-handler';
1517
import alai from 'alai';
18+
import { SQSEvent } from 'aws-lambda';
1619
import { v4 as uuid } from 'uuid';
1720

1821
import DependencyAwareClass from '../core/DependencyAwareClass';
@@ -226,6 +229,8 @@ export default class SQSService<
226229

227230
private $lambda?: LambdaClient;
228231

232+
private accountId?: string;
233+
229234
constructor(di: DependencyInjection<TConfig>) {
230235
super(di);
231236

@@ -241,7 +246,7 @@ export default class SQSService<
241246
REGION,
242247
} = process.env;
243248

244-
const accountId = (di.context.invokedFunctionArn && alai.parse(di.context))
249+
this.accountId = (di.context.invokedFunctionArn && alai.parse(di.context))
245250
|| AWS_ACCOUNT_ID;
246251

247252
if (di.isOffline && !Object.values(SQS_OFFLINE_MODES).includes(offlineMode)) {
@@ -254,7 +259,7 @@ export default class SQSService<
254259
Object.entries(this.queues).map((
255260
([key, queueName]) => [key, useLocalQueues
256261
? `http://${offlineHost}:${offlinePort}/queue/${queueName}`
257-
: `https://sqs.${REGION}.amazonaws.com/${accountId}/${queueName}`]
262+
: `https://sqs.${REGION}.amazonaws.com/${this.accountId}/${queueName}`]
258263
)),
259264
) as Record<QueueName<TConfig>, string>;
260265
}
@@ -487,24 +492,46 @@ export default class SQSService<
487492
}
488493

489494
const prefix = this.di.getLambdaPrefix();
490-
const FunctionName = shortOrLongFunctionName.startsWith(prefix)
495+
const fullFunctionName = shortOrLongFunctionName.startsWith(prefix)
491496
? shortOrLongFunctionName
492497
: `${prefix}${shortOrLongFunctionName}`;
493498

494-
const InvocationType = 'RequestResponse';
499+
const body = messageParameters.MessageBody || '';
500+
const region = process.env.AWS_REGION || 'eu-west-1';
501+
const timestamp = Date.now().toString();
495502

496-
const Payload = JSON.stringify({
503+
const event: SQSEvent = {
497504
Records: [
498505
{
499-
body: messageParameters.MessageBody,
506+
messageId: uuid(),
507+
receiptHandle: 'test',
508+
body,
509+
attributes: {
510+
ApproximateReceiveCount: '1',
511+
SentTimestamp: timestamp,
512+
SenderId: process.env.AWS_ACCESS_KEY_ID || '',
513+
ApproximateFirstReceiveTimestamp: timestamp,
514+
},
515+
messageAttributes: {},
516+
md5OfBody: createHash('md5').update(body).digest('hex'),
517+
eventSource: 'aws:sqs',
518+
eventSourceARN: `arn:aws:sqs:${region}:${this.accountId}:${this.queues[queue]}`,
519+
awsRegion: region,
500520
},
501521
],
502-
});
522+
};
523+
524+
if (messageParameters.MessageGroupId) {
525+
const attributes = event.Records[0].attributes;
526+
attributes.SequenceNumber = '0';
527+
attributes.MessageGroupId = messageParameters.MessageGroupId;
528+
attributes.MessageDeduplicationId = messageParameters.MessageDeduplicationId;
529+
}
503530

504531
await this.lambda.send(new InvokeCommand({
505-
FunctionName,
506-
InvocationType,
507-
Payload,
532+
FunctionName: fullFunctionName,
533+
InvocationType: 'RequestResponse',
534+
Payload: JSON.stringify(event),
508535
}));
509536
}
510537

0 commit comments

Comments
 (0)