From 8796b9edcd34fe54bfedd15a872bb99bdd72572a Mon Sep 17 00:00:00 2001 From: Ryan Barrett Date: Wed, 15 Jan 2025 16:55:33 -0800 Subject: [PATCH] Object.get_or_create: merge repeated fields instead of overwriting --- models.py | 5 +++++ protocol.py | 6 +++++- tests/test_models.py | 16 ++++++++++++++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/models.py b/models.py index c162f5fe..d3f01045 100644 --- a/models.py +++ b/models.py @@ -1132,6 +1132,11 @@ def get_or_create(cls, id, authed_as=None, **props): if set(props.keys()) & set(('as2', 'bsky', 'mf2', 'raw')): obj.clear() + + # merge repeated fields + for field in 'feed', 'copies', 'labels', 'notify', 'users': + getattr(obj, field).extend(props.pop(field, [])) + obj.populate(**{ k: v for k, v in props.items() if v and not isinstance(getattr(Object, k), ndb.ComputedProperty) diff --git a/protocol.py b/protocol.py index ce7d9d55..cbcb1e51 100644 --- a/protocol.py +++ b/protocol.py @@ -933,7 +933,11 @@ def receive(from_cls, obj, authed_as=None, internal=False, received_at=None): error(f'Actor {actor} is opted out or blocked', status=204) # write Object to datastore - orig_props = obj.to_dict() + orig_props = { + **obj.to_dict(), + # structured properties + 'copies': obj.copies, + } obj = Object.get_or_create(id, new=obj.new, changed=obj.changed, authed_as=actor, **orig_props) diff --git a/tests/test_models.py b/tests/test_models.py index 7bac8e0b..fb875a8a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -637,11 +637,23 @@ def check(obj1, obj2): obj7 = Object.get_or_create('http://b.ee/ff', as2={'a': 'b'}, mf2={'c': 'd'}, source_protocol='web') Object.get_or_create('http://b.ee/ff', authed_as='http://b.ee/ff', - users=[ndb.Key(Web, 'me')]) + users=[ndb.Key(Web, 'me')], labels=['feed'], + copies=[Target(protocol='ui', uri='http://foo')]) self.assert_object('http://b.ee/ff', as2={'a': 'b'}, mf2={'c': 'd'}, - users=[ndb.Key(Web, 'me')], + users=[ndb.Key(Web, 'me')], labels=['feed'], + copies=[Target(protocol='ui', uri='http://foo')], source_protocol='web') + # repeated properties should merge, not overwrite + Object.get_or_create('http://b.ee/ff', authed_as='http://b.ee/ff', + users=[ndb.Key(Web, 'you')], labels=['user'], + copies=[Target(protocol='ui', uri='http://bar')]) + self.assert_object('http://b.ee/ff', as2={'a': 'b'}, mf2={'c': 'd'}, + users=[ndb.Key(Web, 'me'), ndb.Key(Web, 'you')], + labels=['feed', 'user'], source_protocol='web', + copies=[Target(protocol='ui', uri='http://foo'), + Target(protocol='ui', uri='http://bar')]) + def test_get_or_create_auth_check(self): Object(id='fake:foo', our_as1={'author': 'fake:alice'}, source_protocol='fake').put()