From a7e4563be917ca724ac25de3dac69c09581da62a Mon Sep 17 00:00:00 2001 From: Daniel Abib Date: Sat, 30 Aug 2025 09:36:24 -0300 Subject: [PATCH 1/5] refactor(parser): Improve SNS models with examples and descriptions Enhances the SNS parser models with field descriptions and examples using Pydantic's Field() functionality. This improvement provides better documentation and metadata for SNS event parsing, following the pattern established in PR #7100. All field descriptions are based on official AWS SNS documentation and include realistic examples from actual test events. Closes #7117 --- .../utilities/parser/models/sns.py | 132 +++++++++++++++--- 1 file changed, 113 insertions(+), 19 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/sns.py b/aws_lambda_powertools/utilities/parser/models/sns.py index 585489e9323..f0ea571446a 100644 --- a/aws_lambda_powertools/utilities/parser/models/sns.py +++ b/aws_lambda_powertools/utilities/parser/models/sns.py @@ -2,27 +2,102 @@ from typing import Dict, List, Literal, Optional, Union from typing import Type as TypingType -from pydantic import BaseModel, model_validator +from pydantic import BaseModel, Field, model_validator from pydantic.networks import HttpUrl class SnsMsgAttributeModel(BaseModel): - Type: str - Value: str + Type: str = Field( + description="The data type of the message attribute (String, Number, Binary, or custom data type).", + examples=["String", "Number", "Binary", "String.Array", "Number.Array"], + ) + Value: str = Field( + description="The value of the message attribute. All values are strings, even for Number types.", + examples=["TestString", "123", "TestBinary", '["item1", "item2"]'], + ) class SnsNotificationModel(BaseModel): - Subject: Optional[str] = None - TopicArn: str - UnsubscribeUrl: HttpUrl - Type: Literal["Notification"] - MessageAttributes: Optional[Dict[str, SnsMsgAttributeModel]] = None - Message: Union[str, TypingType[BaseModel]] - MessageId: str - SigningCertUrl: Optional[HttpUrl] = None # NOTE: FIFO opt-in removes attribute - Signature: Optional[str] = None # NOTE: FIFO opt-in removes attribute - Timestamp: datetime - SignatureVersion: Optional[str] = None # NOTE: FIFO opt-in removes attribute + Subject: Optional[str] = Field( + default=None, + description="The subject parameter provided when the notification was published to the topic.", + examples=["TestInvoke", "Alert: System maintenance", "Order Confirmation", None], + ) + TopicArn: str = Field( + description="The Amazon Resource Name (ARN) for the topic that this message was published to.", + examples=[ + "arn:aws:sns:us-east-2:123456789012:sns-lambda", + "arn:aws:sns:eu-west-1:123456789012:notification-topic", + "arn:aws:sns:us-west-2:123456789012:alerts.fifo", + ], + ) + UnsubscribeUrl: HttpUrl = Field( + description="A URL that you can use to unsubscribe the endpoint from this topic.", + examples=[ + "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486", + "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:123456789012:notification-topic:abcd1234-5678-90ef-ghij-klmnopqrstuv", + ], + ) + Type: Literal["Notification"] = Field( + description="The type of message. For Lambda triggers, this is always 'Notification'.", + examples=["Notification"], + ) + MessageAttributes: Optional[Dict[str, SnsMsgAttributeModel]] = Field( + default=None, + description="User-defined message attributes as key-value pairs with type information.", + examples=[ + {"Test": {"Type": "String", "Value": "TestString"}}, + {"priority": {"Type": "Number", "Value": "1"}, "env": {"Type": "String", "Value": "prod"}}, + None, + ], + ) + Message: Union[str, TypingType[BaseModel]] = Field( + description="The message value specified when the notification was published to the topic.", + examples=[ + "Hello from SNS!", + '{"alert": "CPU usage above 80%", "instance": "i-1234567890abcdef0"}', + '{"order_id": 12345, "status": "confirmed", "total": 99.99}', + ], + ) + MessageId: str = Field( + description="A Universally Unique Identifier, unique for each message published.", + examples=[ + "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "da41e39f-ea4d-435a-b922-c6aae3915ebe", + "f3c8d4e2-1a2b-4c5d-9e8f-7g6h5i4j3k2l", + ], + ) + SigningCertUrl: Optional[HttpUrl] = Field( + default=None, + description="The URL to the certificate that was used to sign the message. Not present for FIFO topics with content-based deduplication.", + examples=[ + "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-1234567890.pem", + "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-0987654321.pem", + None, + ], + ) # NOTE: FIFO opt-in removes attribute + Signature: Optional[str] = Field( + default=None, + description="Base64-encoded SHA1withRSA signature of the message. Not present for FIFO topics with content-based deduplication.", + examples=[ + "tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==", + "EXAMPLEw6JRNwm1LFQL4ICB0bnXrdB8ClRMTQFPGBfHs...EXAMPLEw==", + None, + ], + ) # NOTE: FIFO opt-in removes attribute + Timestamp: datetime = Field( + description="The time (GMT) when the notification was published.", + examples=[ + "2019-01-02T12:45:07.000Z", + "2023-06-15T10:30:00.000Z", + "2023-12-25T18:45:30.123Z", + ], + ) + SignatureVersion: Optional[str] = Field( + default=None, + description="Version of the Amazon SNS signature used. Not present for FIFO topics with content-based deduplication.", + examples=["1", "2", None], + ) # NOTE: FIFO opt-in removes attribute @model_validator(mode="before") def check_sqs_protocol(cls, values): @@ -37,11 +112,30 @@ def check_sqs_protocol(cls, values): class SnsRecordModel(BaseModel): - EventSource: Literal["aws:sns"] - EventVersion: str - EventSubscriptionArn: str - Sns: SnsNotificationModel + EventSource: Literal["aws:sns"] = Field( + description="The AWS service that invoked the function.", + examples=["aws:sns"], + ) + EventVersion: str = Field( + description="The version of the event schema.", + examples=["1.0", "2.0"], + ) + EventSubscriptionArn: str = Field( + description="The Amazon Resource Name (ARN) of the subscription.", + examples=[ + "arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486", + "arn:aws:sns:eu-west-1:123456789012:notification-topic:abcd1234-5678-90ef-ghij-klmnopqrstuv", + ], + ) + Sns: SnsNotificationModel = Field( + description="The SNS message that triggered the Lambda function.", + ) class SnsModel(BaseModel): - Records: List[SnsRecordModel] + Records: List[SnsRecordModel] = Field( + description="A list of SNS message records included in the event.", + examples=[ + [{"EventSource": "aws:sns", "Sns": {"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e"}}] + ], + ) From ad028d9abf93a4b02d6ef00a5a00b62eae9a0e34 Mon Sep 17 00:00:00 2001 From: Daniel Abib Date: Mon, 1 Sep 2025 07:51:56 -0300 Subject: [PATCH 2/5] style: apply ruff formatting to SNS models Fix CI formatting issues as requested by leandrodamascena --- aws_lambda_powertools/utilities/parser/models/sns.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/sns.py b/aws_lambda_powertools/utilities/parser/models/sns.py index f0ea571446a..2db06cbd2e2 100644 --- a/aws_lambda_powertools/utilities/parser/models/sns.py +++ b/aws_lambda_powertools/utilities/parser/models/sns.py @@ -135,7 +135,5 @@ class SnsRecordModel(BaseModel): class SnsModel(BaseModel): Records: List[SnsRecordModel] = Field( description="A list of SNS message records included in the event.", - examples=[ - [{"EventSource": "aws:sns", "Sns": {"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e"}}] - ], + examples=[[{"EventSource": "aws:sns", "Sns": {"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e"}}]], ) From 5c972005bff6cf392c8f268f0edb754c6090e3af Mon Sep 17 00:00:00 2001 From: Daniel Abib Date: Mon, 1 Sep 2025 08:01:40 -0300 Subject: [PATCH 3/5] style: fix line length issues in SNS models Break long lines over 120 characters following ALB/Kinesis pattern as requested by leandrodamascena. Use parentheses for multi-line strings and break long URL examples. --- .../utilities/parser/models/sns.py | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/sns.py b/aws_lambda_powertools/utilities/parser/models/sns.py index 2db06cbd2e2..37aea3da962 100644 --- a/aws_lambda_powertools/utilities/parser/models/sns.py +++ b/aws_lambda_powertools/utilities/parser/models/sns.py @@ -34,8 +34,14 @@ class SnsNotificationModel(BaseModel): UnsubscribeUrl: HttpUrl = Field( description="A URL that you can use to unsubscribe the endpoint from this topic.", examples=[ - "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486", - "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:123456789012:notification-topic:abcd1234-5678-90ef-ghij-klmnopqrstuv", + ( + "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=" + "arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486" + ), + ( + "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=" + "arn:aws:sns:eu-west-1:123456789012:notification-topic:abcd1234-5678-90ef-ghij-klmnopqrstuv" + ), ], ) Type: Literal["Notification"] = Field( @@ -69,7 +75,10 @@ class SnsNotificationModel(BaseModel): ) SigningCertUrl: Optional[HttpUrl] = Field( default=None, - description="The URL to the certificate that was used to sign the message. Not present for FIFO topics with content-based deduplication.", + description=( + "The URL to the certificate that was used to sign the message. " + "Not present for FIFO topics with content-based deduplication." + ), examples=[ "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-1234567890.pem", "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-0987654321.pem", @@ -78,7 +87,10 @@ class SnsNotificationModel(BaseModel): ) # NOTE: FIFO opt-in removes attribute Signature: Optional[str] = Field( default=None, - description="Base64-encoded SHA1withRSA signature of the message. Not present for FIFO topics with content-based deduplication.", + description=( + "Base64-encoded SHA1withRSA signature of the message. " + "Not present for FIFO topics with content-based deduplication." + ), examples=[ "tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==", "EXAMPLEw6JRNwm1LFQL4ICB0bnXrdB8ClRMTQFPGBfHs...EXAMPLEw==", @@ -95,7 +107,10 @@ class SnsNotificationModel(BaseModel): ) SignatureVersion: Optional[str] = Field( default=None, - description="Version of the Amazon SNS signature used. Not present for FIFO topics with content-based deduplication.", + description=( + "Version of the Amazon SNS signature used. " + "Not present for FIFO topics with content-based deduplication." + ), examples=["1", "2", None], ) # NOTE: FIFO opt-in removes attribute From 919e057e84b0337b43507c3a01db94539e33742c Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Mon, 1 Sep 2025 12:07:49 +0100 Subject: [PATCH 4/5] Fix styles --- aws_lambda_powertools/utilities/parser/models/sns.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/sns.py b/aws_lambda_powertools/utilities/parser/models/sns.py index 37aea3da962..d2f4b744412 100644 --- a/aws_lambda_powertools/utilities/parser/models/sns.py +++ b/aws_lambda_powertools/utilities/parser/models/sns.py @@ -108,8 +108,7 @@ class SnsNotificationModel(BaseModel): SignatureVersion: Optional[str] = Field( default=None, description=( - "Version of the Amazon SNS signature used. " - "Not present for FIFO topics with content-based deduplication." + "Version of the Amazon SNS signature used. Not present for FIFO topics with content-based deduplication." ), examples=["1", "2", None], ) # NOTE: FIFO opt-in removes attribute From 8140ae58374724ba2dce273978b3785e01a2588c Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Mon, 1 Sep 2025 12:09:35 +0100 Subject: [PATCH 5/5] Fix styles --- aws_lambda_powertools/utilities/parser/models/sns.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/sns.py b/aws_lambda_powertools/utilities/parser/models/sns.py index d2f4b744412..62b8efc2e17 100644 --- a/aws_lambda_powertools/utilities/parser/models/sns.py +++ b/aws_lambda_powertools/utilities/parser/models/sns.py @@ -35,12 +35,12 @@ class SnsNotificationModel(BaseModel): description="A URL that you can use to unsubscribe the endpoint from this topic.", examples=[ ( - "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=" - "arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486" + "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn= \ + arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486" ), ( - "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=" - "arn:aws:sns:eu-west-1:123456789012:notification-topic:abcd1234-5678-90ef-ghij-klmnopqrstuv" + "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn= \ + arn:aws:sns:eu-west-1:123456789012:notification-topic:abcd1234-5678-90ef-ghij-klmnopqrstuv" ), ], )