kopia lustrzana https://github.com/snarfed/bridgy-fed
rodzic
6d976854dc
commit
a3df7d6d30
2
app.py
2
app.py
|
@ -6,4 +6,4 @@ registered.
|
|||
from flask_app import app
|
||||
|
||||
# import all modules to register their Flask handlers
|
||||
import activitypub, convert, follow, pages, redirect, render, superfeedr, webfinger, webmention, xrpc_actor, xrpc_feed, xrpc_graph
|
||||
import activitypub, convert, follow, pages, redirect, superfeedr, webfinger, webmention, xrpc_actor, xrpc_feed, xrpc_graph
|
||||
|
|
|
@ -73,3 +73,10 @@ def convert(src, dest, _):
|
|||
|
||||
# convert and serve
|
||||
return protocols[dest].serve(obj)
|
||||
|
||||
|
||||
@app.get('/render')
|
||||
def render_redirect():
|
||||
"""Redirect from old /render?id=... endpoint to /convert/..."""
|
||||
id = flask_util.get_required_param('id')
|
||||
return redirect(f'/convert/activitypub/webmention/{id}', code=301)
|
||||
|
|
46
render.py
46
render.py
|
@ -1,46 +0,0 @@
|
|||
"""Renders mf2 proxy pages based on stored Object entities."""
|
||||
import datetime
|
||||
import logging
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from flask import redirect, request
|
||||
from granary import as1, as2, atom, microformats2
|
||||
from oauth_dropins.webutil import flask_util
|
||||
from oauth_dropins.webutil.flask_util import error
|
||||
from oauth_dropins.webutil import util
|
||||
|
||||
import activitypub
|
||||
from flask_app import app, cache
|
||||
import common
|
||||
from models import Object
|
||||
from webmention import Webmention
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@app.get('/render')
|
||||
@flask_util.cached(cache, common.CACHE_TIME)
|
||||
def render():
|
||||
"""Fetches a stored Object and renders it as HTML."""
|
||||
id = flask_util.get_required_param('id')
|
||||
obj = Object.get_by_id(id)
|
||||
if not obj:
|
||||
error(f'No stored object for {id}', status=404)
|
||||
elif not obj.as1:
|
||||
error(f'Stored object for {id} has no AS1', status=404)
|
||||
|
||||
# redirect creates, updates, etc to inner object
|
||||
type = as1.object_type(obj.as1)
|
||||
if type in ('post', 'update', 'delete'):
|
||||
obj_id = as1.get_object(obj.as1).get('id')
|
||||
if obj_id:
|
||||
obj_obj = Object.get_by_id(obj_id)
|
||||
if (obj_obj and obj_obj.as1 and
|
||||
not obj_obj.as1.keys() <= set(['id', 'url', 'objectType'])):
|
||||
logger.info(f'{type} activity, redirecting to Object {obj_id}')
|
||||
return redirect('/render?' + urlencode({'id': obj_id}), code=301)
|
||||
|
||||
if obj.deleted or type == 'delete':
|
||||
return '', 410
|
||||
|
||||
return Webmention.serve(obj)
|
|
@ -206,3 +206,9 @@ A ☕ reply
|
|||
</div>
|
||||
<a class="u-in-reply-to" href="https://fake.com/123"></a>
|
||||
""", resp.get_data(as_text=True), ignore_blanks=True)
|
||||
|
||||
def test_render_endpoint_redirect(self):
|
||||
resp = self.client.get('/render?id=http://foo%3Fbar')
|
||||
self.assertEqual(301, resp.status_code)
|
||||
self.assertEqual(f'/convert/activitypub/webmention/http://foo?bar',
|
||||
resp.headers['Location'])
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
"""Unit tests for render.py."""
|
||||
import copy
|
||||
|
||||
from granary import as2
|
||||
from granary.tests.test_as1 import ACTOR, COMMENT, DELETE_OF_ID, UPDATE
|
||||
|
||||
from flask_app import app
|
||||
import common
|
||||
from models import Object
|
||||
import render
|
||||
from . import testutil
|
||||
|
||||
EXPECTED_HTML = """\
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="0;url=https://fake.com/123456"></head>
|
||||
<body class="">
|
||||
<article class="h-entry">
|
||||
<span class="p-uid">tag:fake.com:123456</span>
|
||||
<time class="dt-published" datetime="2012-12-05T00:58:26+00:00">2012-12-05T00:58:26+00:00</time>
|
||||
<a class="u-url" href="https://fake.com/123456">fake.com/123456</a>
|
||||
<div class="e-content p-name">
|
||||
A ☕ reply
|
||||
</div>
|
||||
<a class="u-in-reply-to" href="https://fake.com/123"></a>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
EXPECTED_AUTHOR_HTML = """\
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="0;url=https://fake.com/123456"></head>
|
||||
<body class="">
|
||||
<article class="h-entry">
|
||||
<span class="p-uid">tag:fake.com:123456</span>
|
||||
<time class="dt-published" datetime="2012-12-05T00:58:26+00:00">2012-12-05T00:58:26+00:00</time>
|
||||
<span class="p-author h-card">
|
||||
<data class="p-uid" value="tag:fake.com:444"></data>
|
||||
<a class="p-name u-url" href="https://plus.google.com/bob">Bob</a>
|
||||
<img class="u-photo" src="https://bob/picture" alt="" />
|
||||
</span>
|
||||
<a class="u-url" href="https://fake.com/123456">fake.com/123456</a>
|
||||
<div class="e-content p-name">
|
||||
A ☕ reply
|
||||
</div>
|
||||
<a class="u-in-reply-to" href="https://fake.com/123"></a>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
class RenderTest(testutil.TestCase):
|
||||
|
||||
def test_render_errors(self):
|
||||
resp = self.client.get(f'/render?id=')
|
||||
self.assertEqual(400, resp.status_code)
|
||||
|
||||
resp = self.client.get(f'/render')
|
||||
self.assertEqual(400, resp.status_code)
|
||||
|
||||
# no Object
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(404, resp.status_code)
|
||||
|
||||
def test_render(self):
|
||||
with self.request_context:
|
||||
Object(id='abc', as2=as2.from_as1(COMMENT)).put()
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assert_multiline_equals(EXPECTED_HTML, resp.get_data(as_text=True), ignore_blanks=True)
|
||||
|
||||
def test_render_with_author(self):
|
||||
with self.request_context:
|
||||
Object(id='abc', as2=as2.from_as1({**COMMENT, 'author': 'def'}),
|
||||
source_protocol='activitypub').put()
|
||||
Object(id='def', as2=as2.from_as1(ACTOR),
|
||||
source_protocol='activitypub').put()
|
||||
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assert_multiline_equals(
|
||||
EXPECTED_AUTHOR_HTML, resp.get_data(as_text=True), ignore_blanks=True)
|
||||
|
||||
def test_render_no_url(self):
|
||||
comment = copy.deepcopy(COMMENT)
|
||||
del comment['url']
|
||||
with self.request_context:
|
||||
Object(id='abc', as2=as2.from_as1(comment)).put()
|
||||
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(200, resp.status_code)
|
||||
expected = EXPECTED_HTML.replace(
|
||||
'\n<meta http-equiv="refresh" content="0;url=https://fake.com/123456">', ''
|
||||
).replace('<a class="u-url" href="https://fake.com/123456">fake.com/123456</a>', '')
|
||||
self.assert_multiline_equals(expected, resp.get_data(as_text=True),
|
||||
ignore_blanks=True)
|
||||
|
||||
def test_render_deleted_object(self):
|
||||
with self.request_context:
|
||||
Object(id='abc', as2={'content': 'foo'}, deleted=True).put()
|
||||
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(410, resp.status_code)
|
||||
|
||||
def test_render_delete_activity(self):
|
||||
with self.request_context:
|
||||
Object(id='abc', as2=as2.from_as1(DELETE_OF_ID)).put()
|
||||
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(410, resp.status_code)
|
||||
|
||||
def test_render_update_inner_obj_exists_redirect(self):
|
||||
with self.request_context:
|
||||
# UPDATE's object field is a full object
|
||||
Object(id='abc', as2=as2.from_as1(UPDATE)).put()
|
||||
Object(id=UPDATE['object']['id'], as2={'content': 'foo'}).put()
|
||||
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(301, resp.status_code)
|
||||
self.assertEqual(f'/render?id=tag%3Afake.com%3A123456',
|
||||
resp.headers['Location'])
|
||||
|
||||
def test_render_delete_inner_obj_exists_redirect(self):
|
||||
with self.request_context:
|
||||
# DELETE_OF_ID's object field is a bare string id
|
||||
Object(id='abc', as2=as2.from_as1(DELETE_OF_ID)).put()
|
||||
Object(id=DELETE_OF_ID['object'], as2={'content': 'foo'}).put()
|
||||
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(301, resp.status_code)
|
||||
self.assertEqual(f'/render?id=tag%3Afake.com%3A123456',
|
||||
resp.headers['Location'])
|
||||
|
||||
def test_render_update_no_inner_obj_serve_as_is(self):
|
||||
with self.request_context:
|
||||
# UPDATE's object field is a full object
|
||||
Object(id='abc', as2=as2.from_as1(UPDATE)).put()
|
||||
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assert_multiline_in("""\
|
||||
<div class="e-content p-name">
|
||||
A ☕ reply
|
||||
</div>
|
||||
<a class="u-in-reply-to" href="https://fake.com/123"></a>
|
||||
""", resp.get_data(as_text=True), ignore_blanks=True)
|
||||
|
||||
def test_render_update_inner_obj_too_minimal_serve_as_is(self):
|
||||
with self.request_context:
|
||||
# UPDATE's object field is a full object
|
||||
Object(id='abc', as2=as2.from_as1(UPDATE)).put()
|
||||
Object(id=UPDATE['object']['id'], as2={'id': 'foo'}).put()
|
||||
|
||||
resp = self.client.get('/render?id=abc')
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assert_multiline_in("""\
|
||||
<div class="e-content p-name">
|
||||
A ☕ reply
|
||||
</div>
|
||||
<a class="u-in-reply-to" href="https://fake.com/123"></a>
|
||||
""", resp.get_data(as_text=True), ignore_blanks=True)
|
||||
|
Ładowanie…
Reference in New Issue