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()