From a14d90c2db5ff96bdd72009a07f1afc0e8ef3595 Mon Sep 17 00:00:00 2001 From: Jessica Tallon Date: Mon, 21 Jul 2014 18:32:47 +0100 Subject: [PATCH] Switch from slug to ID and clean up style to conform to PEP-8 --- mediagoblin/db/models.py | 4 +- mediagoblin/federation/__init__.py | 15 ++++++ mediagoblin/federation/routing.py | 4 +- mediagoblin/federation/views.py | 80 ++++++++++++++++++++---------- mediagoblin/tests/test_api.py | 24 ++++++--- 5 files changed, 88 insertions(+), 39 deletions(-) diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index 8ea16b80..aaceb599 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -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 ), } diff --git a/mediagoblin/federation/__init__.py b/mediagoblin/federation/__init__.py index e69de29b..621845ba 100644 --- a/mediagoblin/federation/__init__.py +++ b/mediagoblin/federation/__init__.py @@ -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 . diff --git a/mediagoblin/federation/routing.py b/mediagoblin/federation/routing.py index c5fa5ce8..2993b388 100644 --- a/mediagoblin/federation/routing.py +++ b/mediagoblin/federation/routing.py @@ -51,12 +51,12 @@ add_route( # object endpoints add_route( "mediagoblin.federation.object", - "/api//", + "/api//", "mediagoblin.federation.views:object" ) add_route( "mediagoblin.federation.object.comments", - "/api///comments", + "/api///comments", "mediagoblin.federation.views:object_comments" ) diff --git a/mediagoblin/federation/views.py b/mediagoblin/federation/views.py index 8db04f3a..86670857 100644 --- a/mediagoblin/federation/views.py +++ b/mediagoblin/federation/views.py @@ -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, diff --git a/mediagoblin/tests/test_api.py b/mediagoblin/tests/test_api.py index 7142ef39..55228edc 100644 --- a/mediagoblin/tests/test_api.py +++ b/mediagoblin/tests/test_api.py @@ -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 -- 2.25.1