From 280a90e1bd7d8167ebe95a35217a8552ddc748cf Mon Sep 17 00:00:00 2001 From: ivica-k Date: Sun, 5 Mar 2023 18:22:53 +0100 Subject: [PATCH 01/12] feat(parser): adds support for S3 event notifications through EventBridge --- .../utilities/parser/models/__init__.py | 9 +- .../utilities/parser/models/s3.py | 33 ++++ docs/utilities/parser.md | 37 ++-- ...tBridgeNotificationObjectCreatedEvent.json | 28 +++ ...tBridgeNotificationObjectDeletedEvent.json | 29 ++++ ...tBridgeNotificationObjectExpiredEvent.json | 28 +++ ...tificationObjectRestoreCompletedEvent.json | 28 +++ tests/functional/parser/test_s3.py | 160 +++++++++++++++++- 8 files changed, 332 insertions(+), 20 deletions(-) create mode 100644 tests/events/s3EventBridgeNotificationObjectCreatedEvent.json create mode 100644 tests/events/s3EventBridgeNotificationObjectDeletedEvent.json create mode 100644 tests/events/s3EventBridgeNotificationObjectExpiredEvent.json create mode 100644 tests/events/s3EventBridgeNotificationObjectRestoreCompletedEvent.json diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index 62e28a62374..cde15e83e98 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -43,7 +43,12 @@ KinesisFirehoseRecordMetadata, ) from .lambda_function_url import LambdaFunctionUrlModel -from .s3 import S3Model, S3RecordModel +from .s3 import ( + S3EventNotificationEventBridgeDetailModel, + S3EventNotificationObjectModel, + S3Model, + S3RecordModel, +) from .s3_object_event import ( S3ObjectConfiguration, S3ObjectContext, @@ -105,6 +110,8 @@ "S3ObjectUserRequest", "S3ObjectConfiguration", "S3ObjectContext", + "S3EventNotificationObjectModel", + "S3EventNotificationEventBridgeDetailModel", "SesModel", "SesRecordModel", "SesMessage", diff --git a/aws_lambda_powertools/utilities/parser/models/s3.py b/aws_lambda_powertools/utilities/parser/models/s3.py index c4453811118..46a58a092c1 100644 --- a/aws_lambda_powertools/utilities/parser/models/s3.py +++ b/aws_lambda_powertools/utilities/parser/models/s3.py @@ -8,6 +8,8 @@ from aws_lambda_powertools.utilities.parser.types import Literal +from .event_bridge import EventBridgeModel + class S3EventRecordGlacierRestoreEventData(BaseModel): lifecycleRestorationExpiryTime: datetime @@ -56,6 +58,37 @@ class S3Message(BaseModel): object: S3Object # noqa: A003,VNE003 +class S3EventNotificationObjectModel(BaseModel): + key: str + size: Optional[NonNegativeFloat] + etag: str + version_id: str = Field(None, alias="version-id") + sequencer: Optional[str] + + +class S3EventNotificationEventBridgeBucketModel(BaseModel): + name: str + + +class S3EventNotificationEventBridgeDetailModel(BaseModel): + version: str + bucket: S3EventNotificationEventBridgeBucketModel + object: S3EventNotificationObjectModel # noqa: A003,VNE003 + request_id: str = Field(None, alias="request-id") + requester: str + source_ip_address: str = Field(None, alias="source-ip-address") + reason: Optional[str] + deletion_type: Optional[str] = Field(None, alias="deletion-type") + restore_expiry_time: Optional[str] = Field(None, alias="restore-expiry-time") + source_storage_class: Optional[str] = Field(None, alias="source-storage-class") + destination_storage_class: Optional[str] = Field(None, alias="destination-storage-class") + destination_access_tier: Optional[str] = Field(None, alias="destination-access-tier") + + +class S3EventNotificationEventBridgeModel(EventBridgeModel): + detail = S3EventNotificationEventBridgeDetailModel + + class S3RecordModel(BaseModel): eventVersion: str eventSource: Literal["aws:s3"] diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index 4afa362f879..02b9d0e0a4c 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -156,24 +156,25 @@ 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 | -| **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 | +| 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 | +| **S3EventNotificationEventBridgeDetailModel** | Lambda Event Source payload for Amazon S3 Notification to EventBridge.
To be used with `envelopes.EventBridgeEnvelope`. | +| **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 | #### Extending built-in models diff --git a/tests/events/s3EventBridgeNotificationObjectCreatedEvent.json b/tests/events/s3EventBridgeNotificationObjectCreatedEvent.json new file mode 100644 index 00000000000..5cc8f2f402f --- /dev/null +++ b/tests/events/s3EventBridgeNotificationObjectCreatedEvent.json @@ -0,0 +1,28 @@ +{ + "version": "0", + "id": "f5f1e65c-dc3a-93ca-6c1e-b1647eac7963", + "detail-type": "Object Created", + "source": "aws.s3", + "account": "123456789012", + "time": "2023-03-08T17:50:14Z", + "region": "eu-west-1", + "resources": [ + "arn:aws:s3:::example-bucket" + ], + "detail": { + "version": "0", + "bucket": { + "name": "example-bucket" + }, + "object": { + "key": "IMG_m7fzo3.jpg", + "size": 184662, + "etag": "4e68adba0abe2dc8653dc3354e14c01d", + "sequencer": "006408CAD69598B05E" + }, + "request-id": "57H08PA84AB1JZW0", + "requester": "123456789012", + "source-ip-address": "34.252.34.74", + "reason": "PutObject" + } +} \ No newline at end of file diff --git a/tests/events/s3EventBridgeNotificationObjectDeletedEvent.json b/tests/events/s3EventBridgeNotificationObjectDeletedEvent.json new file mode 100644 index 00000000000..af52ee2fef0 --- /dev/null +++ b/tests/events/s3EventBridgeNotificationObjectDeletedEvent.json @@ -0,0 +1,29 @@ +{ + "version": "0", + "id": "2ee9cc15-d022-99ea-1fb8-1b1bac4850f9", + "detail-type": "Object Deleted", + "source": "aws.s3", + "account": "111122223333", + "time": "2021-11-12T00:00:00Z", + "region": "ca-central-1", + "resources": [ + "arn:aws:s3:::example-bucket" + ], + "detail": { + "version": "0", + "bucket": { + "name": "example-bucket" + }, + "object": { + "key": "IMG_m7fzo3.jpg", + "size": 184662, + "etag": "4e68adba0abe2dc8653dc3354e14c01d", + "sequencer": "006408CAD69598B05E" + }, + "request-id": "0BH729840619AG5K", + "requester": "123456789012", + "source-ip-address": "34.252.34.74", + "reason": "DeleteObject", + "deletion-type": "Delete Marker Created" + } +} \ No newline at end of file diff --git a/tests/events/s3EventBridgeNotificationObjectExpiredEvent.json b/tests/events/s3EventBridgeNotificationObjectExpiredEvent.json new file mode 100644 index 00000000000..ef506cc3556 --- /dev/null +++ b/tests/events/s3EventBridgeNotificationObjectExpiredEvent.json @@ -0,0 +1,28 @@ +{ + "version": "0", + "id": "ad1de317-e409-eba2-9552-30113f8d88e3", + "detail-type": "Object Deleted", + "source": "aws.s3", + "account": "111122223333", + "time": "2021-11-12T00:00:00Z", + "region": "ca-central-1", + "resources": [ + "arn:aws:s3:::example-bucket" + ], + "detail": { + "version": "0", + "bucket": { + "name": "example-bucket" + }, + "object": { + "key": "IMG_m7fzo3.jpg", + "size": 184662, + "etag": "4e68adba0abe2dc8653dc3354e14c01d", + "sequencer": "006408CAD69598B05E" + }, + "request-id": "20EB74C14654DC47", + "requester": "s3.amazonaws.com", + "reason": "Lifecycle Expiration", + "deletion-type": "Delete Marker Created" + } +} \ No newline at end of file diff --git a/tests/events/s3EventBridgeNotificationObjectRestoreCompletedEvent.json b/tests/events/s3EventBridgeNotificationObjectRestoreCompletedEvent.json new file mode 100644 index 00000000000..5a2e6a4f9ec --- /dev/null +++ b/tests/events/s3EventBridgeNotificationObjectRestoreCompletedEvent.json @@ -0,0 +1,28 @@ +{ + "version": "0", + "id": "6924de0d-13e2-6bbf-c0c1-b903b753565e", + "detail-type": "Object Restore Completed", + "source": "aws.s3", + "account": "111122223333", + "time": "2021-11-12T00:00:00Z", + "region": "ca-central-1", + "resources": [ + "arn:aws:s3:::example-bucket" + ], + "detail": { + "version": "0", + "bucket": { + "name": "example-bucket" + }, + "object": { + "key": "IMG_m7fzo3.jpg", + "size": 184662, + "etag": "4e68adba0abe2dc8653dc3354e14c01d", + "sequencer": "006408CAD69598B05E" + }, + "request-id": "189F19CB7FB1B6A4", + "requester": "s3.amazonaws.com", + "restore-expiry-time": "2021-11-13T00:00:00Z", + "source-storage-class": "GLACIER" + } +} \ No newline at end of file diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index cd903f3052c..53abbe15dee 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -1,7 +1,13 @@ import pytest from aws_lambda_powertools.utilities.parser import ValidationError, event_parser, parse -from aws_lambda_powertools.utilities.parser.models import S3Model, S3RecordModel +from aws_lambda_powertools.utilities.parser.envelopes import EventBridgeEnvelope +from aws_lambda_powertools.utilities.parser.models import ( + S3EventNotificationEventBridgeDetailModel, + S3EventNotificationObjectModel, + S3Model, + S3RecordModel, +) from aws_lambda_powertools.utilities.typing import LambdaContext from tests.functional.utils import load_event @@ -111,6 +117,26 @@ def test_s3_none_etag_value_failed_validation(): parse(event=event_dict, model=S3Model) +def test_s3_eventbridge_notification_object_created_event(): + event_dict = load_event("s3EventBridgeNotificationObjectCreatedEvent.json") + handle_s3_eventbridge_object_created(event_dict, LambdaContext()) + + +def test_s3_eventbridge_notification_object_deleted_event(): + event_dict = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") + handle_s3_eventbridge_object_deleted(event_dict, LambdaContext()) + + +def test_s3_eventbridge_notification_object_expired_event(): + event_dict = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") + handle_s3_eventbridge_object_expired(event_dict, LambdaContext()) + + +def test_s3_eventbridge_notification_object_restore_completed_event(): + event_dict = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") + handle_s3_eventbridge_object_restore_completed(event_dict, LambdaContext()) + + @event_parser(model=S3Model) def handle_s3_delete_object(event: S3Model, _: LambdaContext): records = list(event.Records) @@ -149,3 +175,135 @@ def handle_s3_delete_object(event: S3Model, _: LambdaContext): def test_s3_trigger_event_delete_object(): event_dict = load_event("s3EventDeleteObject.json") handle_s3_delete_object(event_dict, LambdaContext()) + + +@event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) +def handle_s3_eventbridge_object_created(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): + """ + Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from + https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + """ + bucket_name = "example-bucket" + deletion_type = None + destination_access_tier = None + destination_storage_class = None + _object: S3EventNotificationObjectModel = event.object + reason = "PutObject" + request_id = "57H08PA84AB1JZW0" + requester = "123456789012" + restore_expiry_time = None + source_ip_address = "34.252.34.74" + source_storage_class = None + version = "0" + + assert bucket_name == event.bucket.name + assert deletion_type == event.deletion_type + assert destination_access_tier == event.destination_access_tier + assert destination_storage_class == event.destination_storage_class + assert _object == event.object + assert reason == event.reason + assert request_id == event.request_id + assert requester == event.requester + assert restore_expiry_time == event.restore_expiry_time + assert source_ip_address == event.source_ip_address + assert source_storage_class == event.source_storage_class + assert version == event.version + + +@event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) +def handle_s3_eventbridge_object_deleted(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): + """ + Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from + https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + """ + bucket_name = "example-bucket" + deletion_type = "Delete Marker Created" + destination_access_tier = None + destination_storage_class = None + _object: S3EventNotificationObjectModel = event.object + reason = "DeleteObject" + request_id = "0BH729840619AG5K" + requester = "123456789012" + restore_expiry_time = None + source_ip_address = "34.252.34.74" + source_storage_class = None + version = "0" + + assert bucket_name == event.bucket.name + assert deletion_type == event.deletion_type + assert destination_access_tier == event.destination_access_tier + assert destination_storage_class == event.destination_storage_class + assert _object == event.object + assert reason == event.reason + assert request_id == event.request_id + assert requester == event.requester + assert restore_expiry_time == event.restore_expiry_time + assert source_ip_address == event.source_ip_address + assert source_storage_class == event.source_storage_class + assert version == event.version + + +@event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) +def handle_s3_eventbridge_object_expired(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): + """ + Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from + https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + """ + bucket_name = "example-bucket" + deletion_type = "Delete Marker Created" + destination_access_tier = None + destination_storage_class = None + _object: S3EventNotificationObjectModel = event.object + reason = "Lifecycle Expiration" + request_id = "20EB74C14654DC47" + requester = "s3.amazonaws.com" + restore_expiry_time = None + source_ip_address = None + source_storage_class = None + version = "0" + + assert bucket_name == event.bucket.name + assert deletion_type == event.deletion_type + assert destination_access_tier == event.destination_access_tier + assert destination_storage_class == event.destination_storage_class + assert _object == event.object + assert reason == event.reason + assert request_id == event.request_id + assert requester == event.requester + assert restore_expiry_time == event.restore_expiry_time + assert source_ip_address == event.source_ip_address + assert source_storage_class == event.source_storage_class + assert version == event.version + + +@event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) +def handle_s3_eventbridge_object_restore_completed(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): + """ + Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from + https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + """ + bucket_name = "example-bucket" + deletion_type = None + destination_access_tier = None + destination_storage_class = None + _object: S3EventNotificationObjectModel = event.object + reason = None + request_id = "189F19CB7FB1B6A4" + requester = "s3.amazonaws.com" + restore_expiry_time = "2021-11-13T00:00:00Z" + source_ip_address = None + source_storage_class = "GLACIER" + version = "0" + + assert bucket_name == event.bucket.name + assert deletion_type == event.deletion_type + assert destination_access_tier == event.destination_access_tier + assert destination_storage_class == event.destination_storage_class + assert _object == event.object + assert reason == event.reason + assert request_id == event.request_id + assert requester == event.requester + assert restore_expiry_time == event.restore_expiry_time + assert source_ip_address == event.source_ip_address + assert source_storage_class == event.source_storage_class + assert version == event.version From 79301a994e1444f1ec0576055833911c4c740f32 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 10 Mar 2023 13:38:44 +0100 Subject: [PATCH 02/12] docs(parser): fix high-level model name --- docs/utilities/parser.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index 02b9d0e0a4c..dd4f8e8934d 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -156,25 +156,25 @@ 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 | -| **S3EventNotificationEventBridgeDetailModel** | Lambda Event Source payload for Amazon S3 Notification to EventBridge.
To be used with `envelopes.EventBridgeEnvelope`. | -| **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 | +| 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.
To be used with `envelopes.EventBridgeEnvelope`. | +| **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 | #### Extending built-in models From 00138efaf3493bb16b10be2cbe8ee0044873cb6d Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 10 Mar 2023 13:53:56 +0100 Subject: [PATCH 03/12] fix(parser): export parent S3EventNotificationEventBridgeModel --- .../utilities/parser/models/__init__.py | 2 + docs/utilities/parser.md | 38 +++++++++---------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index cde15e83e98..757b9c4fff5 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -45,6 +45,7 @@ from .lambda_function_url import LambdaFunctionUrlModel from .s3 import ( S3EventNotificationEventBridgeDetailModel, + S3EventNotificationEventBridgeModel, S3EventNotificationObjectModel, S3Model, S3RecordModel, @@ -111,6 +112,7 @@ "S3ObjectConfiguration", "S3ObjectContext", "S3EventNotificationObjectModel", + "S3EventNotificationEventBridgeModel", "S3EventNotificationEventBridgeDetailModel", "SesModel", "SesRecordModel", diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index dd4f8e8934d..2ba98d61206 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -156,25 +156,25 @@ 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.
To be used with `envelopes.EventBridgeEnvelope`. | -| **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 | +| 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.
It's compatible with `EventBridgeEnvelope` [envelope](#envelopes). | +| **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 | #### Extending built-in models From 33db0d7dfb1920c8a21e6ccbf6af7fda03e9eb34 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 10 Mar 2023 15:05:15 +0100 Subject: [PATCH 04/12] fix(parser): correctly instantiate nested model --- aws_lambda_powertools/utilities/parser/models/s3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws_lambda_powertools/utilities/parser/models/s3.py b/aws_lambda_powertools/utilities/parser/models/s3.py index 46a58a092c1..01573b6d751 100644 --- a/aws_lambda_powertools/utilities/parser/models/s3.py +++ b/aws_lambda_powertools/utilities/parser/models/s3.py @@ -86,7 +86,7 @@ class S3EventNotificationEventBridgeDetailModel(BaseModel): class S3EventNotificationEventBridgeModel(EventBridgeModel): - detail = S3EventNotificationEventBridgeDetailModel + detail: S3EventNotificationEventBridgeDetailModel class S3RecordModel(BaseModel): From 3f9098547d249e687b23b8c338cc6311520c149e Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 10 Mar 2023 15:06:08 +0100 Subject: [PATCH 05/12] chore(tests): add object created test without envelope --- tests/functional/parser/test_s3.py | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index 53abbe15dee..a8f19061c4c 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -1,9 +1,12 @@ +from datetime import datetime + import pytest from aws_lambda_powertools.utilities.parser import ValidationError, event_parser, parse from aws_lambda_powertools.utilities.parser.envelopes import EventBridgeEnvelope from aws_lambda_powertools.utilities.parser.models import ( S3EventNotificationEventBridgeDetailModel, + S3EventNotificationEventBridgeModel, S3EventNotificationObjectModel, S3Model, S3RecordModel, @@ -122,6 +125,11 @@ def test_s3_eventbridge_notification_object_created_event(): handle_s3_eventbridge_object_created(event_dict, LambdaContext()) +def test_s3_eventbridge_notification_object_created_event_no_envelope(): + event_dict = load_event("s3EventBridgeNotificationObjectCreatedEvent.json") + handle_s3_eventbridge_object_created_no_envelope(event_dict, LambdaContext()) + + def test_s3_eventbridge_notification_object_deleted_event(): event_dict = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") handle_s3_eventbridge_object_deleted(event_dict, LambdaContext()) @@ -210,6 +218,36 @@ def handle_s3_eventbridge_object_created(event: S3EventNotificationEventBridgeDe assert version == event.version +@event_parser(model=S3EventNotificationEventBridgeModel) +def handle_s3_eventbridge_object_created_no_envelope(event: S3EventNotificationEventBridgeModel, _: LambdaContext): + """ + Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from + https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + """ + + raw_event = load_event("s3EventBridgeNotificationObjectCreatedEvent.json") + + assert event.version == raw_event["version"] + assert event.id == raw_event["id"] + assert event.detail_type == raw_event["detail-type"] + assert event.source == raw_event["source"] + assert event.account == raw_event["account"] + assert event.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) + assert event.region == raw_event["region"] + assert event.resources == raw_event["resources"] + + assert event.detail.version == raw_event["detail"]["version"] + assert event.detail.bucket.name == raw_event["detail"]["bucket"]["name"] + assert event.detail.object.key == raw_event["detail"]["object"]["key"] + assert event.detail.object.size == raw_event["detail"]["object"]["size"] + assert event.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] + assert event.detail.request_id == raw_event["detail"]["request-id"] + assert event.detail.requester == raw_event["detail"]["requester"] + assert event.detail.source_ip_address == raw_event["detail"]["source-ip-address"] + assert event.detail.reason == raw_event["detail"]["reason"] + + @event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) def handle_s3_eventbridge_object_deleted(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): """ From 9123c443c81a9d3d1e6028b2b79ce0b75b49ba35 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 10 Mar 2023 15:14:49 +0100 Subject: [PATCH 06/12] chore(tests): add object deleted test without envelope --- tests/functional/parser/test_s3.py | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index a8f19061c4c..d28a74d3bd5 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -135,6 +135,11 @@ def test_s3_eventbridge_notification_object_deleted_event(): handle_s3_eventbridge_object_deleted(event_dict, LambdaContext()) +def test_s3_eventbridge_notification_object_deleted_event_no_envelope(): + event_dict = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") + handle_s3_eventbridge_object_deleted_no_envelope(event_dict, LambdaContext()) + + def test_s3_eventbridge_notification_object_expired_event(): event_dict = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") handle_s3_eventbridge_object_expired(event_dict, LambdaContext()) @@ -281,6 +286,36 @@ def handle_s3_eventbridge_object_deleted(event: S3EventNotificationEventBridgeDe assert version == event.version +@event_parser(model=S3EventNotificationEventBridgeModel) +def handle_s3_eventbridge_object_deleted_no_envelope(event: S3EventNotificationEventBridgeModel, _: LambdaContext): + """ + Tests that the `S3EventNotificationEventBridgeModel` parses events from + https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + """ + raw_event = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") + + assert event.version == raw_event["version"] + assert event.id == raw_event["id"] + assert event.detail_type == raw_event["detail-type"] + assert event.source == raw_event["source"] + assert event.account == raw_event["account"] + assert event.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) + assert event.region == raw_event["region"] + assert event.resources == raw_event["resources"] + + assert event.detail.version == raw_event["detail"]["version"] + assert event.detail.bucket.name == raw_event["detail"]["bucket"]["name"] + assert event.detail.object.key == raw_event["detail"]["object"]["key"] + assert event.detail.object.size == raw_event["detail"]["object"]["size"] + assert event.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] + assert event.detail.request_id == raw_event["detail"]["request-id"] + assert event.detail.requester == raw_event["detail"]["requester"] + assert event.detail.source_ip_address == raw_event["detail"]["source-ip-address"] + assert event.detail.reason == raw_event["detail"]["reason"] + assert event.detail.deletion_type == raw_event["detail"]["deletion-type"] + + @event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) def handle_s3_eventbridge_object_expired(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): """ From ce552072b2d275960a7bbf56d90b24601a64d7ef Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 10 Mar 2023 15:19:59 +0100 Subject: [PATCH 07/12] chore(tests): add object expired test without envelope --- tests/functional/parser/test_s3.py | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index d28a74d3bd5..6aa3ccf3445 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -145,6 +145,11 @@ def test_s3_eventbridge_notification_object_expired_event(): handle_s3_eventbridge_object_expired(event_dict, LambdaContext()) +def test_s3_eventbridge_notification_object_expired_event_no_envelope(): + event_dict = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") + handle_s3_eventbridge_object_expired_no_envelope(event_dict, LambdaContext()) + + def test_s3_eventbridge_notification_object_restore_completed_event(): event_dict = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") handle_s3_eventbridge_object_restore_completed(event_dict, LambdaContext()) @@ -349,6 +354,36 @@ def handle_s3_eventbridge_object_expired(event: S3EventNotificationEventBridgeDe assert version == event.version +@event_parser(model=S3EventNotificationEventBridgeModel) +def handle_s3_eventbridge_object_expired_no_envelope(event: S3EventNotificationEventBridgeModel, _: LambdaContext): + """ + Tests that the `S3EventNotificationEventBridgeModel` parses events from + https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + """ + + raw_event = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") + + assert event.version == raw_event["version"] + assert event.id == raw_event["id"] + assert event.detail_type == raw_event["detail-type"] + assert event.source == raw_event["source"] + assert event.account == raw_event["account"] + assert event.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) + assert event.region == raw_event["region"] + assert event.resources == raw_event["resources"] + + assert event.detail.version == raw_event["detail"]["version"] + assert event.detail.bucket.name == raw_event["detail"]["bucket"]["name"] + assert event.detail.object.key == raw_event["detail"]["object"]["key"] + assert event.detail.object.size == raw_event["detail"]["object"]["size"] + assert event.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] + assert event.detail.request_id == raw_event["detail"]["request-id"] + assert event.detail.requester == raw_event["detail"]["requester"] + assert event.detail.reason == raw_event["detail"]["reason"] + assert event.detail.deletion_type == raw_event["detail"]["deletion-type"] + + @event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) def handle_s3_eventbridge_object_restore_completed(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): """ From e8041b10bd85edd620e804993645971f8cfa01d2 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 10 Mar 2023 15:23:44 +0100 Subject: [PATCH 08/12] chore(tests): add object restore completed test without envelope --- tests/functional/parser/test_s3.py | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index 6aa3ccf3445..92f6207236a 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -155,6 +155,11 @@ def test_s3_eventbridge_notification_object_restore_completed_event(): handle_s3_eventbridge_object_restore_completed(event_dict, LambdaContext()) +def test_s3_eventbridge_notification_object_restore_completed_event_no_envelope(): + event_dict = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") + handle_s3_eventbridge_object_restore_completed_no_envelope(event_dict, LambdaContext()) + + @event_parser(model=S3Model) def handle_s3_delete_object(event: S3Model, _: LambdaContext): records = list(event.Records) @@ -415,3 +420,35 @@ def handle_s3_eventbridge_object_restore_completed(event: S3EventNotificationEve assert source_ip_address == event.source_ip_address assert source_storage_class == event.source_storage_class assert version == event.version + + +@event_parser(model=S3EventNotificationEventBridgeModel) +def handle_s3_eventbridge_object_restore_completed_no_envelope( + event: S3EventNotificationEventBridgeModel, _: LambdaContext +): + """ + Tests that the `S3EventNotificationEventBridgeModel` parses events from + https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + """ + + raw_event = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") + + assert event.version == raw_event["version"] + assert event.id == raw_event["id"] + assert event.detail_type == raw_event["detail-type"] + assert event.source == raw_event["source"] + assert event.account == raw_event["account"] + assert event.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) + assert event.region == raw_event["region"] + assert event.resources == raw_event["resources"] + + assert event.detail.version == raw_event["detail"]["version"] + assert event.detail.bucket.name == raw_event["detail"]["bucket"]["name"] + assert event.detail.object.key == raw_event["detail"]["object"]["key"] + assert event.detail.object.size == raw_event["detail"]["object"]["size"] + assert event.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] + assert event.detail.request_id == raw_event["detail"]["request-id"] + assert event.detail.requester == raw_event["detail"]["requester"] + assert event.detail.restore_expiry_time == raw_event["detail"]["restore-expiry-time"] + assert event.detail.source_storage_class == raw_event["detail"]["source-storage-class"] From 94248267eff9cc143495067c397a203da1a7351b Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 10 Mar 2023 15:33:14 +0100 Subject: [PATCH 09/12] chore: remove envelope ref and consistency test docstring --- docs/utilities/parser.md | 38 +++++++++++++++--------------- tests/functional/parser/test_s3.py | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index 2ba98d61206..8610cb63c15 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -156,25 +156,25 @@ 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.
It's compatible with `EventBridgeEnvelope` [envelope](#envelopes). | -| **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 | +| 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 | #### Extending built-in models diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index 92f6207236a..1ff584653d9 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -236,7 +236,7 @@ def handle_s3_eventbridge_object_created(event: S3EventNotificationEventBridgeDe @event_parser(model=S3EventNotificationEventBridgeModel) def handle_s3_eventbridge_object_created_no_envelope(event: S3EventNotificationEventBridgeModel, _: LambdaContext): """ - Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from + Tests that the `S3EventNotificationEventBridgeModel` parses events from https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html """ From d5baff60d597844f24fb2658b6052a1d7bd40a70 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Tue, 14 Mar 2023 09:03:32 +0100 Subject: [PATCH 10/12] chore: remove envelope specific tests --- tests/functional/parser/test_s3.py | 165 +---------------------------- 1 file changed, 4 insertions(+), 161 deletions(-) diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index 1ff584653d9..4ac7decd627 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -3,11 +3,8 @@ import pytest from aws_lambda_powertools.utilities.parser import ValidationError, event_parser, parse -from aws_lambda_powertools.utilities.parser.envelopes import EventBridgeEnvelope from aws_lambda_powertools.utilities.parser.models import ( - S3EventNotificationEventBridgeDetailModel, S3EventNotificationEventBridgeModel, - S3EventNotificationObjectModel, S3Model, S3RecordModel, ) @@ -125,41 +122,21 @@ def test_s3_eventbridge_notification_object_created_event(): handle_s3_eventbridge_object_created(event_dict, LambdaContext()) -def test_s3_eventbridge_notification_object_created_event_no_envelope(): - event_dict = load_event("s3EventBridgeNotificationObjectCreatedEvent.json") - handle_s3_eventbridge_object_created_no_envelope(event_dict, LambdaContext()) - - def test_s3_eventbridge_notification_object_deleted_event(): event_dict = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") handle_s3_eventbridge_object_deleted(event_dict, LambdaContext()) -def test_s3_eventbridge_notification_object_deleted_event_no_envelope(): - event_dict = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") - handle_s3_eventbridge_object_deleted_no_envelope(event_dict, LambdaContext()) - - def test_s3_eventbridge_notification_object_expired_event(): event_dict = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") handle_s3_eventbridge_object_expired(event_dict, LambdaContext()) -def test_s3_eventbridge_notification_object_expired_event_no_envelope(): - event_dict = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") - handle_s3_eventbridge_object_expired_no_envelope(event_dict, LambdaContext()) - - def test_s3_eventbridge_notification_object_restore_completed_event(): event_dict = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") handle_s3_eventbridge_object_restore_completed(event_dict, LambdaContext()) -def test_s3_eventbridge_notification_object_restore_completed_event_no_envelope(): - event_dict = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") - handle_s3_eventbridge_object_restore_completed_no_envelope(event_dict, LambdaContext()) - - @event_parser(model=S3Model) def handle_s3_delete_object(event: S3Model, _: LambdaContext): records = list(event.Records) @@ -200,41 +177,8 @@ def test_s3_trigger_event_delete_object(): handle_s3_delete_object(event_dict, LambdaContext()) -@event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) -def handle_s3_eventbridge_object_created(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): - """ - Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html - """ - bucket_name = "example-bucket" - deletion_type = None - destination_access_tier = None - destination_storage_class = None - _object: S3EventNotificationObjectModel = event.object - reason = "PutObject" - request_id = "57H08PA84AB1JZW0" - requester = "123456789012" - restore_expiry_time = None - source_ip_address = "34.252.34.74" - source_storage_class = None - version = "0" - - assert bucket_name == event.bucket.name - assert deletion_type == event.deletion_type - assert destination_access_tier == event.destination_access_tier - assert destination_storage_class == event.destination_storage_class - assert _object == event.object - assert reason == event.reason - assert request_id == event.request_id - assert requester == event.requester - assert restore_expiry_time == event.restore_expiry_time - assert source_ip_address == event.source_ip_address - assert source_storage_class == event.source_storage_class - assert version == event.version - - @event_parser(model=S3EventNotificationEventBridgeModel) -def handle_s3_eventbridge_object_created_no_envelope(event: S3EventNotificationEventBridgeModel, _: LambdaContext): +def handle_s3_eventbridge_object_created(event: S3EventNotificationEventBridgeModel, _: LambdaContext): """ Tests that the `S3EventNotificationEventBridgeModel` parses events from https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html @@ -263,41 +207,8 @@ def handle_s3_eventbridge_object_created_no_envelope(event: S3EventNotificationE assert event.detail.reason == raw_event["detail"]["reason"] -@event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) -def handle_s3_eventbridge_object_deleted(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): - """ - Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html - """ - bucket_name = "example-bucket" - deletion_type = "Delete Marker Created" - destination_access_tier = None - destination_storage_class = None - _object: S3EventNotificationObjectModel = event.object - reason = "DeleteObject" - request_id = "0BH729840619AG5K" - requester = "123456789012" - restore_expiry_time = None - source_ip_address = "34.252.34.74" - source_storage_class = None - version = "0" - - assert bucket_name == event.bucket.name - assert deletion_type == event.deletion_type - assert destination_access_tier == event.destination_access_tier - assert destination_storage_class == event.destination_storage_class - assert _object == event.object - assert reason == event.reason - assert request_id == event.request_id - assert requester == event.requester - assert restore_expiry_time == event.restore_expiry_time - assert source_ip_address == event.source_ip_address - assert source_storage_class == event.source_storage_class - assert version == event.version - - @event_parser(model=S3EventNotificationEventBridgeModel) -def handle_s3_eventbridge_object_deleted_no_envelope(event: S3EventNotificationEventBridgeModel, _: LambdaContext): +def handle_s3_eventbridge_object_deleted(event: S3EventNotificationEventBridgeModel, _: LambdaContext): """ Tests that the `S3EventNotificationEventBridgeModel` parses events from https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html @@ -326,41 +237,8 @@ def handle_s3_eventbridge_object_deleted_no_envelope(event: S3EventNotificationE assert event.detail.deletion_type == raw_event["detail"]["deletion-type"] -@event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) -def handle_s3_eventbridge_object_expired(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): - """ - Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html - """ - bucket_name = "example-bucket" - deletion_type = "Delete Marker Created" - destination_access_tier = None - destination_storage_class = None - _object: S3EventNotificationObjectModel = event.object - reason = "Lifecycle Expiration" - request_id = "20EB74C14654DC47" - requester = "s3.amazonaws.com" - restore_expiry_time = None - source_ip_address = None - source_storage_class = None - version = "0" - - assert bucket_name == event.bucket.name - assert deletion_type == event.deletion_type - assert destination_access_tier == event.destination_access_tier - assert destination_storage_class == event.destination_storage_class - assert _object == event.object - assert reason == event.reason - assert request_id == event.request_id - assert requester == event.requester - assert restore_expiry_time == event.restore_expiry_time - assert source_ip_address == event.source_ip_address - assert source_storage_class == event.source_storage_class - assert version == event.version - - @event_parser(model=S3EventNotificationEventBridgeModel) -def handle_s3_eventbridge_object_expired_no_envelope(event: S3EventNotificationEventBridgeModel, _: LambdaContext): +def handle_s3_eventbridge_object_expired(event: S3EventNotificationEventBridgeModel, _: LambdaContext): """ Tests that the `S3EventNotificationEventBridgeModel` parses events from https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html @@ -389,43 +267,8 @@ def handle_s3_eventbridge_object_expired_no_envelope(event: S3EventNotificationE assert event.detail.deletion_type == raw_event["detail"]["deletion-type"] -@event_parser(model=S3EventNotificationEventBridgeDetailModel, envelope=EventBridgeEnvelope) -def handle_s3_eventbridge_object_restore_completed(event: S3EventNotificationEventBridgeDetailModel, _: LambdaContext): - """ - Tests that the `S3EventNotificationEventBridgeDetailModel` parses events from - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html - """ - bucket_name = "example-bucket" - deletion_type = None - destination_access_tier = None - destination_storage_class = None - _object: S3EventNotificationObjectModel = event.object - reason = None - request_id = "189F19CB7FB1B6A4" - requester = "s3.amazonaws.com" - restore_expiry_time = "2021-11-13T00:00:00Z" - source_ip_address = None - source_storage_class = "GLACIER" - version = "0" - - assert bucket_name == event.bucket.name - assert deletion_type == event.deletion_type - assert destination_access_tier == event.destination_access_tier - assert destination_storage_class == event.destination_storage_class - assert _object == event.object - assert reason == event.reason - assert request_id == event.request_id - assert requester == event.requester - assert restore_expiry_time == event.restore_expiry_time - assert source_ip_address == event.source_ip_address - assert source_storage_class == event.source_storage_class - assert version == event.version - - @event_parser(model=S3EventNotificationEventBridgeModel) -def handle_s3_eventbridge_object_restore_completed_no_envelope( - event: S3EventNotificationEventBridgeModel, _: LambdaContext -): +def handle_s3_eventbridge_object_restore_completed(event: S3EventNotificationEventBridgeModel, _: LambdaContext): """ Tests that the `S3EventNotificationEventBridgeModel` parses events from https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html From a50116c1f7dad3ae5aa9b5a4594a4a279147c079 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Tue, 14 Mar 2023 09:14:56 +0100 Subject: [PATCH 11/12] chore: begin functional to unit test migration --- tests/functional/parser/test_s3.py | 148 +---------------------------- tests/unit/parser/__init__.py | 0 tests/unit/parser/test_s3.py | 107 +++++++++++++++++++++ 3 files changed, 108 insertions(+), 147 deletions(-) create mode 100644 tests/unit/parser/__init__.py create mode 100644 tests/unit/parser/test_s3.py diff --git a/tests/functional/parser/test_s3.py b/tests/functional/parser/test_s3.py index 4ac7decd627..cd903f3052c 100644 --- a/tests/functional/parser/test_s3.py +++ b/tests/functional/parser/test_s3.py @@ -1,13 +1,7 @@ -from datetime import datetime - import pytest from aws_lambda_powertools.utilities.parser import ValidationError, event_parser, parse -from aws_lambda_powertools.utilities.parser.models import ( - S3EventNotificationEventBridgeModel, - S3Model, - S3RecordModel, -) +from aws_lambda_powertools.utilities.parser.models import S3Model, S3RecordModel from aws_lambda_powertools.utilities.typing import LambdaContext from tests.functional.utils import load_event @@ -117,26 +111,6 @@ def test_s3_none_etag_value_failed_validation(): parse(event=event_dict, model=S3Model) -def test_s3_eventbridge_notification_object_created_event(): - event_dict = load_event("s3EventBridgeNotificationObjectCreatedEvent.json") - handle_s3_eventbridge_object_created(event_dict, LambdaContext()) - - -def test_s3_eventbridge_notification_object_deleted_event(): - event_dict = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") - handle_s3_eventbridge_object_deleted(event_dict, LambdaContext()) - - -def test_s3_eventbridge_notification_object_expired_event(): - event_dict = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") - handle_s3_eventbridge_object_expired(event_dict, LambdaContext()) - - -def test_s3_eventbridge_notification_object_restore_completed_event(): - event_dict = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") - handle_s3_eventbridge_object_restore_completed(event_dict, LambdaContext()) - - @event_parser(model=S3Model) def handle_s3_delete_object(event: S3Model, _: LambdaContext): records = list(event.Records) @@ -175,123 +149,3 @@ def handle_s3_delete_object(event: S3Model, _: LambdaContext): def test_s3_trigger_event_delete_object(): event_dict = load_event("s3EventDeleteObject.json") handle_s3_delete_object(event_dict, LambdaContext()) - - -@event_parser(model=S3EventNotificationEventBridgeModel) -def handle_s3_eventbridge_object_created(event: S3EventNotificationEventBridgeModel, _: LambdaContext): - """ - Tests that the `S3EventNotificationEventBridgeModel` parses events from - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html - """ - - raw_event = load_event("s3EventBridgeNotificationObjectCreatedEvent.json") - - assert event.version == raw_event["version"] - assert event.id == raw_event["id"] - assert event.detail_type == raw_event["detail-type"] - assert event.source == raw_event["source"] - assert event.account == raw_event["account"] - assert event.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) - assert event.region == raw_event["region"] - assert event.resources == raw_event["resources"] - - assert event.detail.version == raw_event["detail"]["version"] - assert event.detail.bucket.name == raw_event["detail"]["bucket"]["name"] - assert event.detail.object.key == raw_event["detail"]["object"]["key"] - assert event.detail.object.size == raw_event["detail"]["object"]["size"] - assert event.detail.object.etag == raw_event["detail"]["object"]["etag"] - assert event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] - assert event.detail.request_id == raw_event["detail"]["request-id"] - assert event.detail.requester == raw_event["detail"]["requester"] - assert event.detail.source_ip_address == raw_event["detail"]["source-ip-address"] - assert event.detail.reason == raw_event["detail"]["reason"] - - -@event_parser(model=S3EventNotificationEventBridgeModel) -def handle_s3_eventbridge_object_deleted(event: S3EventNotificationEventBridgeModel, _: LambdaContext): - """ - Tests that the `S3EventNotificationEventBridgeModel` parses events from - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html - """ - raw_event = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") - - assert event.version == raw_event["version"] - assert event.id == raw_event["id"] - assert event.detail_type == raw_event["detail-type"] - assert event.source == raw_event["source"] - assert event.account == raw_event["account"] - assert event.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) - assert event.region == raw_event["region"] - assert event.resources == raw_event["resources"] - - assert event.detail.version == raw_event["detail"]["version"] - assert event.detail.bucket.name == raw_event["detail"]["bucket"]["name"] - assert event.detail.object.key == raw_event["detail"]["object"]["key"] - assert event.detail.object.size == raw_event["detail"]["object"]["size"] - assert event.detail.object.etag == raw_event["detail"]["object"]["etag"] - assert event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] - assert event.detail.request_id == raw_event["detail"]["request-id"] - assert event.detail.requester == raw_event["detail"]["requester"] - assert event.detail.source_ip_address == raw_event["detail"]["source-ip-address"] - assert event.detail.reason == raw_event["detail"]["reason"] - assert event.detail.deletion_type == raw_event["detail"]["deletion-type"] - - -@event_parser(model=S3EventNotificationEventBridgeModel) -def handle_s3_eventbridge_object_expired(event: S3EventNotificationEventBridgeModel, _: LambdaContext): - """ - Tests that the `S3EventNotificationEventBridgeModel` parses events from - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html - """ - - raw_event = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") - - assert event.version == raw_event["version"] - assert event.id == raw_event["id"] - assert event.detail_type == raw_event["detail-type"] - assert event.source == raw_event["source"] - assert event.account == raw_event["account"] - assert event.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) - assert event.region == raw_event["region"] - assert event.resources == raw_event["resources"] - - assert event.detail.version == raw_event["detail"]["version"] - assert event.detail.bucket.name == raw_event["detail"]["bucket"]["name"] - assert event.detail.object.key == raw_event["detail"]["object"]["key"] - assert event.detail.object.size == raw_event["detail"]["object"]["size"] - assert event.detail.object.etag == raw_event["detail"]["object"]["etag"] - assert event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] - assert event.detail.request_id == raw_event["detail"]["request-id"] - assert event.detail.requester == raw_event["detail"]["requester"] - assert event.detail.reason == raw_event["detail"]["reason"] - assert event.detail.deletion_type == raw_event["detail"]["deletion-type"] - - -@event_parser(model=S3EventNotificationEventBridgeModel) -def handle_s3_eventbridge_object_restore_completed(event: S3EventNotificationEventBridgeModel, _: LambdaContext): - """ - Tests that the `S3EventNotificationEventBridgeModel` parses events from - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html - """ - - raw_event = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") - - assert event.version == raw_event["version"] - assert event.id == raw_event["id"] - assert event.detail_type == raw_event["detail-type"] - assert event.source == raw_event["source"] - assert event.account == raw_event["account"] - assert event.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) - assert event.region == raw_event["region"] - assert event.resources == raw_event["resources"] - - assert event.detail.version == raw_event["detail"]["version"] - assert event.detail.bucket.name == raw_event["detail"]["bucket"]["name"] - assert event.detail.object.key == raw_event["detail"]["object"]["key"] - assert event.detail.object.size == raw_event["detail"]["object"]["size"] - assert event.detail.object.etag == raw_event["detail"]["object"]["etag"] - assert event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] - assert event.detail.request_id == raw_event["detail"]["request-id"] - assert event.detail.requester == raw_event["detail"]["requester"] - assert event.detail.restore_expiry_time == raw_event["detail"]["restore-expiry-time"] - assert event.detail.source_storage_class == raw_event["detail"]["source-storage-class"] diff --git a/tests/unit/parser/__init__.py b/tests/unit/parser/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/unit/parser/test_s3.py b/tests/unit/parser/test_s3.py new file mode 100644 index 00000000000..6d11ba8b9fd --- /dev/null +++ b/tests/unit/parser/test_s3.py @@ -0,0 +1,107 @@ +from datetime import datetime + +from aws_lambda_powertools.utilities.parser.models import ( + S3EventNotificationEventBridgeModel, +) +from tests.functional.utils import load_event + + +def test_s3_eventbridge_notification_object_created_event(): + raw_event = load_event("s3EventBridgeNotificationObjectCreatedEvent.json") + model = S3EventNotificationEventBridgeModel(**raw_event) + + assert model.version == raw_event["version"] + assert model.id == raw_event["id"] + assert model.detail_type == raw_event["detail-type"] + assert model.source == raw_event["source"] + assert model.account == raw_event["account"] + assert model.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) + assert model.region == raw_event["region"] + assert model.resources == raw_event["resources"] + + assert model.detail.version == raw_event["detail"]["version"] + assert model.detail.bucket.name == raw_event["detail"]["bucket"]["name"] + assert model.detail.object.key == raw_event["detail"]["object"]["key"] + assert model.detail.object.size == raw_event["detail"]["object"]["size"] + assert model.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert model.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] + assert model.detail.request_id == raw_event["detail"]["request-id"] + assert model.detail.requester == raw_event["detail"]["requester"] + assert model.detail.source_ip_address == raw_event["detail"]["source-ip-address"] + assert model.detail.reason == raw_event["detail"]["reason"] + + +def test_s3_eventbridge_notification_object_deleted_event(): + raw_event = load_event("s3EventBridgeNotificationObjectDeletedEvent.json") + model = S3EventNotificationEventBridgeModel(**raw_event) + + assert model.version == raw_event["version"] + assert model.id == raw_event["id"] + assert model.detail_type == raw_event["detail-type"] + assert model.source == raw_event["source"] + assert model.account == raw_event["account"] + assert model.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) + assert model.region == raw_event["region"] + assert model.resources == raw_event["resources"] + + assert model.detail.version == raw_event["detail"]["version"] + assert model.detail.bucket.name == raw_event["detail"]["bucket"]["name"] + assert model.detail.object.key == raw_event["detail"]["object"]["key"] + assert model.detail.object.size == raw_event["detail"]["object"]["size"] + assert model.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert model.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] + assert model.detail.request_id == raw_event["detail"]["request-id"] + assert model.detail.requester == raw_event["detail"]["requester"] + assert model.detail.source_ip_address == raw_event["detail"]["source-ip-address"] + assert model.detail.reason == raw_event["detail"]["reason"] + assert model.detail.deletion_type == raw_event["detail"]["deletion-type"] + + +def test_s3_eventbridge_notification_object_expired_event(): + raw_event = load_event("s3EventBridgeNotificationObjectExpiredEvent.json") + model = S3EventNotificationEventBridgeModel(**raw_event) + + assert model.version == raw_event["version"] + assert model.id == raw_event["id"] + assert model.detail_type == raw_event["detail-type"] + assert model.source == raw_event["source"] + assert model.account == raw_event["account"] + assert model.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) + assert model.region == raw_event["region"] + assert model.resources == raw_event["resources"] + + assert model.detail.version == raw_event["detail"]["version"] + assert model.detail.bucket.name == raw_event["detail"]["bucket"]["name"] + assert model.detail.object.key == raw_event["detail"]["object"]["key"] + assert model.detail.object.size == raw_event["detail"]["object"]["size"] + assert model.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert model.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] + assert model.detail.request_id == raw_event["detail"]["request-id"] + assert model.detail.requester == raw_event["detail"]["requester"] + assert model.detail.reason == raw_event["detail"]["reason"] + assert model.detail.deletion_type == raw_event["detail"]["deletion-type"] + + +def test_s3_eventbridge_notification_object_restore_completed_event(): + raw_event = load_event("s3EventBridgeNotificationObjectRestoreCompletedEvent.json") + model = S3EventNotificationEventBridgeModel(**raw_event) + + assert model.version == raw_event["version"] + assert model.id == raw_event["id"] + assert model.detail_type == raw_event["detail-type"] + assert model.source == raw_event["source"] + assert model.account == raw_event["account"] + assert model.time == datetime.fromisoformat(raw_event["time"].replace("Z", "+00:00")) + assert model.region == raw_event["region"] + assert model.resources == raw_event["resources"] + + assert model.detail.version == raw_event["detail"]["version"] + assert model.detail.bucket.name == raw_event["detail"]["bucket"]["name"] + assert model.detail.object.key == raw_event["detail"]["object"]["key"] + assert model.detail.object.size == raw_event["detail"]["object"]["size"] + assert model.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert model.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] + assert model.detail.request_id == raw_event["detail"]["request-id"] + 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"] From f8d49e0ba6f79af40c680d33e5f102ffb62a9615 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Tue, 14 Mar 2023 09:49:05 +0100 Subject: [PATCH 12/12] fix: allow eventbridge to accept any inherited model --- .../utilities/parser/models/event_bridge.py | 6 ++++-- aws_lambda_powertools/utilities/parser/types.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/event_bridge.py b/aws_lambda_powertools/utilities/parser/models/event_bridge.py index 68359f867bd..eab6c54d12d 100644 --- a/aws_lambda_powertools/utilities/parser/models/event_bridge.py +++ b/aws_lambda_powertools/utilities/parser/models/event_bridge.py @@ -1,8 +1,10 @@ from datetime import datetime -from typing import Any, Dict, List, Optional, Type, Union +from typing import List, Optional from pydantic import BaseModel, Field +from aws_lambda_powertools.utilities.parser.types import RawDictOrModel + class EventBridgeModel(BaseModel): version: str @@ -13,5 +15,5 @@ class EventBridgeModel(BaseModel): region: str resources: List[str] detail_type: str = Field(None, alias="detail-type") - detail: Union[Dict[str, Any], Type[BaseModel]] + detail: RawDictOrModel replay_name: Optional[str] = Field(None, alias="replay-name") diff --git a/aws_lambda_powertools/utilities/parser/types.py b/aws_lambda_powertools/utilities/parser/types.py index 20958bd9c21..e9acceb8963 100644 --- a/aws_lambda_powertools/utilities/parser/types.py +++ b/aws_lambda_powertools/utilities/parser/types.py @@ -1,7 +1,7 @@ """Generics and other shared types used across parser""" import sys -from typing import TypeVar +from typing import Any, Dict, Type, TypeVar, Union from pydantic import BaseModel @@ -14,3 +14,5 @@ Model = TypeVar("Model", bound=BaseModel) EnvelopeModel = TypeVar("EnvelopeModel") EventParserReturnType = TypeVar("EventParserReturnType") +AnyInheritedModel = Union[Type[BaseModel], BaseModel] +RawDictOrModel = Union[Dict[str, Any], AnyInheritedModel]