Fix #1025 - Make API IDs IRIs
This commit is contained in:
parent
f44bd7dc87
commit
9c602458d8
@ -448,10 +448,16 @@ class ActivityMixin(object):
|
|||||||
return self.content
|
return self.content
|
||||||
|
|
||||||
def serialize(self, request):
|
def serialize(self, request):
|
||||||
|
href = request.urlgen(
|
||||||
|
"mediagoblin.federation.object",
|
||||||
|
object_type=self.object_type,
|
||||||
|
id=self.id,
|
||||||
|
qualified=True
|
||||||
|
)
|
||||||
published = UTC.localize(self.published)
|
published = UTC.localize(self.published)
|
||||||
updated = UTC.localize(self.updated)
|
updated = UTC.localize(self.updated)
|
||||||
obj = {
|
obj = {
|
||||||
"id": self.id,
|
"id": href,
|
||||||
"actor": self.get_actor.serialize(request),
|
"actor": self.get_actor.serialize(request),
|
||||||
"verb": self.verb,
|
"verb": self.verb,
|
||||||
"published": published.isoformat(),
|
"published": published.isoformat(),
|
||||||
|
@ -40,6 +40,7 @@ from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
|
|||||||
ActivityMixin
|
ActivityMixin
|
||||||
from mediagoblin.tools.files import delete_media_files
|
from mediagoblin.tools.files import delete_media_files
|
||||||
from mediagoblin.tools.common import import_component
|
from mediagoblin.tools.common import import_component
|
||||||
|
from mediagoblin.tools.routing import extract_url_arguments
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from pytz import UTC
|
from pytz import UTC
|
||||||
@ -526,11 +527,17 @@ class MediaEntry(Base, MediaEntryMixin):
|
|||||||
|
|
||||||
def serialize(self, request, show_comments=True):
|
def serialize(self, request, show_comments=True):
|
||||||
""" Unserialize MediaEntry to object """
|
""" Unserialize MediaEntry to object """
|
||||||
|
href = request.urlgen(
|
||||||
|
"mediagoblin.federation.object",
|
||||||
|
object_type=self.object_type,
|
||||||
|
id=self.id,
|
||||||
|
qualified=True
|
||||||
|
)
|
||||||
author = self.get_uploader
|
author = self.get_uploader
|
||||||
published = UTC.localize(self.created)
|
published = UTC.localize(self.created)
|
||||||
updated = UTC.localize(self.created)
|
updated = UTC.localize(self.created)
|
||||||
context = {
|
context = {
|
||||||
"id": self.id,
|
"id": href,
|
||||||
"author": author.serialize(request),
|
"author": author.serialize(request),
|
||||||
"objectType": self.object_type,
|
"objectType": self.object_type,
|
||||||
"url": self.url_for_self(request.urlgen, qualified=True),
|
"url": self.url_for_self(request.urlgen, qualified=True),
|
||||||
@ -547,12 +554,7 @@ class MediaEntry(Base, MediaEntryMixin):
|
|||||||
},
|
},
|
||||||
"links": {
|
"links": {
|
||||||
"self": {
|
"self": {
|
||||||
"href": request.urlgen(
|
"href": href,
|
||||||
"mediagoblin.federation.object",
|
|
||||||
object_type=self.object_type,
|
|
||||||
id=self.id,
|
|
||||||
qualified=True
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -755,10 +757,16 @@ class MediaComment(Base, MediaCommentMixin):
|
|||||||
|
|
||||||
def serialize(self, request):
|
def serialize(self, request):
|
||||||
""" Unserialize to python dictionary for API """
|
""" Unserialize to python dictionary for API """
|
||||||
|
href = request.urlgen(
|
||||||
|
"mediagoblin.federation.object",
|
||||||
|
object_type=self.object_type,
|
||||||
|
id=self.id,
|
||||||
|
qualified=True
|
||||||
|
)
|
||||||
media = MediaEntry.query.filter_by(id=self.media_entry).first()
|
media = MediaEntry.query.filter_by(id=self.media_entry).first()
|
||||||
author = self.get_author
|
author = self.get_author
|
||||||
context = {
|
context = {
|
||||||
"id": self.id,
|
"id": href,
|
||||||
"objectType": self.object_type,
|
"objectType": self.object_type,
|
||||||
"content": self.content,
|
"content": self.content,
|
||||||
"inReplyTo": media.serialize(request, show_comments=False),
|
"inReplyTo": media.serialize(request, show_comments=False),
|
||||||
@ -770,7 +778,7 @@ class MediaComment(Base, MediaCommentMixin):
|
|||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def unserialize(self, data):
|
def unserialize(self, data, request):
|
||||||
""" Takes API objects and unserializes on existing comment """
|
""" Takes API objects and unserializes on existing comment """
|
||||||
# Do initial checks to verify the object is correct
|
# Do initial checks to verify the object is correct
|
||||||
required_attributes = ["content", "inReplyTo"]
|
required_attributes = ["content", "inReplyTo"]
|
||||||
@ -784,7 +792,10 @@ class MediaComment(Base, MediaCommentMixin):
|
|||||||
|
|
||||||
# Validate that the ID is correct
|
# Validate that the ID is correct
|
||||||
try:
|
try:
|
||||||
media_id = int(data["inReplyTo"]["id"])
|
media_id = int(extract_url_arguments(
|
||||||
|
url=data["inReplyTo"]["id"],
|
||||||
|
urlmap=request.app.url_map
|
||||||
|
)["id"])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -1214,10 +1225,16 @@ class Generator(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def serialize(self, request):
|
def serialize(self, request):
|
||||||
|
href = request.urlgen(
|
||||||
|
"mediagoblin.federation.object",
|
||||||
|
object_type=self.object_type,
|
||||||
|
id=self.id,
|
||||||
|
qualified=True
|
||||||
|
)
|
||||||
published = UTC.localize(self.published)
|
published = UTC.localize(self.published)
|
||||||
updated = UTC.localize(self.updated)
|
updated = UTC.localize(self.updated)
|
||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": href,
|
||||||
"displayName": self.name,
|
"displayName": self.name,
|
||||||
"published": published.isoformat(),
|
"published": published.isoformat(),
|
||||||
"updated": updated.isoformat(),
|
"updated": updated.isoformat(),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
# GN MediaGoblin -- federated, autonomous media hosting
|
||||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
@ -23,6 +23,7 @@ from werkzeug.datastructures import FileStorage
|
|||||||
from mediagoblin.decorators import oauth_required, require_active_login
|
from mediagoblin.decorators import oauth_required, require_active_login
|
||||||
from mediagoblin.federation.decorators import user_has_privilege
|
from mediagoblin.federation.decorators import user_has_privilege
|
||||||
from mediagoblin.db.models import User, MediaEntry, MediaComment, Activity
|
from mediagoblin.db.models import User, MediaEntry, MediaComment, Activity
|
||||||
|
from mediagoblin.tools.routing import extract_url_arguments
|
||||||
from mediagoblin.tools.response import redirect, json_response, json_error, \
|
from mediagoblin.tools.response import redirect, json_response, json_error, \
|
||||||
render_404, render_to_response
|
render_404, render_to_response
|
||||||
from mediagoblin.meddleware.csrf import csrf_exempt
|
from mediagoblin.meddleware.csrf import csrf_exempt
|
||||||
@ -177,7 +178,7 @@ def feed_endpoint(request):
|
|||||||
)
|
)
|
||||||
|
|
||||||
comment = MediaComment(author=request.user.id)
|
comment = MediaComment(author=request.user.id)
|
||||||
comment.unserialize(data["object"])
|
comment.unserialize(data["object"], request)
|
||||||
comment.save()
|
comment.save()
|
||||||
data = {
|
data = {
|
||||||
"verb": "post",
|
"verb": "post",
|
||||||
@ -187,7 +188,11 @@ def feed_endpoint(request):
|
|||||||
|
|
||||||
elif obj.get("objectType", None) == "image":
|
elif obj.get("objectType", None) == "image":
|
||||||
# Posting an image to the feed
|
# Posting an image to the feed
|
||||||
media_id = int(data["object"]["id"])
|
media_id = int(extract_url_arguments(
|
||||||
|
url=data["object"]["id"],
|
||||||
|
urlmap=request.app.url_map
|
||||||
|
)["id"])
|
||||||
|
|
||||||
media = MediaEntry.query.filter_by(id=media_id).first()
|
media = MediaEntry.query.filter_by(id=media_id).first()
|
||||||
|
|
||||||
if media is None:
|
if media is None:
|
||||||
@ -245,7 +250,10 @@ def feed_endpoint(request):
|
|||||||
if "id" not in obj:
|
if "id" not in obj:
|
||||||
return json_error("Object ID has not been specified.")
|
return json_error("Object ID has not been specified.")
|
||||||
|
|
||||||
obj_id = obj["id"]
|
obj_id = int(extract_url_arguments(
|
||||||
|
url=obj["id"],
|
||||||
|
urlmap=request.app.url_map
|
||||||
|
)["id"])
|
||||||
|
|
||||||
# Now try and find object
|
# Now try and find object
|
||||||
if obj["objectType"] == "comment":
|
if obj["objectType"] == "comment":
|
||||||
@ -374,7 +382,7 @@ def object_endpoint(request):
|
|||||||
""" Lookup for a object type """
|
""" Lookup for a object type """
|
||||||
object_type = request.matchdict["object_type"]
|
object_type = request.matchdict["object_type"]
|
||||||
try:
|
try:
|
||||||
object_id = int(request.matchdict["id"])
|
object_id = request.matchdict["id"]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
error = "Invalid object ID '{0}' for '{1}'".format(
|
error = "Invalid object ID '{0}' for '{1}'".format(
|
||||||
request.matchdict["id"],
|
request.matchdict["id"],
|
||||||
|
@ -26,6 +26,7 @@ from webtest import AppError
|
|||||||
from .resources import GOOD_JPG
|
from .resources import GOOD_JPG
|
||||||
from mediagoblin import mg_globals
|
from mediagoblin import mg_globals
|
||||||
from mediagoblin.db.models import User, MediaEntry, MediaComment
|
from mediagoblin.db.models import User, MediaEntry, MediaComment
|
||||||
|
from mediagoblin.tools.routing import extract_url_arguments
|
||||||
from mediagoblin.tests.tools import fixture_add_user
|
from mediagoblin.tests.tools import fixture_add_user
|
||||||
from mediagoblin.moderation.tools import take_away_privileges
|
from mediagoblin.moderation.tools import take_away_privileges
|
||||||
|
|
||||||
@ -187,7 +188,8 @@ class TestAPI(object):
|
|||||||
# Lets change the image uploader to be self.other_user, this is easier
|
# Lets change the image uploader to be self.other_user, this is easier
|
||||||
# than uploading the image as someone else as the way self.mocked_oauth_required
|
# than uploading the image as someone else as the way self.mocked_oauth_required
|
||||||
# and self._upload_image.
|
# and self._upload_image.
|
||||||
media = MediaEntry.query.filter_by(id=data["object"]["id"]).first()
|
id = int(data["object"]["id"].split("/")[-1])
|
||||||
|
media = MediaEntry.query.filter_by(id=id).first()
|
||||||
media.uploader = self.other_user.id
|
media.uploader = self.other_user.id
|
||||||
media.save()
|
media.save()
|
||||||
|
|
||||||
@ -230,13 +232,14 @@ class TestAPI(object):
|
|||||||
image = json.loads(response.body.decode())["object"]
|
image = json.loads(response.body.decode())["object"]
|
||||||
|
|
||||||
# Check everything has been set on the media correctly
|
# Check everything has been set on the media correctly
|
||||||
media = MediaEntry.query.filter_by(id=image["id"]).first()
|
id = int(image["id"].split("/")[-1])
|
||||||
|
media = MediaEntry.query.filter_by(id=id).first()
|
||||||
assert media.title == title
|
assert media.title == title
|
||||||
assert media.description == description
|
assert media.description == description
|
||||||
assert media.license == license
|
assert media.license == license
|
||||||
|
|
||||||
# Check we're being given back everything we should on an update
|
# Check we're being given back everything we should on an update
|
||||||
assert image["id"] == media.id
|
assert int(image["id"].split("/")[-1]) == media.id
|
||||||
assert image["displayName"] == title
|
assert image["displayName"] == title
|
||||||
assert image["content"] == description
|
assert image["content"] == description
|
||||||
assert image["license"] == license
|
assert image["license"] == license
|
||||||
@ -285,10 +288,10 @@ class TestAPI(object):
|
|||||||
request = test_app.get(object_uri)
|
request = test_app.get(object_uri)
|
||||||
|
|
||||||
image = json.loads(request.body.decode())
|
image = json.loads(request.body.decode())
|
||||||
entry = MediaEntry.query.filter_by(id=image["id"]).first()
|
entry_id = int(image["id"].split("/")[-1])
|
||||||
|
entry = MediaEntry.query.filter_by(id=entry_id).first()
|
||||||
|
|
||||||
assert request.status_code == 200
|
assert request.status_code == 200
|
||||||
assert entry.id == image["id"]
|
|
||||||
|
|
||||||
assert "image" in image
|
assert "image" in image
|
||||||
assert "fullImage" in image
|
assert "fullImage" in image
|
||||||
@ -316,7 +319,8 @@ class TestAPI(object):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Find the objects in the database
|
# Find the objects in the database
|
||||||
media = MediaEntry.query.filter_by(id=data["object"]["id"]).first()
|
media_id = int(data["object"]["id"].split("/")[-1])
|
||||||
|
media = MediaEntry.query.filter_by(id=media_id).first()
|
||||||
comment = media.get_comments()[0]
|
comment = media.get_comments()[0]
|
||||||
|
|
||||||
# Tests that it matches in the database
|
# Tests that it matches in the database
|
||||||
@ -324,7 +328,6 @@ class TestAPI(object):
|
|||||||
assert comment.content == content
|
assert comment.content == content
|
||||||
|
|
||||||
# Test that the response is what we should be given
|
# Test that the response is what we should be given
|
||||||
assert comment.id == comment_data["object"]["id"]
|
|
||||||
assert comment.content == comment_data["object"]["content"]
|
assert comment.content == comment_data["object"]["content"]
|
||||||
|
|
||||||
def test_unable_to_post_comment_as_someone_else(self, test_app):
|
def test_unable_to_post_comment_as_someone_else(self, test_app):
|
||||||
@ -379,7 +382,7 @@ class TestAPI(object):
|
|||||||
response, comment_data = self._activity_to_feed(test_app, activity)
|
response, comment_data = self._activity_to_feed(test_app, activity)
|
||||||
|
|
||||||
# change who uploaded the comment as it's easier than changing
|
# change who uploaded the comment as it's easier than changing
|
||||||
comment_id = comment_data["object"]["id"]
|
comment_id = int(comment_data["object"]["id"].split("/")[-1])
|
||||||
comment = MediaComment.query.filter_by(id=comment_id).first()
|
comment = MediaComment.query.filter_by(id=comment_id).first()
|
||||||
comment.author = self.other_user.id
|
comment.author = self.other_user.id
|
||||||
comment.save()
|
comment.save()
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import urlparse
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from werkzeug.routing import Map, Rule
|
from werkzeug.routing import Map, Rule
|
||||||
@ -65,3 +66,17 @@ def mount(mountpoint, routes):
|
|||||||
for endpoint, url, controller in routes:
|
for endpoint, url, controller in routes:
|
||||||
url = "%s/%s" % (mountpoint.rstrip('/'), url.lstrip('/'))
|
url = "%s/%s" % (mountpoint.rstrip('/'), url.lstrip('/'))
|
||||||
add_route(endpoint, url, controller)
|
add_route(endpoint, url, controller)
|
||||||
|
|
||||||
|
def extract_url_arguments(url, urlmap):
|
||||||
|
"""
|
||||||
|
This extracts the URL arguments from a given URL
|
||||||
|
"""
|
||||||
|
parsed_url = urlparse.urlparse(url)
|
||||||
|
map_adapter = urlmap.bind(
|
||||||
|
server_name=parsed_url.netloc,
|
||||||
|
script_name=parsed_url.path,
|
||||||
|
url_scheme=parsed_url.scheme,
|
||||||
|
path_info=parsed_url.path
|
||||||
|
)
|
||||||
|
|
||||||
|
return map_adapter.match()[1]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user