From 502ce6df8a11d8636815b4d3391eeb10d0b471ae Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Tue, 17 Jan 2023 00:03:05 +0000 Subject: [PATCH 01/12] Introduce model for SQS-wrapped S3 event notifications Signed-off-by: Alan Ip --- .../utilities/parser/models/s3_event_notification.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 aws_lambda_powertools/utilities/parser/models/s3_event_notification.py diff --git a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py new file mode 100644 index 00000000000..8f6b6d23180 --- /dev/null +++ b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py @@ -0,0 +1,7 @@ +from pydantic import Json + +from aws_lambda_powertools.utilities.parser.models.s3 import S3Model +from aws_lambda_powertools.utilities.parser.models.sqs import SqsRecordModel + +class SqsS3EventNotificationModel(SqsRecordModel): + body: Json[S3Model] From 72b127d298d9145c843f0fc032cd51c5b6a92313 Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Tue, 17 Jan 2023 00:05:10 +0000 Subject: [PATCH 02/12] Export SqsS3EventNotificationModel Signed-off-by: Alan Ip --- aws_lambda_powertools/utilities/parser/models/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index 62e28a62374..45395ab3c7d 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -44,6 +44,7 @@ ) from .lambda_function_url import LambdaFunctionUrlModel from .s3 import S3Model, S3RecordModel +from .s3_event_notification import SqsS3EventNotificationModel from .s3_object_event import ( S3ObjectConfiguration, S3ObjectContext, @@ -97,6 +98,7 @@ "LambdaFunctionUrlModel", "S3Model", "S3RecordModel", + "SqsS3EventNotificationModel", "S3ObjectLambdaEvent", "S3ObjectUserIdentity", "S3ObjectSessionContext", From 665a99dc9b8a5636affb3590044afec5e440e327 Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Tue, 11 Apr 2023 12:37:45 +0100 Subject: [PATCH 03/12] Re-order exports into (slightly more) alphabetical order Signed-off-by: Alan Ip --- aws_lambda_powertools/utilities/parser/models/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index 4d2c40abf40..d5b75e57b5b 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -104,7 +104,6 @@ "LambdaFunctionUrlModel", "S3Model", "S3RecordModel", - "SqsS3EventNotificationModel", "S3ObjectLambdaEvent", "S3ObjectUserIdentity", "S3ObjectSessionContext", @@ -132,6 +131,7 @@ "SqsRecordModel", "SqsMsgAttributeModel", "SqsAttributesModel", + "SqsS3EventNotificationModel", "APIGatewayProxyEventModel", "APIGatewayEventRequestContext", "APIGatewayEventAuthorizer", From 6c426dd3d79e0cc146ed666268089e4c38a115ef Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Fri, 14 Apr 2023 02:03:08 +0100 Subject: [PATCH 04/12] Differentiate between *RecordModel vs *Model The latter represents an event payload, i.e. a list of records. --- .../utilities/parser/models/__init__.py | 6 +++++- .../utilities/parser/models/s3_event_notification.py | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index d5b75e57b5b..26b68359da3 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -50,7 +50,10 @@ S3Model, S3RecordModel, ) -from .s3_event_notification import SqsS3EventNotificationModel +from .s3_event_notification import ( + SqsS3EventNotificationModel, + SqsS3EventNotificationRecordModel, +) from .s3_object_event import ( S3ObjectConfiguration, S3ObjectContext, @@ -132,6 +135,7 @@ "SqsMsgAttributeModel", "SqsAttributesModel", "SqsS3EventNotificationModel", + "SqsS3EventNotificationRecordModel", "APIGatewayProxyEventModel", "APIGatewayEventRequestContext", "APIGatewayEventAuthorizer", diff --git a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py index 8f6b6d23180..41edf0c4d46 100644 --- a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py +++ b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py @@ -1,7 +1,12 @@ +from typing import List + from pydantic import Json from aws_lambda_powertools.utilities.parser.models.s3 import S3Model -from aws_lambda_powertools.utilities.parser.models.sqs import SqsRecordModel +from aws_lambda_powertools.utilities.parser.models.sqs import SqsRecordModel, SqsModel -class SqsS3EventNotificationModel(SqsRecordModel): +class SqsS3EventNotificationRecordModel(SqsRecordModel): body: Json[S3Model] + +class SqsS3EventNotificationModel(SqsModel): + Records: List[SqsS3EventNotificationRecordModel] From be69e4ca27512c0383d5084bdd375d4e10a7c883 Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Fri, 14 Apr 2023 02:34:48 +0100 Subject: [PATCH 05/12] Add positive functional tests for SqsS3EventNotificationModel Since the SqsS3EventNotificationModel is only responsible for chaining (rather than redefining) the existing SqsModel and S3Model models, let's re-use both the existing stubs and their corresponding assertions. --- tests/functional/parser/test_s3.py | 8 +++-- .../parser/test_sqs_s3_event_notification.py | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/functional/parser/test_sqs_s3_event_notification.py diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index cd903f3052c..f6ed3a5422e 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -6,8 +6,7 @@ from tests.functional.utils import load_event -@event_parser(model=S3Model) -def handle_s3(event: S3Model, _: LambdaContext): +def assert_s3(event: S3Model): records = list(event.Records) assert len(records) == 1 record: S3RecordModel = records[0] @@ -41,6 +40,11 @@ def handle_s3(event: S3Model, _: LambdaContext): assert record.glacierEventData is None +@event_parser(model=S3Model) +def handle_s3(event: S3Model, _: LambdaContext): + assert_s3(event) + + @event_parser(model=S3Model) def handle_s3_glacier(event: S3Model, _: LambdaContext): records = list(event.Records) diff --git a/tests/functional/parser/test_sqs_s3_event_notification.py b/tests/functional/parser/test_sqs_s3_event_notification.py new file mode 100644 index 00000000000..3aed584a150 --- /dev/null +++ b/tests/functional/parser/test_sqs_s3_event_notification.py @@ -0,0 +1,32 @@ +from aws_lambda_powertools.utilities.parser import event_parser +from aws_lambda_powertools.utilities.parser.models import ( + SqsS3EventNotificationModel, + S3Model, +) +from aws_lambda_powertools.utilities.typing import LambdaContext +from tests.functional.parser.schemas import MyAdvancedSqsBusiness +from tests.functional.utils import json_serialize, load_event + +@event_parser(model=SqsS3EventNotificationModel) +def handle_sqs_json_body_containing_s3_notifications(event: SqsS3EventNotificationModel, _: LambdaContext): + return event + +@event_parser(model=MyAdvancedSqsBusiness) +def generate_sqs_event() -> MyAdvancedSqsBusiness: + return load_event("sqsEvent.json") + +@event_parser(model=S3Model) +def generate_s3_event_notification() -> S3Model: + return load_event("s3Event.json") + +def test_handle_sqs_json_body_containing_s3_notifications(): + sqs_event = generate_sqs_event() + s3_event_notification = generate_s3_event_notification() + for record in sqs_event.Records: + record.body = json_serialize(s3_event_notification) + + parsed_event: SqsS3EventNotificationModel = handle_sqs_json_body_containing_s3_notifications(sqs_event, LambdaContext()) + + assert len(parsed_event.Records) == 2 + for parsed_sqs_record in parsed_event.Records: + assert_s3(parsed_sqs_record.body) From ad2ba2c276732c685e93ec79cbb3ae61cb379c04 Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Fri, 14 Apr 2023 03:02:47 +0100 Subject: [PATCH 06/12] Silly me, I may have overcomplicated this... --- .../parser/test_sqs_s3_event_notification.py | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/tests/functional/parser/test_sqs_s3_event_notification.py b/tests/functional/parser/test_sqs_s3_event_notification.py index 3aed584a150..e8b36926caa 100644 --- a/tests/functional/parser/test_sqs_s3_event_notification.py +++ b/tests/functional/parser/test_sqs_s3_event_notification.py @@ -1,31 +1,21 @@ from aws_lambda_powertools.utilities.parser import event_parser -from aws_lambda_powertools.utilities.parser.models import ( - SqsS3EventNotificationModel, - S3Model, -) +from aws_lambda_powertools.utilities.parser.models import SqsS3EventNotificationModel from aws_lambda_powertools.utilities.typing import LambdaContext -from tests.functional.parser.schemas import MyAdvancedSqsBusiness from tests.functional.utils import json_serialize, load_event + @event_parser(model=SqsS3EventNotificationModel) def handle_sqs_json_body_containing_s3_notifications(event: SqsS3EventNotificationModel, _: LambdaContext): return event -@event_parser(model=MyAdvancedSqsBusiness) -def generate_sqs_event() -> MyAdvancedSqsBusiness: - return load_event("sqsEvent.json") - -@event_parser(model=S3Model) -def generate_s3_event_notification() -> S3Model: - return load_event("s3Event.json") def test_handle_sqs_json_body_containing_s3_notifications(): - sqs_event = generate_sqs_event() - s3_event_notification = generate_s3_event_notification() - for record in sqs_event.Records: - record.body = json_serialize(s3_event_notification) + sqs_event_dict = load_event("sqsEvent.json") + s3_event_notification_dict = load_event("s3Event.json") + for record in sqs_event_dict["Records"]: + record["body"] = json_serialize(s3_event_notification_dict) - parsed_event: SqsS3EventNotificationModel = handle_sqs_json_body_containing_s3_notifications(sqs_event, LambdaContext()) + parsed_event: SqsS3EventNotificationModel = handle_sqs_json_body_containing_s3_notifications(sqs_event_dict, LambdaContext()) assert len(parsed_event.Records) == 2 for parsed_sqs_record in parsed_event.Records: From 70e7b1c34b1c9d0f55b39f30f5ac5aa1b7f3b224 Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Fri, 14 Apr 2023 03:04:52 +0100 Subject: [PATCH 07/12] Fix formatting --- .../utilities/parser/models/s3_event_notification.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py index 41edf0c4d46..37d77821099 100644 --- a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py +++ b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py @@ -4,9 +4,11 @@ from aws_lambda_powertools.utilities.parser.models.s3 import S3Model from aws_lambda_powertools.utilities.parser.models.sqs import SqsRecordModel, SqsModel - + + class SqsS3EventNotificationRecordModel(SqsRecordModel): - body: Json[S3Model] + body: Json[S3Model] + class SqsS3EventNotificationModel(SqsModel): - Records: List[SqsS3EventNotificationRecordModel] + Records: List[SqsS3EventNotificationRecordModel] From 251d8baa72c2550fc24fec010197887ec6577cf0 Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Fri, 14 Apr 2023 03:22:27 +0100 Subject: [PATCH 08/12] Add negative functional tests for SqsS3EventNotificationModel --- .../parser/test_sqs_s3_event_notification.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/functional/parser/test_sqs_s3_event_notification.py b/tests/functional/parser/test_sqs_s3_event_notification.py index e8b36926caa..3c71ee79cb1 100644 --- a/tests/functional/parser/test_sqs_s3_event_notification.py +++ b/tests/functional/parser/test_sqs_s3_event_notification.py @@ -1,4 +1,6 @@ -from aws_lambda_powertools.utilities.parser import event_parser +import pytest + +from aws_lambda_powertools.utilities.parser import ValidationError, event_parser from aws_lambda_powertools.utilities.parser.models import SqsS3EventNotificationModel from aws_lambda_powertools.utilities.typing import LambdaContext from tests.functional.utils import json_serialize, load_event @@ -20,3 +22,19 @@ def test_handle_sqs_json_body_containing_s3_notifications(): assert len(parsed_event.Records) == 2 for parsed_sqs_record in parsed_event.Records: assert_s3(parsed_sqs_record.body) + + +def test_handle_sqs_body_invalid_json(): + sqs_event_dict = load_event("sqsEvent.json") + + with pytest.raises(ValidationError): + handle_sqs_json_body_containing_s3_notifications(sqs_event_dict, LambdaContext()) + + +def test_handle_sqs_json_body_containing_arbitrary_json(): + sqs_event_dict = load_event("sqsEvent.json") + for record in sqs_event_dict["Records"]: + record["body"] = json_serialize({"foo": "bar"}) + + with pytest.raises(ValidationError): + handle_sqs_json_body_containing_s3_notifications(sqs_event_dict, LambdaContext()) From 111655265c04d9ad15dea3bd6ffe763c779ef59e Mon Sep 17 00:00:00 2001 From: Alan Ip Date: Fri, 14 Apr 2023 11:25:42 +0100 Subject: [PATCH 09/12] Explicitly import local method Signed-off-by: Alan Ip --- tests/functional/parser/test_sqs_s3_event_notification.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/parser/test_sqs_s3_event_notification.py b/tests/functional/parser/test_sqs_s3_event_notification.py index 3c71ee79cb1..75c35af7773 100644 --- a/tests/functional/parser/test_sqs_s3_event_notification.py +++ b/tests/functional/parser/test_sqs_s3_event_notification.py @@ -3,6 +3,7 @@ from aws_lambda_powertools.utilities.parser import ValidationError, event_parser from aws_lambda_powertools.utilities.parser.models import SqsS3EventNotificationModel from aws_lambda_powertools.utilities.typing import LambdaContext +from tests.functional.parser.test_s3 import assert_s3 from tests.functional.utils import json_serialize, load_event From 31eaa2ecc9c6e417bfc1e8ef0bb3839571a99ec3 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Tue, 25 Apr 2023 21:25:25 +0100 Subject: [PATCH 10/12] parser(sqs3): fixing mypy errors and adding documentation --- .../utilities/parser/models/s3_event_notification.py | 2 +- aws_lambda_powertools/utilities/parser/models/sqs.py | 4 ++-- docs/utilities/parser.md | 1 + tests/functional/parser/test_sqs_s3_event_notification.py | 4 +++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py index 37d77821099..e3bedb2b0b0 100644 --- a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py +++ b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py @@ -3,7 +3,7 @@ from pydantic import Json from aws_lambda_powertools.utilities.parser.models.s3 import S3Model -from aws_lambda_powertools.utilities.parser.models.sqs import SqsRecordModel, SqsModel +from aws_lambda_powertools.utilities.parser.models.sqs import SqsModel, SqsRecordModel class SqsS3EventNotificationRecordModel(SqsRecordModel): diff --git a/aws_lambda_powertools/utilities/parser/models/sqs.py b/aws_lambda_powertools/utilities/parser/models/sqs.py index c92a8361b7c..a1c172c20fc 100644 --- a/aws_lambda_powertools/utilities/parser/models/sqs.py +++ b/aws_lambda_powertools/utilities/parser/models/sqs.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Dict, List, Optional, Type, Union +from typing import Dict, List, Optional, Sequence, Type, Union from pydantic import BaseModel @@ -63,4 +63,4 @@ class SqsRecordModel(BaseModel): class SqsModel(BaseModel): - Records: List[SqsRecordModel] + Records: Sequence[SqsRecordModel] diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index 66103ad474b..9b450e3abf6 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -175,6 +175,7 @@ Parser comes with the following built-in models: | **LambdaFunctionUrlModel** | Lambda Event Source payload for Lambda Function URL payload | | **KafkaSelfManagedEventModel** | Lambda Event Source payload for self managed Kafka payload | | **KafkaMskEventModel** | Lambda Event Source payload for AWS MSK payload | +| **SqsS3EventNotificationModel** | Lambda Event Source payload for SQS-wrapped S3 event notifications | #### Extending built-in models diff --git a/tests/functional/parser/test_sqs_s3_event_notification.py b/tests/functional/parser/test_sqs_s3_event_notification.py index 75c35af7773..21b0781a6bf 100644 --- a/tests/functional/parser/test_sqs_s3_event_notification.py +++ b/tests/functional/parser/test_sqs_s3_event_notification.py @@ -18,7 +18,9 @@ def test_handle_sqs_json_body_containing_s3_notifications(): for record in sqs_event_dict["Records"]: record["body"] = json_serialize(s3_event_notification_dict) - parsed_event: SqsS3EventNotificationModel = handle_sqs_json_body_containing_s3_notifications(sqs_event_dict, LambdaContext()) + parsed_event: SqsS3EventNotificationModel = handle_sqs_json_body_containing_s3_notifications( + sqs_event_dict, LambdaContext() + ) assert len(parsed_event.Records) == 2 for parsed_sqs_record in parsed_event.Records: From f1185836ff023cbe831a11588740013cd099fb61 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 26 Apr 2023 17:25:10 +0100 Subject: [PATCH 11/12] parser(sqs3): improving documentation, refactoring classname and tests --- .../utilities/parser/models/__init__.py | 8 ++-- .../parser/models/s3_event_notification.py | 6 +-- docs/utilities/parser.md | 40 ++++++++--------- tests/events/sqsS3Event.json | 22 ++++++++++ .../parser/test_sqs_s3_event_notification.py | 43 ------------------- tests/unit/parser/test_s3.py | 35 +++++++++++++++ 6 files changed, 84 insertions(+), 70 deletions(-) create mode 100644 tests/events/sqsS3Event.json delete mode 100644 tests/functional/parser/test_sqs_s3_event_notification.py diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index 26b68359da3..5f7a8a6b550 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -51,8 +51,8 @@ S3RecordModel, ) from .s3_event_notification import ( - SqsS3EventNotificationModel, - SqsS3EventNotificationRecordModel, + S3SqsEventNotificationModel, + S3SqsEventNotificationRecordModel, ) from .s3_object_event import ( S3ObjectConfiguration, @@ -134,8 +134,8 @@ "SqsRecordModel", "SqsMsgAttributeModel", "SqsAttributesModel", - "SqsS3EventNotificationModel", - "SqsS3EventNotificationRecordModel", + "S3SqsEventNotificationModel", + "S3SqsEventNotificationRecordModel", "APIGatewayProxyEventModel", "APIGatewayEventRequestContext", "APIGatewayEventAuthorizer", diff --git a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py index e3bedb2b0b0..1bcbc83ac18 100644 --- a/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py +++ b/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py @@ -6,9 +6,9 @@ from aws_lambda_powertools.utilities.parser.models.sqs import SqsModel, SqsRecordModel -class SqsS3EventNotificationRecordModel(SqsRecordModel): +class S3SqsEventNotificationRecordModel(SqsRecordModel): body: Json[S3Model] -class SqsS3EventNotificationModel(SqsModel): - Records: List[SqsS3EventNotificationRecordModel] +class S3SqsEventNotificationModel(SqsModel): + Records: List[S3SqsEventNotificationRecordModel] diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index 9b450e3abf6..38e12c0792d 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -156,26 +156,26 @@ def my_function(): Parser comes with the following built-in models: -| Model name | Description | -| --------------------------------------- | ---------------------------------------------------------------------------- | -| **DynamoDBStreamModel** | Lambda Event Source payload for Amazon DynamoDB Streams | -| **EventBridgeModel** | Lambda Event Source payload for Amazon EventBridge | -| **SqsModel** | Lambda Event Source payload for Amazon SQS | -| **AlbModel** | Lambda Event Source payload for Amazon Application Load Balancer | -| **CloudwatchLogsModel** | Lambda Event Source payload for Amazon CloudWatch Logs | -| **S3Model** | Lambda Event Source payload for Amazon S3 | -| **S3ObjectLambdaEvent** | Lambda Event Source payload for Amazon S3 Object Lambda | -| **S3EventNotificationEventBridgeModel** | Lambda Event Source payload for Amazon S3 Event Notification to EventBridge. | -| **KinesisDataStreamModel** | Lambda Event Source payload for Amazon Kinesis Data Streams | -| **KinesisFirehoseModel** | Lambda Event Source payload for Amazon Kinesis Firehose | -| **SesModel** | Lambda Event Source payload for Amazon Simple Email Service | -| **SnsModel** | Lambda Event Source payload for Amazon Simple Notification Service | -| **APIGatewayProxyEventModel** | Lambda Event Source payload for Amazon API Gateway | -| **APIGatewayProxyEventV2Model** | Lambda Event Source payload for Amazon API Gateway v2 payload | -| **LambdaFunctionUrlModel** | Lambda Event Source payload for Lambda Function URL payload | -| **KafkaSelfManagedEventModel** | Lambda Event Source payload for self managed Kafka payload | -| **KafkaMskEventModel** | Lambda Event Source payload for AWS MSK payload | -| **SqsS3EventNotificationModel** | Lambda Event Source payload for SQS-wrapped S3 event notifications | +| Model name | Description | +| --------------------------------------- | ------------------------------------------------------------------------------------- | +| **AlbModel** | Lambda Event Source payload for Amazon Application Load Balancer | +| **APIGatewayProxyEventModel** | Lambda Event Source payload for Amazon API Gateway | +| **APIGatewayProxyEventV2Model** | Lambda Event Source payload for Amazon API Gateway v2 payload | +| **CloudwatchLogsModel** | Lambda Event Source payload for Amazon CloudWatch Logs | +| **DynamoDBStreamModel** | Lambda Event Source payload for Amazon DynamoDB Streams | +| **EventBridgeModel** | Lambda Event Source payload for Amazon EventBridge | +| **KafkaMskEventModel** | Lambda Event Source payload for AWS MSK payload | +| **KafkaSelfManagedEventModel** | Lambda Event Source payload for self managed Kafka payload | +| **KinesisDataStreamModel** | Lambda Event Source payload for Amazon Kinesis Data Streams | +| **KinesisFirehoseModel** | Lambda Event Source payload for Amazon Kinesis Firehose | +| **LambdaFunctionUrlModel** | Lambda Event Source payload for Lambda Function URL payload | +| **S3EventNotificationEventBridgeModel** | Lambda Event Source payload for Amazon S3 Event Notification to EventBridge. | +| **S3Model** | Lambda Event Source payload for Amazon S3 | +| **S3ObjectLambdaEvent** | Lambda Event Source payload for Amazon S3 Object Lambda | +| **S3SqsEventNotificationModel** | Lambda Event Source payload for S3 event notifications wrapped in SQS event (S3->SQS) | +| **SesModel** | Lambda Event Source payload for Amazon Simple Email Service | +| **SnsModel** | Lambda Event Source payload for Amazon Simple Notification Service | +| **SqsModel** | Lambda Event Source payload for Amazon SQS | #### Extending built-in models diff --git a/tests/events/sqsS3Event.json b/tests/events/sqsS3Event.json new file mode 100644 index 00000000000..55863af12b0 --- /dev/null +++ b/tests/events/sqsS3Event.json @@ -0,0 +1,22 @@ +{ + "Records":[ + { + "messageId":"ca3e7a89-c358-40e5-8aa0-5da01403c267", + "receiptHandle":"AQEBE7XoI7IQRLF7SrpiW9W4BanmOWe8UtVDbv6/CEZYKf/OktSNIb4j689tQfR4k44V/LY20lZ5VpxYt2GTYCsSLKTcBalTJaRX9CKu/hVqy/23sSNiKxnP56D+VLSn+hU275+AP1h4pUL0d9gLdRB2haX8xiM+LcGfis5Jl8BBXtoxKRF60O87O9/NvCmmXLeqkJuexfyEZNyed0fFCRXFXSjbmThG0OIQgcrGI8glBRGPA8htns58VtXFsSaPYNoqP3p5n6+ewKKVLD0lfm+0DlnLKRa+mjvFBaSer9KK1ff+Aq6zJ6HynPwADj+aF70Hwimc2zImYe51SLEF/E2csYlMNZYI/2qXW0m9R7wJ/XDTV4g2+h+BMTxsKnJQ6NQd", + "body":"{\"Records\":[{\"eventVersion\":\"2.1\",\"eventSource\":\"aws:s3\",\"awsRegion\":\"us-east-1\",\"eventTime\":\"2023-04-12T20:43:38.021Z\",\"eventName\":\"ObjectCreated:Put\",\"userIdentity\":{\"principalId\":\"A1YQ72UWCM96UF\"},\"requestParameters\":{\"sourceIPAddress\":\"93.108.161.96\"},\"responseElements\":{\"x-amz-request-id\":\"YMSSR8BZJ2Y99K6P\",\"x-amz-id-2\":\"6ASrUfj5xpn859fIq+6FXflOex/SKl/rjfiMd7wRzMg/zkHKR22PDpnh7KD3uq//cuOTbdX4DInN5eIs+cR0dY1z2Mc5NDP/\"},\"s3\":{\"s3SchemaVersion\":\"1.0\",\"configurationId\":\"SNS\",\"bucket\":{\"name\":\"xxx\",\"ownerIdentity\":{\"principalId\":\"A1YQ72UWCM96UF\"},\"arn\":\"arn:aws:s3:::xxx\"},\"object\":{\"key\":\"test.pdf\",\"size\":104681,\"eTag\":\"2e3ad1e983318bbd8e73b080e2997980\",\"versionId\":\"yd3d4HaWOT2zguDLvIQLU6ptDTwKBnQV\",\"sequencer\":\"00643717F9F8B85354\"}}}]}", + "attributes":{ + "ApproximateReceiveCount":"1", + "SentTimestamp":"1681332219270", + "SenderId":"AIDAJHIPRHEMV73VRJEBU", + "ApproximateFirstReceiveTimestamp":"1681332239270" + }, + "messageAttributes":{ + + }, + "md5OfBody":"16f4460f4477d8d693a5abe94fdbbd73", + "eventSource":"aws:sqs", + "eventSourceARN":"arn:aws:sqs:us-east-1:123456789012:SQS", + "awsRegion":"us-east-1" + } + ] + } diff --git a/tests/functional/parser/test_sqs_s3_event_notification.py b/tests/functional/parser/test_sqs_s3_event_notification.py deleted file mode 100644 index 21b0781a6bf..00000000000 --- a/tests/functional/parser/test_sqs_s3_event_notification.py +++ /dev/null @@ -1,43 +0,0 @@ -import pytest - -from aws_lambda_powertools.utilities.parser import ValidationError, event_parser -from aws_lambda_powertools.utilities.parser.models import SqsS3EventNotificationModel -from aws_lambda_powertools.utilities.typing import LambdaContext -from tests.functional.parser.test_s3 import assert_s3 -from tests.functional.utils import json_serialize, load_event - - -@event_parser(model=SqsS3EventNotificationModel) -def handle_sqs_json_body_containing_s3_notifications(event: SqsS3EventNotificationModel, _: LambdaContext): - return event - - -def test_handle_sqs_json_body_containing_s3_notifications(): - sqs_event_dict = load_event("sqsEvent.json") - s3_event_notification_dict = load_event("s3Event.json") - for record in sqs_event_dict["Records"]: - record["body"] = json_serialize(s3_event_notification_dict) - - parsed_event: SqsS3EventNotificationModel = handle_sqs_json_body_containing_s3_notifications( - sqs_event_dict, LambdaContext() - ) - - assert len(parsed_event.Records) == 2 - for parsed_sqs_record in parsed_event.Records: - assert_s3(parsed_sqs_record.body) - - -def test_handle_sqs_body_invalid_json(): - sqs_event_dict = load_event("sqsEvent.json") - - with pytest.raises(ValidationError): - handle_sqs_json_body_containing_s3_notifications(sqs_event_dict, LambdaContext()) - - -def test_handle_sqs_json_body_containing_arbitrary_json(): - sqs_event_dict = load_event("sqsEvent.json") - for record in sqs_event_dict["Records"]: - record["body"] = json_serialize({"foo": "bar"}) - - with pytest.raises(ValidationError): - handle_sqs_json_body_containing_s3_notifications(sqs_event_dict, LambdaContext()) diff --git a/tests/unit/parser/test_s3.py b/tests/unit/parser/test_s3.py index 6d11ba8b9fd..42124a8d058 100644 --- a/tests/unit/parser/test_s3.py +++ b/tests/unit/parser/test_s3.py @@ -1,7 +1,12 @@ +import json from datetime import datetime +import pytest + +from aws_lambda_powertools.utilities.parser import ValidationError from aws_lambda_powertools.utilities.parser.models import ( S3EventNotificationEventBridgeModel, + S3SqsEventNotificationModel, ) from tests.functional.utils import load_event @@ -105,3 +110,33 @@ def test_s3_eventbridge_notification_object_restore_completed_event(): assert model.detail.requester == raw_event["detail"]["requester"] assert model.detail.restore_expiry_time == raw_event["detail"]["restore-expiry-time"] assert model.detail.source_storage_class == raw_event["detail"]["source-storage-class"] + + +def test_s3_sqs_event_notification(): + raw_event = load_event("sqsS3Event.json") + model = S3SqsEventNotificationModel(**raw_event) + + body = json.loads(raw_event["Records"][0]["body"]) + + assert model.Records[0].body.Records[0].eventVersion == body["Records"][0]["eventVersion"] + assert model.Records[0].body.Records[0].eventSource == body["Records"][0]["eventSource"] + assert model.Records[0].body.Records[0].eventTime == datetime.fromisoformat( + body["Records"][0]["eventTime"].replace("Z", "+00:00") + ) + assert model.Records[0].body.Records[0].eventName == body["Records"][0]["eventName"] + + +def test_s3_sqs_event_notification_body_invalid_json(): + raw_event = load_event("s3Event.json") + + with pytest.raises(ValidationError): + S3SqsEventNotificationModel(**raw_event) + + +def test_s3_sqs_event_notification_body_containing_arbitrary_json(): + raw_event = load_event("sqsS3Event.json") + for record in raw_event["Records"]: + record["body"] = {"foo": "bar"} + + with pytest.raises(ValidationError): + S3SqsEventNotificationModel(**raw_event) From 26cf2b76178bdd510e1e405d24fc09e33a1b94ec Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Mon, 1 May 2023 18:55:45 +0100 Subject: [PATCH 12/12] tests: addressing Ruben's feedback --- tests/events/{sqsS3Event.json => s3SqsEvent.json} | 0 tests/unit/parser/test_s3.py | 9 ++++++--- 2 files changed, 6 insertions(+), 3 deletions(-) rename tests/events/{sqsS3Event.json => s3SqsEvent.json} (100%) diff --git a/tests/events/sqsS3Event.json b/tests/events/s3SqsEvent.json similarity index 100% rename from tests/events/sqsS3Event.json rename to tests/events/s3SqsEvent.json diff --git a/tests/unit/parser/test_s3.py b/tests/unit/parser/test_s3.py index 42124a8d058..6ae2656ddd2 100644 --- a/tests/unit/parser/test_s3.py +++ b/tests/unit/parser/test_s3.py @@ -113,7 +113,7 @@ def test_s3_eventbridge_notification_object_restore_completed_event(): def test_s3_sqs_event_notification(): - raw_event = load_event("sqsS3Event.json") + raw_event = load_event("s3SqsEvent.json") model = S3SqsEventNotificationModel(**raw_event) body = json.loads(raw_event["Records"][0]["body"]) @@ -127,14 +127,17 @@ def test_s3_sqs_event_notification(): def test_s3_sqs_event_notification_body_invalid_json(): - raw_event = load_event("s3Event.json") + raw_event = load_event("s3SqsEvent.json") + + for record in raw_event["Records"]: + record["body"] = "invalid body" with pytest.raises(ValidationError): S3SqsEventNotificationModel(**raw_event) def test_s3_sqs_event_notification_body_containing_arbitrary_json(): - raw_event = load_event("sqsS3Event.json") + raw_event = load_event("s3SqsEvent.json") for record in raw_event["Records"]: record["body"] = {"foo": "bar"}