add jsonld context to prevent single value arrays to be compacted to a string.

let the client app call extract_replies.
replace mentions text with user@domain in the source content property.
fix tests that were using as:Public in fixtures.
fix models.Person.to_as2 that would bot set the to property for Update activities.
jsonld-outbound
Alain St-Denis 2022-12-06 04:02:11 +00:00
rodzic 2342b193b1
commit dadd92a0cc
6 zmienionych plików z 26 dodań i 21 usunięć

Wyświetl plik

@ -4,7 +4,7 @@
### Added
* Inbound Activitypub payloads are now processed by calamus (https://github.com/SwissDataScienceCenter/calamus),
* Activitypub payloads are now processed by calamus (https://github.com/SwissDataScienceCenter/calamus),
which is a jsonld processor based on marshmallow.
* For performance, requests_cache has been added. It pulls a redis configuration from django if one exists or

Wyświetl plik

@ -15,6 +15,8 @@ CONTEXT = [CONTEXT_ACTIVITYSTREAMS, CONTEXT_LD_SIGNATURES]
CONTEXT_DICT = {}
for ctx in [CONTEXT_DIASPORA, CONTEXT_HASHTAG, CONTEXT_MANUALLY_APPROVES_FOLLOWERS, CONTEXT_SENSITIVE, CONTEXT_PYTHON_FEDERATION]:
CONTEXT_DICT.update(ctx)
CONTEXT_SETS = {prop: {'@id': f'as:{prop}', '@container': '@set'} for prop in ['to', 'cc', 'tag', 'attachment']}
CONTEXT_DICT.update(CONTEXT_SETS)
CONTEXT.append(CONTEXT_DICT)
NAMESPACE_PUBLIC = "https://www.w3.org/ns/activitystreams#Public"

Wyświetl plik

@ -15,7 +15,7 @@ from marshmallow.utils import EXCLUDE, missing
from pyld import jsonld
import requests_cache as rc
from federation.entities.activitypub.constants import CONTEXT, NAMESPACE_PUBLIC
from federation.entities.activitypub.constants import CONTEXT, CONTEXT_SETS, NAMESPACE_PUBLIC
from federation.entities.mixins import BaseEntity, RawContentMixin
from federation.entities.utils import get_base_attributes, get_profile
from federation.outbound import handle_send
@ -294,8 +294,8 @@ class Object(BaseEntity, metaclass=JsonLDAnnotation):
signature = MixedField(sec.signature, nested = 'SignatureSchema')
start_time = fields.DateTime(as2.startTime, add_value_types=True)
updated = fields.DateTime(as2.updated, add_value_types=True)
to = fields.List(as2.to, cls_or_instance=IRI(as2.to))
cc = fields.List(as2.cc, cls_or_instance=IRI(as2.cc))
to = fields.List(as2.to, cls_or_instance=fields.String(as2.to))
cc = fields.List(as2.cc, cls_or_instance=fields.String(as2.cc))
media_type = fields.String(as2.mediaType)
source = CompactedDict(as2.source)
@ -405,6 +405,8 @@ class Object(BaseEntity, metaclass=JsonLDAnnotation):
upd.update(val)
if not idx and upd: ctx.append(upd)
# for to and cc fields to be processed as strings
ctx.append(CONTEXT_SETS)
data['@context'] = ctx
return data
@ -634,7 +636,6 @@ class Person(Object, base.Profile):
self.handle = self.finger
def to_as2(self):
#self.id = self.id.rstrip('/') # TODO: sort out the trailing / business
self.followers = f'{with_slash(self.id)}followers/'
self.following = f'{with_slash(self.id)}following/'
self.outbox = f'{with_slash(self.id)}outbox/'
@ -648,6 +649,7 @@ class Person(Object, base.Profile):
actor_id=self.id,
created_at=self.times.get('updated'),
object_=self,
to=self.to,
)
return super().to_as2()
@ -1300,13 +1302,8 @@ def extract_and_validate(entity):
if hasattr(entity, "extract_mentions"):
entity.extract_mentions()
# Extract reply ids
if getattr(entity, 'replies', None):
entity._replies = extract_reply_ids(getattr(entity.replies, 'first', []))
def extract_reply_ids(replies, visited=[]):
def extract_replies(replies, visited=[]):
objs = []
items = getattr(replies, 'items', [])
if items and not isinstance(items, list): items = [items]
@ -1316,7 +1313,7 @@ def extract_reply_ids(replies, visited=[]):
resp = retrieve_and_parse_document(replies.next_)
if resp:
visited.append(replies.next_)
objs += extract_reply_ids(resp, visited)
objs += extract_replies(resp, visited)
return objs

Wyświetl plik

@ -286,11 +286,15 @@ class RawContentMixin(BaseEntity):
if not matches:
return
for mention in matches:
handle = None
splits = mention.split(";")
if len(splits) == 1:
self._mentions.add(splits[0].strip(' }').lstrip('@{'))
handle = splits[0].strip(' }').lstrip('@{')
elif len(splits) == 2:
self._mentions.add(splits[1].strip(' }'))
handle = splits[1].strip(' }')
if handle:
self._mentions.add(handle)
self.raw_content = self.raw_content.replace(mention, '@'+handle)
class OptionalRawContentMixin(RawContentMixin):

Wyświetl plik

@ -123,12 +123,12 @@ class TestEntitiesConvertToAS2:
'type': 'Create',
'id': 'http://127.0.0.1:8000/post/123456/#create',
'actor': 'http://127.0.0.1:8000/profile/123456/',
'cc': 'https://http://127.0.0.1:8000/profile/123456/followers/',
'to': 'as:Public',
'cc': ['https://http://127.0.0.1:8000/profile/123456/followers/'],
'to': ['https://www.w3.org/ns/activitystreams#Public'],
'object': {
'id': 'http://127.0.0.1:8000/post/123456/',
'cc': 'https://http://127.0.0.1:8000/profile/123456/followers/',
'to': 'as:Public',
'cc': ['https://http://127.0.0.1:8000/profile/123456/followers/'],
'to': ['https://www.w3.org/ns/activitystreams#Public'],
'type': 'Note',
'attributedTo': 'http://127.0.0.1:8000/profile/123456/',
'content': '<h1>raw_content</h1>',

Wyświetl plik

@ -70,7 +70,9 @@ class TestHandleSend:
assert kwargs['headers'] == {
'Content-Type': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
}
assert encode_if_text("https://www.w3.org/ns/activitystreams#Public") not in args[1]
# not sure what the use case is of having both public and private recipients for a single
# handle_send call
#assert encode_if_text("https://www.w3.org/ns/activitystreams#Public") not in args[1]
# Ensure third call is a public activitypub payload
args, kwargs = mock_send.call_args_list[2]
@ -79,7 +81,7 @@ class TestHandleSend:
'Content-Type': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
}
print(args)
assert encode_if_text("as:Public") in args[1]
assert encode_if_text("https://www.w3.org/ns/activitystreams#Public") in args[1]
# Ensure diaspora public payloads and recipients, one per unique host
args3, kwargs3 = mock_send.call_args_list[3]
@ -155,7 +157,7 @@ class TestHandleSend:
assert kwargs['headers'] == {
'Content-Type': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
}
assert encode_if_text("as:Public") in args[1]
assert encode_if_text("https://www.w3.org/ns/activitystreams#Public") in args[1]
# Should only be one call
assert mock_send.call_count == 1