diff --git a/CHANGELOG.md b/CHANGELOG.md index c91b487..5dad18e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ * Extract Diaspora guid from ActivityPub payloads implementing the Diaspora extension. * Add Diaspora extension and guid to outbound ActivityPub payloads, if available. For profiles, also add handle. + * Add ActivityPub ID to outbound Diaspora payloads of types comment, post and profile, + if an URL given as `id`. ### Changed diff --git a/federation/entities/diaspora/entities.py b/federation/entities/diaspora/entities.py index 28fca4b..8027628 100644 --- a/federation/entities/diaspora/entities.py +++ b/federation/entities/diaspora/entities.py @@ -13,7 +13,7 @@ class DiasporaComment(DiasporaRelayableMixin, Comment): def to_xml(self): element = etree.Element(self._tag_name) - struct_to_xml(element, [ + properties = [ {"guid": self.guid}, {"parent_guid": self.root_target_guid or self.target_guid}, {"thread_parent_guid": self.target_guid}, @@ -22,7 +22,12 @@ class DiasporaComment(DiasporaRelayableMixin, Comment): {"text": self.raw_content}, {"author": self.handle}, {"created_at": format_dt(self.created_at)}, - ]) + ] + if self.id and self.id.startswith("http"): + properties.append({ + "activitypub_id": self.id, + }) + struct_to_xml(element, properties) return element @@ -37,14 +42,19 @@ class DiasporaPost(DiasporaEntityMixin, Post): def to_xml(self): """Convert to XML message.""" element = etree.Element(self._tag_name) - struct_to_xml(element, [ + properties = [ {"text": self.raw_content}, {"guid": self.guid}, {"author": self.handle}, {"public": "true" if self.public else "false"}, {"created_at": format_dt(self.created_at)}, {"provider_display_name": self.provider_display_name}, - ]) + ] + if self.id and self.id.startswith("http"): + properties.append({ + "activitypub_id": self.id, + }) + struct_to_xml(element, properties) return element @@ -101,7 +111,7 @@ class DiasporaProfile(DiasporaEntityMixin, Profile): def to_xml(self): """Convert to XML message.""" element = etree.Element(self._tag_name) - struct_to_xml(element, [ + properties = [ {"author": self.handle}, {"first_name": self.name}, {"last_name": ""}, # We only have one field - splitting it would be artificial @@ -114,7 +124,12 @@ class DiasporaProfile(DiasporaEntityMixin, Profile): {"searchable": "true" if self.public else "false"}, {"nsfw": "true" if self.nsfw else "false"}, {"tag_string": " ".join(["#%s" % tag for tag in self.tag_list])}, - ]) + ] + if self.id and self.id.startswith("http"): + properties.append({ + "activitypub_id": self.id, + }) + struct_to_xml(element, properties) return element diff --git a/federation/tests/entities/diaspora/test_entities.py b/federation/tests/entities/diaspora/test_entities.py index 3c792a0..4c5f3a8 100644 --- a/federation/tests/entities/diaspora/test_entities.py +++ b/federation/tests/entities/diaspora/test_entities.py @@ -21,6 +21,17 @@ class TestEntitiesConvertToXML: b"Socialhome" assert etree.tostring(result) == converted + def test_post_to_xml__with_activitypub_id(self, diasporapost_activitypub_id): + result = diasporapost_activitypub_id.to_xml() + assert result.tag == "status_message" + assert len(result.find("created_at").text) > 0 + result.find("created_at").text = "" # timestamp makes testing painful + converted = b"raw_contentguid" \ + b"alice@example.comtrue" \ + b"Socialhome" \ + b"https://domain.tld/id" + assert etree.tostring(result) == converted + def test_comment_to_xml(self, diasporacomment): result = diasporacomment.to_xml() assert result.tag == "comment" @@ -33,6 +44,18 @@ class TestEntitiesConvertToXML: b"" assert etree.tostring(result) == converted + def test_comment_to_xml__with_activitypub_id(self, diasporacomment_activitypub_id): + result = diasporacomment_activitypub_id.to_xml() + assert result.tag == "comment" + assert len(result.find("created_at").text) > 0 + result.find("created_at").text = "" # timestamp makes testing painful + converted = b"guidtarget_guid" \ + b"target_guid" \ + b"signature" \ + b"raw_contentalice@example.com" \ + b"https://domain.tld/id" + assert etree.tostring(result) == converted + def test_nested_comment_to_xml(self, diasporanestedcomment): result = diasporanestedcomment.to_xml() assert result.tag == "comment" @@ -64,6 +87,17 @@ class TestEntitiesConvertToXML: b"false#socialfederation #federation" assert etree.tostring(result) == converted + def test_profile_to_xml__with_activitypub_id(self, diasporaprofile_activitypub_id): + result = diasporaprofile_activitypub_id.to_xml() + assert result.tag == "profile" + converted = b"alice@example.com" \ + b"Bob Bobertsonurllarge" \ + b"urlsmallurlmedium" \ + b"foobartrue" \ + b"false#socialfederation #federation" \ + b"http://example.com/alice" + assert etree.tostring(result) == converted + def test_retraction_to_xml(self, diasporaretraction): result = diasporaretraction.to_xml() assert result.tag == "retraction" diff --git a/federation/tests/fixtures/entities.py b/federation/tests/fixtures/entities.py index c0ae703..6542a27 100644 --- a/federation/tests/fixtures/entities.py +++ b/federation/tests/fixtures/entities.py @@ -253,6 +253,20 @@ def diasporacomment(): ) +@pytest.fixture +def diasporacomment_activitypub_id(): + return DiasporaComment( + raw_content="raw_content", + signature="signature", + id="https://domain.tld/id", + guid="guid", + actor_id="alice@example.com", + handle="alice@example.com", + target_id="target_guid", + target_guid="target_guid", + ) + + @pytest.fixture def diasporanestedcomment(): return DiasporaComment( @@ -306,6 +320,19 @@ def diasporapost(): ) +@pytest.fixture +def diasporapost_activitypub_id(): + return DiasporaPost( + raw_content="raw_content", + public=True, + provider_display_name="Socialhome", + id="https://domain.tld/id", + guid="guid", + actor_id="alice@example.com", + handle="alice@example.com", + ) + + @pytest.fixture def diasporaprofile(): return DiasporaProfile( @@ -319,6 +346,19 @@ def diasporaprofile(): ) +@pytest.fixture +def diasporaprofile_activitypub_id(): + return DiasporaProfile( + raw_content="foobar", name="Bob Bobertson", public=True, + tag_list=["socialfederation", "federation"], image_urls={ + "large": "urllarge", "medium": "urlmedium", "small": "urlsmall" + }, + id="http://example.com/alice", + handle="alice@example.com", + guid="guid", + ) + + @pytest.fixture def diasporareshare(): base_entity = ShareFactory()