Repository: Kinto/kinto

Issue:
Crash on DELETE /buckets when quota plugin is enabled
Another collateral damage of #945 

A `http DELETE /v1/buckets` when the quota plugin is enabled leads to:

```
  File "/home/mathieu/Code/Mozilla/kinto/.venv/lib/python3.5/site-packages/zope/interface/adapter.py", line 598, in subscribers
    subscription(*objects)
  File "/home/mathieu/Code/Mozilla/kinto/.venv/lib/python3.5/site-packages/pyramid/config/adapters.py", line 130, in subscriber_wrapper
    return derived_subscriber(*arg)
  File "/home/mathieu/Code/Mozilla/kinto/.venv/lib/python3.5/site-packages/pyramid/config/adapters.py", line 103, in derived_subscriber
    return subscriber(arg[0])
  File "/home/mathieu/Code/Mozilla/kinto/kinto/plugins/quotas/listener.py", line 47, in on_resource_changed
    bucket_id = payload['bucket_id']
KeyError: 'bucket_id'

```
Crash on DELETE /buckets when quota plugin is enabled
Another collateral damage of #945 

A `http DELETE /v1/buckets` when the quota plugin is enabled leads to:

```
  File "/home/mathieu/Code/Mozilla/kinto/.venv/lib/python3.5/site-packages/zope/interface/adapter.py", line 598, in subscribers
    subscription(*objects)
  File "/home/mathieu/Code/Mozilla/kinto/.venv/lib/python3.5/site-packages/pyramid/config/adapters.py", line 130, in subscriber_wrapper
    return derived_subscriber(*arg)
  File "/home/mathieu/Code/Mozilla/kinto/.venv/lib/python3.5/site-packages/pyramid/config/adapters.py", line 103, in derived_subscriber
    return subscriber(arg[0])
  File "/home/mathieu/Code/Mozilla/kinto/kinto/plugins/quotas/listener.py", line 47, in on_resource_changed
    bucket_id = payload['bucket_id']
KeyError: 'bucket_id'

```


Discussion:

Resolving patch:
diff --git a/kinto/plugins/quotas/listener.py b/kinto/plugins/quotas/listener.py
--- a/kinto/plugins/quotas/listener.py
+++ b/kinto/plugins/quotas/listener.py
@@ -41,10 +41,15 @@ def on_resource_changed(event):
     payload = event.payload
     action = payload['action']
     resource_name = payload['resource_name']
-    event_uri = payload['uri']
+
+    if action == 'delete' and resource_name == 'bucket':
+        # Deleting a bucket already deletes everything underneath (including
+        # quotas info). See kinto/views/bucket.
+        return
 
     settings = event.request.registry.settings
 
+    event_uri = payload['uri']
     bucket_id = payload['bucket_id']
     bucket_uri = instance_uri(event.request, 'bucket', id=bucket_id)
     collection_id = None
@@ -72,11 +77,6 @@ def on_resource_changed(event):
 
     storage = event.request.registry.storage
 
-    if action == 'delete' and resource_name == 'bucket':
-        # Deleting a bucket already deletes everything underneath (including
-        # quotas info). See kinto/views/bucket.
-        return
-
     targets = []
     for impacted in event.impacted_records:
         target = impacted['new' if action != 'delete' else 'old']


Test patch:
diff --git a/tests/plugins/test_quotas.py b/tests/plugins/test_quotas.py
--- a/tests/plugins/test_quotas.py
+++ b/tests/plugins/test_quotas.py
@@ -129,6 +129,17 @@ def test_tracks_bucket_attributes_update(self):
             "storage_size": storage_size
         })
 
+    def test_delete_all_buckets_destroys_all_quota_entries(self):
+        self.app.put("/buckets/a", headers=self.headers)
+        self.app.put("/buckets/b", headers=self.headers)
+
+        self.app.delete("/buckets", headers=self.headers)
+
+        stored_in_backend, _ = self.storage.get_all(
+            parent_id='/buckets/*',
+            collection_id=QUOTA_RESOURCE_NAME)
+        assert len(stored_in_backend) == 0
+
     def test_bucket_delete_destroys_its_quota_entries(self):
         self.create_bucket()
         self.app.delete(self.bucket_uri, headers=self.headers)