changeset 222:698af5d720ad

Reshape Idavoll as a PubSubResource. PubSubResource is Wokkel's newer interface for building (parts of) XMPP publish-subscribe services and replaces the old interface of PubSubService. It is more flexible for adding new protocol, allows for node-as-code (providing a specific backend per node), and permits accepting requests for different entities (virtual hosts or PEP-like settings). This moves over the current backend to use the new interface, so new code for previously unsupported protocol can be added down the line.
author Ralph Meijer <ralphm@ik.nu>
date Sat, 16 Oct 2010 21:03:38 +0200
parents a430976f2977
children 0eafdced5f24
files idavoll/backend.py idavoll/tap.py idavoll/test/test_backend.py
diffstat 3 files changed, 152 insertions(+), 122 deletions(-) [+]
line wrap: on
line diff
--- a/idavoll/backend.py	Sat Oct 16 20:56:51 2010 +0200
+++ b/idavoll/backend.py	Sat Oct 16 21:03:38 2010 +0200
@@ -21,10 +21,11 @@
 from twisted.python import components, log
 from twisted.internet import defer, reactor
 from twisted.words.protocols.jabber.error import StanzaError
-from twisted.words.xish import domish, utility
+from twisted.words.xish import utility
 
-from wokkel.iwokkel import IDisco, IPubSubService
-from wokkel.pubsub import PubSubService, PubSubError
+from wokkel import disco
+from wokkel.iwokkel import IPubSubResource
+from wokkel.pubsub import PubSubResource, PubSubError
 
 from idavoll import error, iidavoll
 from idavoll.iidavoll import IBackendService, ILeafNode
@@ -474,12 +475,33 @@
 
 
 
-class PubSubServiceFromBackend(PubSubService):
+class PubSubResourceFromBackend(PubSubResource):
     """
     Adapts a backend to an xmpp publish-subscribe service.
     """
 
-    implements(IDisco)
+    features = [
+        "config-node",
+        "create-nodes",
+        "delete-any",
+        "delete-nodes",
+        "item-ids",
+        "meta-data",
+        "publish",
+        "purge-nodes",
+        "retract-items",
+        "retrieve-affiliations",
+        "retrieve-default",
+        "retrieve-items",
+        "retrieve-subscriptions",
+        "subscribe",
+    ]
+
+    discoIdentity = disco.DiscoIdentity('pubsub',
+                                        'service',
+                                        'Idavoll publish-subscribe service')
+
+    pubsubService = None
 
     _errorMap = {
         error.NodeNotFound: ('item-not-found', None, None),
@@ -506,48 +528,25 @@
     }
 
     def __init__(self, backend):
-        PubSubService.__init__(self)
+        PubSubResource.__init__(self)
 
         self.backend = backend
         self.hideNodes = False
 
-        self.pubSubFeatures = self._getPubSubFeatures()
-
         self.backend.registerNotifier(self._notify)
         self.backend.registerPreDelete(self._preDelete)
 
-
-    def _getPubSubFeatures(self):
-        features = [
-            "config-node",
-            "create-nodes",
-            "delete-any",
-            "delete-nodes",
-            "item-ids",
-            "meta-data",
-            "publish",
-            "purge-nodes",
-            "retract-items",
-            "retrieve-affiliations",
-            "retrieve-default",
-            "retrieve-items",
-            "retrieve-subscriptions",
-            "subscribe",
-        ]
-
         if self.backend.supportsInstantNodes():
-            features.append("instant-nodes")
+            self.features.append("instant-nodes")
 
         if self.backend.supportsOutcastAffiliation():
-            features.append("outcast-affiliation")
+            self.features.append("outcast-affiliation")
 
         if self.backend.supportsPersistentItems():
-            features.append("persistent-items")
+            self.features.append("persistent-items")
 
         if self.backend.supportsPublisherAffiliation():
-            features.append("publisher-affiliation")
-
-        return features
+            self.features.append("publisher-affiliation")
 
 
     def _notify(self, data):
@@ -559,19 +558,21 @@
             subscription = data['subscription']
             d = defer.succeed([(subscription.subscriber, [subscription],
                                 items)])
-        d.addCallback(lambda notifications: self.notifyPublish(self.serviceJID,
-                                                               nodeIdentifier,
-                                                               notifications))
+        d.addCallback(lambda notifications: self.pubsubService.notifyPublish(
+                                                self.serviceJID,
+                                                nodeIdentifier,
+                                                notifications))
 
 
     def _preDelete(self, data):
         nodeIdentifier = data['nodeIdentifier']
         redirectURI = data.get('redirectURI', None)
         d = self.backend.getSubscribers(nodeIdentifier)
-        d.addCallback(lambda subscribers: self.notifyDelete(self.serviceJID,
-                                                            nodeIdentifier,
-                                                            subscribers,
-                                                            redirectURI))
+        d.addCallback(lambda subscribers: self.pubsubService.notifyDelete(
+                                                self.serviceJID,
+                                                nodeIdentifier,
+                                                subscribers,
+                                                redirectURI))
         return d
 
 
@@ -589,7 +590,7 @@
         raise exc
 
 
-    def getNodeInfo(self, requestor, service, nodeIdentifier):
+    def getInfo(self, requestor, service, nodeIdentifier):
         info = {}
 
         def saveType(result):
@@ -614,85 +615,97 @@
         return d
 
 
-    def getNodes(self, requestor, service):
+    def getNodes(self, requestor, service, nodeIdentifier):
         if service.resource:
             return defer.succeed([])
         d = self.backend.getNodes()
         return d.addErrback(self._mapErrors)
 
 
-    def publish(self, requestor, service, nodeIdentifier, items):
-        d = self.backend.publish(nodeIdentifier, items, requestor)
-        return d.addErrback(self._mapErrors)
-
-
-    def subscribe(self, requestor, service, nodeIdentifier, subscriber):
-        d = self.backend.subscribe(nodeIdentifier, subscriber, requestor)
-        return d.addErrback(self._mapErrors)
-
-
-    def unsubscribe(self, requestor, service, nodeIdentifier, subscriber):
-        d = self.backend.unsubscribe(nodeIdentifier, subscriber, requestor)
-        return d.addErrback(self._mapErrors)
-
-
-    def subscriptions(self, requestor, service):
-        d = self.backend.getSubscriptions(requestor)
-        return d.addErrback(self._mapErrors)
-
-
-    def affiliations(self, requestor, service):
-        d = self.backend.getAffiliations(requestor)
-        return d.addErrback(self._mapErrors)
-
-
-    def create(self, requestor, service, nodeIdentifier):
-        d = self.backend.createNode(nodeIdentifier, requestor)
-        return d.addErrback(self._mapErrors)
-
-
     def getConfigurationOptions(self):
         return self.backend.nodeOptions
 
 
-    def getDefaultConfiguration(self, requestor, service, nodeType):
-        d = self.backend.getDefaultConfiguration(nodeType)
+    def publish(self, request):
+        d = self.backend.publish(request.nodeIdentifier,
+                                 request.items,
+                                 request.sender)
+        return d.addErrback(self._mapErrors)
+
+
+    def subscribe(self, request):
+        d = self.backend.subscribe(request.nodeIdentifier,
+                                   request.subscriber,
+                                   request.sender)
         return d.addErrback(self._mapErrors)
 
 
-    def getConfiguration(self, requestor, service, nodeIdentifier):
-        d = self.backend.getNodeConfiguration(nodeIdentifier)
+    def unsubscribe(self, request):
+        d = self.backend.unsubscribe(request.nodeIdentifier,
+                                     request.subscriber,
+                                     request.sender)
+        return d.addErrback(self._mapErrors)
+
+
+    def subscriptions(self, request):
+        d = self.backend.getSubscriptions(request.sender)
         return d.addErrback(self._mapErrors)
 
 
-    def setConfiguration(self, requestor, service, nodeIdentifier, options):
-        d = self.backend.setNodeConfiguration(nodeIdentifier, options,
-                                                requestor)
+    def affiliations(self, request):
+        d = self.backend.getAffiliations(request.sender)
+        return d.addErrback(self._mapErrors)
+
+
+    def create(self, request):
+        d = self.backend.createNode(request.nodeIdentifier,
+                                    request.sender)
+        return d.addErrback(self._mapErrors)
+
+
+    def default(self, request):
+        d = self.backend.getDefaultConfiguration(request.nodeType)
         return d.addErrback(self._mapErrors)
 
 
-    def items(self, requestor, service, nodeIdentifier, maxItems,
-                    itemIdentifiers):
-        d = self.backend.getItems(nodeIdentifier, requestor, maxItems,
-                                   itemIdentifiers)
+    def configureGet(self, request):
+        d = self.backend.getNodeConfiguration(request.nodeIdentifier)
         return d.addErrback(self._mapErrors)
 
 
-    def retract(self, requestor, service, nodeIdentifier, itemIdentifiers):
-        d = self.backend.retractItem(nodeIdentifier, itemIdentifiers,
-                                      requestor)
+    def configureSet(self, request):
+        d = self.backend.setNodeConfiguration(request.nodeIdentifier,
+                                              request.options,
+                                              request.sender)
+        return d.addErrback(self._mapErrors)
+
+
+    def items(self, request):
+        d = self.backend.getItems(request.nodeIdentifier,
+                                  request.sender,
+                                  request.maxItems,
+                                  request.itemIdentifiers)
         return d.addErrback(self._mapErrors)
 
 
-    def purge(self, requestor, service, nodeIdentifier):
-        d = self.backend.purgeNode(nodeIdentifier, requestor)
+    def retract(self, request):
+        d = self.backend.retractItem(request.nodeIdentifier,
+                                     request.itemIdentifiers,
+                                     request.sender)
         return d.addErrback(self._mapErrors)
 
 
-    def delete(self, requestor, service, nodeIdentifier):
-        d = self.backend.deleteNode(nodeIdentifier, requestor)
+    def purge(self, request):
+        d = self.backend.purgeNode(request.nodeIdentifier,
+                                   request.sender)
         return d.addErrback(self._mapErrors)
 
-components.registerAdapter(PubSubServiceFromBackend,
+
+    def delete(self, request):
+        d = self.backend.deleteNode(request.nodeIdentifier,
+                                    request.sender)
+        return d.addErrback(self._mapErrors)
+
+components.registerAdapter(PubSubResourceFromBackend,
                            IBackendService,
-                           IPubSubService)
+                           IPubSubResource)
--- a/idavoll/tap.py	Sat Oct 16 20:56:51 2010 +0200
+++ b/idavoll/tap.py	Sat Oct 16 21:03:38 2010 +0200
@@ -8,7 +8,8 @@
 from wokkel.component import Component
 from wokkel.disco import DiscoHandler
 from wokkel.generic import FallbackHandler, VersionHandler
-from wokkel.iwokkel import IPubSubService
+from wokkel.iwokkel import IPubSubResource
+from wokkel.pubsub import PubSubService
 
 from idavoll import __version__
 from idavoll.backend import BackendService
@@ -38,6 +39,8 @@
 
         self['jid'] = JID(self['jid'])
 
+
+
 def makeService(config):
     s = service.MultiService()
 
@@ -80,9 +83,12 @@
     VersionHandler('Idavoll', __version__).setHandlerParent(cs)
     DiscoHandler().setHandlerParent(cs)
 
-    ps = IPubSubService(bs)
+    resource = IPubSubResource(bs)
+    resource.hideNodes = config["hide-nodes"]
+    resource.serviceJID = config["jid"]
+
+    ps = PubSubService(resource)
     ps.setHandlerParent(cs)
-    ps.hideNodes = config["hide-nodes"]
-    ps.serviceJID = config["jid"]
+    resource.pubsubService = ps
 
     return s
--- a/idavoll/test/test_backend.py	Sat Oct 16 20:56:51 2010 +0200
+++ b/idavoll/test/test_backend.py	Sat Oct 16 21:03:38 2010 +0200
@@ -447,11 +447,11 @@
 
 
 
-class PubSubServiceFromBackendTest(unittest.TestCase):
+class PubSubResourceFromBackendTest(unittest.TestCase):
 
-    def test_interfaceIBackend(self):
-        s = backend.PubSubServiceFromBackend(BaseTestBackend())
-        self.assertTrue(verifyObject(iwokkel.IPubSubService, s))
+    def test_interface(self):
+        resource = backend.PubSubResourceFromBackend(BaseTestBackend())
+        self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource))
 
 
     def test_preDelete(self):
@@ -477,13 +477,14 @@
             d1.callback(None)
 
         d1 = defer.Deferred()
-        s = backend.PubSubServiceFromBackend(TestBackend())
-        s.serviceJID = SERVICE
-        s.notifyDelete = notifyDelete
-        self.assertTrue(verifyObject(iwokkel.IPubSubService, s))
-        self.assertNotIdentical(None, s.backend.preDeleteFn)
+        resource = backend.PubSubResourceFromBackend(TestBackend())
+        resource.serviceJID = SERVICE
+        resource.pubsubService = pubsub.PubSubService()
+        resource.pubsubService.notifyDelete = notifyDelete
+        self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource))
+        self.assertNotIdentical(None, resource.backend.preDeleteFn)
         data = {'nodeIdentifier': 'test'}
-        d2 = s.backend.preDeleteFn(data)
+        d2 = resource.backend.preDeleteFn(data)
         return defer.DeferredList([d1, d2], fireOnOneErrback=1)
 
 
@@ -512,14 +513,15 @@
             d1.callback(None)
 
         d1 = defer.Deferred()
-        s = backend.PubSubServiceFromBackend(TestBackend())
-        s.serviceJID = SERVICE
-        s.notifyDelete = notifyDelete
-        self.assertTrue(verifyObject(iwokkel.IPubSubService, s))
-        self.assertNotIdentical(None, s.backend.preDeleteFn)
+        resource = backend.PubSubResourceFromBackend(TestBackend())
+        resource.serviceJID = SERVICE
+        resource.pubsubService = pubsub.PubSubService()
+        resource.pubsubService.notifyDelete = notifyDelete
+        self.assertTrue(verifyObject(iwokkel.IPubSubResource, resource))
+        self.assertNotIdentical(None, resource.backend.preDeleteFn)
         data = {'nodeIdentifier': 'test',
                 'redirectURI': uri}
-        d2 = s.backend.preDeleteFn(data)
+        d2 = resource.backend.preDeleteFn(data)
         return defer.DeferredList([d1, d2], fireOnOneErrback=1)
 
 
@@ -535,14 +537,19 @@
         def cb(e):
             self.assertEquals('unexpected-request', e.condition)
 
-        s = backend.PubSubServiceFromBackend(TestBackend())
-        d = s.unsubscribe(OWNER, SERVICE, 'test', OWNER)
+        resource = backend.PubSubResourceFromBackend(TestBackend())
+        request = pubsub.PubSubRequest()
+        request.sender = OWNER
+        request.recipient = SERVICE
+        request.nodeIdentifier = 'test'
+        request.subscriber = OWNER
+        d = resource.unsubscribe(request)
         self.assertFailure(d, StanzaError)
         d.addCallback(cb)
         return d
 
 
-    def test_getNodeInfo(self):
+    def test_getInfo(self):
         """
         Test retrieving node information.
         """
@@ -560,8 +567,8 @@
             self.assertIn('meta-data', info)
             self.assertEquals({'pubsub#persist_items': True}, info['meta-data'])
 
-        s = backend.PubSubServiceFromBackend(TestBackend())
-        d = s.getNodeInfo(OWNER, SERVICE, 'test')
+        resource = backend.PubSubResourceFromBackend(TestBackend())
+        d = resource.getInfo(OWNER, SERVICE, 'test')
         d.addCallback(cb)
         return d
 
@@ -577,12 +584,12 @@
                          "label": "Deliver payloads with event notifications"}
             }
 
-        s = backend.PubSubServiceFromBackend(TestBackend())
-        options = s.getConfigurationOptions()
+        resource = backend.PubSubResourceFromBackend(TestBackend())
+        options = resource.getConfigurationOptions()
         self.assertIn("pubsub#persist_items", options)
 
 
-    def test_getDefaultConfiguration(self):
+    def test_default(self):
         class TestBackend(BaseTestBackend):
             def getDefaultConfiguration(self, nodeType):
                 options = {"pubsub#persist_items": True,
@@ -594,7 +601,11 @@
         def cb(options):
             self.assertEquals(True, options["pubsub#persist_items"])
 
-        s = backend.PubSubServiceFromBackend(TestBackend())
-        d = s.getDefaultConfiguration(OWNER, SERVICE, 'leaf')
+        resource = backend.PubSubResourceFromBackend(TestBackend())
+        request = pubsub.PubSubRequest()
+        request.sender = OWNER
+        request.recipient = SERVICE
+        request.nodeType = 'leaf'
+        d = resource.default(request)
         d.addCallback(cb)
         return d