Mercurial > libervia-backend
diff sat/plugins/plugin_comp_ap_gateway/http_server.py @ 3904:0aa7023dcd08
component AP gateway: events:
- XMPP Events <=> AP Events conversion
- `Join`/`Leave` activities are converted to RSVP attachments and vice versa
- fix caching/notification on item published on a virtual pubsub node
- add Ad-Hoc command to convert XMPP Jid/Node to virtual AP Account
- handle `Update` activity
- on `convertAndPostItems`, `Update` activity is used instead of `Create` if a version of
the item is already present in cache
- `events` field is added to actor data (and to `endpoints`), it links the `outbox` of the
actor mapping the same JID with the Events node (i.e. it links to the Events node of the
entity)
- fix subscription to nodes which are not the microblog one
rel 372
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 22 Sep 2022 00:01:41 +0200 |
parents | aa7197b67c26 |
children | 6fa4ca0c047e |
line wrap: on
line diff
--- a/sat/plugins/plugin_comp_ap_gateway/http_server.py Wed Sep 21 22:43:55 2022 +0200 +++ b/sat/plugins/plugin_comp_ap_gateway/http_server.py Thu Sep 22 00:01:41 2022 +0200 @@ -41,9 +41,9 @@ from .constants import ( NS_AP, CONTENT_TYPE_AP, TYPE_ACTOR, TYPE_INBOX, TYPE_SHARED_INBOX, TYPE_OUTBOX, - AP_REQUEST_TYPES, PAGE_SIZE, ACTIVITY_TYPES_LOWER, ACTIVIY_NO_ACCOUNT_ALLOWED, - SIGN_HEADERS, HS2019, SIGN_EXP, TYPE_FOLLOWERS, TYPE_FOLLOWING, TYPE_ITEM, TYPE_LIKE, - TYPE_REACTION, ST_AP_CACHE + TYPE_EVENT, AP_REQUEST_TYPES, PAGE_SIZE, ACTIVITY_TYPES_LOWER, + ACTIVIY_NO_ACCOUNT_ALLOWED, SIGN_HEADERS, HS2019, SIGN_EXP, TYPE_FOLLOWERS, + TYPE_FOLLOWING, TYPE_ITEM, TYPE_LIKE, TYPE_REACTION, ST_AP_CACHE ) from .regex import RE_SIG_PARAM @@ -84,6 +84,7 @@ f"internal error: {failure_.value}" ) request.finish() + raise failure_ async def webfinger(self, request): url_parsed = parse.urlparse(request.uri.decode()) @@ -295,11 +296,14 @@ f"happen. Ignoring object from {signing_actor}\n{data}" ) raise exceptions.DataError("unexpected field in item") - if node is None: - node = self.apg._m.namespace client = await self.apg.getVirtualClient(signing_actor) objects = await self.apg.apGetList(data, "object") for obj in objects: + if node is None: + if obj.get("type") == TYPE_EVENT: + node = self.apg._events.namespace + else: + node = self.apg._m.namespace sender = await self.apg.apGetSenderActor(obj) if repeated: # we don't check sender when item is repeated, as it should be different @@ -353,6 +357,7 @@ "Ignoring object not attributed to signing actor: {obj}" ) continue + await self.apg.newAPItem(client, account_jid, node, obj) async def handleCreateActivity( @@ -367,6 +372,20 @@ ): await self.handleNewAPItems(request, data, account_jid, node, signing_actor) + async def handleUpdateActivity( + self, + request: "HTTPRequest", + data: dict, + account_jid: Optional[jid.JID], + node: Optional[str], + ap_account: Optional[str], + ap_url: str, + signing_actor: str + ): + # Update is the same as create: the item ID stays the same, thus the item will be + # overwritten + await self.handleNewAPItems(request, data, account_jid, node, signing_actor) + async def handleAnnounceActivity( self, request: "HTTPRequest", @@ -511,6 +530,32 @@ "reactions": {"operation": "update", "add": [data["content"]]} }) + async def handleJoinActivity( + self, + request: "HTTPRequest", + data: dict, + account_jid: Optional[jid.JID], + node: Optional[str], + ap_account: Optional[str], + ap_url: str, + signing_actor: str + ) -> None: + client = await self.apg.getVirtualClient(signing_actor) + await self.handleAttachmentItem(client, data, {"rsvp": {"attending": "yes"}}) + + async def handleLeaveActivity( + self, + request: "HTTPRequest", + data: dict, + account_jid: Optional[jid.JID], + node: Optional[str], + ap_account: Optional[str], + ap_url: str, + signing_actor: str + ) -> None: + client = await self.apg.getVirtualClient(signing_actor) + await self.handleAttachmentItem(client, data, {"rsvp": {"attending": "no"}}) + async def APActorRequest( self, request: "HTTPRequest", @@ -531,6 +576,13 @@ preferred_username = ap_account.split("@", 1)[0] identity_data = await self.apg._i.getIdentity(self.apg.client, account_jid) + if node and node.startswith(self.apg._events.namespace): + events = outbox + else: + events_account = await self.apg.getAPAccountFromJidAndNode( + account_jid, self.apg._events.namespace + ) + events = self.apg.buildAPURL(TYPE_OUTBOX, events_account) actor_data = { "@context": [ @@ -543,6 +595,7 @@ "preferredUsername": preferred_username, "inbox": inbox, "outbox": outbox, + "events": events, "followers": followers, "following": following, "publicKey": { @@ -551,7 +604,8 @@ "publicKeyPem": self.apg.public_key_pem }, "endpoints": { - "sharedInbox": shared_inbox + "sharedInbox": shared_inbox, + "events": events, }, } @@ -633,13 +687,17 @@ base_url = self.getCanonicalURL(request) url = f"{base_url}?{parse.urlencode(query_data, True)}" - data = { - "@context": "https://www.w3.org/ns/activitystreams", - "id": url, - "type": "OrderedCollectionPage", - "partOf": base_url, - "orderedItems" : [ - await self.apg.mbdata2APitem( + if node and node.startswith(self.apg._events.namespace): + ordered_items = [ + await self.apg.ap_events.event_data_2_ap_item( + self.apg._events.event_elt_2_event_data(item), + account_jid + ) + for item in reversed(items) + ] + else: + ordered_items = [ + await self.apg.mb_data_2_ap_item( self.apg.client, await self.apg._m.item2mbdata( self.apg.client, @@ -650,6 +708,12 @@ ) for item in reversed(items) ] + data = { + "@context": ["https://www.w3.org/ns/activitystreams"], + "id": url, + "type": "OrderedCollectionPage", + "partOf": base_url, + "orderedItems": ordered_items } # AP OrderedCollection must be in reversed chronological order, thus the opposite @@ -718,7 +782,7 @@ url_first_page = f"{url}?{parse.urlencode({'page': 'first'})}" url_last_page = f"{url}?{parse.urlencode({'page': 'last'})}" return { - "@context": "https://www.w3.org/ns/activitystreams", + "@context": ["https://www.w3.org/ns/activitystreams"], "id": url, "totalItems": items_count, "type": "OrderedCollection", @@ -737,8 +801,6 @@ ) -> None: if signing_actor is None: raise exceptions.InternalError("signing_actor must be set for inbox requests") - if node is None: - node = self.apg._m.namespace try: data = json.load(request.content) if not isinstance(data, dict): @@ -805,7 +867,7 @@ url = self.getCanonicalURL(request) return { - "@context": "https://www.w3.org/ns/activitystreams", + "@context": ["https://www.w3.org/ns/activitystreams"], "type": "OrderedCollection", "id": url, "totalItems": len(subscribers), @@ -844,7 +906,7 @@ url = self.getCanonicalURL(request) return { - "@context": "https://www.w3.org/ns/activitystreams", + "@context": ["https://www.w3.org/ns/activitystreams"], "type": "OrderedCollection", "id": url, "totalItems": len(subscriptions), @@ -901,7 +963,8 @@ return static.File(str(avatar_path)).render(request) elif request_type == "item": ret_data = await self.apg.apGetLocalObject(ap_url) - ret_data["@context"] = NS_AP + if "@context" not in ret_data: + ret_data["@context"] = [NS_AP] else: if len(extra_args) > 1: log.warning(f"unexpected extra arguments: {extra_args!r}")