changeset 4393:49aa73c8fc30

component email gateway: implement pubsub relationships and pubsub extended discovery: - Pubsub relationships are used to set node hierarchy (comments nodes are set as children of corresponding pubsub nodes). - XEP-0499 Extended Pubsub Discovery is used to return hierarchy of pubsub nodes and items. rel 463
author Goffi <goffi@goffi.org>
date Sun, 31 Aug 2025 12:34:37 +0200
parents dcda916f16f6
children e28cd3454c60
files libervia/backend/plugins/plugin_comp_email_gateway/__init__.py libervia/backend/plugins/plugin_comp_email_gateway/pubsub_service.py
diffstat 2 files changed, 48 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_comp_email_gateway/__init__.py	Sun Aug 31 12:34:30 2025 +0200
+++ b/libervia/backend/plugins/plugin_comp_email_gateway/__init__.py	Sun Aug 31 12:34:37 2025 +0200
@@ -72,6 +72,7 @@
 from libervia.backend.plugins.plugin_xep_0277 import Comment, MbData, XEP_0277
 from libervia.backend.plugins.plugin_xep_0373 import binary_to_ascii_armor
 from libervia.backend.plugins.plugin_xep_0498 import XEP_0498
+from libervia.backend.plugins.plugin_xep_0499 import XEP_0499, ExtDiscoOptions
 from libervia.backend.tools.common import date_utils, regex, uri
 from libervia.backend.tools.utils import aio
 
@@ -94,7 +95,7 @@
     C.PI_TYPE: C.PLUG_TYPE_ENTRY_POINT,
     C.PI_PROTOCOLS: [],
     C.PI_DEPENDENCIES: [
-        "XEP-0033", "XEP-0077", "XEP-0106", "XEP-0277", "XEP-0498", "GRE", "GRE-MIME",
+        "XEP-0033", "XEP-0077", "XEP-0106", "XEP-0277", "XEP-0498", "XEP-0499", "GRE", "GRE-MIME",
         "GRE-OpenPGP", "PUBSUB_CACHE", "TEXT_SYNTAXES"
     ],
     C.PI_RECOMMENDATIONS: [],
@@ -146,6 +147,7 @@
         self._shim = cast(XEP_0131, host.plugins["XEP-0131"])
         self._mb = cast(XEP_0277, host.plugins["XEP-0277"])
         self._pfs = cast(XEP_0498, host.plugins["XEP-0498"])
+        self._ext_disco = cast(XEP_0499, host.plugins["XEP-0499"])
         self._gre = cast(GRE, host.plugins["GRE"])
         self._syntax = cast(TextSyntaxes, host.plugins["TEXT_SYNTAXES"])
         # TODO: For the moment, all credentials are kept in cache; we should only keep the
@@ -1284,6 +1286,7 @@
                         entity = user_jid,
                         affiliation = Affiliation.owner
                     )],
+                    "parent_node": root_node
                 },
             )
         else:
--- a/libervia/backend/plugins/plugin_comp_email_gateway/pubsub_service.py	Sun Aug 31 12:34:30 2025 +0200
+++ b/libervia/backend/plugins/plugin_comp_email_gateway/pubsub_service.py	Sun Aug 31 12:34:37 2025 +0200
@@ -31,6 +31,7 @@
 from libervia.backend.memory.sqla_mapping import AccessModel, Affiliation
 from libervia.backend.plugins.plugin_pubsub_cache import PubsubCache
 from libervia.backend.plugins.plugin_xep_0498 import NodeData
+from libervia.backend.plugins.plugin_xep_0499 import ExtDiscoOptions, ExtDiscoResult, DiscoPubsubItem, DiscoPubsubNode
 from libervia.backend.tools.utils import ensure_deferred
 if TYPE_CHECKING:
     from . import EmailGatewayComponent
@@ -220,6 +221,7 @@
     def __init__(self, gateway: "EmailGatewayComponent"):
         self.gateway = gateway
         self._pfs = gateway._pfs
+        gateway._ext_disco.register_handler(self.on_extended_disco)
         resource = EmailGWPubsubResource(self)
         super().__init__(resource)
         self.host = gateway.host
@@ -229,6 +231,48 @@
             "name": "Libervia Email Gateway",
         }
 
+    @property
+    def client(self) -> SatXMPPComponent:
+        client = self.gateway.client
+        assert client is not None
+        return client
+
+    async def on_extended_disco(
+        self,
+        client: SatXMPPComponent,
+        iq_elt: domish.Element,
+        ext_disco_options: ExtDiscoOptions
+    ) -> ExtDiscoResult:
+        """Handle pubsub extended disco"""
+        query_elt = iq_elt.query
+        assert query_elt is not None
+        node = query_elt.getAttribute("node")
+        if not "nodes" in ext_disco_options.type:
+            raise NotImplementedError("Returning items only is not yet supported.")
+        depth = ext_disco_options.depth
+        nodes = await G.storage.get_pubsub_nodes(
+            client,
+            service=client.jid,
+            node_name = node,
+            linked_node=ext_disco_options.linked_nodes,
+            depth=depth,
+            with_items="items" in ext_disco_options.type
+        )
+
+        if depth == 0:
+            # If depth == 0, we don't want to use `from_sqlalchemy_full_hierarchy` to
+            # avoid returning children, and because children `parent` and `child_nodes`
+            # fields are not loaded.
+            return ExtDiscoResult([
+                DiscoPubsubNode.from_sqlalchemy(node, self.client.jid)
+                for node in nodes
+            ])
+        else:
+            return ExtDiscoResult([
+                DiscoPubsubNode.from_sqlalchemy_full_hierarchy(node, self.client.jid)
+                for node in nodes
+            ])
+
     @ensure_deferred
     async def getDiscoInfo(
         self, requestor: jid.JID, target: jid.JID, nodeIdentifier: str = ""