Skip to content

feat(event_sources,parser): add support for DeadLetterQueueSourceArn for SQS #4133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions aws_lambda_powertools/utilities/data_classes/sqs_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ def message_deduplication_id(self) -> Optional[str]:
the 5-minute deduplication interval."""
return self.get("MessageDeduplicationId")

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


class SQSMessageAttribute(DictWrapper):
"""The user-specified message attribute value."""
Expand Down
1 change: 1 addition & 0 deletions aws_lambda_powertools/utilities/parser/models/sqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class SqsAttributesModel(BaseModel):
SentTimestamp: datetime
SequenceNumber: Optional[str] = None
AWSTraceHeader: Optional[str] = None
DeadLetterQueueSourceArn: Optional[str] = None


class SqsMsgAttributeModel(BaseModel):
Expand Down
21 changes: 21 additions & 0 deletions tests/events/sqsDlqTriggerEvent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"Records": [
{
"messageId": "db37cc61-1bb0-4e77-b6f3-7cf87f44a72a",
"receiptHandle": "AQEBl1pqxv+ZHkarVAWZUyWgj2mmqJGLBTo6YFOi/bw1QpBTpJBGJPLOTZrjKztKIbAB8EXkG7zHlbkn+Ze/AHMKKuhST9azHu8LyF4Ffu9uPkZc5xzggXlfFBWH3TUKyV+F5Obaj3esyX8YfM/zfgjbRuu5nc2tfPhvaSYEaTZsdMpzIB5tyKvHxAltLxK7upRHeoT768M9UrFYswarFTBn8piDbnsPsUhi8Q9G4Q4xSI0fLQANmryBsRJIzGQTVxenDad+MJ7XEL+hD3p2DmW+ycvv6WD7bdedqQuroQG8+ca1Dz7s3CBbXw9ZZnUziPa7LH1j1Lky5bAxpNF+BlurRS9pFBnomhwpylrGxtGfaEmUW1G7jnrG97sZNOLOFUykbQgroZPXmjzMBdvtgq9ZmQfCch3LOXN267+PKc56VR4=",
"body": "hello world",
"attributes": {
"DeadLetterQueueSourceArn": "arn:aws:sqs:eu-central-1:123456789012:sqs-redrive-SampleQueue-RNvLCpwGmLi7",
"ApproximateReceiveCount": "2",
"SentTimestamp": "1713185156609",
"SenderId": "AMCXIENQZJOLO23YVJ4VO",
"ApproximateFirstReceiveTimestamp": "1713185156612"
},
"messageAttributes": {},
"md5OfBody": "6a204bd89f3c8348afd5c77c717a097a",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:sqs-redrive-SampleDLQ-Emgp9MFSLBZm",
"awsRegion": "eu-central-1"
}
]
}
27 changes: 27 additions & 0 deletions tests/unit/data_classes/test_sqs_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,33 @@ def test_seq_trigger_event():
assert record_2.json_body == {"message": "foo1"}


def test_sqs_dlq_trigger_event():
raw_event = load_event("sqsDlqTriggerEvent.json")
parsed_event = SQSEvent(raw_event)

records = list(parsed_event.records)
record = records[0]
attributes = record.attributes

assert len(records) == 1
assert record.message_id == raw_event["Records"][0]["messageId"]
assert record.receipt_handle == raw_event["Records"][0]["receiptHandle"]
assert record.body == raw_event["Records"][0]["body"]
assert attributes.aws_trace_header is None
raw_attributes = raw_event["Records"][0]["attributes"]
assert attributes.approximate_receive_count == raw_attributes["ApproximateReceiveCount"]
assert attributes.sent_timestamp == raw_attributes["SentTimestamp"]
assert attributes.sender_id == raw_attributes["SenderId"]
assert attributes.approximate_first_receive_timestamp == raw_attributes["ApproximateFirstReceiveTimestamp"]
assert attributes.sequence_number is None
assert attributes.message_group_id is None
assert attributes.message_deduplication_id is None
assert (
attributes.dead_letter_queue_source_arn
== raw_attributes["DeadLetterQueueSourceArn"]
)


def test_decode_nested_s3_event():
raw_event = load_event("s3SqsEvent.json")
event = SQSEvent(raw_event)
Expand Down
36 changes: 36 additions & 0 deletions tests/unit/parser/test_sqs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest

from aws_lambda_powertools.utilities.parser import ValidationError, envelopes, parse
from aws_lambda_powertools.utilities.parser.models import SqsModel
from tests.functional.utils import load_event
from tests.functional.validator.conftest import sqs_event # noqa: F401
from tests.unit.parser.schemas import MyAdvancedSqsBusiness, MySqsBusiness
Expand Down Expand Up @@ -85,3 +86,38 @@ def test_handle_sqs_trigger_event_no_envelope():
assert test_attr.stringValue == message_attributes_raw["stringValue"]
assert test_attr.binaryValue == message_attributes_raw["binaryValue"]
assert test_attr.dataType == message_attributes_raw["dataType"]


def test_sqs_dlq_trigger_event():
raw_event = load_event("sqsDlqTriggerEvent.json")
parsed_event = SqsModel(**raw_event)

records = parsed_event.Records
record = records[0]
raw_record = raw_event["Records"][0]
assert len(records) == 1

assert record.messageId == raw_record["messageId"]
assert record.receiptHandle == raw_record["receiptHandle"]
assert record.body == raw_record["body"]
assert record.eventSource == raw_record["eventSource"]
assert record.eventSourceARN == raw_record["eventSourceARN"]
assert record.awsRegion == raw_record["awsRegion"]
assert record.md5OfBody == raw_record["md5OfBody"]

attributes = record.attributes
assert attributes.AWSTraceHeader is None
assert attributes.ApproximateReceiveCount == raw_record["attributes"]["ApproximateReceiveCount"]
assert attributes.SequenceNumber is None
assert attributes.MessageGroupId is None
assert attributes.MessageDeduplicationId is None
assert attributes.SenderId == raw_record["attributes"]["SenderId"]
convert_time = int(round(attributes.ApproximateFirstReceiveTimestamp.timestamp() * 1000))
assert convert_time == int(raw_record["attributes"]["ApproximateFirstReceiveTimestamp"])
convert_time = int(round(attributes.SentTimestamp.timestamp() * 1000))
assert convert_time == int(raw_record["attributes"]["SentTimestamp"])

assert (
attributes.DeadLetterQueueSourceArn
== raw_record["attributes"]["DeadLetterQueueSourceArn"]
)
Loading