kopia lustrzana https://github.com/snarfed/bridgy-fed
add Object.replace_copies_with_originals
rodzic
dcfdf35416
commit
96b84511eb
84
models.py
84
models.py
|
@ -1,4 +1,5 @@
|
|||
"""Datastore model classes."""
|
||||
import copy
|
||||
from datetime import timedelta, timezone
|
||||
import itertools
|
||||
import json
|
||||
|
@ -614,26 +615,12 @@ class Object(StringIdModel):
|
|||
|
||||
# populate actor/author if necessary and available
|
||||
type = obj.get('objectType')
|
||||
owner_field = ('actor' if type == 'activity'
|
||||
field = ('actor' if type == 'activity'
|
||||
else 'author' if type not in as1.ACTOR_TYPES
|
||||
else None)
|
||||
if owner_field and owner:
|
||||
# logger.debug(f'Replacing {owner_field} {obj.get(owner_field)}...')
|
||||
|
||||
# load matching user, if any
|
||||
user = User.get_for_copy(owner)
|
||||
if user:
|
||||
if user.obj and user.obj.as1:
|
||||
obj[owner_field] = {
|
||||
**user.obj.as1,
|
||||
'id': user.key.id(),
|
||||
}
|
||||
else:
|
||||
obj[owner_field] = user.key.id()
|
||||
else:
|
||||
obj[owner_field] = owner
|
||||
|
||||
# logger.debug(f' with {obj[owner_field]}')
|
||||
if field and owner:
|
||||
# logger.debug(f'Replacing {field} {obj.get(field)} with {owner}')
|
||||
obj[field] = owner
|
||||
|
||||
return obj
|
||||
|
||||
|
@ -928,6 +915,67 @@ class Object(StringIdModel):
|
|||
{util.ellipsize(name, chars=40)}
|
||||
</a>"""
|
||||
|
||||
def replace_copies_with_originals(self):
|
||||
"""Replaces ids copied from other protocols with their original ids.
|
||||
|
||||
Specifically, replaces these AS1 fields in place:
|
||||
|
||||
* ``actor``
|
||||
* ``author``
|
||||
* ``object``
|
||||
* ``object.actor``
|
||||
* ``object.author``
|
||||
* ``object.id``
|
||||
* ``object.inReplyTo``
|
||||
* ``tags.[objectType=mention].url``
|
||||
|
||||
Looks up values in :attr:`User.copies` and :attr:`Object.copies` and
|
||||
replaces with their key ids.
|
||||
"""
|
||||
if not self.as1 or not self.source_protocol:
|
||||
return
|
||||
|
||||
outer_obj = copy.deepcopy(self.as1)
|
||||
inner_obj = outer_obj['object'] = as1.get_object(outer_obj)
|
||||
fields = 'actor', 'author', 'inReplyTo'
|
||||
mention_tags = [t for t in (as1.get_objects(outer_obj, 'tags')
|
||||
+ as1.get_objects(inner_obj, 'tags'))
|
||||
if t.get('objectType') == 'mention']
|
||||
|
||||
# batch lookup matching users
|
||||
ids = util.trim_nulls(
|
||||
[outer_obj.get(f) for f in fields]
|
||||
+ [inner_obj.get(f) for f in fields]
|
||||
+ [inner_obj.get('id')]
|
||||
+ [m.get('url') for m in mention_tags])
|
||||
origs = (User.get_for_copies(ids)
|
||||
+ Object.query(Object.copies.uri.IN(ids)).fetch())
|
||||
|
||||
replaced = False
|
||||
def replace(obj, field):
|
||||
val = obj.get(field)
|
||||
if val:
|
||||
for orig in origs:
|
||||
target = Target(uri=val, protocol=self.source_protocol)
|
||||
if target in orig.copies:
|
||||
logger.debug(
|
||||
f'Replacing copy {target} with original {orig.key.id()}')
|
||||
obj[field] = orig.key.id()
|
||||
nonlocal replaced
|
||||
replaced = True
|
||||
|
||||
for obj in outer_obj, inner_obj:
|
||||
for field in 'actor', 'author', 'inReplyTo':
|
||||
replace(obj, field)
|
||||
|
||||
replace(inner_obj, 'id')
|
||||
|
||||
for tag in mention_tags:
|
||||
replace(tag, 'url')
|
||||
|
||||
if replaced:
|
||||
self.our_as1 = outer_obj
|
||||
|
||||
|
||||
class Follower(ndb.Model):
|
||||
"""A follower of a Bridgy Fed user."""
|
||||
|
|
|
@ -18,7 +18,7 @@ from oauth_dropins.webutil.testutil import NOW, requests_response
|
|||
from oauth_dropins.webutil import util
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from .testutil import Fake, TestCase
|
||||
from .testutil import Fake, OtherFake, TestCase
|
||||
|
||||
from atproto import ATProto
|
||||
from models import Follower, Object, OBJECT_EXPIRE_AGE, Target, User
|
||||
|
@ -579,31 +579,9 @@ class ObjectTest(TestCase):
|
|||
'object': 'http://example.com/original/post',
|
||||
}
|
||||
|
||||
# no user
|
||||
obj = Object(id='at://did:plc:foo/co.ll/123', bsky=like_bsky)
|
||||
self.assert_equals(like_as1, obj.as1)
|
||||
|
||||
# matching user without Object
|
||||
user = Fake(id='fake:user',
|
||||
copies=[Target(uri='did:plc:foo', protocol='atproto')])
|
||||
user.put()
|
||||
self.assertEqual({
|
||||
**like_as1,
|
||||
'actor': 'fake:user',
|
||||
}, obj.as1)
|
||||
|
||||
# matching user with Object
|
||||
user.obj = self.store_object(id='at://did:plc:foo/profile/self',
|
||||
our_as1={'foo': 'bar'})
|
||||
user.put()
|
||||
self.assertEqual({
|
||||
**like_as1,
|
||||
'actor': {
|
||||
'id': 'fake:user',
|
||||
'foo': 'bar',
|
||||
},
|
||||
}, obj.as1)
|
||||
|
||||
def test_as1_from_mf2_uses_url_as_id(self):
|
||||
obj = Object(mf2={
|
||||
'properties': {
|
||||
|
@ -734,6 +712,83 @@ class ObjectTest(TestCase):
|
|||
'object': {},
|
||||
}, obj.key.get().as2)
|
||||
|
||||
def test_replace_copies_with_originals_empty(self):
|
||||
obj = Object()
|
||||
obj.replace_copies_with_originals()
|
||||
self.assertIsNone(obj.as1)
|
||||
|
||||
def test_replace_copies_with_originals_follow(self):
|
||||
follow = {
|
||||
'id': 'fake:follow',
|
||||
'objectType': 'activity',
|
||||
'verb': 'follow',
|
||||
'actor': 'fake:alice',
|
||||
'object': 'fake:bob',
|
||||
}
|
||||
obj = Object(our_as1=follow, source_protocol='fake')
|
||||
|
||||
# no matching copy users
|
||||
obj.replace_copies_with_originals()
|
||||
self.assert_equals(follow, obj.our_as1)
|
||||
|
||||
# matching copy users
|
||||
self.make_user('other:alice', cls=OtherFake,
|
||||
copies=[Target(uri='fake:alice', protocol='fake')])
|
||||
self.make_user('other:bob', cls=OtherFake,
|
||||
copies=[Target(uri='fake:bob', protocol='fake')])
|
||||
obj.replace_copies_with_originals()
|
||||
self.assert_equals({
|
||||
**follow,
|
||||
'actor': 'other:alice',
|
||||
'object': {'id': 'other:bob'},
|
||||
}, obj.our_as1)
|
||||
|
||||
def test_replace_copies_with_originals_reply(self):
|
||||
reply = {
|
||||
'objectType': 'activity',
|
||||
'verb': 'create',
|
||||
'object': {
|
||||
'id': 'fake:reply',
|
||||
'objectType': 'note',
|
||||
'inReplyTo': 'fake:post',
|
||||
'author': 'fake:alice',
|
||||
'tags': [{
|
||||
'objectType': 'mention',
|
||||
'url': 'fake:bob',
|
||||
}],
|
||||
},
|
||||
}
|
||||
obj = Object(our_as1=reply, source_protocol='fake')
|
||||
|
||||
# no matching copy users or objects
|
||||
obj.replace_copies_with_originals()
|
||||
self.assert_equals(reply, obj.our_as1)
|
||||
|
||||
# matching copies
|
||||
self.make_user('other:alice', cls=OtherFake,
|
||||
copies=[Target(uri='fake:alice', protocol='fake')])
|
||||
self.make_user('other:bob', cls=OtherFake,
|
||||
copies=[Target(uri='fake:bob', protocol='fake')])
|
||||
self.store_object(id='other:post',
|
||||
copies=[Target(uri='fake:post', protocol='fake')])
|
||||
self.store_object(id='other:reply',
|
||||
copies=[Target(uri='fake:reply', protocol='fake')])
|
||||
|
||||
obj.replace_copies_with_originals()
|
||||
self.assert_equals({
|
||||
'objectType': 'activity',
|
||||
'verb': 'create',
|
||||
'object': {
|
||||
'id': 'other:reply',
|
||||
'objectType': 'note',
|
||||
'inReplyTo': 'other:post',
|
||||
'author': 'other:alice',
|
||||
'tags': [{
|
||||
'objectType': 'mention',
|
||||
'url': 'other:bob',
|
||||
}],
|
||||
},
|
||||
}, obj.our_as1)
|
||||
|
||||
class FollowerTest(TestCase):
|
||||
|
||||
|
|
|
@ -1386,6 +1386,36 @@ class ProtocolReceiveTest(TestCase):
|
|||
)
|
||||
self.assertEqual(2, Follower.query().count())
|
||||
|
||||
def test_replace_actor_copies_with_originals(self):
|
||||
Fake.fetchable = {
|
||||
'fake:alice': {},
|
||||
'fake:bob': {},
|
||||
}
|
||||
|
||||
follow = {
|
||||
'id': 'fake:follow',
|
||||
'objectType': 'activity',
|
||||
'verb': 'follow',
|
||||
'actor': 'fake:alice',
|
||||
'object': 'fake:bob',
|
||||
}
|
||||
|
||||
# no matching copy users
|
||||
Fake.receive(Object(id='fake:follow', our_as1=follow, source_protocol='fake'))
|
||||
self.assert_equals(follow, Object.get_by_id('fake:follow').our_as1)
|
||||
|
||||
# matching copy users
|
||||
self.make_user('other:alice', cls=OtherFake,
|
||||
copies=[Target(uri='fake:alice', protocol='fake')])
|
||||
self.make_user('other:bob', cls=OtherFake,
|
||||
copies=[Target(uri='fake:bob', protocol='fake')])
|
||||
Fake.receive(Object(id='fake:follow', our_as1=follow, source_protocol='fake'))
|
||||
self.assert_equals({
|
||||
**follow,
|
||||
'actor': 'other:alice',
|
||||
'object': 'other:bob',
|
||||
}, Object.get_by_id('fake:follow').our_as1)
|
||||
|
||||
def test_receive_task_handler(self):
|
||||
note = {
|
||||
'id': 'fake:post',
|
||||
|
|
Ładowanie…
Reference in New Issue