make /render redirect to /convert/...

#512
pull/521/head
Ryan Barrett 2023-05-24 16:09:44 -07:00
rodzic 6d976854dc
commit a3df7d6d30
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
5 zmienionych plików z 14 dodań i 212 usunięć

2
app.py
Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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'])

Wyświetl plik

@ -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)