Using Enum for s3 notification events (#7238)
This commit is contained in:
		
							parent
							
								
									1c3c4953cf
								
							
						
					
					
						commit
						494eac54aa
					
				| @ -2,6 +2,8 @@ from typing import TYPE_CHECKING, Any, Optional, Union | |||||||
| 
 | 
 | ||||||
| from moto.core.exceptions import RESTError | from moto.core.exceptions import RESTError | ||||||
| 
 | 
 | ||||||
|  | from .notifications import S3NotificationEvent | ||||||
|  | 
 | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from moto.s3.models import FakeDeleteMarker |     from moto.s3.models import FakeDeleteMarker | ||||||
| 
 | 
 | ||||||
| @ -281,10 +283,13 @@ class InvalidNotificationDestination(S3ClientError): | |||||||
| class InvalidNotificationEvent(S3ClientError): | class InvalidNotificationEvent(S3ClientError): | ||||||
|     code = 400 |     code = 400 | ||||||
| 
 | 
 | ||||||
|     def __init__(self) -> None: |     def __init__(self, event_name: str) -> None: | ||||||
|         super().__init__( |         super().__init__( | ||||||
|             "InvalidArgument", |             "InvalidArgument", | ||||||
|             "The event is not supported for notifications", |             ( | ||||||
|  |                 f"The event '{event_name}' is not supported for notifications. " | ||||||
|  |                 f"Supported events are as follows: {S3NotificationEvent.events()}" | ||||||
|  |             ), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2088,7 +2088,10 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider): | |||||||
|         bucket.keys.setlist(key_name, keys) |         bucket.keys.setlist(key_name, keys) | ||||||
| 
 | 
 | ||||||
|         notifications.send_event( |         notifications.send_event( | ||||||
|             self.account_id, notifications.S3_OBJECT_CREATE_PUT, bucket, new_key |             self.account_id, | ||||||
|  |             notifications.S3NotificationEvent.OBJECT_CREATED_PUT_EVENT, | ||||||
|  |             bucket, | ||||||
|  |             new_key, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         return new_key |         return new_key | ||||||
| @ -2640,7 +2643,10 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider): | |||||||
| 
 | 
 | ||||||
|         # Send notifications that an object was copied |         # Send notifications that an object was copied | ||||||
|         notifications.send_event( |         notifications.send_event( | ||||||
|             self.account_id, notifications.S3_OBJECT_CREATE_COPY, bucket, new_key |             self.account_id, | ||||||
|  |             notifications.S3NotificationEvent.OBJECT_CREATED_COPY_EVENT, | ||||||
|  |             bucket, | ||||||
|  |             new_key, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def put_bucket_acl(self, bucket_name: str, acl: Optional[FakeAcl]) -> None: |     def put_bucket_acl(self, bucket_name: str, acl: Optional[FakeAcl]) -> None: | ||||||
|  | |||||||
| @ -1,11 +1,53 @@ | |||||||
| import json | import json | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
|  | from enum import Enum | ||||||
| from typing import Any, Dict, List | from typing import Any, Dict, List | ||||||
| 
 | 
 | ||||||
| _EVENT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" | _EVENT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" | ||||||
| 
 | 
 | ||||||
| S3_OBJECT_CREATE_COPY = "s3:ObjectCreated:Copy" | 
 | ||||||
| S3_OBJECT_CREATE_PUT = "s3:ObjectCreated:Put" | class S3NotificationEvent(str, Enum): | ||||||
|  |     REDUCED_REDUNDANCY_LOST_OBJECT_EVENT = "s3:ReducedRedundancyLostObject" | ||||||
|  |     OBJCET_CREATED_EVENT = "s3:ObjectCreated:*" | ||||||
|  |     OBJECT_CREATED_PUT_EVENT = "s3:ObjectCreated:Put" | ||||||
|  |     OBJECT_CREATED_POST_EVENT = "s3:ObjectCreated:Post" | ||||||
|  |     OBJECT_CREATED_COPY_EVENT = "s3:ObjectCreated:Copy" | ||||||
|  |     OBJECT_CREATED_COMPLETE_MULTIPART_UPLOAD_EVENT = ( | ||||||
|  |         "s3:ObjectCreated:CompleteMultipartUpload" | ||||||
|  |     ) | ||||||
|  |     OBJECT_REMOVED_EVENT = "s3:ObjectRemoved:*" | ||||||
|  |     OBJECTREMOVED_DELETE_EVENT = "s3:ObjectRemoved:Delete" | ||||||
|  |     OBJECTREMOVED_DELETE_MARKER_CREATED_EVENT = "s3:ObjectRemoved:DeleteMarkerCreated" | ||||||
|  |     OBJECT_RESTORE_EVENT = "s3:ObjectRestore:*" | ||||||
|  |     OBJECT_RESTORE_POST_EVENT = "s3:ObjectRestore:Post" | ||||||
|  |     OBJECT_RESTORE_COMPLETED_EVENT = "s3:ObjectRestore:Completed" | ||||||
|  |     REPLICATION_EVENT = "s3:Replication:*" | ||||||
|  |     REPLICATION_OPERATION_FAILED_REPLICATION_EVENT = ( | ||||||
|  |         "s3:Replication:OperationFailedReplication" | ||||||
|  |     ) | ||||||
|  |     REPLICATION_OPERATION_NOT_TRACKED_EVENT = "s3:Replication:OperationNotTracked" | ||||||
|  |     REPLICATION_OPERATION_MISSED_THRESHOLD_EVENT = ( | ||||||
|  |         "s3:Replication:OperationMissedThreshold" | ||||||
|  |     ) | ||||||
|  |     REPLICATION_OPERATION_REPLICATED_AFTER_THRESHOLD_EVENT = ( | ||||||
|  |         "s3:Replication:OperationReplicatedAfterThreshold" | ||||||
|  |     ) | ||||||
|  |     OBJECT_RESTORE_DELETE_EVENT = "s3:ObjectRestore:Delete" | ||||||
|  |     LIFECYCLE_TRANSITION_EVENT = "s3:LifecycleTransition" | ||||||
|  |     INTELLIGENT_TIERING_EVENT = "s3:IntelligentTiering" | ||||||
|  |     OBJECT_ACL_EVENT = "s3:ObjectAcl:Put" | ||||||
|  |     LIFECYCLE_EXPIRATION_EVENT = "s3:LifecycleExpiration:*" | ||||||
|  |     LIFECYCLEEXPIRATION_DELETE_EVENT = "s3:LifecycleExpiration:Delete" | ||||||
|  |     LIFECYCLE_EXPIRATION_DELETE_MARKER_CREATED_EVENT = ( | ||||||
|  |         "s3:LifecycleExpiration:DeleteMarkerCreated" | ||||||
|  |     ) | ||||||
|  |     OBJECT_TAGGING_EVENT = "s3:ObjectTagging:*" | ||||||
|  |     OBJECT_TAGGING_PUT_EVENT = "s3:ObjectTagging:Put" | ||||||
|  |     OBJECTTAGGING_DELETE_EVENT = "s3:ObjectTagging:Delete" | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def events(self) -> List[str]: | ||||||
|  |         return sorted([item.value for item in S3NotificationEvent]) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _get_s3_event( | def _get_s3_event( | ||||||
| @ -41,7 +83,9 @@ def _get_region_from_arn(arn: str) -> str: | |||||||
|     return arn.split(":")[3] |     return arn.split(":")[3] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def send_event(account_id: str, event_name: str, bucket: Any, key: Any) -> None: | def send_event( | ||||||
|  |     account_id: str, event_name: S3NotificationEvent, bucket: Any, key: Any | ||||||
|  | ) -> None: | ||||||
|     if bucket.notification_configuration is None: |     if bucket.notification_configuration is None: | ||||||
|         return |         return | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -63,6 +63,7 @@ from .models import ( | |||||||
|     get_canned_acl, |     get_canned_acl, | ||||||
|     s3_backends, |     s3_backends, | ||||||
| ) | ) | ||||||
|  | from .notifications import S3NotificationEvent | ||||||
| from .select_object_content import serialize_select | from .select_object_content import serialize_select | ||||||
| from .utils import ( | from .utils import ( | ||||||
|     ARCHIVE_STORAGE_CLASSES, |     ARCHIVE_STORAGE_CLASSES, | ||||||
| @ -2113,18 +2114,6 @@ class S3Response(BaseResponse): | |||||||
|             ("CloudFunction", "lambda"), |             ("CloudFunction", "lambda"), | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|         event_names = [ |  | ||||||
|             "s3:ReducedRedundancyLostObject", |  | ||||||
|             "s3:ObjectCreated:*", |  | ||||||
|             "s3:ObjectCreated:Put", |  | ||||||
|             "s3:ObjectCreated:Post", |  | ||||||
|             "s3:ObjectCreated:Copy", |  | ||||||
|             "s3:ObjectCreated:CompleteMultipartUpload", |  | ||||||
|             "s3:ObjectRemoved:*", |  | ||||||
|             "s3:ObjectRemoved:Delete", |  | ||||||
|             "s3:ObjectRemoved:DeleteMarkerCreated", |  | ||||||
|         ] |  | ||||||
| 
 |  | ||||||
|         found_notifications = ( |         found_notifications = ( | ||||||
|             0  # Tripwire -- if this is not ever set, then there were no notifications |             0  # Tripwire -- if this is not ever set, then there were no notifications | ||||||
|         ) |         ) | ||||||
| @ -2151,8 +2140,8 @@ class S3Response(BaseResponse): | |||||||
|                         n["Event"] = [n["Event"]] |                         n["Event"] = [n["Event"]] | ||||||
| 
 | 
 | ||||||
|                     for event in n["Event"]: |                     for event in n["Event"]: | ||||||
|                         if event not in event_names: |                         if event not in S3NotificationEvent.events(): | ||||||
|                             raise InvalidNotificationEvent() |                             raise InvalidNotificationEvent(event) | ||||||
| 
 | 
 | ||||||
|                     # Parse out the filters: |                     # Parse out the filters: | ||||||
|                     if n.get("Filter"): |                     if n.get("Filter"): | ||||||
|  | |||||||
| @ -2336,7 +2336,7 @@ def test_put_bucket_notification_errors(): | |||||||
|     assert err.value.response["Error"]["Code"] == "InvalidArgument" |     assert err.value.response["Error"]["Code"] == "InvalidArgument" | ||||||
|     assert ( |     assert ( | ||||||
|         err.value.response["Error"]["Message"] |         err.value.response["Error"]["Message"] | ||||||
|         == "The event is not supported for notifications" |         == "The event 'notarealeventname' is not supported for notifications. Supported events are as follows: ['s3:IntelligentTiering', 's3:LifecycleExpiration:*', 's3:LifecycleExpiration:Delete', 's3:LifecycleExpiration:DeleteMarkerCreated', 's3:LifecycleTransition', 's3:ObjectAcl:Put', 's3:ObjectCreated:*', 's3:ObjectCreated:CompleteMultipartUpload', 's3:ObjectCreated:Copy', 's3:ObjectCreated:Post', 's3:ObjectCreated:Put', 's3:ObjectRemoved:*', 's3:ObjectRemoved:Delete', 's3:ObjectRemoved:DeleteMarkerCreated', 's3:ObjectRestore:*', 's3:ObjectRestore:Completed', 's3:ObjectRestore:Delete', 's3:ObjectRestore:Post', 's3:ObjectTagging:*', 's3:ObjectTagging:Delete', 's3:ObjectTagging:Put', 's3:ReducedRedundancyLostObject', 's3:Replication:*', 's3:Replication:OperationFailedReplication', 's3:Replication:OperationMissedThreshold', 's3:Replication:OperationNotTracked', 's3:Replication:OperationReplicatedAfterThreshold']" | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user