Switch from slug to ID and clean up style to conform to PEP-8
authorJessica Tallon <jessica@megworld.co.uk>
Mon, 21 Jul 2014 17:32:47 +0000 (18:32 +0100)
committerJessica Tallon <jessica@megworld.co.uk>
Tue, 22 Jul 2014 22:13:17 +0000 (23:13 +0100)
mediagoblin/db/models.py
mediagoblin/federation/__init__.py
mediagoblin/federation/routing.py
mediagoblin/federation/views.py
mediagoblin/tests/test_api.py

index 8ea16b8081745fced2822365f5c7b8ba4aad2a50..aaceb5994d19a951da8fb5fbf4f8144c5c1aafe4 100644 (file)
@@ -466,7 +466,7 @@ class MediaEntry(Base, MediaEntryMixin):
                     "href": request.urlgen(
                         "mediagoblin.federation.object",
                         objectType=self.objectType,
-                        slug=self.slug,
+                        id=self.id,
                         qualified=True
                     ),
                 },
@@ -492,7 +492,7 @@ class MediaEntry(Base, MediaEntryMixin):
                 "url": request.urlgen(
                         "mediagoblin.federation.object.comments",
                         objectType=self.objectType,
-                        uuid=self.slug,
+                        id=self.id,
                         qualified=True
                         ),
             }
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..621845bae0b12696d5a3cd09ca53cc22b4bfec22 100644 (file)
@@ -0,0 +1,15 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors.  See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
index c5fa5ce82c87c650042d5c76fd13d081ebcbd1ab..2993b3883a71187773c06388cc6060bb428aa1b9 100644 (file)
@@ -51,12 +51,12 @@ add_route(
 # object endpoints
 add_route(
     "mediagoblin.federation.object",
-    "/api/<string:objectType>/<string:slug>",
+    "/api/<string:objectType>/<string:id>",
     "mediagoblin.federation.views:object"
     )
 add_route(
     "mediagoblin.federation.object.comments",
-    "/api/<string:objectType>/<string:uuid>/comments",
+    "/api/<string:objectType>/<string:id>/comments",
     "mediagoblin.federation.views:object_comments"
 )
 
index 8db04f3aad4aa2d9eeceba96b2131af48549917b..86670857d5956bb8c991cf5c39831522c64ca0ce 100644 (file)
@@ -75,7 +75,10 @@ def uploads(request):
     request.user = requested_user[0]
     if request.method == "POST":
         # Wrap the data in the werkzeug file wrapper
-        mimetype = request.headers.get("Content-Type", "application/octal-stream")
+        if "Content-Type" not in request.headers:
+            error = "Must supply 'Content-Type' header to upload media."
+            return json_response({"error": error}, status=400)
+        mimetype = request.headers["Content-Type"]
         filename = mimetypes.guess_all_extensions(mimetype)
         filename = 'unknown' + filename[0] if filename else filename
         file_data = FileStorage(
@@ -136,8 +139,8 @@ def feed(request):
 
             media = media.first()
             if not media.unserialize(data["object"]):
-                error = {"error": "Invalid 'image' with id '{0}'".format(obj_id)}
-                return json_response(error, status=400)
+                error = "Invalid 'image' with id '{0}'".format(media_id)
+                return json_response({"error": error}, status=400)
             media.save()
             media.media_manager.api_add_to_feed(request, media)
 
@@ -181,13 +184,13 @@ def feed(request):
         if obj["objectType"] == "comment":
             comment = MediaComment.query.filter_by(id=obj_id)
             if comment is None:
-                error = {"error": "No such 'comment' with id '{0}'.".format(obj_id)}
-                return json_response(error, status=400)
+                error = "No such 'comment' with id '{0}'.".format(obj_id)
+                return json_response({"error": error}, status=400)
 
             comment = comment[0]
             if not comment.unserialize(data["object"]):
-                error = {"error": "Invalid 'comment' with id '{0}'".format(obj_id)}
-                return json_response(error, status=400)
+                error = "Invalid 'comment' with id '{0}'".format(obj_id)
+                return json_response({"error": error}, status=400)
 
             comment.save()
 
@@ -200,13 +203,13 @@ def feed(request):
         elif obj["objectType"] == "image":
             image = MediaEntry.query.filter_by(id=obj_id)
             if image is None:
-                error = {"error": "No such 'image' with the id '{0}'.".format(obj_id)}
-                return json_response(error, status=400)
+                error = "No such 'image' with the id '{0}'.".format(obj_id)
+                return json_response({"error": error}, status=400)
 
             image = image[0]
             if not image.unserialize(obj):
-                error = {"error": "Invalid 'image' with id '{0}'".format(obj_id)}
-                return json_response(error, status=400)
+                "Invalid 'image' with id '{0}'".format(obj_id)
+                return json_response({"error": error}, status=400)
             image.save()
 
             activity = {
@@ -254,16 +257,17 @@ def feed(request):
 
     # Now lookup the user's feed.
     for media in MediaEntry.query.all():
-        feed["items"].append({
+        item = {
             "verb": "post",
             "object": media.serialize(request),
             "actor": request.user.serialize(request),
             "content": "{0} posted a picture".format(request.user.username),
             "id": 1,
-        })
-        feed["items"][-1]["updated"] = feed["items"][-1]["object"]["updated"]
-        feed["items"][-1]["published"] = feed["items"][-1]["object"]["published"]
-        feed["items"][-1]["url"] = feed["items"][-1]["object"]["url"]
+        }
+        item["updated"] = item["object"]["updated"]
+        item["published"] = item["object"]["published"]
+        item["url"] = item["object"]["url"]
+        feed["items"].append(item)
     feed["totalItems"] = len(feed["items"])
 
     return json_response(feed)
@@ -272,16 +276,27 @@ def feed(request):
 def object(request, raw_obj=False):
     """ Lookup for a object type """
     object_type = request.matchdict["objectType"]
-    slug = request.matchdict["slug"]
+    try:
+        object_id = int(request.matchdict["id"])
+    except ValueError:
+        error = "Invalid object ID '{0}' for '{1}'".format(
+            request.matchdict["id"],
+            object_type
+        )
+        return json_response({"error": error}, status=400)
+
     if object_type not in ["image"]:
         error = "Unknown type: {0}".format(object_type)
         # not sure why this is 404, maybe ask evan. Maybe 400?
         return json_response({"error": error}, status=404)
 
-    media = MediaEntry.query.filter_by(slug=slug).first()
+    media = MediaEntry.query.filter_by(id=object_id).first()
     if media is None:
         # no media found with that uuid
-        error = "Can't find a {0} with ID = {1}".format(object_type, slug)
+        error = "Can't find '{0}' with ID '{1}'".format(
+            object_type,
+            object_id
+        )
         return json_response({"error": error}, status=404)
 
     if raw_obj:
@@ -302,7 +317,7 @@ def object_comments(request):
             "url": request.urlgen(
                 "mediagoblin.federation.object.comments",
                 objectType=media.objectType,
-                uuid=media.slug,
+                uuid=media.id,
                 qualified=True
             )
         })
@@ -320,31 +335,42 @@ def object_comments(request):
 # Well known
 ##
 def host_meta(request):
-    """ This is /.well-known/host-meta - provides URL's to resources on server """
+    """ /.well-known/host-meta - provide URLs to resources """
     links = []
 
-    # Client registration links
     links.append({
         "ref": "registration_endpoint",
-        "href": request.urlgen("mediagoblin.oauth.client_register", qualified=True),
+        "href": request.urlgen(
+            "mediagoblin.oauth.client_register",
+            qualified=True
+        ),
     })
     links.append({
         "ref": "http://apinamespace.org/oauth/request_token",
-        "href": request.urlgen("mediagoblin.oauth.request_token", qualified=True),
+        "href": request.urlgen(
+            "mediagoblin.oauth.request_token",
+            qualified=True
+        ),
     })
     links.append({
         "ref": "http://apinamespace.org/oauth/authorize",
-        "href": request.urlgen("mediagoblin.oauth.authorize", qualified=True),
+        "href": request.urlgen(
+            "mediagoblin.oauth.authorize",
+            qualified=True
+        ),
     })
     links.append({
         "ref": "http://apinamespace.org/oauth/access_token",
-        "href": request.urlgen("mediagoblin.oauth.access_token", qualified=True),
+        "href": request.urlgen(
+            "mediagoblin.oauth.access_token",
+            qualified=True
+        ),
     })
 
     return json_response({"links": links})
 
 def whoami(request):
-    """ This is /api/whoami - This is a HTTP redirect to api profile """
+    """ /api/whoami - HTTP redirect to API profile """
     profile = request.urlgen(
         "mediagoblin.federation.user.profile",
         username=request.user.username,
index 7142ef39caa0b1536bc7c03bba9bf98ba2b027b7..55228edc70d684f25ae48f3a47832b0cac2cff4e 100644 (file)
@@ -33,6 +33,7 @@ from mediagoblin.federation.task import collect_garbage
 from mediagoblin.moderation.tools import take_away_privileges
 
 class TestAPI(object):
+    """ Test mediagoblin's pump.io complient APIs """
 
     @pytest.fixture(autouse=True)
     def setup(self, test_app):
@@ -48,7 +49,8 @@ class TestAPI(object):
         else:
             headers = {"Content-Type": "application/json"}
 
-        with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required):
+        with mock.patch("mediagoblin.decorators.oauth_required",
+                        new_callable=self.mocked_oauth_required):
             response = test_app.post(
                 "/api/user/{0}/feed".format(self.user.username),
                 json.dumps(activity),
@@ -66,7 +68,8 @@ class TestAPI(object):
         }
 
 
-        with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required):
+        with mock.patch("mediagoblin.decorators.oauth_required",
+                        new_callable=self.mocked_oauth_required):
             response = test_app.post(
                 "/api/user/{0}/uploads".format(self.user.username),
                 data,
@@ -137,7 +140,8 @@ class TestAPI(object):
 
         activity = {"verb": "update", "object": image}
 
-        with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required):
+        with mock.patch("mediagoblin.decorators.oauth_required",
+                        new_callable=self.mocked_oauth_required):
             response = test_app.post(
                 "/api/user/{0}/feed".format(self.user.username),
                 json.dumps(activity),
@@ -171,9 +175,10 @@ class TestAPI(object):
             "Content-Length": str(len(data)),
         }
 
-        with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required):
+        with mock.patch("mediagoblin.decorators.oauth_required",
+                        new_callable=self.mocked_oauth_required):
             with pytest.raises(AppError) as excinfo:
-                response = test_app.post(
+                test_app.post(
                     "/api/user/{0}/uploads".format(self.user.username),
                     data,
                     headers=headers
@@ -198,7 +203,8 @@ class TestAPI(object):
         object_uri = image["links"]["self"]["href"]
         object_uri = object_uri.replace("http://localhost:80", "")
 
-        with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required):
+        with mock.patch("mediagoblin.decorators.oauth_required",
+                        new_callable=self.mocked_oauth_required):
             request = test_app.get(object_uri)
 
         image = json.loads(request.body)
@@ -247,7 +253,8 @@ class TestAPI(object):
     def test_profile(self, test_app):
         """ Tests profile endpoint """
         uri = "/api/user/{0}/profile".format(self.user.username)
-        with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required):
+        with mock.patch("mediagoblin.decorators.oauth_required",
+                        new_callable=self.mocked_oauth_required):
             response = test_app.get(uri)
             profile = json.loads(response.body)
 
@@ -262,6 +269,7 @@ class TestAPI(object):
         """ Test old media entry are removed by GC task """
         # Create a media entry that's unprocessed and over an hour old.
         entry_id = 72
+        now = datetime.datetime.now(pytz.UTC)
         file_data = FileStorage(
             stream=open(GOOD_JPG, "rb"),
             filename="mah_test.jpg",
@@ -275,7 +283,7 @@ class TestAPI(object):
         entry.title = "Mah Image"
         entry.slug = "slugy-slug-slug"
         entry.media_type = 'image'
-        entry.uploaded = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=2)
+        entry.uploaded = now - datetime.timedelta(days=2)
         entry.save()
 
         # Validate the model exists