Changeset 59:e984452207e0


Ignore:
Timestamp:
Apr 22, 2009, 5:04:03 PM (12 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Convert:
svn:b33ecbfc-034c-dc11-8662-000475d9059e/trunk@171
Message:

Provide PubSubResource?, modeled after Twisted Web resources.

Author: ralphm.
Fixes #47.

This should make it rather easy to make publish-subscribe enabled services.

Location:
wokkel
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • wokkel/iwokkel.py

    r48 r59  
    278278                             C{list} of L{domish.Element})
    279279        """
     280
    280281
    281282    def notifyDelete(service, nodeIdentifier, subscribers,
     
    527528        @type nodeIdentifier: C{unicode}
    528529        """
     530
     531
     532
     533class IPubSubResource(Interface):
     534
     535    def locateResource(request):
     536        """
     537        Locate a resource that will handle the request.
     538
     539        @param request: The publish-subscribe request.
     540        @type request: L{wokkel.pubsub.PubSubRequest}
     541        """
     542
     543
     544    def getInfo(requestor, service, nodeIdentifier):
     545        """
     546        Get node type and meta data.
     547
     548        @param requestor: The entity the request originated from.
     549        @type requestor: L{jid.JID}
     550        @param service: The publish-subscribe service entity.
     551        @type service: L{jid.JID}
     552        @param nodeIdentifier: Identifier of the node to request the info for.
     553        @type nodeIdentifier: L{unicode}
     554        @return: A deferred that fires with a dictionary. If not empty,
     555                 it must have the keys C{'type'} and C{'meta-data'} to keep
     556                 respectively the node type and a dictionary with the meta
     557                 data for that node.
     558        @rtype: L{defer.Deferred}
     559        """
     560
     561
     562    def getNodes(requestor, service, nodeIdentifier):
     563        """
     564        Get all nodes contained by this node.
     565
     566        @param requestor: The entity the request originated from.
     567        @type requestor: L{jid.JID}
     568        @param service: The publish-subscribe service entity.
     569        @type service: L{jid.JID}
     570        @param nodeIdentifier: Identifier of the node to request the childs for.
     571        @type nodeIdentifier: L{unicode}
     572        @return: A deferred that fires with a list of child node identifiers.
     573        @rtype: L{defer.Deferred}
     574        """
     575
     576
     577    def getConfigurationOptions():
     578        """
     579        Retrieve all known node configuration options.
     580
     581        The returned dictionary holds the possible node configuration options
     582        by option name. The value of each entry represents the specifics for
     583        that option in a dictionary:
     584
     585         - C{'type'} (C{str}): The option's type (see
     586           L{Field<wokkel.data_form.Field>}'s doc string for possible values).
     587         - C{'label'} (C{unicode}): A human readable label for this option.
     588         - C{'options'} (C{dict}): Optional list of possible values for this
     589           option.
     590
     591        Example::
     592
     593            {
     594            "pubsub#persist_items":
     595                {"type": "boolean",
     596                 "label": "Persist items to storage"},
     597            "pubsub#deliver_payloads":
     598                {"type": "boolean",
     599                 "label": "Deliver payloads with event notifications"},
     600            "pubsub#send_last_published_item":
     601                {"type": "list-single",
     602                 "label": "When to send the last published item",
     603                 "options": {
     604                     "never": "Never",
     605                     "on_sub": "When a new subscription is processed"}
     606                }
     607            }
     608
     609        @rtype: C{dict}.
     610        """
     611
     612
     613    def publish(request):
     614        """
     615        Called when a publish request has been received.
     616
     617        @param request: The publish-subscribe request.
     618        @type request: L{wokkel.pubsub.PubSubRequest}
     619        @return: deferred that fires on success.
     620        @rtype: L{defer.Deferred}
     621        """
     622
     623
     624    def subscribe(request):
     625        """
     626        Called when a subscribe request has been received.
     627
     628        @param request: The publish-subscribe request.
     629        @type request: L{wokkel.pubsub.PubSubRequest}
     630        @return: A deferred that fires with a
     631                 L{Subscription<wokkel.pubsub.Subscription>}.
     632        @rtype: L{defer.Deferred}
     633        """
     634
     635
     636    def unsubscribe(request):
     637        """
     638        Called when a subscribe request has been received.
     639
     640        @param request: The publish-subscribe request.
     641        @type request: L{wokkel.pubsub.PubSubRequest}
     642        @return: A deferred that fires with C{None} when unsubscription has
     643                 succeeded.
     644        @rtype: L{defer.Deferred}
     645        """
     646
     647
     648    def subscriptions(request):
     649        """
     650        Called when a subscriptions retrieval request has been received.
     651
     652        @param request: The publish-subscribe request.
     653        @type request: L{wokkel.pubsub.PubSubRequest}
     654        @return: A deferred that fires with a C{list} of subscriptions as
     655                 L{Subscription<wokkel.pubsub.Subscription>}.
     656        @rtype: L{defer.Deferred}
     657        """
     658
     659
     660    def affiliations(request):
     661        """
     662        Called when a affiliations retrieval request has been received.
     663
     664        @param request: The publish-subscribe request.
     665        @type request: L{wokkel.pubsub.PubSubRequest}
     666        @return: A deferred that fires with a C{list} of affiliations as
     667                 C{tuple}s of (node identifier as C{unicode}, affiliation state
     668                 as C{str}). The affiliation can be C{'owner'}, C{'publisher'},
     669                 or C{'outcast'}.
     670        @rtype: L{defer.Deferred}
     671        """
     672
     673
     674    def create(request):
     675        """
     676        Called when a node creation request has been received.
     677
     678        @param request: The publish-subscribe request.
     679        @type request: L{wokkel.pubsub.PubSubRequest}
     680        @return: A deferred that fires with a C{unicode} that represents
     681                 the identifier of the new node.
     682        @rtype: L{defer.Deferred}
     683        """
     684
     685
     686    def default(request):
     687        """
     688        Called when a default node configuration request has been received.
     689
     690        @param request: The publish-subscribe request.
     691        @type request: L{wokkel.pubsub.PubSubRequest}
     692        @return: A deferred that fires with a C{dict} representing the default
     693                 node configuration. Keys are C{str}s that represent the
     694                 field name. Values can be of types C{unicode}, C{int} or
     695                 C{bool}.
     696        @rtype: L{defer.Deferred}
     697        """
     698
     699
     700    def configureGet(request):
     701        """
     702        Called when a node configuration retrieval request has been received.
     703
     704        @param request: The publish-subscribe request.
     705        @type request: L{wokkel.pubsub.PubSubRequest}
     706        @return: A deferred that fires with a C{dict} representing the node
     707                 configuration. Keys are C{str}s that represent the field name.
     708                 Values can be of types C{unicode}, C{int} or C{bool}.
     709        @rtype: L{defer.Deferred}
     710        """
     711
     712
     713    def configureSet(request):
     714        """
     715        Called when a node configuration change request has been received.
     716
     717        @param request: The publish-subscribe request.
     718        @type request: L{wokkel.pubsub.PubSubRequest}
     719        @return: A deferred that fires with C{None} when the node's
     720                 configuration has been changed.
     721        @rtype: L{defer.Deferred}
     722        """
     723
     724
     725    def items(request):
     726        """
     727        Called when a items retrieval request has been received.
     728
     729        @param request: The publish-subscribe request.
     730        @type request: L{wokkel.pubsub.PubSubRequest}
     731        @return: A deferred that fires with a C{list} of L{pubsub.Item}.
     732        @rtype: L{defer.Deferred}
     733        """
     734
     735
     736    def retract(request):
     737        """
     738        Called when a item retraction request has been received.
     739
     740        @param request: The publish-subscribe request.
     741        @type request: L{wokkel.pubsub.PubSubRequest}
     742        @return: A deferred that fires with C{None} when the given items have
     743                 been retracted.
     744        @rtype: L{defer.Deferred}
     745        """
     746
     747
     748    def purge(request):
     749        """
     750        Called when a node purge request has been received.
     751
     752        @param request: The publish-subscribe request.
     753        @type request: L{wokkel.pubsub.PubSubRequest}
     754        @return: A deferred that fires with C{None} when the node has been
     755                 purged.
     756        @rtype: L{defer.Deferred}
     757        """
     758
     759
     760    def delete(request):
     761        """
     762        Called when a node deletion request has been received.
     763
     764        @param request: The publish-subscribe request.
     765        @type request: L{wokkel.pubsub.PubSubRequest}
     766        @return: A deferred that fires with C{None} when the node has been
     767                 deleted.
     768        @rtype: L{defer.Deferred}
     769        """
  • wokkel/pubsub.py

    r58 r59  
    1414
    1515from twisted.internet import defer
     16from twisted.python import log
    1617from twisted.words.protocols.jabber import jid, error, xmlstream
    1718from twisted.words.xish import domish
     
    1920from wokkel import disco, data_form, generic, shim
    2021from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
    21 from wokkel.iwokkel import IPubSubClient, IPubSubService
     22from wokkel.iwokkel import IPubSubClient, IPubSubService, IPubSubResource
    2223
    2324# Iq get and set XPath queries
     
    8586class Unsupported(PubSubError):
    8687    def __init__(self, feature, text=None):
     88        self.feature = feature
    8789        PubSubError.__init__(self, 'feature-not-implemented',
    8890                                   'unsupported',
     
    9092                                   text)
    9193
     94    def __str__(self):
     95        message = PubSubError.__str__(self)
     96        message += ', feature %r' % self.feature
     97        return message
    9298
    9399
     
    236242        'purge': ['node'],
    237243        'delete': ['node'],
    238         'affiliationsGet': [],
     244        'affiliationsGet': ['nodeOrEmpty'],
    239245        'affiliationsSet': [],
    240         'subscriptionsGet': [],
     246        'subscriptionsGet': ['nodeOrEmpty'],
    241247        'subscriptionsSet': [],
    242248    }
     
    807813            }
    808814
    809 
    810     def __init__(self):
     815    _legacyHandlers = {
     816        'publish': ('publish', ['sender', 'recipient',
     817                                'nodeIdentifier', 'items']),
     818        'subscribe': ('subscribe', ['sender', 'recipient',
     819                                    'nodeIdentifier', 'subscriber']),
     820        'unsubscribe': ('unsubscribe', ['sender', 'recipient',
     821                                        'nodeIdentifier', 'subscriber']),
     822        'subscriptions': ('subscriptions', ['sender', 'recipient']),
     823        'affiliations': ('affiliations', ['sender', 'recipient']),
     824        'create': ('create', ['sender', 'recipient', 'nodeIdentifier']),
     825        'getConfigurationOptions': ('getConfigurationOptions', []),
     826        'default': ('getDefaultConfiguration',
     827                    ['sender', 'recipient', 'nodeType']),
     828        'configureGet': ('getConfiguration', ['sender', 'recipient',
     829                                              'nodeIdentifier']),
     830        'configureSet': ('setConfiguration', ['sender', 'recipient',
     831                                              'nodeIdentifier', 'options']),
     832        'items': ('items', ['sender', 'recipient', 'nodeIdentifier',
     833                            'maxItems', 'itemIdentifiers']),
     834        'retract': ('retract', ['sender', 'recipient', 'nodeIdentifier',
     835                                'itemIdentifiers']),
     836        'purge': ('purge', ['sender', 'recipient', 'nodeIdentifier']),
     837        'delete': ('delete', ['sender', 'recipient', 'nodeIdentifier']),
     838    }
     839
     840    hideNodes = False
     841
     842    def __init__(self, resource=None):
     843        self.resource = resource
    811844        self.discoIdentity = {'category': 'pubsub',
    812845                              'type': 'generic',
     
    821854
    822855    def getDiscoInfo(self, requestor, target, nodeIdentifier):
    823         info = []
    824 
    825         if not nodeIdentifier:
    826             category, idType, name = self.discoIdentity
    827             info.append(disco.DiscoIdentity(category, idType, name))
    828 
    829             info.append(disco.DiscoFeature(disco.NS_DISCO_ITEMS))
    830             info.extend([disco.DiscoFeature("%s#%s" % (NS_PUBSUB, feature))
    831                          for feature in self.pubSubFeatures])
    832 
    833         def toInfo(nodeInfo):
     856        def toInfo(nodeInfo, info):
    834857            if not nodeInfo:
    835                 return
     858                return info
    836859
    837860            (nodeType, metaData) = nodeInfo['type'], nodeInfo['meta-data']
     
    853876                info.append(form)
    854877
    855         d = self.getNodeInfo(requestor, target, nodeIdentifier or '')
    856         d.addCallback(toInfo)
    857         d.addBoth(lambda result: info)
     878            return info
     879
     880        info = []
     881
     882        request = PubSubRequest('discoInfo')
     883
     884        if self.resource is not None:
     885            resource = self.resource.locateResource(request)
     886            identity = resource.discoIdentity
     887            features = resource.features
     888            getInfo = resource.getInfo
     889        else:
     890            category, idType, name = self.discoIdentity
     891            identity = disco.DiscoIdentity(category, idType, name)
     892            features = self.pubSubFeatures
     893            getInfo = self.getNodeInfo
     894
     895        if not nodeIdentifier:
     896            info.append(identity)
     897            info.append(disco.DiscoFeature(disco.NS_DISCO_ITEMS))
     898            info.extend([disco.DiscoFeature("%s#%s" % (NS_PUBSUB, feature))
     899                         for feature in features])
     900
     901        d = getInfo(requestor, target, nodeIdentifier or '')
     902        d.addCallback(toInfo, info)
     903        d.addErrback(log.err)
    858904        return d
    859905
    860906
    861907    def getDiscoItems(self, requestor, target, nodeIdentifier):
    862         if nodeIdentifier or self.hideNodes:
    863             return defer.succeed([])
    864 
    865         d = self.getNodes(requestor, target)
     908        if self.hideNodes:
     909            d = defer.succeed([])
     910        elif self.resource is not None:
     911            request = PubSubRequest('discoInfo')
     912            resource = self.resource.locateResource(request)
     913            d = resource.getNodes(requestor, target, nodeIdentifier)
     914        elif nodeIdentifier:
     915            d = self.getNodes(requestor, target)
     916        else:
     917            d = defer.succeed([])
     918           
     919
     920
    866921        d.addCallback(lambda nodes: [disco.DiscoItem(target, node)
    867922                                     for node in nodes])
     
    871926    def _onPubSubRequest(self, iq):
    872927        request = PubSubRequest.fromElement(iq)
    873         handler = getattr(self, '_on_%s' % request.verb)
    874         return handler(request)
    875 
    876 
    877     def _on_publish(self, request):
    878         return self.publish(request.sender, request.recipient,
    879                             request.nodeIdentifier, request.items)
    880 
    881 
    882     def _on_subscribe(self, request):
    883 
    884         def toResponse(result):
    885             response = domish.Element((NS_PUBSUB, "pubsub"))
    886             subscription = response.addElement("subscription")
    887             if result.nodeIdentifier:
    888                 subscription["node"] = result.nodeIdentifier
    889             subscription["jid"] = result.subscriber.full()
    890             subscription["subscription"] = result.state
     928
     929        if self.resource is not None:
     930            resource = self.resource.locateResource(request)
     931        else:
     932            resource = self
     933
     934        # Preprocess the request, knowing the handling resource
     935        try:
     936            preProcessor = getattr(self, '_preProcess_%s' % request.verb)
     937        except AttributeError:
     938            pass
     939        else:
     940            request = preProcessor(resource, request)
     941            if request is None:
     942                return defer.succeed(None)
     943
     944        # Process the request itself,
     945        if resource is not self:
     946            try:
     947                handler = getattr(resource, request.verb)
     948            except AttributeError:
     949                # fix lookup feature
     950                text = "Request verb: %s" % request.verb
     951                return defer.fail(Unsupported('', text))
     952
     953            d = handler(request)
     954        else:
     955            handlerName, argNames = self._legacyHandlers[request.verb]
     956            handler = getattr(self, handlerName)
     957            args = [getattr(request, arg) for arg in argNames]
     958            d = handler(*args)
     959
     960        # If needed, translate the result into a response
     961        try:
     962            cb = getattr(self, '_toResponse_%s' % request.verb)
     963        except AttributeError:
     964            pass
     965        else:
     966            d.addCallback(cb, resource, request)
     967
     968        return d
     969
     970
     971    def _toResponse_subscribe(self, result, resource, request):
     972        response = domish.Element((NS_PUBSUB, "pubsub"))
     973        subscription = response.addElement("subscription")
     974        if result.nodeIdentifier:
     975            subscription["node"] = result.nodeIdentifier
     976        subscription["jid"] = result.subscriber.full()
     977        subscription["subscription"] = result.state
     978        return response
     979
     980
     981    def _toResponse_subscriptions(self, result, resource, request):
     982        response = domish.Element((NS_PUBSUB, 'pubsub'))
     983        subscriptions = response.addElement('subscriptions')
     984        for subscription in result:
     985            item = subscriptions.addElement('subscription')
     986            item['node'] = subscription.nodeIdentifier
     987            item['jid'] = subscription.subscriber.full()
     988            item['subscription'] = subscription.state
     989        return response
     990
     991
     992    def _toResponse_affiliations(self, result, resource, request):
     993        response = domish.Element((NS_PUBSUB, 'pubsub'))
     994        affiliations = response.addElement('affiliations')
     995
     996        for nodeIdentifier, affiliation in result:
     997            item = affiliations.addElement('affiliation')
     998            item['node'] = nodeIdentifier
     999            item['affiliation'] = affiliation
     1000
     1001        return response
     1002
     1003
     1004    def _toResponse_create(self, result, resource, request):
     1005        if not request.nodeIdentifier or request.nodeIdentifier != result:
     1006            response = domish.Element((NS_PUBSUB, 'pubsub'))
     1007            create = response.addElement('create')
     1008            create['node'] = result
    8911009            return response
    892 
    893         d = self.subscribe(request.sender, request.recipient,
    894                            request.nodeIdentifier, request.subscriber)
    895         d.addCallback(toResponse)
    896         return d
    897 
    898 
    899     def _on_unsubscribe(self, request):
    900         return self.unsubscribe(request.sender, request.recipient,
    901                                 request.nodeIdentifier, request.subscriber)
    902 
    903 
    904     def _on_optionsGet(self, request):
    905         raise Unsupported('subscription-options')
    906 
    907 
    908     def _on_optionsSet(self, request):
    909         raise Unsupported('subscription-options')
    910 
    911 
    912     def _on_subscriptions(self, request):
    913 
    914         def toResponse(result):
    915             response = domish.Element((NS_PUBSUB, 'pubsub'))
    916             subscriptions = response.addElement('subscriptions')
    917             for subscription in result:
    918                 item = subscriptions.addElement('subscription')
    919                 item['node'] = subscription.nodeIdentifier
    920                 item['jid'] = subscription.subscriber.full()
    921                 item['subscription'] = subscription.state
    922             return response
    923 
    924         d = self.subscriptions(request.sender, request.recipient)
    925         d.addCallback(toResponse)
    926         return d
    927 
    928 
    929     def _on_affiliations(self, request):
    930 
    931         def toResponse(result):
    932             response = domish.Element((NS_PUBSUB, 'pubsub'))
    933             affiliations = response.addElement('affiliations')
    934 
    935             for nodeIdentifier, affiliation in result:
    936                 item = affiliations.addElement('affiliation')
    937                 item['node'] = nodeIdentifier
    938                 item['affiliation'] = affiliation
    939 
    940             return response
    941 
    942         d = self.affiliations(request.sender, request.recipient)
    943         d.addCallback(toResponse)
    944         return d
    945 
    946 
    947     def _on_create(self, request):
    948 
    949         def toResponse(result):
    950             if not request.nodeIdentifier or request.nodeIdentifier != result:
    951                 response = domish.Element((NS_PUBSUB, 'pubsub'))
    952                 create = response.addElement('create')
    953                 create['node'] = result
    954                 return response
    955             else:
    956                 return None
    957 
    958         d = self.create(request.sender, request.recipient,
    959                         request.nodeIdentifier)
    960         d.addCallback(toResponse)
    961         return d
     1010        else:
     1011            return None
    9621012
    9631013
     
    9781028
    9791029
    980     def _formFromConfiguration(self, values):
    981         options = self.getConfigurationOptions()
     1030    def _formFromConfiguration(self, resource, values):
     1031        options = resource.getConfigurationOptions()
    9821032        fields = self._makeFields(options, values)
    9831033        form = data_form.Form(formType="form",
     
    9881038
    9891039
    990     def _checkConfiguration(self, values):
    991         options = self.getConfigurationOptions()
     1040    def _checkConfiguration(self, resource, values):
     1041        options = resource.getConfigurationOptions()
    9921042        processedValues = {}
    9931043
     
    10131063
    10141064
    1015     def _on_default(self, request):
    1016 
    1017         def toResponse(options):
    1018             response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
    1019             default = response.addElement("default")
    1020             default.addChild(self._formFromConfiguration(options).toElement())
    1021             return response
    1022 
     1065    def _preProcess_default(self, resource, request):
    10231066        if request.nodeType not in ('leaf', 'collection'):
    1024             return defer.fail(error.StanzaError('not-acceptable'))
    1025 
    1026         d = self.getDefaultConfiguration(request.sender, request.recipient,
    1027                                          request.nodeType)
    1028         d.addCallback(toResponse)
    1029         return d
    1030 
    1031 
    1032     def _on_configureGet(self, request):
    1033         def toResponse(options):
    1034             response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
    1035             configure = response.addElement("configure")
    1036             form = self._formFromConfiguration(options)
    1037             configure.addChild(form.toElement())
    1038 
    1039             if request.nodeIdentifier:
    1040                 configure["node"] = request.nodeIdentifier
    1041 
    1042             return response
    1043 
    1044         d = self.getConfiguration(request.sender, request.recipient,
    1045                                   request.nodeIdentifier)
    1046         d.addCallback(toResponse)
    1047         return d
    1048 
    1049 
    1050     def _on_configureSet(self, request):
     1067            raise error.StanzaError('not-acceptable')
     1068        else:
     1069            return request
     1070
     1071
     1072    def _toResponse_default(self, options, resource, request):
     1073        response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
     1074        default = response.addElement("default")
     1075        form = self._formFromConfiguration(resource, options)
     1076        default.addChild(form.toElement())
     1077        return response
     1078
     1079
     1080    def _toResponse_configureGet(self, options, resource, request):
     1081        response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
     1082        configure = response.addElement("configure")
     1083        form = self._formFromConfiguration(resource, options)
     1084        configure.addChild(form.toElement())
     1085
     1086        if request.nodeIdentifier:
     1087            configure["node"] = request.nodeIdentifier
     1088
     1089        return response
     1090
     1091
     1092    def _preProcess_configureSet(self, resource, request):
    10511093        if request.options:
    1052             request.options = self._checkConfiguration(request.options)
    1053             return self.setConfiguration(request.sender, request.recipient,
    1054                                          request.nodeIdentifier,
    1055                                          request.options)
     1094            request.options = self._checkConfiguration(resource,
     1095                                                       request.options)
     1096            return request
    10561097        else:
    10571098            return None
    10581099
    10591100
    1060 
    1061     def _on_items(self, request):
    1062 
    1063         def toResponse(result):
    1064             response = domish.Element((NS_PUBSUB, 'pubsub'))
    1065             items = response.addElement('items')
    1066             items["node"] = request.nodeIdentifier
    1067 
    1068             for item in result:
    1069                 items.addChild(item)
    1070 
    1071             return response
    1072 
    1073         d = self.items(request.sender, request.recipient,
    1074                        request.nodeIdentifier, request.maxItems,
    1075                        request.itemIdentifiers)
    1076         d.addCallback(toResponse)
    1077         return d
    1078 
    1079 
    1080     def _on_retract(self, request):
    1081         return self.retract(request.sender, request.recipient,
    1082                             request.nodeIdentifier, request.itemIdentifiers)
    1083 
    1084 
    1085     def _on_purge(self, request):
    1086         return self.purge(request.sender, request.recipient,
    1087                           request.nodeIdentifier)
    1088 
    1089 
    1090     def _on_delete(self, request):
    1091         return self.delete(request.sender, request.recipient,
    1092                            request.nodeIdentifier)
    1093 
    1094 
    1095     def _on_affiliationsGet(self, iq):
    1096         raise Unsupported('modify-affiliations')
    1097 
    1098 
    1099     def _on_affiliationsSet(self, iq):
    1100         raise Unsupported('modify-affiliations')
    1101 
    1102 
    1103     def _on_subscriptionsGet(self, iq):
    1104         raise Unsupported('manage-subscriptions')
    1105 
    1106 
    1107     def _on_subscriptionsSet(self, iq):
    1108         raise Unsupported('manage-subscriptions')
    1109 
    1110     # public methods
     1101    def _toResponse_items(self, result, resource, request):
     1102        response = domish.Element((NS_PUBSUB, 'pubsub'))
     1103        items = response.addElement('items')
     1104        items["node"] = request.nodeIdentifier
     1105
     1106        for item in result:
     1107            items.addChild(item)
     1108
     1109        return response
     1110
    11111111
    11121112    def _createNotification(self, eventType, service, nodeIdentifier,
     
    11311131
    11321132        return message
     1133
     1134    # public methods
    11331135
    11341136    def notifyPublish(self, service, nodeIdentifier, notifications):
     
    12161218    def delete(self, requestor, service, nodeIdentifier):
    12171219        raise Unsupported('delete-nodes')
     1220
     1221
     1222
     1223class PubSubResource(object):
     1224
     1225    implements(IPubSubResource)
     1226
     1227    features = []
     1228    discoIdentity = disco.DiscoIdentity('pubsub',
     1229                                        'service',
     1230                                        'Publish-Subscribe Service')
     1231
     1232
     1233    def locateResource(self, request):
     1234        return self
     1235
     1236
     1237    def getInfo(self, requestor, service, nodeIdentifier):
     1238        return defer.succeed(None)
     1239
     1240
     1241    def getNodes(self, requestor, service, nodeIdentifier):
     1242        return defer.succeed([])
     1243
     1244
     1245    def getConfigurationOptions(self):
     1246        return {}
     1247
     1248
     1249    def publish(self, request):
     1250        return defer.fail(Unsupported('publish'))
     1251
     1252
     1253    def subscribe(self, request):
     1254        return defer.fail(Unsupported('subscribe'))
     1255
     1256
     1257    def unsubscribe(self, request):
     1258        return defer.fail(Unsupported('subscribe'))
     1259
     1260
     1261    def subscriptions(self, request):
     1262        return defer.fail(Unsupported('retrieve-subscriptions'))
     1263
     1264
     1265    def affiliations(self, request):
     1266        return defer.fail(Unsupported('retrieve-affiliations'))
     1267
     1268
     1269    def create(self, request):
     1270        return defer.fail(Unsupported('create-nodes'))
     1271
     1272
     1273    def default(self, request):
     1274        return defer.fail(Unsupported('retrieve-default'))
     1275
     1276
     1277    def configureGet(self, request):
     1278        return defer.fail(Unsupported('config-node'))
     1279
     1280
     1281    def configureSet(self, request):
     1282        return defer.fail(Unsupported('config-node'))
     1283
     1284
     1285    def items(self, request):
     1286        return defer.fail(Unsupported('retrieve-items'))
     1287
     1288
     1289    def retract(self, request):
     1290        return defer.fail(Unsupported('retract-items'))
     1291
     1292
     1293    def purge(self, request):
     1294        return defer.fail(Unsupported('purge-nodes'))
     1295
     1296
     1297    def delete(self, request):
     1298        return defer.fail(Unsupported('delete-nodes'))
     1299
     1300
     1301    def affiliationsGet(self, request):
     1302        return defer.fail(Unsupported('modify-affiliations'))
     1303
     1304
     1305    def affiliationsSet(self, request):
     1306        return defer.fail(Unsupported('modify-affiliations'))
     1307
     1308
     1309    def subscriptionsGet(self, request):
     1310        return defer.fail(Unsupported('manage-subscriptions'))
     1311
     1312
     1313    def subscriptionsSet(self, request):
     1314        return defer.fail(Unsupported('manage-subscriptions'))
  • wokkel/test/test_pubsub.py

    r58 r59  
    12231223    def setUp(self):
    12241224        self.stub = XmlStreamStub()
    1225         self.service = pubsub.PubSubService()
     1225        self.resource = pubsub.PubSubResource()
     1226        self.service = pubsub.PubSubService(self.resource)
    12261227        self.service.send = self.stub.xmlstream.send
    12271228
     
    12611262        """
    12621263        def cb(info):
    1263             self.assertEqual(2, len(info))
    1264 
    1265         def getNodeInfo(requestor, target, nodeIdentifier):
    1266             return defer.succeed(None)
    1267 
    1268         self.service.getNodeInfo = getNodeInfo
     1264            discoInfo = disco.DiscoInfo()
     1265            for item in info:
     1266                discoInfo.append(item)
     1267            self.assertIn(('pubsub', 'service'), discoInfo.identities)
     1268            self.assertIn(disco.NS_DISCO_ITEMS, discoInfo.features)
     1269
    12691270        d = self.service.getDiscoInfo(JID('user@example.org/home'),
    12701271                                      JID('pubsub.example.org'), '')
     
    12831284            self.assertIn(('pubsub', 'collection'), discoInfo.identities)
    12841285
    1285         def getNodeInfo(requestor, target, nodeIdentifier):
     1286        def getInfo(requestor, target, nodeIdentifier):
    12861287            return defer.succeed({'type': 'collection',
    12871288                                  'meta-data': {}})
    12881289
    1289         self.service.getNodeInfo = getNodeInfo
     1290        self.resource.getInfo = getInfo
    12901291        d = self.service.getDiscoInfo(JID('user@example.org/home'),
    12911292                                      JID('pubsub.example.org'), '')
     
    13081309            self.assertIn('pubsub#node_type', form.fields)
    13091310
    1310         def getNodeInfo(requestor, target, nodeIdentifier):
     1311        def getInfo(requestor, target, nodeIdentifier):
    13111312            metaData = [{'var': 'pubsub#persist_items',
    13121313                         'label': 'Persist items to storage',
     
    13141315            return defer.succeed({'type': 'leaf', 'meta-data': metaData})
    13151316
    1316         self.service.getNodeInfo = getNodeInfo
     1317        self.resource.getInfo = getInfo
    13171318        d = self.service.getDiscoInfo(JID('user@example.org/home'),
    13181319                                      JID('pubsub.example.org'), '')
     
    13211322
    13221323
    1323     def test_onPublish(self):
     1324    def test_getDiscoInfoResourceFeatures(self):
     1325        """
     1326        Test getDiscoInfo with the resource features.
     1327        """
     1328        def cb(info):
     1329            discoInfo = disco.DiscoInfo()
     1330            for item in info:
     1331                discoInfo.append(item)
     1332            self.assertIn('http://jabber.org/protocol/pubsub#publish',
     1333                          discoInfo.features)
     1334
     1335        self.resource.features = ['publish']
     1336        d = self.service.getDiscoInfo(JID('user@example.org/home'),
     1337                                      JID('pubsub.example.org'), '')
     1338        d.addCallback(cb)
     1339        return d
     1340
     1341
     1342    def test_getDiscoItemsRoot(self):
     1343        """
     1344        Test getDiscoItems on the root node.
     1345        """
     1346        def getNodes(requestor, service, nodeIdentifier):
     1347            return defer.succeed(['node1', 'node2'])
     1348
     1349        def cb(items):
     1350            self.assertEqual(2, len(items))
     1351            item1, item2 = items
     1352
     1353            self.assertEqual(JID('pubsub.example.org'), item1.entity)
     1354            self.assertEqual('node1', item1.nodeIdentifier)
     1355
     1356            self.assertEqual(JID('pubsub.example.org'), item2.entity)
     1357            self.assertEqual('node2', item2.nodeIdentifier)
     1358
     1359        self.resource.getNodes = getNodes
     1360        d = self.service.getDiscoItems(JID('user@example.org/home'),
     1361                                       JID('pubsub.example.org'),
     1362                                       '')
     1363        d.addCallback(cb)
     1364        return d
     1365
     1366
     1367    def test_getDiscoItemsRootHideNodes(self):
     1368        """
     1369        Test getDiscoItems on the root node.
     1370        """
     1371        def getNodes(requestor, service, nodeIdentifier):
     1372            raise Exception("Unexpected call to getNodes")
     1373
     1374        def cb(items):
     1375            self.assertEqual([], items)
     1376
     1377        self.service.hideNodes = True
     1378        self.resource.getNodes = getNodes
     1379        d = self.service.getDiscoItems(JID('user@example.org/home'),
     1380                                       JID('pubsub.example.org'),
     1381                                       '')
     1382        d.addCallback(cb)
     1383        return d
     1384
     1385
     1386    def test_getDiscoItemsNonRoot(self):
     1387        """
     1388        Test getDiscoItems on a non-root node.
     1389        """
     1390        def getNodes(requestor, service, nodeIdentifier):
     1391            return defer.succeed(['node1', 'node2'])
     1392
     1393        def cb(items):
     1394            self.assertEqual(2, len(items))
     1395
     1396        self.resource.getNodes = getNodes
     1397        d = self.service.getDiscoItems(JID('user@example.org/home'),
     1398                                       JID('pubsub.example.org'),
     1399                                       'test')
     1400        d.addCallback(cb)
     1401        return d
     1402
     1403
     1404    def test_on_publish(self):
    13241405        """
    13251406        A publish request should result in L{PubSubService.publish} being
     
    13361417        """
    13371418
    1338         def publish(requestor, service, nodeIdentifier, items):
     1419        def publish(request):
    13391420            return defer.succeed(None)
    13401421
    1341         self.service.publish = publish
    1342         verify.verifyObject(iwokkel.IPubSubService, self.service)
     1422        self.resource.publish = publish
     1423        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
    13431424        return self.handleRequest(xml)
    13441425
    13451426
    1346     def test_onSubscribe(self):
     1427    def test_on_subscribe(self):
    13471428        """
    13481429        A successful subscription should return the current subscription.
     
    13581439        """
    13591440
    1360         def subscribe(requestor, service, nodeIdentifier, subscriber):
    1361             return defer.succeed(pubsub.Subscription(nodeIdentifier,
    1362                                                      subscriber,
     1441        def subscribe(request):
     1442            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
     1443                                                     request.subscriber,
    13631444                                                     'subscribed'))
    13641445
     
    13721453            self.assertEqual('subscribed', subscription['subscription'])
    13731454
    1374         self.service.subscribe = subscribe
    1375         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1376         d = self.handleRequest(xml)
    1377         d.addCallback(cb)
    1378         return d
    1379 
    1380 
    1381     def test_onSubscribeEmptyNode(self):
     1455        self.resource.subscribe = subscribe
     1456        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1457        d = self.handleRequest(xml)
     1458        d.addCallback(cb)
     1459        return d
     1460
     1461
     1462    def test_on_subscribeEmptyNode(self):
    13821463        """
    13831464        A successful subscription on root node should return no node attribute.
     
    13931474        """
    13941475
    1395         def subscribe(requestor, service, nodeIdentifier, subscriber):
    1396             return defer.succeed(pubsub.Subscription(nodeIdentifier,
    1397                                                      subscriber,
     1476        def subscribe(request):
     1477            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
     1478                                                     request.subscriber,
    13981479                                                     'subscribed'))
    13991480
     
    14011482            self.assertFalse(element.subscription.hasAttribute('node'))
    14021483
    1403         self.service.subscribe = subscribe
    1404         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1405         d = self.handleRequest(xml)
    1406         d.addCallback(cb)
    1407         return d
    1408 
    1409 
    1410     def test_onUnsubscribe(self):
     1484        self.resource.subscribe = subscribe
     1485        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1486        d = self.handleRequest(xml)
     1487        d.addCallback(cb)
     1488        return d
     1489
     1490
     1491    def test_on_unsubscribe(self):
    14111492        """
    14121493        A successful unsubscription should return an empty response.
     
    14221503        """
    14231504
    1424         def unsubscribe(requestor, service, nodeIdentifier, subscriber):
     1505        def unsubscribe(request):
    14251506            return defer.succeed(None)
    14261507
     
    14281509            self.assertIdentical(None, element)
    14291510
    1430         self.service.unsubscribe = unsubscribe
    1431         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1432         d = self.handleRequest(xml)
    1433         d.addCallback(cb)
    1434         return d
    1435 
    1436 
    1437     def test_onOptionsGet(self):
     1511        self.resource.unsubscribe = unsubscribe
     1512        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1513        d = self.handleRequest(xml)
     1514        d.addCallback(cb)
     1515        return d
     1516
     1517
     1518    def test_on_optionsGet(self):
    14381519        """
    14391520        Getting subscription options is not supported.
     
    14601541
    14611542
    1462     def test_onOptionsSet(self):
     1543    def test_on_optionsSet(self):
    14631544        """
    14641545        Setting subscription options is not supported.
     
    14921573
    14931574
    1494     def test_onSubscriptions(self):
     1575    def test_on_subscriptions(self):
    14951576        """
    14961577        A subscriptions request should result in
     
    15071588        </iq>
    15081589        """
     1590
     1591        def subscriptions(request):
     1592            subscription = pubsub.Subscription('test', JID('user@example.org'),
     1593                                               'subscribed')
     1594            return defer.succeed([subscription])
    15091595
    15101596        def cb(element):
     
    15211607            self.assertEqual('subscribed', subscription['subscription'])
    15221608
    1523 
    1524         def subscriptions(requestor, service):
    1525             subscription = pubsub.Subscription('test', JID('user@example.org'),
    1526                                                'subscribed')
    1527             return defer.succeed([subscription])
    1528 
    1529         self.service.subscriptions = subscriptions
    1530         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1531         d = self.handleRequest(xml)
    1532         d.addCallback(cb)
    1533         return d
    1534 
    1535 
    1536     def test_onAffiliations(self):
     1609        self.resource.subscriptions = subscriptions
     1610        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1611        d = self.handleRequest(xml)
     1612        d.addCallback(cb)
     1613        return d
     1614
     1615
     1616    def test_on_affiliations(self):
    15371617        """
    15381618        A subscriptions request should result in
     
    15491629        </iq>
    15501630        """
     1631
     1632        def affiliations(request):
     1633            affiliation = ('test', 'owner')
     1634            return defer.succeed([affiliation])
    15511635
    15521636        def cb(element):
     
    15621646            self.assertEqual('owner', affiliation['affiliation'])
    15631647
    1564 
    1565         def affiliations(requestor, service):
    1566             affiliation = ('test', 'owner')
    1567             return defer.succeed([affiliation])
    1568 
    1569         self.service.affiliations = affiliations
    1570         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1571         d = self.handleRequest(xml)
    1572         d.addCallback(cb)
    1573         return d
    1574 
    1575 
    1576     def test_onCreate(self):
     1648        self.resource.affiliations = affiliations
     1649        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1650        d = self.handleRequest(xml)
     1651        d.addCallback(cb)
     1652        return d
     1653
     1654
     1655    def test_on_create(self):
    15771656        """
    15781657        Replies to create node requests don't return the created node.
     
    15881667        """
    15891668
    1590         def create(requestor, service, nodeIdentifier):
    1591             return defer.succeed(nodeIdentifier)
     1669        def create(request):
     1670            return defer.succeed(request.nodeIdentifier)
    15921671
    15931672        def cb(element):
    15941673            self.assertIdentical(None, element)
    15951674
    1596         self.service.create = create
    1597         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1598         d = self.handleRequest(xml)
    1599         d.addCallback(cb)
    1600         return d
    1601 
    1602 
    1603     def test_onCreateChanged(self):
     1675        self.resource.create = create
     1676        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1677        d = self.handleRequest(xml)
     1678        d.addCallback(cb)
     1679        return d
     1680
     1681
     1682    def test_on_createChanged(self):
    16041683        """
    16051684        Replies to create node requests return the created node if changed.
     
    16151694        """
    16161695
    1617         def create(requestor, service, nodeIdentifier):
     1696        def create(request):
    16181697            return defer.succeed(u'myrenamednode')
    16191698
     
    16251704                             element.create.getAttribute('node'))
    16261705
    1627         self.service.create = create
    1628         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1629         d = self.handleRequest(xml)
    1630         d.addCallback(cb)
    1631         return d
    1632 
    1633 
    1634     def test_onCreateInstant(self):
     1706        self.resource.create = create
     1707        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1708        d = self.handleRequest(xml)
     1709        d.addCallback(cb)
     1710        return d
     1711
     1712
     1713    def test_on_createInstant(self):
    16351714        """
    16361715        Replies to create instant node requests return the created node.
     
    16461725        """
    16471726
    1648         def create(requestor, service, nodeIdentifier):
     1727        def create(request):
    16491728            return defer.succeed(u'random')
    16501729
     
    16551734            self.assertEqual(u'random', element.create.getAttribute('node'))
    16561735
    1657         self.service.create = create
    1658         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1659         d = self.handleRequest(xml)
    1660         d.addCallback(cb)
    1661         return d
    1662 
    1663 
    1664     def test_onDefault(self):
     1736        self.resource.create = create
     1737        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1738        d = self.handleRequest(xml)
     1739        d.addCallback(cb)
     1740        return d
     1741
     1742
     1743    def test_on_default(self):
    16651744        """
    16661745        A default request should result in
     
    16871766                }
    16881767
    1689         def getDefaultConfiguration(requestor, service, nodeType):
     1768        def default(request):
    16901769            return defer.succeed({})
    16911770
     
    16971776            self.assertEqual(NS_PUBSUB_CONFIG, form.formNamespace)
    16981777
    1699         self.service.getConfigurationOptions = getConfigurationOptions
    1700         self.service.getDefaultConfiguration = getDefaultConfiguration
    1701         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1702         d = self.handleRequest(xml)
    1703         d.addCallback(cb)
    1704         return d
    1705 
    1706 
    1707     def test_onDefaultCollection(self):
     1778        self.resource.getConfigurationOptions = getConfigurationOptions
     1779        self.resource.default = default
     1780        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1781        d = self.handleRequest(xml)
     1782        d.addCallback(cb)
     1783        return d
     1784
     1785
     1786    def test_on_defaultCollection(self):
    17081787        """
    17091788        Responses to default requests should depend on passed node type.
     
    17361815                }
    17371816
    1738         def getDefaultConfiguration(requestor, service, nodeType):
     1817        def default(request):
    17391818            return defer.succeed({})
    17401819
    1741         self.service.getConfigurationOptions = getConfigurationOptions
    1742         self.service.getDefaultConfiguration = getDefaultConfiguration
    1743         verify.verifyObject(iwokkel.IPubSubService, self.service)
     1820        self.resource.getConfigurationOptions = getConfigurationOptions
     1821        self.resource.default = default
     1822        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
    17441823        return self.handleRequest(xml)
    17451824
    17461825
    1747     def test_onDefaultUnknownNodeType(self):
     1826    def test_on_defaultUnknownNodeType(self):
    17481827        """
    17491828        A default request should result in
    1750         L{PubSubService.getDefaultConfiguration} being called.
     1829        L{PubSubResource.default} being called.
    17511830        """
    17521831
     
    17701849        """
    17711850
    1772         def getDefaultConfiguration(requestor, service, nodeType):
     1851        def default(request):
    17731852            self.fail("Unexpected call to getConfiguration")
    17741853
     
    17761855            self.assertEquals('not-acceptable', result.condition)
    17771856
    1778         self.service.getDefaultConfiguration = getDefaultConfiguration
    1779         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1780         d = self.handleRequest(xml)
    1781         self.assertFailure(d, error.StanzaError)
    1782         d.addCallback(cb)
    1783         return d
    1784 
    1785 
    1786     def test_onConfigureGet(self):
    1787         """
    1788         On a node configuration get request L{PubSubService.getConfiguration}
    1789         is called and results in a data form with the configuration.
     1857        self.resource.default = default
     1858        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1859        d = self.handleRequest(xml)
     1860        self.assertFailure(d, error.StanzaError)
     1861        d.addCallback(cb)
     1862        return d
     1863
     1864
     1865    def test_on_configureGet(self):
     1866        """
     1867        On a node configuration get
     1868        requestL{PubSubResource.configureGet} is called and results in a
     1869        data form with the configuration.
    17901870        """
    17911871
     
    18121892                }
    18131893
    1814         def getConfiguration(requestor, service, nodeIdentifier):
     1894        def configureGet(request):
    18151895            return defer.succeed({'pubsub#deliver_payloads': '0',
    18161896                                  'pubsub#persist_items': '1',
     
    18461926            self.assertNotIn('x-myfield', fields)
    18471927
    1848 
    1849         self.service.getConfigurationOptions = getConfigurationOptions
    1850         self.service.getConfiguration = getConfiguration
    1851         verify.verifyObject(iwokkel.IPubSubService, self.service)
    1852         d = self.handleRequest(xml)
    1853         d.addCallback(cb)
    1854         return d
    1855 
    1856 
    1857     def test_onConfigureSet(self):
     1928        self.resource.getConfigurationOptions = getConfigurationOptions
     1929        self.resource.configureGet = configureGet
     1930        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1931        d = self.handleRequest(xml)
     1932        d.addCallback(cb)
     1933        return d
     1934
     1935
     1936    def test_on_configureSet(self):
    18581937        """
    18591938        On a node configuration set request the Data Form is parsed and
    1860         L{PubSubService.setConfiguration} is called with the passed options.
     1939        L{PubSubResource.configureSet} is called with the passed options.
    18611940        """
    18621941
     
    18881967                }
    18891968
    1890         def setConfiguration(requestor, service, nodeIdentifier, options):
     1969        def configureSet(request):
    18911970            self.assertEqual({'pubsub#deliver_payloads': False,
    1892                               'pubsub#persist_items': True}, options)
     1971                              'pubsub#persist_items': True}, request.options)
    18931972            return defer.succeed(None)
    18941973
    1895         self.service.getConfigurationOptions = getConfigurationOptions
    1896         self.service.setConfiguration = setConfiguration
    1897         verify.verifyObject(iwokkel.IPubSubService, self.service)
     1974        self.resource.getConfigurationOptions = getConfigurationOptions
     1975        self.resource.configureSet = configureSet
     1976        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
    18981977        return self.handleRequest(xml)
    18991978
    19001979
    1901     def test_onConfigureSetCancel(self):
    1902         """
    1903         The node configuration is cancelled, L{PubSubService.setConfiguration}
    1904         not called.
     1980    def test_on_configureSetCancel(self):
     1981        """
     1982        The node configuration is cancelled,
     1983        L{PubSubResource.configureSet} not called.
    19051984        """
    19061985
     
    19201999        """
    19212000
    1922         def setConfiguration(requestor, service, nodeIdentifier, options):
     2001        def configureSet(request):
    19232002            self.fail("Unexpected call to setConfiguration")
    19242003
    1925         self.service.setConfiguration = setConfiguration
    1926         verify.verifyObject(iwokkel.IPubSubService, self.service)
     2004        self.resource.configureSet = configureSet
     2005        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
    19272006        return self.handleRequest(xml)
    19282007
    19292008
    1930     def test_onConfigureSetIgnoreUnknown(self):
     2009    def test_on_configureSetIgnoreUnknown(self):
    19312010        """
    19322011        On a node configuration set request unknown fields should be ignored.
     
    19602039                }
    19612040
    1962         def setConfiguration(requestor, service, nodeIdentifier, options):
    1963             self.assertEquals(['pubsub#deliver_payloads'], options.keys())
    1964 
    1965         self.service.getConfigurationOptions = getConfigurationOptions
    1966         self.service.setConfiguration = setConfiguration
    1967         verify.verifyObject(iwokkel.IPubSubService, self.service)
     2041        def configureSet(request):
     2042            self.assertEquals(['pubsub#deliver_payloads'],
     2043                              request.options.keys())
     2044
     2045        self.resource.getConfigurationOptions = getConfigurationOptions
     2046        self.resource.configureSet = configureSet
     2047        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
    19682048        return self.handleRequest(xml)
    19692049
    19702050
    1971     def test_onConfigureSetBadFormType(self):
     2051    def test_on_configureSetBadFormType(self):
    19722052        """
    19732053        On a node configuration set request unknown fields should be ignored.
     
    20002080
    20012081
    2002     def test_onItems(self):
     2082    def test_on_items(self):
    20032083        """
    20042084        On a items request, return all items for the given node.
     
    20132093        """
    20142094
    2015         def items(requestor, service, nodeIdentifier, maxItems,
    2016                   itemIdentifiers):
     2095        def items(request):
    20172096            return defer.succeed([pubsub.Item('current')])
    20182097
     
    20272106            self.assertEqual('current', item['id'])
    20282107
    2029         self.service.items = items
    2030         d = self.handleRequest(xml)
    2031         d.addCallback(cb)
    2032         return d
    2033 
    2034 
    2035     def test_onRetract(self):
    2036         """
    2037         A retract request should result in L{PubSubService.retract} being
    2038         called.
     2108        self.resource.items = items
     2109        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     2110        d = self.handleRequest(xml)
     2111        d.addCallback(cb)
     2112        return d
     2113
     2114
     2115    def test_on_retract(self):
     2116        """
     2117        A retract request should result in L{PubSubResource.retract}
     2118        being called.
    20392119        """
    20402120
     
    20512131        """
    20522132
    2053         def retract(requestor, service, nodeIdentifier, itemIdentifiers):
     2133        def retract(request):
    20542134            return defer.succeed(None)
    20552135
    2056         self.service.retract = retract
     2136        self.resource.retract = retract
     2137        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
    20572138        return self.handleRequest(xml)
    20582139
    20592140
    2060     def test_onPurge(self):
    2061         """
    2062         A purge request should result in L{PubSubService.purge} being
     2141    def test_on_purge(self):
     2142        """
     2143        A purge request should result in L{PubSubResource.purge} being
    20632144        called.
    20642145        """
     
    20732154        """
    20742155
    2075         def purge(requestor, service, nodeIdentifier):
     2156        def purge(request):
    20762157            return defer.succeed(None)
    20772158
    2078         self.service.purge = purge
     2159        self.resource.purge = purge
     2160        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
    20792161        return self.handleRequest(xml)
    20802162
    20812163
    2082     def test_onDelete(self):
    2083         """
    2084         A delete request should result in L{PubSubService.delete} being
     2164    def test_on_delete(self):
     2165        """
     2166        A delete request should result in L{PubSubResource.delete} being
    20852167        called.
    20862168        """
     
    20952177        """
    20962178
    2097         def delete(requestor, service, nodeIdentifier):
     2179        def delete(request):
    20982180            return defer.succeed(None)
    20992181
    2100         self.service.delete = delete
     2182        self.resource.delete = delete
     2183        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
    21012184        return self.handleRequest(xml)
    21022185
     
    21492232
    21502233
    2151     def test_onSubscriptionsGet(self):
     2234    def test_on_subscriptionsGet(self):
    21522235        """
    21532236        Getting subscription options is not supported.
     
    21762259
    21772260
    2178     def test_onSubscriptionsSet(self):
     2261    def test_on_subscriptionsSet(self):
    21792262        """
    21802263        Setting subscription options is not supported.
     
    22032286
    22042287
    2205     def test_onAffiliationsGet(self):
     2288    def test_on_affiliationsGet(self):
    22062289        """
    22072290        Getting subscription options is not supported.
     
    22302313
    22312314
    2232     def test_onAffiliationsSet(self):
     2315    def test_on_affiliationsSet(self):
    22332316        """
    22342317        Setting subscription options is not supported.
     
    22552338        d.addCallback(cb)
    22562339        return d
     2340
     2341
     2342
     2343class PubSubServiceWithoutResourceTest(unittest.TestCase, TestableRequestHandlerMixin):
     2344
     2345    def setUp(self):
     2346        self.stub = XmlStreamStub()
     2347        self.service = pubsub.PubSubService()
     2348        self.service.send = self.stub.xmlstream.send
    22572349
    22582350
     
    26052697        d.addCallback(cb)
    26062698        return d
     2699
     2700
     2701
     2702class PubSubResourceTest(unittest.TestCase):
     2703
     2704    def setUp(self):
     2705        self.resource = pubsub.PubSubResource()
     2706
     2707
     2708    def test_interface(self):
     2709        """
     2710        Do instances of L{pubsub.PubSubResource} provide L{iwokkel.IPubSubResource}?
     2711        """
     2712        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     2713
     2714
     2715    def test_getNodes(self):
     2716        """
     2717        Default getNodes returns an empty list.
     2718        """
     2719        def cb(nodes):
     2720            self.assertEquals([], nodes)
     2721
     2722        d = self.resource.getNodes(JID('user@example.org/home'),
     2723                                   JID('pubsub.example.org'),
     2724                                   '')
     2725        d.addCallback(cb)
     2726        return d
     2727
     2728
     2729    def test_publish(self):
     2730        """
     2731        Non-overridden L{PubSubResource.publish} yields unsupported
     2732        error.
     2733        """
     2734
     2735        def cb(result):
     2736            self.assertEquals('feature-not-implemented', result.condition)
     2737            self.assertEquals('unsupported', result.appCondition.name)
     2738            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2739            self.assertEquals('publish', result.appCondition['feature'])
     2740
     2741        d = self.resource.publish(pubsub.PubSubRequest())
     2742        self.assertFailure(d, error.StanzaError)
     2743        d.addCallback(cb)
     2744        return d
     2745
     2746
     2747    def test_subscribe(self):
     2748        """
     2749        Non-overridden subscriptions yields unsupported error.
     2750        """
     2751
     2752        def cb(result):
     2753            self.assertEquals('feature-not-implemented', result.condition)
     2754            self.assertEquals('unsupported', result.appCondition.name)
     2755            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2756            self.assertEquals('subscribe', result.appCondition['feature'])
     2757
     2758        d = self.resource.subscribe(pubsub.PubSubRequest())
     2759        self.assertFailure(d, error.StanzaError)
     2760        d.addCallback(cb)
     2761        return d
     2762
     2763
     2764    def test_unsubscribe(self):
     2765        """
     2766        Non-overridden unsubscribe yields unsupported error.
     2767        """
     2768
     2769        def cb(result):
     2770            self.assertEquals('feature-not-implemented', result.condition)
     2771            self.assertEquals('unsupported', result.appCondition.name)
     2772            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2773            self.assertEquals('subscribe', result.appCondition['feature'])
     2774
     2775        d = self.resource.unsubscribe(pubsub.PubSubRequest())
     2776        self.assertFailure(d, error.StanzaError)
     2777        d.addCallback(cb)
     2778        return d
     2779
     2780
     2781    def test_subscriptions(self):
     2782        """
     2783        Non-overridden subscriptions yields unsupported error.
     2784        """
     2785
     2786        def cb(result):
     2787            self.assertEquals('feature-not-implemented', result.condition)
     2788            self.assertEquals('unsupported', result.appCondition.name)
     2789            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2790            self.assertEquals('retrieve-subscriptions',
     2791                              result.appCondition['feature'])
     2792
     2793        d = self.resource.subscriptions(pubsub.PubSubRequest())
     2794        self.assertFailure(d, error.StanzaError)
     2795        d.addCallback(cb)
     2796        return d
     2797
     2798
     2799    def test_affiliations(self):
     2800        """
     2801        Non-overridden affiliations yields unsupported error.
     2802        """
     2803
     2804        def cb(result):
     2805            self.assertEquals('feature-not-implemented', result.condition)
     2806            self.assertEquals('unsupported', result.appCondition.name)
     2807            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2808            self.assertEquals('retrieve-affiliations',
     2809                              result.appCondition['feature'])
     2810
     2811        d = self.resource.affiliations(pubsub.PubSubRequest())
     2812        self.assertFailure(d, error.StanzaError)
     2813        d.addCallback(cb)
     2814        return d
     2815
     2816
     2817    def test_create(self):
     2818        """
     2819        Non-overridden create yields unsupported error.
     2820        """
     2821
     2822        def cb(result):
     2823            self.assertEquals('feature-not-implemented', result.condition)
     2824            self.assertEquals('unsupported', result.appCondition.name)
     2825            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2826            self.assertEquals('create-nodes', result.appCondition['feature'])
     2827
     2828        d = self.resource.create(pubsub.PubSubRequest())
     2829        self.assertFailure(d, error.StanzaError)
     2830        d.addCallback(cb)
     2831        return d
     2832
     2833
     2834    def test_default(self):
     2835        """
     2836        Non-overridden default yields unsupported error.
     2837        """
     2838
     2839        def cb(result):
     2840            self.assertEquals('feature-not-implemented', result.condition)
     2841            self.assertEquals('unsupported', result.appCondition.name)
     2842            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2843            self.assertEquals('retrieve-default',
     2844                              result.appCondition['feature'])
     2845
     2846        d = self.resource.default(pubsub.PubSubRequest())
     2847        self.assertFailure(d, error.StanzaError)
     2848        d.addCallback(cb)
     2849        return d
     2850
     2851
     2852    def test_configureGet(self):
     2853        """
     2854        Non-overridden configureGet yields unsupported
     2855        error.
     2856        """
     2857
     2858        def cb(result):
     2859            self.assertEquals('feature-not-implemented', result.condition)
     2860            self.assertEquals('unsupported', result.appCondition.name)
     2861            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2862            self.assertEquals('config-node', result.appCondition['feature'])
     2863
     2864        d = self.resource.configureGet(pubsub.PubSubRequest())
     2865        self.assertFailure(d, error.StanzaError)
     2866        d.addCallback(cb)
     2867        return d
     2868
     2869
     2870    def test_configureSet(self):
     2871        """
     2872        Non-overridden configureSet yields unsupported error.
     2873        """
     2874
     2875        def cb(result):
     2876            self.assertEquals('feature-not-implemented', result.condition)
     2877            self.assertEquals('unsupported', result.appCondition.name)
     2878            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2879            self.assertEquals('config-node', result.appCondition['feature'])
     2880
     2881        d = self.resource.configureSet(pubsub.PubSubRequest())
     2882        self.assertFailure(d, error.StanzaError)
     2883        d.addCallback(cb)
     2884        return d
     2885
     2886
     2887    def test_items(self):
     2888        """
     2889        Non-overridden items yields unsupported error.
     2890        """
     2891
     2892        def cb(result):
     2893            self.assertEquals('feature-not-implemented', result.condition)
     2894            self.assertEquals('unsupported', result.appCondition.name)
     2895            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2896            self.assertEquals('retrieve-items', result.appCondition['feature'])
     2897
     2898        d = self.resource.items(pubsub.PubSubRequest())
     2899        self.assertFailure(d, error.StanzaError)
     2900        d.addCallback(cb)
     2901        return d
     2902
     2903
     2904    def test_retract(self):
     2905        """
     2906        Non-overridden retract yields unsupported error.
     2907        """
     2908
     2909        def cb(result):
     2910            self.assertEquals('feature-not-implemented', result.condition)
     2911            self.assertEquals('unsupported', result.appCondition.name)
     2912            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2913            self.assertEquals('retract-items', result.appCondition['feature'])
     2914
     2915        d = self.resource.retract(pubsub.PubSubRequest())
     2916        self.assertFailure(d, error.StanzaError)
     2917        d.addCallback(cb)
     2918        return d
     2919
     2920
     2921    def test_purge(self):
     2922        """
     2923        Non-overridden purge yields unsupported error.
     2924        """
     2925
     2926        def cb(result):
     2927            self.assertEquals('feature-not-implemented', result.condition)
     2928            self.assertEquals('unsupported', result.appCondition.name)
     2929            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2930            self.assertEquals('purge-nodes', result.appCondition['feature'])
     2931
     2932        d = self.resource.purge(pubsub.PubSubRequest())
     2933        self.assertFailure(d, error.StanzaError)
     2934        d.addCallback(cb)
     2935        return d
     2936
     2937
     2938    def test_delete(self):
     2939        """
     2940        Non-overridden delete yields unsupported error.
     2941        """
     2942
     2943        def cb(result):
     2944            self.assertEquals('feature-not-implemented', result.condition)
     2945            self.assertEquals('unsupported', result.appCondition.name)
     2946            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     2947            self.assertEquals('delete-nodes', result.appCondition['feature'])
     2948
     2949        d = self.resource.delete(pubsub.PubSubRequest())
     2950        self.assertFailure(d, error.StanzaError)
     2951        d.addCallback(cb)
     2952        return d
Note: See TracChangeset for help on using the changeset viewer.