Skip to content

Commit a21ce5e

Browse files
authored
fix S3 bucket notification configuration with single Event type specified (localstack#419)
1 parent 9e034ba commit a21ce5e

File tree

3 files changed

+30
-5
lines changed

3 files changed

+30
-5
lines changed

localstack/services/s3/s3_listener.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from localstack.constants import DEFAULT_REGION
1414
from localstack.utils import persistence
1515
from localstack.utils.aws import aws_stack
16-
from localstack.utils.common import short_uid, timestamp, TIMESTAMP_FORMAT_MILLIS, to_str, to_bytes
16+
from localstack.utils.common import short_uid, timestamp, TIMESTAMP_FORMAT_MILLIS, to_str, to_bytes, clone
1717
from localstack.utils.analytics import event_publisher
1818
from localstack.services.generic_proxy import ProxyListener
1919

@@ -383,17 +383,21 @@ def forward_request(self, method, path, data, headers):
383383
if method == 'PUT':
384384
parsed = xmltodict.parse(data)
385385
notif_config = parsed.get('NotificationConfiguration')
386+
S3_NOTIFICATIONS.pop(bucket, None)
386387
for dest in ['Queue', 'Topic', 'CloudFunction']:
387388
config = notif_config.get('%sConfiguration' % (dest))
388389
if config:
389-
# TODO: what if we have multiple destinations - would we overwrite the config?
390+
events = config.get('Event')
391+
if isinstance(events, six.string_types):
392+
events = [events]
390393
notification_details = {
391394
'Id': config.get('Id'),
392-
'Event': config.get('Event'),
395+
'Event': events,
393396
dest: config.get(dest),
394397
'Filter': config.get('Filter')
395398
}
396-
S3_NOTIFICATIONS[bucket] = json.loads(json.dumps(notification_details))
399+
# TODO: what if we have multiple destinations - would we overwrite the config?
400+
S3_NOTIFICATIONS[bucket] = clone(notification_details)
397401

398402
# return response for ?notification request
399403
return response

tests/integration/test_notifications.py

+21
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,24 @@ def test_bucket_notifications():
107107

108108
# receive, assert, and delete message from SQS
109109
receive_assert_delete(queue_url, [{'key': test_key2}, {'name': TEST_BUCKET_NAME_WITH_NOTIFICATIONS}], sqs_client)
110+
111+
# delete notification config
112+
s3_client.put_bucket_notification_configuration(
113+
Bucket=TEST_BUCKET_NAME_WITH_NOTIFICATIONS, NotificationConfiguration={})
114+
config = s3_client.get_bucket_notification_configuration(Bucket=TEST_BUCKET_NAME_WITH_NOTIFICATIONS)
115+
assert not config.get('QueueConfigurations')
116+
117+
# put notification config with single event type
118+
event = 's3:ObjectCreated:*'
119+
s3_client.put_bucket_notification_configuration(Bucket=TEST_BUCKET_NAME_WITH_NOTIFICATIONS,
120+
NotificationConfiguration={
121+
'QueueConfigurations': [{
122+
'Id': 'id123456',
123+
'QueueArn': queue_arn,
124+
'Events': [event]
125+
}]
126+
}
127+
)
128+
config = s3_client.get_bucket_notification_configuration(Bucket=TEST_BUCKET_NAME_WITH_NOTIFICATIONS)
129+
config = config['QueueConfigurations'][0]
130+
assert config['Events'] == [event]

tests/integration/test_s3.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ def test_bucket_policy():
3030
)
3131
assert response['ResponseMetadata']['HTTPStatusCode'] == 204
3232

33-
# retrieve and check notification config
33+
# retrieve and check policy config
3434
saved_policy = s3_client.get_bucket_policy(Bucket=TEST_BUCKET_NAME_WITH_POLICY)['Policy']
3535
assert json.loads(saved_policy) == policy

0 commit comments

Comments
 (0)