diff --git a/moto/s3/models.py b/moto/s3/models.py index 14bd40c5c..af52a9b79 100644 --- a/moto/s3/models.py +++ b/moto/s3/models.py @@ -2455,6 +2455,13 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider): key_name=key.name, acl=multipart.acl, ) + + notifications.send_event( + self.account_id, + notifications.S3NotificationEvent.OBJECT_CREATED_COMPLETE_MULTIPART_UPLOAD_EVENT, + bucket, + bucket.keys.get(multipart.key_name), + ) return key def get_all_multiparts(self, bucket_name: str) -> Dict[str, FakeMultipart]: diff --git a/tests/test_s3/test_s3_eventbridge_integration.py b/tests/test_s3/test_s3_eventbridge_integration.py index dda1f2c04..9ccb25cbb 100644 --- a/tests/test_s3/test_s3_eventbridge_integration.py +++ b/tests/test_s3/test_s3_eventbridge_integration.py @@ -9,10 +9,15 @@ import requests from moto import mock_aws, settings from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID +from moto.settings import S3_UPLOAD_PART_MIN_SIZE from tests.test_s3.test_s3_multipart import reduced_min_part_size REGION_NAME = "us-east-1" -REDUCED_PART_SIZE = 256 + +if settings.TEST_DECORATOR_MODE: + REDUCED_PART_SIZE = 256 +else: + REDUCED_PART_SIZE = S3_UPLOAD_PART_MIN_SIZE def _seteup_bucket_notification_eventbridge( @@ -144,6 +149,56 @@ def test_copy_object_notification(): assert event_message["detail"]["reason"] == "ObjectCreated" +@mock_aws +@reduced_min_part_size +def test_complete_multipart_upload_notification(): + resource_names = _seteup_bucket_notification_eventbridge() + bucket_name = resource_names["bucket_name"] + s3_client = boto3.client("s3", region_name=REGION_NAME) + object_key = "testkey" + + part1 = b"0" * REDUCED_PART_SIZE + part2 = b"1" + multipart = s3_client.create_multipart_upload(Bucket=bucket_name, Key=object_key) + up1 = s3_client.upload_part( + Body=BytesIO(part1), + PartNumber=1, + Bucket=bucket_name, + Key=object_key, + UploadId=multipart["UploadId"], + ) + up2 = s3_client.upload_part( + Body=BytesIO(part2), + PartNumber=2, + Bucket=bucket_name, + Key=object_key, + UploadId=multipart["UploadId"], + ) + + s3_client.complete_multipart_upload( + Bucket=bucket_name, + Key=object_key, + MultipartUpload={ + "Parts": [ + {"ETag": up1["ETag"], "PartNumber": 1}, + {"ETag": up2["ETag"], "PartNumber": 2}, + ] + }, + UploadId=multipart["UploadId"], + ) + + events = _get_send_events() + assert len(events) == 2 # [PutObject event, CompleteMultipartUpload event] + event_message = json.loads(events[-1]["message"]) + assert event_message["detail-type"] == "Object Created" + assert event_message["source"] == "aws.s3" + assert event_message["account"] == ACCOUNT_ID + assert event_message["region"] == REGION_NAME + assert event_message["detail"]["bucket"]["name"] == bucket_name + assert event_message["detail"]["object"]["key"] == object_key + assert event_message["detail"]["reason"] == "ObjectCreated" + + @mock_aws def test_delete_object_notification(): resource_names = _seteup_bucket_notification_eventbridge()