Skip to content

Commit 16e2e7d

Browse files
authored
feat(event_sources,parser): add support for DeadLetterQueueSourceArn for SQS (#4133)
1 parent 1d79c72 commit 16e2e7d

File tree

5 files changed

+92
-0
lines changed

5 files changed

+92
-0
lines changed

aws_lambda_powertools/utilities/data_classes/sqs_event.py

+7
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ def message_deduplication_id(self) -> Optional[str]:
5555
the 5-minute deduplication interval."""
5656
return self.get("MessageDeduplicationId")
5757

58+
@property
59+
def dead_letter_queue_source_arn(self) -> Optional[str]:
60+
"""The SQS queue ARN that sent the record to this DLQ.
61+
Only present when a Lambda function is using a DLQ as an event source.
62+
"""
63+
return self.get("DeadLetterQueueSourceArn")
64+
5865

5966
class SQSMessageAttribute(DictWrapper):
6067
"""The user-specified message attribute value."""

aws_lambda_powertools/utilities/parser/models/sqs.py

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class SqsAttributesModel(BaseModel):
1515
SentTimestamp: datetime
1616
SequenceNumber: Optional[str] = None
1717
AWSTraceHeader: Optional[str] = None
18+
DeadLetterQueueSourceArn: Optional[str] = None
1819

1920

2021
class SqsMsgAttributeModel(BaseModel):

tests/events/sqsDlqTriggerEvent.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"Records": [
3+
{
4+
"messageId": "db37cc61-1bb0-4e77-b6f3-7cf87f44a72a",
5+
"receiptHandle": "AQEBl1pqxv+ZHkarVAWZUyWgj2mmqJGLBTo6YFOi/bw1QpBTpJBGJPLOTZrjKztKIbAB8EXkG7zHlbkn+Ze/AHMKKuhST9azHu8LyF4Ffu9uPkZc5xzggXlfFBWH3TUKyV+F5Obaj3esyX8YfM/zfgjbRuu5nc2tfPhvaSYEaTZsdMpzIB5tyKvHxAltLxK7upRHeoT768M9UrFYswarFTBn8piDbnsPsUhi8Q9G4Q4xSI0fLQANmryBsRJIzGQTVxenDad+MJ7XEL+hD3p2DmW+ycvv6WD7bdedqQuroQG8+ca1Dz7s3CBbXw9ZZnUziPa7LH1j1Lky5bAxpNF+BlurRS9pFBnomhwpylrGxtGfaEmUW1G7jnrG97sZNOLOFUykbQgroZPXmjzMBdvtgq9ZmQfCch3LOXN267+PKc56VR4=",
6+
"body": "hello world",
7+
"attributes": {
8+
"DeadLetterQueueSourceArn": "arn:aws:sqs:eu-central-1:123456789012:sqs-redrive-SampleQueue-RNvLCpwGmLi7",
9+
"ApproximateReceiveCount": "2",
10+
"SentTimestamp": "1713185156609",
11+
"SenderId": "AMCXIENQZJOLO23YVJ4VO",
12+
"ApproximateFirstReceiveTimestamp": "1713185156612"
13+
},
14+
"messageAttributes": {},
15+
"md5OfBody": "6a204bd89f3c8348afd5c77c717a097a",
16+
"eventSource": "aws:sqs",
17+
"eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:sqs-redrive-SampleDLQ-Emgp9MFSLBZm",
18+
"awsRegion": "eu-central-1"
19+
}
20+
]
21+
}

tests/unit/data_classes/test_sqs_event.py

+27
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,33 @@ def test_seq_trigger_event():
4444
assert record_2.json_body == {"message": "foo1"}
4545

4646

47+
def test_sqs_dlq_trigger_event():
48+
raw_event = load_event("sqsDlqTriggerEvent.json")
49+
parsed_event = SQSEvent(raw_event)
50+
51+
records = list(parsed_event.records)
52+
record = records[0]
53+
attributes = record.attributes
54+
55+
assert len(records) == 1
56+
assert record.message_id == raw_event["Records"][0]["messageId"]
57+
assert record.receipt_handle == raw_event["Records"][0]["receiptHandle"]
58+
assert record.body == raw_event["Records"][0]["body"]
59+
assert attributes.aws_trace_header is None
60+
raw_attributes = raw_event["Records"][0]["attributes"]
61+
assert attributes.approximate_receive_count == raw_attributes["ApproximateReceiveCount"]
62+
assert attributes.sent_timestamp == raw_attributes["SentTimestamp"]
63+
assert attributes.sender_id == raw_attributes["SenderId"]
64+
assert attributes.approximate_first_receive_timestamp == raw_attributes["ApproximateFirstReceiveTimestamp"]
65+
assert attributes.sequence_number is None
66+
assert attributes.message_group_id is None
67+
assert attributes.message_deduplication_id is None
68+
assert (
69+
attributes.dead_letter_queue_source_arn
70+
== raw_attributes["DeadLetterQueueSourceArn"]
71+
)
72+
73+
4774
def test_decode_nested_s3_event():
4875
raw_event = load_event("s3SqsEvent.json")
4976
event = SQSEvent(raw_event)

tests/unit/parser/test_sqs.py

+36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22

33
from aws_lambda_powertools.utilities.parser import ValidationError, envelopes, parse
4+
from aws_lambda_powertools.utilities.parser.models import SqsModel
45
from tests.functional.utils import load_event
56
from tests.functional.validator.conftest import sqs_event # noqa: F401
67
from tests.unit.parser.schemas import MyAdvancedSqsBusiness, MySqsBusiness
@@ -85,3 +86,38 @@ def test_handle_sqs_trigger_event_no_envelope():
8586
assert test_attr.stringValue == message_attributes_raw["stringValue"]
8687
assert test_attr.binaryValue == message_attributes_raw["binaryValue"]
8788
assert test_attr.dataType == message_attributes_raw["dataType"]
89+
90+
91+
def test_sqs_dlq_trigger_event():
92+
raw_event = load_event("sqsDlqTriggerEvent.json")
93+
parsed_event = SqsModel(**raw_event)
94+
95+
records = parsed_event.Records
96+
record = records[0]
97+
raw_record = raw_event["Records"][0]
98+
assert len(records) == 1
99+
100+
assert record.messageId == raw_record["messageId"]
101+
assert record.receiptHandle == raw_record["receiptHandle"]
102+
assert record.body == raw_record["body"]
103+
assert record.eventSource == raw_record["eventSource"]
104+
assert record.eventSourceARN == raw_record["eventSourceARN"]
105+
assert record.awsRegion == raw_record["awsRegion"]
106+
assert record.md5OfBody == raw_record["md5OfBody"]
107+
108+
attributes = record.attributes
109+
assert attributes.AWSTraceHeader is None
110+
assert attributes.ApproximateReceiveCount == raw_record["attributes"]["ApproximateReceiveCount"]
111+
assert attributes.SequenceNumber is None
112+
assert attributes.MessageGroupId is None
113+
assert attributes.MessageDeduplicationId is None
114+
assert attributes.SenderId == raw_record["attributes"]["SenderId"]
115+
convert_time = int(round(attributes.ApproximateFirstReceiveTimestamp.timestamp() * 1000))
116+
assert convert_time == int(raw_record["attributes"]["ApproximateFirstReceiveTimestamp"])
117+
convert_time = int(round(attributes.SentTimestamp.timestamp() * 1000))
118+
assert convert_time == int(raw_record["attributes"]["SentTimestamp"])
119+
120+
assert (
121+
attributes.DeadLetterQueueSourceArn
122+
== raw_record["attributes"]["DeadLetterQueueSourceArn"]
123+
)

0 commit comments

Comments
 (0)