Repository: deis/deis

Issue:
App endpoints 500 when App no longer exists
Rather than returning a 500, we need to have the views return a 404 if the queryset can't find the application, maybe using `get_object_or_404`.

```
2013-09-19 10:20:08 [7327] [DEBUG] GET /api/apps/jovial-headwind/containers
ERROR Internal Server Error: /api/apps/jovial-headwind/containers
Traceback (most recent call last):
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 115, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 77, in wrapped_view
    return view_func(*args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/views.py", line 327, in dispatch
    response = self.handle_exception(exc)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/views.py", line 324, in dispatch
    response = handler(request, *args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/mixins.py", line 74, in list
    self.object_list = self.filter_queryset(self.get_queryset())
  File "/opt/deis/controller/api/views.py", line 405, in get_queryset
    owner=self.request.user, id=self.kwargs['id'])
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/db/models/manager.py", line 143, in get
    return self.get_query_set().get(*args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/db/models/query.py", line 404, in get
    self.model._meta.object_name)
DoesNotExist: App matching query does not exist.

==> /var/log/deis/access.log <==
63.231.71.227 - - [19/Sep/2013:10:20:08] "GET /api/apps/jovial-headwind/containers HTTP/1.0" 500 - "-" "python-requests/1.2.3 CPython/2.7.2 Darwin/12.4.0"
```



Discussion:
@mboersma tossing this your way since I have a ton of doc work for 0.0.8.  Should be very straightforward.


Resolving patch:
diff --git a/api/views.py b/api/views.py
--- a/api/views.py
+++ b/api/views.py
@@ -330,7 +330,8 @@ def destroy(self, request, **kwargs):
 class BaseAppViewSet(OwnerViewSet):
 
     def get_queryset(self, **kwargs):
-        app = models.App.objects.get(owner=self.request.user, id=self.kwargs['id'])
+        app = get_object_or_404(models.App.objects.filter(
+            owner=self.request.user, id=self.kwargs['id']))
         return self.model.objects.filter(owner=self.request.user, app=app)
 
     def get_object(self, *args, **kwargs):
@@ -401,8 +402,8 @@ class AppContainerViewSet(OwnerViewSet):
     serializer_class = serializers.ContainerSerializer
 
     def get_queryset(self, **kwargs):
-        app = models.App.objects.get(
-            owner=self.request.user, id=self.kwargs['id'])
+        app = get_object_or_404(models.App.objects.filter(
+            owner=self.request.user, id=self.kwargs['id']))
         qs = self.model.objects.filter(owner=self.request.user, app=app)
         container_type = self.kwargs.get('type')
         if container_type:


Test patch:
diff --git a/api/tests/app.py b/api/tests/app.py
--- a/api/tests/app.py
+++ b/api/tests/app.py
@@ -197,6 +197,13 @@ def test_app_errors(self):
         body = {'command': 'ls -al'}
         response = self.client.post(url, json.dumps(body), content_type='application/json')
         self.assertContains(response, 'No nodes available to run command', status_code=400)
+        url = '/api/apps/{app_id}'.format(**locals())
+        response = self.client.delete(url)
+        self.assertEquals(response.status_code, 204)
+        for endpoint in ('containers', 'config', 'releases', 'builds'):
+            url = '/api/apps/{app_id}/{endpoint}'.format(**locals())
+            response = self.client.get(url)
+            self.assertEquals(response.status_code, 404)
 
 
 FAKE_LOG_DATA = """