kopia lustrzana https://github.com/snarfed/bridgy-fed
rename Protocol.serve => .convert, move Content-Type to class constant
rodzic
c3c3c17c9d
commit
acb1c703a3
|
@ -64,6 +64,7 @@ class ActivityPub(User, Protocol):
|
|||
"""
|
||||
ABBREV = 'ap'
|
||||
LOGO_HTML = '<img src="/static/fediverse_logo.svg">'
|
||||
CONTENT_TYPE = as2.CONTENT_TYPE
|
||||
|
||||
def _pre_put_hook(self):
|
||||
"""Validate id, require URL, don't allow Bridgy Fed domains.
|
||||
|
@ -317,10 +318,16 @@ class ActivityPub(User, Protocol):
|
|||
return False
|
||||
|
||||
@classmethod
|
||||
def serve(cls, obj):
|
||||
"""Serves a :class:`models.Object` as AS2."""
|
||||
return (postprocess_as2(as2.from_as1(obj.as1)),
|
||||
{'Content-Type': as2.CONTENT_TYPE})
|
||||
def convert(cls, obj):
|
||||
"""Convert a :class:`models.Object` to AS2.
|
||||
|
||||
Args:
|
||||
obj (models.Object)
|
||||
|
||||
Returns:
|
||||
dict: AS2 JSON
|
||||
"""
|
||||
return postprocess_as2(as2.from_as1(obj.as1))
|
||||
|
||||
@classmethod
|
||||
def verify_signature(cls, activity):
|
||||
|
|
22
atproto.py
22
atproto.py
|
@ -59,6 +59,7 @@ class ATProto(User, Protocol):
|
|||
ABBREV = 'atproto'
|
||||
LOGO_HTML = '<img src="/static/atproto_logo.png">'
|
||||
PDS_URL = f'https://{ABBREV}{common.SUPERDOMAIN}/'
|
||||
CONTENT_TYPE = 'application/json'
|
||||
|
||||
def _pre_put_hook(self):
|
||||
"""Validate id, require did:plc or non-blocklisted did:web.
|
||||
|
@ -374,16 +375,21 @@ class ATProto(User, Protocol):
|
|||
return True
|
||||
|
||||
@classmethod
|
||||
def serve(cls, obj):
|
||||
"""Serves a :class:`models.Object` as AS2.
|
||||
def convert(cls, obj):
|
||||
"""Converts a :class:`models.Object` to ``app.bsky.*`` lexicon JSON.
|
||||
|
||||
This is minimally implemented to serve ``app.bsky.*`` lexicon data, but
|
||||
BGSes and other clients will generally receive ATProto commits via
|
||||
``com.atproto.sync.subscribeRepos`` subscriptions, not BF-specific
|
||||
``/convert/...`` HTTP requests, so this should never be used in
|
||||
practice.
|
||||
This is implemented, but BGSes and other clients will generally receive
|
||||
ATProto commits via ``com.atproto.sync.subscribeRepos`` subscriptions,
|
||||
not BF-specific ``/convert/...`` HTTP requests, so in practice, this
|
||||
should be used rarely, if ever.
|
||||
|
||||
Args:
|
||||
obj (models.Object)
|
||||
|
||||
Returns:
|
||||
dict: JSON object
|
||||
"""
|
||||
return bluesky.from_as1(obj.as1), {'Content-Type': 'application/json'}
|
||||
return bluesky.from_as1(obj.as1)
|
||||
|
||||
|
||||
# URL route is registered in hub.py
|
||||
|
|
|
@ -102,7 +102,7 @@ def convert(dest, _, src=None):
|
|||
return '', 410
|
||||
|
||||
# convert and serve
|
||||
return dest_cls.serve(obj)
|
||||
return dest_cls.convert(obj), {'Content-Type': dest_cls.CONTENT_TYPE}
|
||||
|
||||
|
||||
@app.get('/render')
|
||||
|
|
15
protocol.py
15
protocol.py
|
@ -61,10 +61,13 @@ class Protocol:
|
|||
OTHER_LABELS (list of str): label aliases
|
||||
ABBREV (str): lower case abbreviation, used in URL paths
|
||||
LOGO_HTML (str): logo emoji or ``<img>`` tag
|
||||
CONTENT_TYPE (str): MIME type of this protocol's native data format,
|
||||
appropriate for the ``Content-Type`` HTTP header.
|
||||
"""
|
||||
ABBREV = None
|
||||
OTHER_LABELS = ()
|
||||
LOGO_HTML = ''
|
||||
CONTENT_TYPE = None
|
||||
|
||||
def __init__(self):
|
||||
assert False
|
||||
|
@ -407,12 +410,11 @@ class Protocol:
|
|||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def serve(cls, obj):
|
||||
"""Returns this protocol's Flask response for a given :class:`Object`.
|
||||
def convert(cls, obj):
|
||||
"""Converts an :class:`Object` to this protocol's data format.
|
||||
|
||||
For example, an HTML string and ``text/html`` for :class:`Web`,
|
||||
or a dict with AS2 JSON and ``application/activity+json`` for
|
||||
:class:`ActivityPub`.
|
||||
For example, an HTML string for :class:`Web`, or a dict with AS2 JSON
|
||||
and ``application/activity+json`` for :class:`ActivityPub`.
|
||||
|
||||
To be implemented by subclasses.
|
||||
|
||||
|
@ -420,8 +422,7 @@ class Protocol:
|
|||
obj (models.Object):
|
||||
|
||||
Returns:
|
||||
(str, dict): (response body, HTTP headers) tuple appropriate to be
|
||||
returned from a Flask handler
|
||||
converted object data
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ def redir(to):
|
|||
return f'Object not found: {to}', 404
|
||||
|
||||
g.user = Web.get_or_create(util.domain_from_link(to), direct=False, obj=obj)
|
||||
ret, _ = ActivityPub.serve(obj)
|
||||
ret = ActivityPub.convert(obj)
|
||||
logger.info(f'Returning: {json_dumps(ret, indent=2)}')
|
||||
return ret, {
|
||||
'Content-Type': accept_type,
|
||||
|
|
|
@ -1975,10 +1975,9 @@ class ActivityPubUtilsTest(TestCase):
|
|||
self.assertIsNone(obj.as1)
|
||||
|
||||
@skip
|
||||
def test_serve(self):
|
||||
def test_convert(self):
|
||||
obj = Object(id='http://orig', as2=LIKE)
|
||||
self.assertEqual((LIKE_WRAPPED, {'Content-Type': 'application/activity+json'}),
|
||||
ActivityPub.serve(obj))
|
||||
self.assertEqual(LIKE_WRAPPED, ActivityPub.convert(obj))
|
||||
|
||||
def test_postprocess_as2_idempotent(self):
|
||||
g.user = self.make_user('foo.com')
|
||||
|
|
|
@ -233,16 +233,14 @@ class ATProtoTest(TestCase):
|
|||
},
|
||||
)
|
||||
|
||||
def test_serve(self):
|
||||
def test_convert(self):
|
||||
obj = self.store_object(id='http://orig', our_as1=ACTOR_AS)
|
||||
expected = trim_nulls({
|
||||
**ACTOR_PROFILE_BSKY,
|
||||
'avatar': None,
|
||||
'banner': None,
|
||||
})
|
||||
self.assertEqual(
|
||||
(expected, {'Content-Type': 'application/json'}),
|
||||
ATProto.serve(obj))
|
||||
self.assertEqual(expected, ATProto.convert(obj))
|
||||
|
||||
@patch('requests.get', return_value=requests_response('', status=404))
|
||||
def test_web_url(self, mock_get):
|
||||
|
|
|
@ -2269,9 +2269,8 @@ class WebUtilTest(TestCase):
|
|||
'target': 'https://user.com/post',
|
||||
}, kwargs['data'])
|
||||
|
||||
def test_serve(self, _, __):
|
||||
def test_convert(self, _, __):
|
||||
obj = Object(id='http://orig', mf2=ACTOR_MF2)
|
||||
html, headers = Web.serve(obj)
|
||||
self.assert_multiline_equals("""\
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
@ -2284,8 +2283,7 @@ class WebUtilTest(TestCase):
|
|||
</span>
|
||||
</body>
|
||||
</html>
|
||||
""", html, ignore_blanks=True)
|
||||
self.assertEqual({'Content-Type': 'text/html; charset=utf-8'}, headers)
|
||||
""", Web.convert(obj), ignore_blanks=True)
|
||||
|
||||
def test_target_for(self, _, __):
|
||||
self.assertIsNone(Web.target_for(Object(id='x', source_protocol='web')))
|
||||
|
|
|
@ -64,6 +64,7 @@ COMMENT = {
|
|||
|
||||
class Fake(User, protocol.Protocol):
|
||||
ABBREV = 'fa'
|
||||
CONTENT_TYPE = 'fa/ke'
|
||||
|
||||
# maps string ids to dict AS1 objects that can be fetched
|
||||
fetchable = {}
|
||||
|
@ -122,10 +123,9 @@ class Fake(User, protocol.Protocol):
|
|||
return False
|
||||
|
||||
@classmethod
|
||||
def serve(cls, obj):
|
||||
logger.info(f'{cls.__name__}.load {obj.key.id()}')
|
||||
return (f'{cls.__name__} object {obj.key.id()}',
|
||||
{'Accept': 'fake/protocol'})
|
||||
def convert(cls, obj):
|
||||
logger.info(f'{cls.__name__}.convert {obj.key.id()}')
|
||||
return obj.as1
|
||||
|
||||
@classmethod
|
||||
def target_for(cls, obj, shared=False):
|
||||
|
|
14
web.py
14
web.py
|
@ -69,6 +69,7 @@ class Web(User, Protocol):
|
|||
ABBREV = 'web'
|
||||
OTHER_LABELS = ('webmention',)
|
||||
LOGO_HTML = '🕸️'
|
||||
CONTENT_TYPE = common.CONTENT_TYPE_HTML
|
||||
|
||||
has_redirects = ndb.BooleanProperty()
|
||||
redirects_error = ndb.TextProperty()
|
||||
|
@ -437,8 +438,15 @@ class Web(User, Protocol):
|
|||
return True
|
||||
|
||||
@classmethod
|
||||
def serve(cls, obj):
|
||||
"""Serves an :class:`Object` as HTML."""
|
||||
def convert(cls, obj):
|
||||
"""Converts a :class:`Object` to HTML.
|
||||
|
||||
Args:
|
||||
obj (models.Object)
|
||||
|
||||
Returns:
|
||||
str:
|
||||
"""
|
||||
obj_as1 = obj.as1
|
||||
|
||||
from_proto = PROTOCOLS.get(obj.source_protocol)
|
||||
|
@ -463,7 +471,7 @@ class Web(User, Protocol):
|
|||
refresh = f'<meta http-equiv="refresh" content="0;url={url}">'
|
||||
html = html.replace(utf8, utf8 + '\n' + refresh)
|
||||
|
||||
return html, {'Content-Type': common.CONTENT_TYPE_HTML}
|
||||
return html
|
||||
|
||||
|
||||
@app.get('/web-site')
|
||||
|
|
Ładowanie…
Reference in New Issue