# HG changeset patch # User Ralph Meijer # Date 1287255818 -7200 # Node ID 698af5d720adae8b46d436d740e2679269d84047 # Parent a430976f2977e813a0fbd6c48524d73ea728194c 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. diff -r a430976f2977 -r 698af5d720ad idavoll/backend.py --- 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) diff -r a430976f2977 -r 698af5d720ad idavoll/tap.py --- 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 diff -r a430976f2977 -r 698af5d720ad idavoll/test/test_backend.py --- 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