Repository: Kinto/kinto

Issue:
Accessing /buckets/default/collections/ raise a 500
Verified with Kinto 4.1.1 and 4.3.0

```
$ http https://kinto.dev.mozaws.net/v1/buckets/default/collections/ --auth toto:toot
  File "~/venvs/kinto/local/lib/python2.7/site-packages/pyramid/tweens.py", line 22, in excview_tween
    response = handler(request)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/pyramid_tm/__init__.py", line 109, in tm_tween
    reraise(*exc_info)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/pyramid_tm/__init__.py", line 88, in tm_tween
    response = handler(request)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/pyramid/router.py", line 158, in handle_request
    view_name
  File "~/venvs/kinto/local/lib/python2.7/site-packages/pyramid/view.py", line 547, in _call_view
    response = view_callable(context, request)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/pyramid/viewderivers.py", line 413, in viewresult_to_response
    result = view(context, request)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/pyramid/viewderivers.py", line 147, in _requestonly_view
    response = view(request)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/kinto/plugins/default_bucket/__init__.py", line 140, in default_bucket
    create_collection(request, bucket_id)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/kinto/plugins/default_bucket/__init__.py", line 63, in create_collection
    uri=collection_uri)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/kinto/plugins/default_bucket/__init__.py", line 74, in resource_create_object
    resource_name, matchdict = view_lookup(request, uri)
  File "~/venvs/kinto/local/lib/python2.7/site-packages/kinto/core/utils.py", line 415, in view_lookup
    raise ValueError("URI has no route")
ValueError: URI has no route
```



Discussion:
In that case we want a redirection to `/buckets/default/collections`

I thought we already had automatic redirect for Django users :)


Resolving patch:
diff --git a/kinto/plugins/default_bucket/__init__.py b/kinto/plugins/default_bucket/__init__.py
--- a/kinto/plugins/default_bucket/__init__.py
+++ b/kinto/plugins/default_bucket/__init__.py
@@ -39,7 +39,7 @@ def create_bucket(request, bucket_id):
 def create_collection(request, bucket_id):
     # Do nothing if current request does not involve a collection.
     subpath = request.matchdict.get('subpath')
-    if not (subpath and subpath.startswith('collections/')):
+    if not (subpath and subpath.rstrip('/').startswith('collections/')):
         return
 
     collection_id = subpath.split('/')[1]


Test patch:
diff --git a/tests/plugins/test_default_bucket.py b/tests/plugins/test_default_bucket.py
--- a/tests/plugins/test_default_bucket.py
+++ b/tests/plugins/test_default_bucket.py
@@ -221,6 +221,31 @@ def test_formatted_error_are_passed_through(self):
                                  status=400)
             self.assertEqual(resp.body, response.body)
 
+    def test_trailing_slash_redirection_works_for_default_bucket(self):
+        collection_url = '/buckets/default/'
+        resp = self.app.get(collection_url, headers=self.headers, status=307)
+        assert resp.headers['Location'].endswith('/buckets/default')
+
+    def test_trailing_slash_redirection_works_for_collections(self):
+        collection_url = '/buckets/default/collections/'
+        resp = self.app.get(collection_url, headers=self.headers, status=307)
+        assert resp.headers['Location'].endswith('/buckets/default/collections')
+
+    def test_trailing_slash_redirection_works_for_collection(self):
+        collection_url = '/buckets/default/collections/foo/'
+        resp = self.app.get(collection_url, headers=self.headers, status=307)
+        assert resp.headers['Location'].endswith('/buckets/default/collections/foo')
+
+    def test_trailing_slash_redirection_works_for_records(self):
+        records_url = '/buckets/default/collections/foo/records/'
+        resp = self.app.get(records_url, headers=self.headers, status=307)
+        assert resp.headers['Location'].endswith('/buckets/default/collections/foo/records')
+
+    def test_trailing_slash_redirection_works_for_record(self):
+        records_url = '/buckets/default/collections/foo/records/bar/'
+        resp = self.app.get(records_url, headers=self.headers, status=307)
+        assert resp.headers['Location'].endswith('/buckets/default/collections/foo/records/bar')
+
 
 class HelloViewTest(DefaultBucketWebTest):