kopia lustrzana https://github.com/snarfed/bridgy-fed
noop, rename Protocol.get_object => Protocol.load
rodzic
e6be9cabb8
commit
e41ce9216c
|
@ -165,7 +165,7 @@ class ActivityPub(Protocol):
|
||||||
error('Invalid Digest header, required for HTTP Signature', status=401)
|
error('Invalid Digest header, required for HTTP Signature', status=401)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
key_actor = cls.get_object(keyId)
|
key_actor = cls.load(keyId)
|
||||||
except BadGateway:
|
except BadGateway:
|
||||||
obj_id = as1.get_object(activity).get('id')
|
obj_id = as1.get_object(activity).get('id')
|
||||||
if (activity.get('type') == 'Delete' and obj_id and
|
if (activity.get('type') == 'Delete' and obj_id and
|
||||||
|
|
|
@ -159,7 +159,7 @@ class FollowCallback(indieauth.Callback):
|
||||||
return redirect(f'/user/{domain}/following')
|
return redirect(f'/user/{domain}/following')
|
||||||
|
|
||||||
# TODO: make this generic across protocols
|
# TODO: make this generic across protocols
|
||||||
followee = activitypub.ActivityPub.get_object(as2_url).as2
|
followee = activitypub.ActivityPub.load(as2_url).as2
|
||||||
id = followee.get('id')
|
id = followee.get('id')
|
||||||
inbox = followee.get('inbox')
|
inbox = followee.get('inbox')
|
||||||
if not id or not inbox:
|
if not id or not inbox:
|
||||||
|
@ -237,7 +237,7 @@ class UnfollowCallback(indieauth.Callback):
|
||||||
if isinstance(followee, str):
|
if isinstance(followee, str):
|
||||||
# fetch as AS2 to get full followee with inbox
|
# fetch as AS2 to get full followee with inbox
|
||||||
followee_id = followee
|
followee_id = followee
|
||||||
followee = activitypub.ActivityPub.get_object(followee_id).as2
|
followee = activitypub.ActivityPub.load(followee_id).as2
|
||||||
|
|
||||||
inbox = followee.get('inbox')
|
inbox = followee.get('inbox')
|
||||||
if not inbox:
|
if not inbox:
|
||||||
|
|
|
@ -342,13 +342,12 @@ class Object(StringIdModel):
|
||||||
self.labels.remove('activity')
|
self.labels.remove('activity')
|
||||||
|
|
||||||
def _post_put_hook(self, future):
|
def _post_put_hook(self, future):
|
||||||
"""Update :meth:`Protocol.get_object` cache."""
|
"""Update :meth:`Protocol.load` cache."""
|
||||||
# TODO: assert that as1 id is same as key id? in pre put hook?
|
# TODO: assert that as1 id is same as key id? in pre put hook?
|
||||||
logger.info(f'Wrote Object {self.key.id()} {self.type} {self.status or ""} {self.labels} for {len(self.domains)} users')
|
logger.info(f'Wrote Object {self.key.id()} {self.type} {self.status or ""} {self.labels} for {len(self.domains)} users')
|
||||||
if '#' not in self.key.id():
|
if '#' not in self.key.id():
|
||||||
get_object = protocol.Protocol.get_object
|
key = protocol.Protocol.load.cache_key(protocol.Protocol, self.key.id())
|
||||||
key = get_object.cache_key(protocol.Protocol, self.key.id())
|
protocol.Protocol.load.cache[key] = self
|
||||||
get_object.cache[key] = self
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""Clears all data properties."""
|
"""Clears all data properties."""
|
||||||
|
|
|
@ -201,12 +201,12 @@ class Protocol:
|
||||||
|
|
||||||
# fetch actor if necessary so we have name, profile photo, etc
|
# fetch actor if necessary so we have name, profile photo, etc
|
||||||
if actor and actor.keys() == set(['id']):
|
if actor and actor.keys() == set(['id']):
|
||||||
actor = obj.as2['actor'] = cls.get_object(actor['id']).as2
|
actor = obj.as2['actor'] = cls.load(actor['id']).as2
|
||||||
|
|
||||||
# fetch object if necessary so we can render it in feeds
|
# fetch object if necessary so we can render it in feeds
|
||||||
if obj.type == 'share' and inner_obj.keys() == set(['id']):
|
if obj.type == 'share' and inner_obj.keys() == set(['id']):
|
||||||
inner_obj = obj.as2['object'] = as2.from_as1(
|
inner_obj = obj.as2['object'] = as2.from_as1(
|
||||||
cls.get_object(inner_obj_id).as1)
|
cls.load(inner_obj_id).as1)
|
||||||
|
|
||||||
if obj.type == 'follow':
|
if obj.type == 'follow':
|
||||||
cls.accept_follow(obj)
|
cls.accept_follow(obj)
|
||||||
|
@ -360,7 +360,7 @@ class Protocol:
|
||||||
@classmethod
|
@classmethod
|
||||||
@cached(LRUCache(1000), key=lambda cls, id: util.fragmentless(id),
|
@cached(LRUCache(1000), key=lambda cls, id: util.fragmentless(id),
|
||||||
lock=threading.Lock())
|
lock=threading.Lock())
|
||||||
def get_object(cls, id):
|
def load(cls, id):
|
||||||
"""Loads and returns an Object from memory cache, datastore, or HTTP fetch.
|
"""Loads and returns an Object from memory cache, datastore, or HTTP fetch.
|
||||||
|
|
||||||
Assumes id is a URL. Any fragment at the end is stripped before loading.
|
Assumes id is a URL. Any fragment at the end is stripped before loading.
|
||||||
|
|
|
@ -831,7 +831,7 @@ class ActivityPubTest(testutil.TestCase):
|
||||||
_, mock_get, ___):
|
_, mock_get, ___):
|
||||||
# actor with a public key
|
# actor with a public key
|
||||||
self.key_id_obj.key.delete()
|
self.key_id_obj.key.delete()
|
||||||
Protocol.get_object.cache.clear()
|
Protocol.load.cache.clear()
|
||||||
mock_get.return_value = self.as2_resp({
|
mock_get.return_value = self.as2_resp({
|
||||||
**ACTOR,
|
**ACTOR,
|
||||||
'publicKey': {
|
'publicKey': {
|
||||||
|
@ -909,7 +909,7 @@ class ActivityPubTest(testutil.TestCase):
|
||||||
|
|
||||||
def test_delete_actor_not_fetchable(self, _, mock_get, ___):
|
def test_delete_actor_not_fetchable(self, _, mock_get, ___):
|
||||||
self.key_id_obj.key.delete()
|
self.key_id_obj.key.delete()
|
||||||
Protocol.get_object.cache.clear()
|
Protocol.load.cache.clear()
|
||||||
|
|
||||||
mock_get.return_value = requests_response(status=410)
|
mock_get.return_value = requests_response(status=410)
|
||||||
got = self.post('/inbox', json={**DELETE, 'object': 'http://my/key/id'})
|
got = self.post('/inbox', json={**DELETE, 'object': 'http://my/key/id'})
|
||||||
|
@ -919,7 +919,7 @@ class ActivityPubTest(testutil.TestCase):
|
||||||
self.key_id_obj.as2 = None
|
self.key_id_obj.as2 = None
|
||||||
self.key_id_obj.deleted = True
|
self.key_id_obj.deleted = True
|
||||||
self.key_id_obj.put()
|
self.key_id_obj.put()
|
||||||
Protocol.get_object.cache.clear()
|
Protocol.load.cache.clear()
|
||||||
|
|
||||||
got = self.post('/inbox', json={**DELETE, 'object': 'http://my/key/id'})
|
got = self.post('/inbox', json={**DELETE, 'object': 'http://my/key/id'})
|
||||||
self.assertEqual(202, got.status_code)
|
self.assertEqual(202, got.status_code)
|
||||||
|
@ -945,7 +945,7 @@ class ActivityPubTest(testutil.TestCase):
|
||||||
labels=['activity'])
|
labels=['activity'])
|
||||||
|
|
||||||
obj.deleted = True
|
obj.deleted = True
|
||||||
self.assert_entities_equal(obj, Protocol.get_object.cache['http://an/obj'])
|
self.assert_entities_equal(obj, Protocol.load.cache['http://an/obj'])
|
||||||
|
|
||||||
def test_update_note(self, *mocks):
|
def test_update_note(self, *mocks):
|
||||||
Object(id='https://a/note', as2={}).put()
|
Object(id='https://a/note', as2={}).put()
|
||||||
|
@ -970,7 +970,7 @@ class ActivityPubTest(testutil.TestCase):
|
||||||
labels=['activity'])
|
labels=['activity'])
|
||||||
|
|
||||||
self.assert_entities_equal(Object.get_by_id('https://a/note'),
|
self.assert_entities_equal(Object.get_by_id('https://a/note'),
|
||||||
Protocol.get_object.cache['https://a/note'])
|
Protocol.load.cache['https://a/note'])
|
||||||
|
|
||||||
def test_inbox_webmention_discovery_connection_fails(self, mock_head,
|
def test_inbox_webmention_discovery_connection_fails(self, mock_head,
|
||||||
mock_get, mock_post):
|
mock_get, mock_post):
|
||||||
|
@ -1263,14 +1263,14 @@ class ActivityPubUtilsTest(testutil.TestCase):
|
||||||
|
|
||||||
# TODO: make these generic and use FakeProtocol
|
# TODO: make these generic and use FakeProtocol
|
||||||
@patch('requests.get')
|
@patch('requests.get')
|
||||||
def test_get_object_http(self, mock_get):
|
def test_load_http(self, mock_get):
|
||||||
mock_get.return_value = AS2
|
mock_get.return_value = AS2
|
||||||
|
|
||||||
id = 'http://the/id'
|
id = 'http://the/id'
|
||||||
self.assertIsNone(Object.get_by_id(id))
|
self.assertIsNone(Object.get_by_id(id))
|
||||||
|
|
||||||
# first time fetches over HTTP
|
# first time fetches over HTTP
|
||||||
got = ActivityPub.get_object(id)
|
got = ActivityPub.load(id)
|
||||||
self.assert_equals(id, got.key.id())
|
self.assert_equals(id, got.key.id())
|
||||||
self.assert_equals(AS2_OBJ, got.as2)
|
self.assert_equals(AS2_OBJ, got.as2)
|
||||||
mock_get.assert_has_calls([self.as2_req(id)])
|
mock_get.assert_has_calls([self.as2_req(id)])
|
||||||
|
@ -1279,49 +1279,49 @@ class ActivityPubUtilsTest(testutil.TestCase):
|
||||||
got.key.delete()
|
got.key.delete()
|
||||||
mock_get.reset_mock()
|
mock_get.reset_mock()
|
||||||
|
|
||||||
got = ActivityPub.get_object(id)
|
got = ActivityPub.load(id)
|
||||||
self.assert_equals(id, got.key.id())
|
self.assert_equals(id, got.key.id())
|
||||||
self.assert_equals(AS2_OBJ, got.as2)
|
self.assert_equals(AS2_OBJ, got.as2)
|
||||||
mock_get.assert_not_called()
|
mock_get.assert_not_called()
|
||||||
|
|
||||||
@patch('requests.get')
|
@patch('requests.get')
|
||||||
def test_get_object_datastore(self, mock_get):
|
def test_load_datastore(self, mock_get):
|
||||||
id = 'http://the/id'
|
id = 'http://the/id'
|
||||||
stored = Object(id=id, as2=AS2_OBJ)
|
stored = Object(id=id, as2=AS2_OBJ)
|
||||||
stored.put()
|
stored.put()
|
||||||
Protocol.get_object.cache.clear()
|
Protocol.load.cache.clear()
|
||||||
|
|
||||||
# first time loads from datastore
|
# first time loads from datastore
|
||||||
got = ActivityPub.get_object(id)
|
got = ActivityPub.load(id)
|
||||||
self.assert_entities_equal(stored, got)
|
self.assert_entities_equal(stored, got)
|
||||||
mock_get.assert_not_called()
|
mock_get.assert_not_called()
|
||||||
|
|
||||||
# second time is in cache
|
# second time is in cache
|
||||||
stored.key.delete()
|
stored.key.delete()
|
||||||
got = ActivityPub.get_object(id)
|
got = ActivityPub.load(id)
|
||||||
self.assert_entities_equal(stored, got)
|
self.assert_entities_equal(stored, got)
|
||||||
mock_get.assert_not_called()
|
mock_get.assert_not_called()
|
||||||
|
|
||||||
@patch('requests.get')
|
@patch('requests.get')
|
||||||
def test_get_object_strips_fragment(self, mock_get):
|
def test_load_strips_fragment(self, mock_get):
|
||||||
stored = Object(id='http://the/id', as2=AS2_OBJ)
|
stored = Object(id='http://the/id', as2=AS2_OBJ)
|
||||||
stored.put()
|
stored.put()
|
||||||
Protocol.get_object.cache.clear()
|
Protocol.load.cache.clear()
|
||||||
|
|
||||||
got = ActivityPub.get_object('http://the/id#ignore')
|
got = ActivityPub.load('http://the/id#ignore')
|
||||||
self.assert_entities_equal(stored, got)
|
self.assert_entities_equal(stored, got)
|
||||||
mock_get.assert_not_called()
|
mock_get.assert_not_called()
|
||||||
|
|
||||||
@patch('requests.get')
|
@patch('requests.get')
|
||||||
def test_get_object_datastore_no_as2(self, mock_get):
|
def test_load_datastore_no_as2(self, mock_get):
|
||||||
"""If the stored Object has no as2, we should fall back to HTTP."""
|
"""If the stored Object has no as2, we should fall back to HTTP."""
|
||||||
id = 'http://the/id'
|
id = 'http://the/id'
|
||||||
stored = Object(id=id, as2={}, status='in progress')
|
stored = Object(id=id, as2={}, status='in progress')
|
||||||
stored.put()
|
stored.put()
|
||||||
Protocol.get_object.cache.clear()
|
Protocol.load.cache.clear()
|
||||||
|
|
||||||
mock_get.return_value = AS2
|
mock_get.return_value = AS2
|
||||||
got = ActivityPub.get_object(id)
|
got = ActivityPub.load(id)
|
||||||
mock_get.assert_has_calls([self.as2_req(id)])
|
mock_get.assert_has_calls([self.as2_req(id)])
|
||||||
|
|
||||||
self.assert_equals(id, got.key.id())
|
self.assert_equals(id, got.key.id())
|
||||||
|
|
|
@ -295,20 +295,20 @@ class ObjectTest(testutil.TestCase):
|
||||||
'href="/user/user.com"><img src="" class="profile"> Alice</a>',
|
'href="/user/user.com"><img src="" class="profile"> Alice</a>',
|
||||||
obj.actor_link())
|
obj.actor_link())
|
||||||
|
|
||||||
def test_put_updates_get_object_cache(self):
|
def test_put_updates_load_cache(self):
|
||||||
obj = Object(id='x', as2={})
|
obj = Object(id='x', as2={})
|
||||||
obj.put()
|
obj.put()
|
||||||
key = Protocol.get_object.cache_key(Protocol, 'x')
|
key = Protocol.load.cache_key(Protocol, 'x')
|
||||||
self.assert_entities_equal(obj, Protocol.get_object.cache[key])
|
self.assert_entities_equal(obj, Protocol.load.cache[key])
|
||||||
|
|
||||||
def test_put_fragment_id_doesnt_update_get_object_cache(self):
|
def test_put_fragment_id_doesnt_update_load_cache(self):
|
||||||
obj = Object(id='x#y', as2={})
|
obj = Object(id='x#y', as2={})
|
||||||
obj.put()
|
obj.put()
|
||||||
|
|
||||||
self.assertNotIn(Protocol.get_object.cache_key(Protocol, 'x#y'),
|
self.assertNotIn(Protocol.load.cache_key(Protocol, 'x#y'),
|
||||||
Protocol.get_object.cache)
|
Protocol.load.cache)
|
||||||
self.assertNotIn(Protocol.get_object.cache_key(Protocol, 'x'),
|
self.assertNotIn(Protocol.load.cache_key(Protocol, 'x'),
|
||||||
Protocol.get_object.cache)
|
Protocol.load.cache)
|
||||||
|
|
||||||
def test_computed_properties_without_as1(self):
|
def test_computed_properties_without_as1(self):
|
||||||
Object(id='a').put()
|
Object(id='a').put()
|
||||||
|
|
|
@ -59,23 +59,23 @@ class ProtocolTest(testutil.TestCase):
|
||||||
type='comment',
|
type='comment',
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_get_object(self):
|
def test_load(self):
|
||||||
obj = Object(id='foo', our_as1={'x': 'y'})
|
obj = Object(id='foo', our_as1={'x': 'y'})
|
||||||
FakeProtocol.objects = {'foo': obj}
|
FakeProtocol.objects = {'foo': obj}
|
||||||
self.assert_entities_equal(obj, FakeProtocol.get_object('foo'))
|
self.assert_entities_equal(obj, FakeProtocol.load('foo'))
|
||||||
self.assertIsNotNone(Object.get_by_id('foo'))
|
self.assertIsNotNone(Object.get_by_id('foo'))
|
||||||
self.assertEqual(['foo'], FakeProtocol.fetched)
|
self.assertEqual(['foo'], FakeProtocol.fetched)
|
||||||
|
|
||||||
def test_get_object_already_stored(self):
|
def test_load_already_stored(self):
|
||||||
stored = Object(id='foo', our_as1={'x': 'y'})
|
stored = Object(id='foo', our_as1={'x': 'y'})
|
||||||
stored.put()
|
stored.put()
|
||||||
self.assert_entities_equal(stored, FakeProtocol.get_object('foo'))
|
self.assert_entities_equal(stored, FakeProtocol.load('foo'))
|
||||||
self.assertEqual([], FakeProtocol.fetched)
|
self.assertEqual([], FakeProtocol.fetched)
|
||||||
|
|
||||||
@patch('requests.get')
|
@patch('requests.get')
|
||||||
def test_get_object_empty_deleted(self, mock_get):
|
def test_load_empty_deleted(self, mock_get):
|
||||||
stored = Object(id='foo', deleted=True)
|
stored = Object(id='foo', deleted=True)
|
||||||
stored.put()
|
stored.put()
|
||||||
|
|
||||||
self.assert_entities_equal(stored, FakeProtocol.get_object('foo'))
|
self.assert_entities_equal(stored, FakeProtocol.load('foo'))
|
||||||
mock_get.assert_not_called()
|
mock_get.assert_not_called()
|
||||||
|
|
|
@ -71,7 +71,7 @@ class TestCase(unittest.TestCase, testutil.Asserts):
|
||||||
app.testing = True
|
app.testing = True
|
||||||
cache.clear()
|
cache.clear()
|
||||||
protocol.seen_ids.clear()
|
protocol.seen_ids.clear()
|
||||||
protocol.Protocol.get_object.cache.clear()
|
protocol.Protocol.load.cache.clear()
|
||||||
common.webmention_discover.cache.clear()
|
common.webmention_discover.cache.clear()
|
||||||
|
|
||||||
FakeProtocol.objects = {}
|
FakeProtocol.objects = {}
|
||||||
|
|
|
@ -366,7 +366,7 @@ class WebmentionView(View):
|
||||||
# fetch target page as AS2 object
|
# fetch target page as AS2 object
|
||||||
try:
|
try:
|
||||||
# TODO: make this generic across protocols
|
# TODO: make this generic across protocols
|
||||||
target_stored = activitypub.ActivityPub.get_object(target)
|
target_stored = activitypub.ActivityPub.load(target)
|
||||||
target_obj = target_stored.as2 or as2.from_as1(target_stored.as1)
|
target_obj = target_stored.as2 or as2.from_as1(target_stored.as1)
|
||||||
except (requests.HTTPError, BadGateway) as e:
|
except (requests.HTTPError, BadGateway) as e:
|
||||||
resp = getattr(e, 'requests_response', None)
|
resp = getattr(e, 'requests_response', None)
|
||||||
|
@ -392,7 +392,7 @@ class WebmentionView(View):
|
||||||
if not inbox_url:
|
if not inbox_url:
|
||||||
# fetch actor as AS object
|
# fetch actor as AS object
|
||||||
# TODO: make this generic across protocols
|
# TODO: make this generic across protocols
|
||||||
actor_obj = activitypub.ActivityPub.get_object(actor)
|
actor_obj = activitypub.ActivityPub.load(actor)
|
||||||
actor = actor_obj.as2 or as2.from_as1(actor_obj.as1)
|
actor = actor_obj.as2 or as2.from_as1(actor_obj.as1)
|
||||||
inbox_url = actor.get('inbox')
|
inbox_url = actor.get('inbox')
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue