From ef1ba45c7e6fa021977a23bfaf90b0a3be3bb44f Mon Sep 17 00:00:00 2001 From: Benjamin Gorman <8076bgorman@gmail.com> Date: Sat, 20 Apr 2024 22:29:11 +0100 Subject: [PATCH 1/5] Make etag Optional on S3EventNotificationObjectModel --- aws_lambda_powertools/utilities/data_classes/s3_event.py | 4 ++-- aws_lambda_powertools/utilities/parser/models/s3.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aws_lambda_powertools/utilities/data_classes/s3_event.py b/aws_lambda_powertools/utilities/data_classes/s3_event.py index 802f1663edb..756c483cdcc 100644 --- a/aws_lambda_powertools/utilities/data_classes/s3_event.py +++ b/aws_lambda_powertools/utilities/data_classes/s3_event.py @@ -37,9 +37,9 @@ def size(self) -> str: return self["size"] @property - def etag(self) -> str: + def etag(self) -> Optional[str]: """Object etag""" - return self["etag"] + return self.get("etag") @property def version_id(self) -> str: diff --git a/aws_lambda_powertools/utilities/parser/models/s3.py b/aws_lambda_powertools/utilities/parser/models/s3.py index db6c41d30f3..63d2f30129f 100644 --- a/aws_lambda_powertools/utilities/parser/models/s3.py +++ b/aws_lambda_powertools/utilities/parser/models/s3.py @@ -61,7 +61,7 @@ class S3Message(BaseModel): class S3EventNotificationObjectModel(BaseModel): key: str size: Optional[NonNegativeFloat] = None - etag: str + etag: Optional[str] version_id: str = Field(None, alias="version-id") sequencer: Optional[str] = None From 68712d52332c9a4f4202737acaadf5252cd6059f Mon Sep 17 00:00:00 2001 From: Benjamin Gorman <8076bgorman@gmail.com> Date: Thu, 25 Apr 2024 12:23:42 +0100 Subject: [PATCH 2/5] Remove etag and size from test event --- tests/events/s3EventBridgeNotificationObjectDeletedEvent.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/events/s3EventBridgeNotificationObjectDeletedEvent.json b/tests/events/s3EventBridgeNotificationObjectDeletedEvent.json index af52ee2fef0..7c0995338a8 100644 --- a/tests/events/s3EventBridgeNotificationObjectDeletedEvent.json +++ b/tests/events/s3EventBridgeNotificationObjectDeletedEvent.json @@ -16,8 +16,6 @@ }, "object": { "key": "IMG_m7fzo3.jpg", - "size": 184662, - "etag": "4e68adba0abe2dc8653dc3354e14c01d", "sequencer": "006408CAD69598B05E" }, "request-id": "0BH729840619AG5K", @@ -26,4 +24,4 @@ "reason": "DeleteObject", "deletion-type": "Delete Marker Created" } -} \ No newline at end of file +} From bfbdcaac0ca4e257bcc60ed66effa4040bbc0cfd Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Tue, 7 May 2024 11:37:31 +0200 Subject: [PATCH 3/5] fix: use getters for optional etag and size; default to sane defaults when possible --- .../utilities/data_classes/s3_event.py | 16 ++++++++-------- .../test_s3_eventbridge_notification.py | 4 ++-- tests/unit/parser/test_s3_notification.py | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/aws_lambda_powertools/utilities/data_classes/s3_event.py b/aws_lambda_powertools/utilities/data_classes/s3_event.py index 756c483cdcc..81fdd5a544a 100644 --- a/aws_lambda_powertools/utilities/data_classes/s3_event.py +++ b/aws_lambda_powertools/utilities/data_classes/s3_event.py @@ -32,14 +32,14 @@ def key(self) -> str: return unquote_plus(self["key"]) @property - def size(self) -> str: - """Object size""" - return self["size"] + def size(self) -> Optional[int]: + """Object size. Object deletion event doesn't contain size.""" + return self.get("size") @property - def etag(self) -> Optional[str]: - """Object etag""" - return self.get("etag") + def etag(self) -> str: + """Object etag. Object deletion event doesn't contain etag; we default to empty string""" + return self.get("etag", "") @property def version_id(self) -> str: @@ -178,8 +178,8 @@ def size(self) -> int: @property def etag(self) -> str: - """object eTag""" - return self["s3"]["object"]["eTag"] + """Object eTag. Object deletion event doesn't contain eTag; we default to empty string""" + return self["s3"]["object"].get("eTag", "") @property def version_id(self) -> Optional[str]: diff --git a/tests/unit/data_classes/test_s3_eventbridge_notification.py b/tests/unit/data_classes/test_s3_eventbridge_notification.py index 391c3fa1788..ae27ad2965f 100644 --- a/tests/unit/data_classes/test_s3_eventbridge_notification.py +++ b/tests/unit/data_classes/test_s3_eventbridge_notification.py @@ -26,10 +26,10 @@ def test_s3_eventbridge_notification_detail_parsed(raw_event: Dict): assert parsed_event.detail.deletion_type == raw_event["detail"].get("deletion-type") assert parsed_event.detail.destination_access_tier == raw_event["detail"].get("destination-access-tier") assert parsed_event.detail.destination_storage_class == raw_event["detail"].get("destination-storage-class") - assert parsed_event.detail.object.etag == raw_event["detail"]["object"]["etag"] + assert parsed_event.detail.object.etag == raw_event["detail"]["object"].get("etag", "") assert parsed_event.detail.object.key == raw_event["detail"]["object"]["key"] assert parsed_event.detail.object.sequencer == raw_event["detail"]["object"]["sequencer"] - assert parsed_event.detail.object.size == raw_event["detail"]["object"]["size"] + assert parsed_event.detail.object.size == raw_event["detail"]["object"].get("size") assert parsed_event.detail.reason == raw_event["detail"].get("reason") assert parsed_event.detail.version == raw_event["detail"].get("version") assert parsed_event.detail.request_id == raw_event["detail"]["request-id"] diff --git a/tests/unit/parser/test_s3_notification.py b/tests/unit/parser/test_s3_notification.py index c77c70095a3..d1bb4458cff 100644 --- a/tests/unit/parser/test_s3_notification.py +++ b/tests/unit/parser/test_s3_notification.py @@ -52,8 +52,8 @@ def test_s3_eventbridge_notification_object_deleted_event(): 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.size == raw_event["detail"]["object"].get("size") + assert model.detail.object.etag == raw_event["detail"]["object"].get("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"] From 4893b2e0f27f01e72a5ffb9487c1287e6262486b Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Tue, 7 May 2024 11:44:47 +0200 Subject: [PATCH 4/5] chore: ignore false positive mypy --- aws_lambda_powertools/utilities/data_classes/s3_event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws_lambda_powertools/utilities/data_classes/s3_event.py b/aws_lambda_powertools/utilities/data_classes/s3_event.py index 81fdd5a544a..1a6f9c541a3 100644 --- a/aws_lambda_powertools/utilities/data_classes/s3_event.py +++ b/aws_lambda_powertools/utilities/data_classes/s3_event.py @@ -39,7 +39,7 @@ def size(self) -> Optional[int]: @property def etag(self) -> str: """Object etag. Object deletion event doesn't contain etag; we default to empty string""" - return self.get("etag", "") + return self.get("etag", "") # type: ignore[return-value] # false positive @property def version_id(self) -> str: From 0fe964da679eb5e7ec743f9ed96ee6a8cd58b0a9 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Tue, 7 May 2024 11:55:33 +0200 Subject: [PATCH 5/5] fix(parser): set default value for etag field --- aws_lambda_powertools/utilities/parser/models/s3.py | 2 +- tests/unit/parser/test_s3_notification.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/s3.py b/aws_lambda_powertools/utilities/parser/models/s3.py index 63d2f30129f..cb5df44e193 100644 --- a/aws_lambda_powertools/utilities/parser/models/s3.py +++ b/aws_lambda_powertools/utilities/parser/models/s3.py @@ -61,7 +61,7 @@ class S3Message(BaseModel): class S3EventNotificationObjectModel(BaseModel): key: str size: Optional[NonNegativeFloat] = None - etag: Optional[str] + etag: str = Field(default="") version_id: str = Field(None, alias="version-id") sequencer: Optional[str] = None diff --git a/tests/unit/parser/test_s3_notification.py b/tests/unit/parser/test_s3_notification.py index d1bb4458cff..ca83851d06c 100644 --- a/tests/unit/parser/test_s3_notification.py +++ b/tests/unit/parser/test_s3_notification.py @@ -53,7 +53,7 @@ def test_s3_eventbridge_notification_object_deleted_event(): 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"].get("size") - assert model.detail.object.etag == raw_event["detail"]["object"].get("etag") + assert model.detail.object.etag == raw_event["detail"]["object"].get("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"]