source: ralphm-patches/pubsub_request.patch @ 4:c8c73d9e5496

Last change on this file since 4:c8c73d9e5496 was 4:c8c73d9e5496, checked in by Ralph Meijer <ralphm@…>, 11 years ago

Save bunch of work.

File size: 87.0 KB
  • wokkel/generic.py

    Add PubSubRequest that parses requests and replaces separate parameters.
    
    The publish-subscribe protocol has a large number of optional features. Adding
    support for a feature often means more information needs to be passed, and
    required changing the signature of the methods that need to be overridden by
    adaptors.
    
    This change adds the new PubSubRequest class that features a fromElement
    method that creates a new instance, parses a request and then fills the
    instance attributes where appropriate. This instance is then the only argument
    that is passed to the corresponding handler methods.
    
    diff -r 884ac8d88411 wokkel/generic.py
    a b  
    1010from zope.interface import implements
    1111
    1212from twisted.internet import defer
    13 from twisted.words.protocols.jabber import error
     13from twisted.words.protocols.jabber import error, jid
    1414from twisted.words.protocols.jabber.xmlstream import toResponse
    1515from twisted.words.xish import domish, utility
    1616
     
    155155        self.sink = utility.EventDispatcher()
    156156        self.source.send = lambda obj: self.sink.dispatch(obj)
    157157        self.sink.send = lambda obj: self.source.dispatch(obj)
     158
     159
     160
     161class Stanza(object):
     162    """
     163    Abstract representation of a stanza.
     164
     165    @ivar sender: The sending entity.
     166    @type sender: L{jid.JID}
     167    @ivar recipient: The receiving entity.
     168    @type recipient: L{jid.JID}
     169    """
     170
     171    sender = None
     172    recipient = None
     173    stanzaType = None
     174
     175    @classmethod
     176    def fromElement(Class, element):
     177        stanza = Class()
     178        stanza.parseElement(element)
     179        return stanza
     180
     181
     182    def parseElement(self, element):
     183        self.sender = jid.internJID(element['from'])
     184        if element.hasAttribute('from'):
     185            self.sender = jid.internJID(element['from'])
     186        if element.hasAttribute('to'):
     187            self.recipient = jid.internJID(element['to'])
     188        self.stanzaType = element.getAttribute('type')
  • wokkel/iwokkel.py

    diff -r 884ac8d88411 wokkel/iwokkel.py
    a b  
    278278                             C{list} of L{domish.Element})
    279279        """
    280280
     281
    281282    def notifyDelete(service, nodeIdentifier, subscribers,
    282283                     redirectURI=None):
    283284        """
     
    295296        @type redirectURI: C{str}
    296297        """
    297298
    298     def publish(requestor, service, nodeIdentifier, items):
     299
     300    def publish(request):
    299301        """
    300302        Called when a publish request has been received.
    301303
    302         @param requestor: The entity the request originated from.
    303         @type requestor: L{jid.JID}
    304         @param service: The entity the request was addressed to.
    305         @type service: L{jid.JID}
    306         @param nodeIdentifier: The identifier of the node to publish to.
    307         @type nodeIdentifier: C{unicode}
    308         @param items: The items to be published as L{domish} elements.
    309         @type items: C{list} of C{domish.Element}
     304        @param request: The publish-subscribe request.
     305        @type request: L{wokkel.pubsub.PubSubRequest}
    310306        @return: deferred that fires on success.
    311307        @rtype: L{defer.Deferred}
    312308        """
    313309
    314     def subscribe(requestor, service, nodeIdentifier, subscriber):
     310
     311    def subscribe(request):
    315312        """
    316313        Called when a subscribe request has been received.
    317314
    318         @param requestor: The entity the request originated from.
    319         @type requestor: L{jid.JID}
    320         @param service: The entity the request was addressed to.
    321         @type service: L{jid.JID}
    322         @param nodeIdentifier: The identifier of the node to subscribe to.
    323         @type nodeIdentifier: C{unicode}
    324         @param subscriber: The entity to be subscribed.
    325         @type subscriber: L{jid.JID}
     315        @param request: The publish-subscribe request.
     316        @type request: L{wokkel.pubsub.PubSubRequest}
    326317        @return: A deferred that fires with a
    327318                 L{Subscription<wokkel.pubsub.Subscription>}.
    328319        @rtype: L{defer.Deferred}
    329320        """
    330321
    331     def unsubscribe(requestor, service, nodeIdentifier, subscriber):
     322
     323    def unsubscribe(request):
    332324        """
    333325        Called when a subscribe request has been received.
    334326
    335         @param requestor: The entity the request originated from.
    336         @type requestor: L{jid.JID}
    337         @param service: The entity the request was addressed to.
    338         @type service: L{jid.JID}
    339         @param nodeIdentifier: The identifier of the node to unsubscribe from.
    340         @type nodeIdentifier: C{unicode}
    341         @param subscriber: The entity to be unsubscribed.
    342         @type subscriber: L{jid.JID}
     327        @param request: The publish-subscribe request.
     328        @type request: L{wokkel.pubsub.PubSubRequest}
    343329        @return: A deferred that fires with C{None} when unsubscription has
    344330                 succeeded.
    345331        @rtype: L{defer.Deferred}
    346332        """
    347333
    348     def subscriptions(requestor, service):
     334
     335    def subscriptions(request):
    349336        """
    350337        Called when a subscriptions retrieval request has been received.
    351338
    352         @param requestor: The entity the request originated from.
    353         @type requestor: L{jid.JID}
    354         @param service: The entity the request was addressed to.
    355         @type service: L{jid.JID}
     339        @param request: The publish-subscribe request.
     340        @type request: L{wokkel.pubsub.PubSubRequest}
    356341        @return: A deferred that fires with a C{list} of subscriptions as
    357342                 L{Subscription<wokkel.pubsub.Subscription>}.
    358343        @rtype: L{defer.Deferred}
    359344        """
    360345
    361     def affiliations(requestor, service):
     346
     347    def affiliations(request):
    362348        """
    363349        Called when a affiliations retrieval request has been received.
    364350
    365         @param requestor: The entity the request originated from.
    366         @type requestor: L{jid.JID}
    367         @param service: The entity the request was addressed to.
    368         @type service: L{jid.JID}
     351        @param request: The publish-subscribe request.
     352        @type request: L{wokkel.pubsub.PubSubRequest}
    369353        @return: A deferred that fires with a C{list} of affiliations as
    370354                 C{tuple}s of (node identifier as C{unicode}, affiliation state
    371355                 as C{str}). The affiliation can be C{'owner'}, C{'publisher'},
     
    373357        @rtype: L{defer.Deferred}
    374358        """
    375359
    376     def create(requestor, service, nodeIdentifier):
     360
     361    def create(request):
    377362        """
    378363        Called when a node creation request has been received.
    379364
    380         @param requestor: The entity the request originated from.
    381         @type requestor: L{jid.JID}
    382         @param service: The entity the request was addressed to.
    383         @type service: L{jid.JID}
    384         @param nodeIdentifier: The suggestion for the identifier of the node to
    385                                be created. If the request did not include a
    386                                suggestion for the node identifier, the value
    387                                is C{None}.
    388         @type nodeIdentifier: C{unicode} or C{NoneType}
     365        @param request: The publish-subscribe request.
     366        @type request: L{wokkel.pubsub.PubSubRequest}
    389367        @return: A deferred that fires with a C{unicode} that represents
    390368                 the identifier of the new node.
    391369        @rtype: L{defer.Deferred}
    392370        """
    393371
     372
    394373    def getConfigurationOptions():
    395374        """
    396375        Retrieve all known node configuration options.
     
    426405        @rtype: C{dict}.
    427406        """
    428407
    429     def getDefaultConfiguration(requestor, service, nodeType):
     408
     409    def getDefaultConfiguration(request):
    430410        """
    431411        Called when a default node configuration request has been received.
    432412
    433         @param requestor: The entity the request originated from.
    434         @type requestor: L{jid.JID}
    435         @param service: The entity the request was addressed to.
    436         @type service: L{jid.JID}
    437         @param nodeType: The type of node for which the configuration is
    438                          retrieved, C{'leaf'} or C{'collection'}.
    439         @type nodeType: C{str}
     413        @param request: The publish-subscribe request.
     414        @type request: L{wokkel.pubsub.PubSubRequest}
    440415        @return: A deferred that fires with a C{dict} representing the default
    441416                 node configuration. Keys are C{str}s that represent the
    442417                 field name. Values can be of types C{unicode}, C{int} or
     
    444419        @rtype: L{defer.Deferred}
    445420        """
    446421
    447     def getConfiguration(requestor, service, nodeIdentifier):
     422
     423    def getConfiguration(request):
    448424        """
    449425        Called when a node configuration retrieval request has been received.
    450426
    451         @param requestor: The entity the request originated from.
    452         @type requestor: L{jid.JID}
    453         @param service: The entity the request was addressed to.
    454         @type service: L{jid.JID}
    455         @param nodeIdentifier: The identifier of the node to retrieve the
    456                                configuration from.
    457         @type nodeIdentifier: C{unicode}
     427        @param request: The publish-subscribe request.
     428        @type request: L{wokkel.pubsub.PubSubRequest}
    458429        @return: A deferred that fires with a C{dict} representing the node
    459430                 configuration. Keys are C{str}s that represent the field name.
    460431                 Values can be of types C{unicode}, C{int} or C{bool}.
    461432        @rtype: L{defer.Deferred}
    462433        """
    463434
    464     def setConfiguration(requestor, service, nodeIdentifier, options):
     435
     436    def setConfiguration(request):
    465437        """
    466438        Called when a node configuration change request has been received.
    467439
    468         @param requestor: The entity the request originated from.
    469         @type requestor: L{jid.JID}
    470         @param service: The entity the request was addressed to.
    471         @type service: L{jid.JID}
    472         @param nodeIdentifier: The identifier of the node to change the
    473                                configuration of.
    474         @type nodeIdentifier: C{unicode}
     440        @param request: The publish-subscribe request.
     441        @type request: L{wokkel.pubsub.PubSubRequest}
    475442        @return: A deferred that fires with C{None} when the node's
    476443                 configuration has been changed.
    477444        @rtype: L{defer.Deferred}
    478445        """
    479446
    480     def items(requestor, service, nodeIdentifier, maxItems, itemIdentifiers):
     447
     448    def items(request):
    481449        """
    482450        Called when a items retrieval request has been received.
    483451
    484         @param requestor: The entity the request originated from.
    485         @type requestor: L{jid.JID}
    486         @param service: The entity the request was addressed to.
    487         @type service: L{jid.JID}
    488         @param nodeIdentifier: The identifier of the node to retrieve items
    489                                from.
    490         @type nodeIdentifier: C{unicode}
     452        @param request: The publish-subscribe request.
     453        @type request: L{wokkel.pubsub.PubSubRequest}
     454        @return: A deferred that fires with a C{list} of L{pubsub.Item}.
     455        @rtype: L{defer.Deferred}
    491456        """
    492457
    493     def retract(requestor, service, nodeIdentifier, itemIdentifiers):
     458
     459    def retract(request):
    494460        """
    495461        Called when a item retraction request has been received.
    496462
    497         @param requestor: The entity the request originated from.
    498         @type requestor: L{jid.JID}
    499         @param service: The entity the request was addressed to.
    500         @type service: L{jid.JID}
    501         @param nodeIdentifier: The identifier of the node to retract items
    502                                from.
    503         @type nodeIdentifier: C{unicode}
     463        @param request: The publish-subscribe request.
     464        @type request: L{wokkel.pubsub.PubSubRequest}
     465        @return: A deferred that fires with C{None} when the given items have
     466                 been retracted.
     467        @rtype: L{defer.Deferred}
    504468        """
    505469
    506     def purge(requestor, service, nodeIdentifier):
     470
     471    def purge(request):
    507472        """
    508473        Called when a node purge request has been received.
    509474
    510         @param requestor: The entity the request originated from.
    511         @type requestor: L{jid.JID}
    512         @param service: The entity the request was addressed to.
    513         @type service: L{jid.JID}
    514         @param nodeIdentifier: The identifier of the node to be purged.
    515         @type nodeIdentifier: C{unicode}
     475        @param request: The publish-subscribe request.
     476        @type request: L{wokkel.pubsub.PubSubRequest}
     477        @return: A deferred that fires with C{None} when the node has been
     478                 purged.
     479        @rtype: L{defer.Deferred}
    516480        """
    517481
    518     def delete(requestor, service, nodeIdentifier):
     482
     483    def delete(request):
    519484        """
    520485        Called when a node deletion request has been received.
    521486
    522         @param requestor: The entity the request originated from.
    523         @type requestor: L{jid.JID}
    524         @param service: The entity the request was addressed to.
    525         @type service: L{jid.JID}
    526         @param nodeIdentifier: The identifier of the node to be delete.
    527         @type nodeIdentifier: C{unicode}
     487        @param request: The publish-subscribe request.
     488        @type request: L{wokkel.pubsub.PubSubRequest}
     489        @return: A deferred that fires with C{None} when the node has been
     490                 deleted.
     491        @rtype: L{defer.Deferred}
    528492        """
  • wokkel/pubsub.py

    diff -r 884ac8d88411 wokkel/pubsub.py
    a b  
    1616from twisted.words.protocols.jabber import jid, error, xmlstream
    1717from twisted.words.xish import domish
    1818
    19 from wokkel import disco, data_form, shim
     19from wokkel import disco, data_form, generic, shim
    2020from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
    2121from wokkel.iwokkel import IPubSubClient, IPubSubService
    2222
     
    3232NS_PUBSUB_NODE_CONFIG = NS_PUBSUB + "#node_config"
    3333NS_PUBSUB_META_DATA = NS_PUBSUB + "#meta-data"
    3434
    35 # In publish-subscribe namespace XPath query selector.
    36 IN_NS_PUBSUB = '[@xmlns="' + NS_PUBSUB + '"]'
    37 IN_NS_PUBSUB_OWNER = '[@xmlns="' + NS_PUBSUB_OWNER + '"]'
    38 
    39 # Publish-subscribe XPath queries
    40 PUBSUB_ELEMENT = '/pubsub' + IN_NS_PUBSUB
    41 PUBSUB_OWNER_ELEMENT = '/pubsub' + IN_NS_PUBSUB_OWNER
    42 PUBSUB_GET = IQ_GET + PUBSUB_ELEMENT
    43 PUBSUB_SET = IQ_SET + PUBSUB_ELEMENT
    44 PUBSUB_OWNER_GET = IQ_GET + PUBSUB_OWNER_ELEMENT
    45 PUBSUB_OWNER_SET = IQ_SET + PUBSUB_OWNER_ELEMENT
    46 
    47 # Publish-subscribe command XPath queries
    48 PUBSUB_PUBLISH = PUBSUB_SET + '/publish' + IN_NS_PUBSUB
    49 PUBSUB_CREATE = PUBSUB_SET + '/create' + IN_NS_PUBSUB
    50 PUBSUB_SUBSCRIBE = PUBSUB_SET + '/subscribe' + IN_NS_PUBSUB
    51 PUBSUB_UNSUBSCRIBE = PUBSUB_SET + '/unsubscribe' + IN_NS_PUBSUB
    52 PUBSUB_OPTIONS_GET = PUBSUB_GET + '/options' + IN_NS_PUBSUB
    53 PUBSUB_OPTIONS_SET = PUBSUB_SET + '/options' + IN_NS_PUBSUB
    54 PUBSUB_DEFAULT = PUBSUB_OWNER_GET + '/default' + IN_NS_PUBSUB_OWNER
    55 PUBSUB_CONFIGURE_GET = PUBSUB_OWNER_GET + '/configure' + IN_NS_PUBSUB_OWNER
    56 PUBSUB_CONFIGURE_SET = PUBSUB_OWNER_SET + '/configure' + IN_NS_PUBSUB_OWNER
    57 PUBSUB_SUBSCRIPTIONS = PUBSUB_GET + '/subscriptions' + IN_NS_PUBSUB
    58 PUBSUB_AFFILIATIONS = PUBSUB_GET + '/affiliations' + IN_NS_PUBSUB
    59 PUBSUB_AFFILIATIONS_GET = PUBSUB_OWNER_GET + '/affiliations' + \
    60                           IN_NS_PUBSUB_OWNER
    61 PUBSUB_AFFILIATIONS_SET = PUBSUB_OWNER_SET + '/affiliations' + \
    62                           IN_NS_PUBSUB_OWNER
    63 PUBSUB_SUBSCRIPTIONS_GET = PUBSUB_OWNER_GET + '/subscriptions' + \
    64                           IN_NS_PUBSUB_OWNER
    65 PUBSUB_SUBSCRIPTIONS_SET = PUBSUB_OWNER_SET + '/subscriptions' + \
    66                           IN_NS_PUBSUB_OWNER
    67 PUBSUB_ITEMS = PUBSUB_GET + '/items' + IN_NS_PUBSUB
    68 PUBSUB_RETRACT = PUBSUB_SET + '/retract' + IN_NS_PUBSUB
    69 PUBSUB_PURGE = PUBSUB_OWNER_SET + '/purge' + IN_NS_PUBSUB_OWNER
    70 PUBSUB_DELETE = PUBSUB_OWNER_SET + '/delete' + IN_NS_PUBSUB_OWNER
     35# XPath to match pubsub requests
     36PUBSUB_REQUEST = '/iq[@type="get" or @type="set"]/' + \
     37                    'pubsub[@xmlns="' + NS_PUBSUB + '" or ' + \
     38                           '@xmlns="' + NS_PUBSUB_OWNER + '"]'
    7139
    7240class SubscriptionPending(Exception):
    7341    """
     
    167135
    168136
    169137
    170 class _PubSubRequest(xmlstream.IQ):
     138class PubSubRequest(generic.Stanza):
    171139    """
    172     Publish subscribe request.
     140    A publish-subscribe request.
    173141
    174     @ivar verb: Request verb
    175     @type verb: C{str}
    176     @ivar namespace: Request namespace.
    177     @type namespace: C{str}
    178     @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'}
    179     @type method: C{str}
    180     @ivar command: Command element of the request. This is the direct child of
    181                    the C{pubsub} element in the C{namespace} with the name
    182                    C{verb}.
     142    The set of instance variables used depends on the type of request. If
     143    a variable is not applicable or not passed in the request, its value is
     144    C{None}.
     145
     146    @ivar verb: The type of publish-subscribe request. See L{_requestVerbMap}.
     147    @type verb: C{str}.
     148
     149    @ivar affiliations: Affiliations to be modified.
     150    @type affiliations: C{set}
     151    @ivar items: The items to be published, as L{domish.Element}s.
     152    @type items: C{list}
     153    @ivar itemIdentifiers: Identifiers of the items to be retrieved or
     154                           retracted.
     155    @type itemIdentifiers: C{set}
     156    @ivar maxItems: Maximum number of items to retrieve.
     157    @type maxItems: C{int}.
     158    @ivar nodeIdentifier: Identifier of the node the request is about.
     159    @type nodeIdentifier: C{unicode}
     160    @ivar nodeType: The type of node that should be created, or for which the
     161                    configuration is retrieved. C{'leaf'} or C{'collection'}.
     162    @type nodeType: C{str}
     163    @ivar options: Configurations options for nodes, subscriptions and publish
     164                   requests.
     165    @type options: L{data_form.Form}
     166    @ivar subscriber: The subscribing entity.
     167    @type subscriber: L{JID}
     168    @ivar subscriptionIdentifier: Identifier for a specific subscription.
     169    @type subscriptionIdentifier: C{unicode}
     170    @ivar subscriptions: Subscriptions to be modified, as a set of
     171                         L{Subscription}.
     172    @type subscriptions: C{set}
    183173    """
    184174
    185     def __init__(self, xs, verb, namespace=NS_PUBSUB, method='set'):
    186         xmlstream.IQ.__init__(self, xs, method)
    187         self.addElement((namespace, 'pubsub'))
     175    verb = None
    188176
    189         self.command = self.pubsub.addElement(verb)
     177    affiliations = None
     178    items = None
     179    itemIdentifiers = None
     180    maxItems = None
     181    nodeIdentifier = None
     182    nodeType = None
     183    options = None
     184    subscriber = None
     185    subscriptionIdentifier = None
     186    subscriptions = None
    190187
     188    # Map request iq type and subelement name to request verb
     189    _requestVerbMap = {
     190        ('set', NS_PUBSUB, 'publish'): 'publish',
     191        ('set', NS_PUBSUB, 'subscribe'): 'subscribe',
     192        ('set', NS_PUBSUB, 'unsubscribe'): 'unsubscribe',
     193        ('get', NS_PUBSUB, 'options'): 'optionsGet',
     194        ('set', NS_PUBSUB, 'options'): 'optionsSet',
     195        ('get', NS_PUBSUB, 'subscriptions'): 'subscriptions',
     196        ('get', NS_PUBSUB, 'affiliations'): 'affiliations',
     197        ('set', NS_PUBSUB, 'create'): 'create',
     198        ('get', NS_PUBSUB_OWNER, 'default'): 'default',
     199        ('get', NS_PUBSUB_OWNER, 'configure'): 'configureGet',
     200        ('set', NS_PUBSUB_OWNER, 'configure'): 'configureSet',
     201        ('get', NS_PUBSUB, 'items'): 'items',
     202        ('set', NS_PUBSUB, 'retract'): 'retract',
     203        ('set', NS_PUBSUB_OWNER, 'purge'): 'purge',
     204        ('set', NS_PUBSUB_OWNER, 'delete'): 'delete',
     205        ('get', NS_PUBSUB_OWNER, 'affiliations'): 'affiliationsGet',
     206        ('set', NS_PUBSUB_OWNER, 'affiliations'): 'affiliationsSet',
     207        ('get', NS_PUBSUB_OWNER, 'subscriptions'): 'subscriptionsGet',
     208        ('set', NS_PUBSUB_OWNER, 'subscriptions'): 'subscriptionsSet',
     209    }
    191210
    192     def send(self, to):
     211    # Map request verb to request iq type and subelement name
     212    _verbRequestMap = dict(((v, k) for k, v in _requestVerbMap.iteritems()))
     213
     214    # Map request verb to parameter handler names
     215    _parameters = {
     216        'publish': ['node', 'items'],
     217        'subscribe': ['nodeOrEmpty', 'jid'],
     218        'unsubscribe': ['nodeOrEmpty', 'jid'],
     219        'optionsGet': [],
     220        'optionsSet': [],
     221        'subscriptions': [],
     222        'affiliations': [],
     223        'create': ['nodeOrNone'],
     224        'default': ['default'],
     225        'configureGet': ['nodeOrEmpty'],
     226        'configureSet': ['nodeOrEmpty', 'configure'],
     227        'items': ['node', 'maxItems', 'itemIdentifiers'],
     228        'retract': ['node', 'itemIdentifiers'],
     229        'purge': ['node'],
     230        'delete': ['node'],
     231        'affiliationsGet': [],
     232        'affiliationsSet': [],
     233        'subscriptionsGet': [],
     234        'subscriptionsSet': [],
     235    }
     236
     237    def __init__(self, verb=None):
     238        self.verb = verb
     239
     240
     241    @staticmethod
     242    def _findForm(element, formNamespace):
    193243        """
    194         Send out request.
     244        Find a Data Form.
    195245
    196         Extends L{xmlstream.IQ.send} by requiring the C{to} parameter to be
    197         a L{JID} instance.
     246        Look for an element that represents a Data Form with the specified
     247        form namespace as a child element of the given element.
     248        """
     249        if not element:
     250            return None
    198251
    199         @param to: Entity to send the request to.
    200         @type to: L{JID}
     252        form = None
     253        for child in element.elements():
     254            try:
     255                form = data_form.Form.fromElement(child)
     256            except data_form.Error:
     257                continue
     258
     259            if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
     260                continue
     261
     262        return form
     263
     264
     265    def _parse_node(self, verbElement):
    201266        """
    202         destination = to.full()
    203         return xmlstream.IQ.send(self, destination)
     267        Parse the required node identifier out of the verbElement.
     268        """
     269        try:
     270            self.nodeIdentifier = verbElement["node"]
     271        except KeyError:
     272            raise BadRequest('nodeid-required')
     273
     274
     275    def _render_node(self, verbElement):
     276        """
     277        Render the required node identifier on the verbElement.
     278        """
     279        if not self.nodeIdentifier:
     280            raise Exception("Node identifier is required")
     281
     282        verbElement['node'] = self.nodeIdentifier
     283
     284
     285    def _parse_nodeOrEmpty(self, verbElement):
     286        """
     287        Parse the node identifier out of the verbElement. May be empty.
     288        """
     289        self.nodeIdentifier = verbElement.getAttribute("node", '')
     290
     291
     292    def _render_nodeOrEmpty(self, verbElement):
     293        """
     294        Render the node identifier on the verbElement. May be empty.
     295        """
     296        if self.nodeIdentifier:
     297            verbElement['node'] = self.nodeIdentifier
     298
     299
     300    def _parse_nodeOrNone(self, verbElement):
     301        """
     302        Parse the optional node identifier out of the verbElement.
     303        """
     304        self.nodeIdentifier = verbElement.getAttribute("node")
     305
     306
     307    def _render_nodeOrNone(self, verbElement):
     308        """
     309        Render the optional node identifier on the verbElement.
     310        """
     311        if self.nodeIdentifier:
     312            verbElement['node'] = self.nodeIdentifier
     313
     314
     315    def _parse_items(self, verbElement):
     316        """
     317        Parse items out of the verbElement for publish requests.
     318        """
     319        self.items = []
     320        for element in verbElement.elements():
     321            if element.uri == NS_PUBSUB and element.name == 'item':
     322                self.items.append(element)
     323
     324
     325    def _render_items(self, verbElement):
     326        """
     327        Render items into the verbElement for publish requests.
     328        """
     329        if self.items:
     330            for item in self.items:
     331                verbElement.addChild(item)
     332
     333
     334    def _parse_jid(self, verbElement):
     335        """
     336        Parse subscriber out of the verbElement for un-/subscribe requests.
     337        """
     338        try:
     339            self.subscriber = jid.internJID(verbElement["jid"])
     340        except KeyError:
     341            raise BadRequest('jid-required')
     342
     343
     344    def _render_jid(self, verbElement):
     345        """
     346        Render subscriber into the verbElement for un-/subscribe requests.
     347        """
     348        verbElement['jid'] = self.subscriber.full()
     349
     350
     351    def _parse_default(self, verbElement):
     352        """
     353        Parse node type out of a request for the default node configuration.
     354        """
     355        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     356        if form and form.formType == 'submit':
     357            values = form.getValues()
     358            self.nodeType = values.get('pubsub#node_type', 'leaf')
     359        else:
     360            self.nodeType = 'leaf'
     361
     362
     363    def _parse_configure(self, verbElement):
     364        """
     365        Parse options out of a request for setting the node configuration.
     366        """
     367        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     368        if form:
     369            if form.formType == 'submit':
     370                self.options = form.getValues()
     371            elif form.formType == 'cancel':
     372                self.options = {}
     373            else:
     374                raise BadRequest("Unexpected form type %r" % form.formType)
     375        else:
     376            raise BadRequest("Missing configuration form")
     377
     378
     379
     380    def _parse_itemIdentifiers(self, verbElement):
     381        """
     382        Parse item identifiers out of items and retract requests.
     383        """
     384        self.itemIdentifiers = []
     385        for element in verbElement.elements():
     386            if element.uri == NS_PUBSUB and element.name == 'item':
     387                try:
     388                    self.itemIdentifiers.append(element["id"])
     389                except KeyError:
     390                    raise BadRequest()
     391
     392
     393    def _render_itemIdentifiers(self, verbElement):
     394        """
     395        Render item identifiers into items and retract requests.
     396        """
     397        if self.itemIdentifiers:
     398            for itemIdentifier in self.itemIdentifiers:
     399                item = verbElement.addElement('item')
     400                item['id'] = itemIdentifier
     401
     402
     403    def _parse_maxItems(self, verbElement):
     404        """
     405        Parse maximum items out of an items request.
     406        """
     407        value = verbElement.getAttribute('max_items')
     408
     409        if value:
     410            try:
     411                self.maxItems = int(value)
     412            except ValueError:
     413                raise BadRequest(text="Field max_items requires a positive " +
     414                                      "integer value")
     415
     416
     417    def _render_maxItems(self, verbElement):
     418        """
     419        Parse maximum items into an items request.
     420        """
     421        if self.maxItems:
     422            verbElement['max_items'] = unicode(self.maxItems)
     423
     424
     425    def parseElement(self, element):
     426        """
     427        Parse the publish-subscribe verb and parameters out of a request.
     428        """
     429        generic.Stanza.parseElement(self, element)
     430
     431        for child in element.pubsub.elements():
     432            key = (self.stanzaType, child.uri, child.name)
     433            try:
     434                verb = self._requestVerbMap[key]
     435            except KeyError:
     436                continue
     437            else:
     438                self.verb = verb
     439                break
     440
     441        if not self.verb:
     442            raise NotImplementedError()
     443
     444        for parameter in self._parameters[verb]:
     445            getattr(self, '_parse_%s' % parameter)(child)
     446
     447
     448    def send(self, xs):
     449        """
     450        Send this request to its recipient.
     451
     452        This renders all of the relevant parameters for this specific
     453        requests into an L{xmlstream.IQ}, and invoke its C{send} method.
     454        This returns a deferred that fires upon reception of a response. See
     455        L{xmlstream.IQ} for details.
     456
     457        @param xs: The XML stream to send the request on.
     458        @type xs: L{xmlstream.XmlStream}
     459        @rtype: L{defer.Deferred}.
     460        """
     461
     462        try:
     463            (self.stanzaType,
     464             childURI,
     465             childName) = self._verbRequestMap[self.verb]
     466        except KeyError:
     467            raise NotImplementedError()
     468
     469        iq = xmlstream.IQ(xs, self.stanzaType)
     470        iq.addElement((childURI, 'pubsub'))
     471        verbElement = iq.pubsub.addElement(childName)
     472
     473        if self.sender:
     474            iq['from'] = self.sender.full()
     475        if self.recipient:
     476            iq['to'] = self.recipient.full()
     477
     478        for parameter in self._parameters[self.verb]:
     479            getattr(self, '_render_%s' % parameter)(verbElement)
     480
     481        return iq.send()
    204482
    205483
    206484
     
    336614        @param nodeIdentifier: Optional suggestion for the id of the node.
    337615        @type nodeIdentifier: C{unicode}
    338616        """
    339 
    340 
    341         request = _PubSubRequest(self.xmlstream, 'create')
    342         if nodeIdentifier:
    343             request.command['node'] = nodeIdentifier
     617        request = PubSubRequest('create')
     618        request.recipient = service
     619        request.nodeIdentifier = nodeIdentifier
    344620
    345621        def cb(iq):
    346622            try:
     
    350626                new_node = nodeIdentifier
    351627            return new_node
    352628
    353         return request.send(service).addCallback(cb)
     629        d = request.send(self.xmlstream)
     630        d.addCallback(cb)
     631        return d
    354632
    355633
    356634    def deleteNode(self, service, nodeIdentifier):
     
    362640        @param nodeIdentifier: The identifier of the node.
    363641        @type nodeIdentifier: C{unicode}
    364642        """
    365         request = _PubSubRequest(self.xmlstream, 'delete', NS_PUBSUB_OWNER)
    366         request.command['node'] = nodeIdentifier
    367         return request.send(service)
     643        request = PubSubRequest('delete')
     644        request.recipient = service
     645        request.nodeIdentifier = nodeIdentifier
     646        return request.send(self.xmlstream)
    368647
    369648
    370649    def subscribe(self, service, nodeIdentifier, subscriber):
     
    379658                           will get notifications of new published items.
    380659        @type subscriber: L{JID}
    381660        """
    382         request = _PubSubRequest(self.xmlstream, 'subscribe')
    383         if nodeIdentifier:
    384             request.command['node'] = nodeIdentifier
    385         request.command['jid'] = subscriber.full()
     661        request = PubSubRequest('subscribe')
     662        request.recipient = service
     663        request.nodeIdentifier = nodeIdentifier
     664        request.subscriber = subscriber
    386665
    387666        def cb(iq):
    388667            subscription = iq.pubsub.subscription["subscription"]
     
    397676                # yielded a stanza error.
    398677                return None
    399678
    400         return request.send(service).addCallback(cb)
     679        d = request.send(self.xmlstream)
     680        d.addCallback(cb)
     681        return d
    401682
    402683
    403684    def unsubscribe(self, service, nodeIdentifier, subscriber):
     
    411692        @param subscriber: The entity to unsubscribe from the node.
    412693        @type subscriber: L{JID}
    413694        """
    414         request = _PubSubRequest(self.xmlstream, 'unsubscribe')
    415         if nodeIdentifier:
    416             request.command['node'] = nodeIdentifier
    417         request.command['jid'] = subscriber.full()
    418         return request.send(service)
     695        request = PubSubRequest('unsubscribe')
     696        request.recipient = service
     697        request.nodeIdentifier = nodeIdentifier
     698        request.subscriber = subscriber
     699        return request.send(self.xmlstream)
    419700
    420701
    421702    def publish(self, service, nodeIdentifier, items=None):
     
    429710        @param items: Optional list of L{Item}s to publish.
    430711        @type items: C{list}
    431712        """
    432         request = _PubSubRequest(self.xmlstream, 'publish')
    433         request.command['node'] = nodeIdentifier
    434         if items:
    435             for item in items:
    436                 request.command.addChild(item)
    437 
    438         return request.send(service)
     713        request = PubSubRequest('publish')
     714        request.recipient = service
     715        request.nodeIdentifier = nodeIdentifier
     716        request.items = items
     717        return request.send(self.xmlstream)
    439718
    440719
    441720    def items(self, service, nodeIdentifier, maxItems=None):
     
    449728        @param maxItems: Optional limit on the number of retrieved items.
    450729        @type maxItems: C{int}
    451730        """
    452         request = _PubSubRequest(self.xmlstream, 'items', method='get')
    453         if nodeIdentifier:
    454             request.command['node'] = nodeIdentifier
     731        request = PubSubRequest('items')
     732        request.recipient = service
     733        request.nodeIdentifier = nodeIdentifier
    455734        if maxItems:
    456             request.command["max_items"] = str(int(maxItems))
     735            request.maxItems = str(int(maxItems))
    457736
    458737        def cb(iq):
    459738            items = []
     
    462741                    items.append(element)
    463742            return items
    464743
    465         return request.send(service).addCallback(cb)
     744        d = request.send(self.xmlstream)
     745        d.addCallback(cb)
     746        return d
    466747
    467748
    468749
     
    497778    implements(IPubSubService)
    498779
    499780    iqHandlers = {
    500             PUBSUB_PUBLISH: '_onPublish',
    501             PUBSUB_CREATE: '_onCreate',
    502             PUBSUB_SUBSCRIBE: '_onSubscribe',
    503             PUBSUB_OPTIONS_GET: '_onOptionsGet',
    504             PUBSUB_OPTIONS_SET: '_onOptionsSet',
    505             PUBSUB_AFFILIATIONS: '_onAffiliations',
    506             PUBSUB_ITEMS: '_onItems',
    507             PUBSUB_RETRACT: '_onRetract',
    508             PUBSUB_SUBSCRIPTIONS: '_onSubscriptions',
    509             PUBSUB_UNSUBSCRIBE: '_onUnsubscribe',
    510 
    511             PUBSUB_AFFILIATIONS_GET: '_onAffiliationsGet',
    512             PUBSUB_AFFILIATIONS_SET: '_onAffiliationsSet',
    513             PUBSUB_CONFIGURE_GET: '_onConfigureGet',
    514             PUBSUB_CONFIGURE_SET: '_onConfigureSet',
    515             PUBSUB_DEFAULT: '_onDefault',
    516             PUBSUB_PURGE: '_onPurge',
    517             PUBSUB_DELETE: '_onDelete',
    518             PUBSUB_SUBSCRIPTIONS_GET: '_onSubscriptionsGet',
    519             PUBSUB_SUBSCRIPTIONS_SET: '_onSubscriptionsSet',
    520 
     781            '/*': '_onPubSubRequest',
    521782            }
    522783
    523784
     
    530791
    531792
    532793    def connectionMade(self):
    533         self.xmlstream.addObserver(PUBSUB_GET, self.handleRequest)
    534         self.xmlstream.addObserver(PUBSUB_SET, self.handleRequest)
    535         self.xmlstream.addObserver(PUBSUB_OWNER_GET, self.handleRequest)
    536         self.xmlstream.addObserver(PUBSUB_OWNER_SET, self.handleRequest)
     794        self.xmlstream.addObserver(PUBSUB_REQUEST, self.handleRequest)
    537795
    538796
    539797    def getDiscoInfo(self, requestor, target, nodeIdentifier):
     
    585843        return d
    586844
    587845
    588     def _findForm(self, element, formNamespace):
    589         if not element:
    590             return None
     846    def _onPubSubRequest(self, iq):
     847        request = PubSubRequest.fromElement(iq)
     848        handler = getattr(self, '_on_%s' % request.verb)
     849        return handler(request)
    591850
    592         form = None
    593         for child in element.elements():
    594             try:
    595                 form = data_form.Form.fromElement(child)
    596             except data_form.Error:
    597                 continue
    598851
    599             if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
    600                 continue
     852    def _on_publish(self, request):
     853        return self.publish(request)
    601854
    602         return form
    603855
    604 
    605     def _getParameter_node(self, commandElement):
    606         try:
    607             return commandElement["node"]
    608         except KeyError:
    609             raise BadRequest('nodeid-required')
    610 
    611 
    612     def _getParameter_nodeOrEmpty(self, commandElement):
    613         return commandElement.getAttribute("node", '')
    614 
    615 
    616     def _getParameter_jid(self, commandElement):
    617         try:
    618             return jid.internJID(commandElement["jid"])
    619         except KeyError:
    620             raise BadRequest('jid-required')
    621 
    622 
    623     def _getParameter_max_items(self, commandElement):
    624         value = commandElement.getAttribute('max_items')
    625 
    626         if value:
    627             try:
    628                 return int(value)
    629             except ValueError:
    630                 raise BadRequest(text="Field max_items requires a positive " +
    631                                       "integer value")
    632         else:
    633             return None
    634 
    635 
    636     def _getParameters(self, iq, *names):
    637         requestor = jid.internJID(iq["from"]).userhostJID()
    638         service = jid.internJID(iq["to"])
    639 
    640         params = [requestor, service]
    641 
    642         if names:
    643             command = names[0]
    644             commandElement = getattr(iq.pubsub, command)
    645             if not commandElement:
    646                 raise Exception("Could not find command element %r" % command)
    647 
    648         for name in names[1:]:
    649             try:
    650                 getter = getattr(self, '_getParameter_' + name)
    651             except KeyError:
    652                 raise Exception("No parameter getter for this name")
    653 
    654             params.append(getter(commandElement))
    655 
    656         return params
    657 
    658 
    659     def _onPublish(self, iq):
    660         requestor, service, nodeIdentifier = self._getParameters(
    661                 iq, 'publish', 'node')
    662 
    663         items = []
    664         for element in iq.pubsub.publish.elements():
    665             if element.uri == NS_PUBSUB and element.name == 'item':
    666                 items.append(element)
    667 
    668         return self.publish(requestor, service, nodeIdentifier, items)
    669 
    670 
    671     def _onSubscribe(self, iq):
    672         requestor, service, nodeIdentifier, subscriber = self._getParameters(
    673                 iq, 'subscribe', 'nodeOrEmpty', 'jid')
     856    def _on_subscribe(self, request):
    674857
    675858        def toResponse(result):
    676859            response = domish.Element((NS_PUBSUB, "pubsub"))
     
    681864            subscription["subscription"] = result.state
    682865            return response
    683866
    684         d = self.subscribe(requestor, service, nodeIdentifier, subscriber)
     867        d = self.subscribe(request)
    685868        d.addCallback(toResponse)
    686869        return d
    687870
    688871
    689     def _onUnsubscribe(self, iq):
    690         requestor, service, nodeIdentifier, subscriber = self._getParameters(
    691                 iq, 'unsubscribe', 'nodeOrEmpty', 'jid')
     872    def _on_unsubscribe(self, request):
     873        return self.unsubscribe(request)
    692874
    693         return self.unsubscribe(requestor, service, nodeIdentifier, subscriber)
    694875
    695 
    696     def _onOptionsGet(self, iq):
     876    def _on_optionsGet(self, request):
    697877        raise Unsupported('subscription-options')
    698878
    699879
    700     def _onOptionsSet(self, iq):
     880    def _on_optionsSet(self, request):
    701881        raise Unsupported('subscription-options')
    702882
    703883
    704     def _onSubscriptions(self, iq):
    705         requestor, service = self._getParameters(iq)
     884    def _on_subscriptions(self, request):
    706885
    707886        def toResponse(result):
    708887            response = domish.Element((NS_PUBSUB, 'pubsub'))
     
    714893                item['subscription'] = subscription.state
    715894            return response
    716895
    717         d = self.subscriptions(requestor, service)
     896        d = self.subscriptions(request)
    718897        d.addCallback(toResponse)
    719898        return d
    720899
    721900
    722     def _onAffiliations(self, iq):
    723         requestor, service = self._getParameters(iq)
     901    def _on_affiliations(self, request):
    724902
    725903        def toResponse(result):
    726904            response = domish.Element((NS_PUBSUB, 'pubsub'))
     
    733911
    734912            return response
    735913
    736         d = self.affiliations(requestor, service)
     914        d = self.affiliations(request)
    737915        d.addCallback(toResponse)
    738916        return d
    739917
    740918
    741     def _onCreate(self, iq):
    742         requestor, service = self._getParameters(iq)
    743         nodeIdentifier = iq.pubsub.create.getAttribute("node")
     919    def _on_create(self, request):
    744920
    745921        def toResponse(result):
    746             if not nodeIdentifier or nodeIdentifier != result:
     922            if not request.nodeIdentifier or request.nodeIdentifier != result:
    747923                response = domish.Element((NS_PUBSUB, 'pubsub'))
    748924                create = response.addElement('create')
    749925                create['node'] = result
     
    751927            else:
    752928                return None
    753929
    754         d = self.create(requestor, service, nodeIdentifier)
     930        d = self.create(request)
    755931        d.addCallback(toResponse)
    756932        return d
    757933
     
    771947            fields.append(data_form.Field.fromDict(option))
    772948        return fields
    773949
     950
    774951    def _formFromConfiguration(self, values):
    775952        options = self.getConfigurationOptions()
    776953        fields = self._makeFields(options, values)
     
    780957
    781958        return form
    782959
     960
    783961    def _checkConfiguration(self, values):
    784962        options = self.getConfigurationOptions()
    785963        processedValues = {}
     
    805983        return processedValues
    806984
    807985
    808     def _onDefault(self, iq):
    809         requestor, service = self._getParameters(iq)
     986    def _on_default(self, request):
    810987
    811988        def toResponse(options):
    812989            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
     
    814991            default.addChild(self._formFromConfiguration(options).toElement())
    815992            return response
    816993
    817         form = self._findForm(iq.pubsub.config, NS_PUBSUB_NODE_CONFIG)
    818         values = form and form.formType == 'result' and form.getValues() or {}
    819         nodeType = values.get('pubsub#node_type', 'leaf')
    820 
    821         if nodeType not in ('leaf', 'collections'):
     994        if request.nodeType not in ('leaf', 'collection'):
    822995            return defer.fail(error.StanzaError('not-acceptable'))
    823996
    824         d = self.getDefaultConfiguration(requestor, service, nodeType)
     997        d = self.getDefaultConfiguration(request)
    825998        d.addCallback(toResponse)
    826999        return d
    8271000
    8281001
    829     def _onConfigureGet(self, iq):
    830         requestor, service, nodeIdentifier = self._getParameters(
    831                 iq, 'configure', 'nodeOrEmpty')
    832 
     1002    def _on_configureGet(self, request):
    8331003        def toResponse(options):
    8341004            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
    8351005            configure = response.addElement("configure")
    836             configure.addChild(self._formFromConfiguration(options).toElement())
     1006            form = self._formFromConfiguration(options)
     1007            configure.addChild(form.toElement())
    8371008
    838             if nodeIdentifier:
    839                 configure["node"] = nodeIdentifier
     1009            if request.nodeIdentifier:
     1010                configure["node"] = request.nodeIdentifier
    8401011
    8411012            return response
    8421013
    843         d = self.getConfiguration(requestor, service, nodeIdentifier)
     1014        d = self.getConfiguration(request)
    8441015        d.addCallback(toResponse)
    8451016        return d
    8461017
    8471018
    848     def _onConfigureSet(self, iq):
    849         requestor, service, nodeIdentifier = self._getParameters(
    850                 iq, 'configure', 'nodeOrEmpty')
     1019    def _on_configureSet(self, request):
     1020        if request.options:
     1021            request.options = self._checkConfiguration(request.options)
     1022            return self.setConfiguration(request)
     1023        else:
     1024            return None
    8511025
    852         # Search configuration form with correct FORM_TYPE and process it
    8531026
    854         form = self._findForm(iq.pubsub.configure, NS_PUBSUB_NODE_CONFIG)
    8551027
    856         if form:
    857             if form.formType == 'submit':
    858                 options = self._checkConfiguration(form.getValues())
    859 
    860                 return self.setConfiguration(requestor, service,
    861                                              nodeIdentifier, options)
    862             elif form.formType == 'cancel':
    863                 return None
    864 
    865         raise BadRequest()
    866 
    867 
    868     def _onItems(self, iq):
    869         requestor, service, nodeIdentifier, maxItems = self._getParameters(
    870                 iq, 'items', 'nodeOrEmpty', 'max_items')
    871 
    872         itemIdentifiers = []
    873         for child in iq.pubsub.items.elements():
    874             if child.name == 'item' and child.uri == NS_PUBSUB:
    875                 try:
    876                     itemIdentifiers.append(child["id"])
    877                 except KeyError:
    878                     raise BadRequest()
     1028    def _on_items(self, request):
    8791029
    8801030        def toResponse(result):
    8811031            response = domish.Element((NS_PUBSUB, 'pubsub'))
    8821032            items = response.addElement('items')
    883             if nodeIdentifier:
    884                 items["node"] = nodeIdentifier
     1033            items["node"] = request.nodeIdentifier
    8851034
    8861035            for item in result:
    8871036                items.addChild(item)
    8881037
    8891038            return response
    8901039
    891         d = self.items(requestor, service, nodeIdentifier, maxItems,
    892                        itemIdentifiers)
     1040        d = self.items(request)
    8931041        d.addCallback(toResponse)
    8941042        return d
    8951043
    8961044
    897     def _onRetract(self, iq):
    898         requestor, service, nodeIdentifier = self._getParameters(
    899                 iq, 'retract', 'node')
     1045    def _on_retract(self, request):
     1046        return self.retract(request)
    9001047
    901         itemIdentifiers = []
    902         for child in iq.pubsub.retract.elements():
    903             if child.uri == NS_PUBSUB and child.name == 'item':
    904                 try:
    905                     itemIdentifiers.append(child["id"])
    906                 except KeyError:
    907                     raise BadRequest()
    9081048
    909         return self.retract(requestor, service, nodeIdentifier,
    910                             itemIdentifiers)
     1049    def _on_purge(self, request):
     1050        return self.purge(request)
    9111051
    9121052
    913     def _onPurge(self, iq):
    914         requestor, service, nodeIdentifier = self._getParameters(
    915                 iq, 'purge', 'node')
    916         return self.purge(requestor, service, nodeIdentifier)
     1053    def _on_delete(self, request):
     1054        return self.delete(request)
    9171055
    9181056
    919     def _onDelete(self, iq):
    920         requestor, service, nodeIdentifier = self._getParameters(
    921                 iq, 'delete', 'node')
    922         return self.delete(requestor, service, nodeIdentifier)
    923 
    924 
    925     def _onAffiliationsGet(self, iq):
     1057    def _on_affiliationsGet(self, iq):
    9261058        raise Unsupported('modify-affiliations')
    9271059
    9281060
    929     def _onAffiliationsSet(self, iq):
     1061    def _on_affiliationsSet(self, iq):
    9301062        raise Unsupported('modify-affiliations')
    9311063
    9321064
    933     def _onSubscriptionsGet(self, iq):
     1065    def _on_subscriptionsGet(self, iq):
    9341066        raise Unsupported('manage-subscriptions')
    9351067
    9361068
    937     def _onSubscriptionsSet(self, iq):
     1069    def _on_subscriptionsSet(self, iq):
    9381070        raise Unsupported('manage-subscriptions')
    9391071
    9401072    # public methods
     
    9901122        return []
    9911123
    9921124
    993     def publish(self, requestor, service, nodeIdentifier, items):
     1125    def publish(self, request):
    9941126        raise Unsupported('publish')
    9951127
    9961128
    997     def subscribe(self, requestor, service, nodeIdentifier, subscriber):
     1129    def subscribe(self, request):
    9981130        raise Unsupported('subscribe')
    9991131
    10001132
    1001     def unsubscribe(self, requestor, service, nodeIdentifier, subscriber):
     1133    def unsubscribe(self, request):
    10021134        raise Unsupported('subscribe')
    10031135
    10041136
    1005     def subscriptions(self, requestor, service):
     1137    def subscriptions(self, request):
    10061138        raise Unsupported('retrieve-subscriptions')
    10071139
    10081140
    1009     def affiliations(self, requestor, service):
     1141    def affiliations(self, request):
    10101142        raise Unsupported('retrieve-affiliations')
    10111143
    10121144
    1013     def create(self, requestor, service, nodeIdentifier):
     1145    def create(self, request):
    10141146        raise Unsupported('create-nodes')
    10151147
    10161148
     
    10181150        return {}
    10191151
    10201152
    1021     def getDefaultConfiguration(self, requestor, service, nodeType):
     1153    def getDefaultConfiguration(self, request):
    10221154        raise Unsupported('retrieve-default')
    10231155
    10241156
    1025     def getConfiguration(self, requestor, service, nodeIdentifier):
     1157    def getConfiguration(self, request):
    10261158        raise Unsupported('config-node')
    10271159
    10281160
    1029     def setConfiguration(self, requestor, service, nodeIdentifier, options):
     1161    def setConfiguration(self, request):
    10301162        raise Unsupported('config-node')
    10311163
    10321164
    1033     def items(self, requestor, service, nodeIdentifier, maxItems,
    1034                     itemIdentifiers):
     1165    def items(self, request):
    10351166        raise Unsupported('retrieve-items')
    10361167
    10371168
    1038     def retract(self, requestor, service, nodeIdentifier, itemIdentifiers):
     1169    def retract(self, request):
    10391170        raise Unsupported('retract-items')
    10401171
    10411172
    1042     def purge(self, requestor, service, nodeIdentifier):
     1173    def purge(self, request):
    10431174        raise Unsupported('purge-nodes')
    10441175
    10451176
    1046     def delete(self, requestor, service, nodeIdentifier):
     1177    def delete(self, request):
    10471178        raise Unsupported('delete-nodes')
  • wokkel/test/test_pubsub.py

    diff -r 884ac8d88411 wokkel/test/test_pubsub.py
    a b  
    507507        verify.verifyObject(iwokkel.IPubSubService, self.service)
    508508
    509509
     510    def test_connectionMade(self):
     511        """
     512        Verify setup of observers in L{pubsub.connectionMade}.
     513        """
     514        requests = []
     515
     516        def handleRequest(iq):
     517            requests.append(iq)
     518
     519        self.service.xmlstream = self.stub.xmlstream
     520        self.service.handleRequest = handleRequest
     521        self.service.connectionMade()
     522
     523        for namespace in (NS_PUBSUB, NS_PUBSUB_OWNER):
     524            for stanzaType in ('get', 'set'):
     525                iq = domish.Element((None, 'iq'))
     526                iq['type'] = stanzaType
     527                iq.addElement((namespace, 'pubsub'))
     528                self.stub.xmlstream.dispatch(iq)
     529
     530        self.assertEqual(4, len(requests))
     531
     532
    510533    def test_getDiscoInfo(self):
    511534        """
    512535        Test getDiscoInfo calls getNodeInfo and returns some minimal info.
     
    561584        </iq>
    562585        """
    563586
    564         def publish(requestor, service, nodeIdentifier, items):
    565             self.assertEqual(JID('user@example.org'), requestor)
    566             self.assertEqual(JID('pubsub.example.org'), service)
    567             self.assertEqual('test', nodeIdentifier)
    568             self.assertEqual([], items)
     587        def publish(request):
     588            self.assertEqual(JID('user@example.org'), request.sender)
     589            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     590            self.assertEqual('test', request.nodeIdentifier)
     591            self.assertEqual([], request.items)
    569592            return defer.succeed(None)
    570593
    571594        self.service.publish = publish
     595        verify.verifyObject(iwokkel.IPubSubService, self.service)
    572596        return self.handleRequest(xml)
    573597
    574598
     599    def test_onPublishItems(self):
     600        """
     601        A publish request with items should pass the items onto C{publish}.
     602        """
     603
     604        xml = """
     605        <iq type='set' to='pubsub.example.org'
     606                       from='user@example.org'>
     607          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     608            <publish node='test'>
     609              <item id="item1"/>
     610              <item id="item2"/>
     611            </publish>
     612          </pubsub>
     613        </iq>
     614        """
     615
     616        def publish(request):
     617            self.assertEqual(2, len(request.items))
     618            self.assertEqual(u'item1', request.items[0]["id"])
     619            self.assertEqual(u'item2', request.items[1]["id"])
     620            return defer.succeed(None)
     621
     622        self.service.publish = publish
     623        verify.verifyObject(iwokkel.IPubSubService, self.service)
     624        return self.handleRequest(xml)
     625
     626
     627    def test_onSubscribe(self):
     628        """
     629        A successful subscription should return the current subscription.
     630        """
     631
     632        xml = """
     633        <iq type='set' to='pubsub.example.org'
     634                       from='user@example.org'>
     635          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     636            <subscribe node='test' jid='user@example.org/Home'/>
     637          </pubsub>
     638        </iq>
     639        """
     640
     641        def subscribe(request):
     642            self.assertEqual(JID('user@example.org'), request.sender)
     643            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     644            self.assertEqual('test', request.nodeIdentifier)
     645            self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     646            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
     647                                                     request.subscriber,
     648                                                     'subscribed'))
     649
     650        def cb(element):
     651            self.assertEqual('pubsub', element.name)
     652            self.assertEqual(NS_PUBSUB, element.uri)
     653            subscription = element.subscription
     654            self.assertEqual(NS_PUBSUB, subscription.uri)
     655            self.assertEqual('test', subscription['node'])
     656            self.assertEqual('user@example.org/Home', subscription['jid'])
     657            self.assertEqual('subscribed', subscription['subscription'])
     658
     659        self.service.subscribe = subscribe
     660        verify.verifyObject(iwokkel.IPubSubService, self.service)
     661        d = self.handleRequest(xml)
     662        d.addCallback(cb)
     663        return d
     664
     665
     666    def test_onSubscribeEmptyNode(self):
     667        """
     668        A successful subscription on root node should return no node attribute.
     669        """
     670
     671        xml = """
     672        <iq type='set' to='pubsub.example.org'
     673                       from='user@example.org'>
     674          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     675            <subscribe jid='user@example.org/Home'/>
     676          </pubsub>
     677        </iq>
     678        """
     679
     680        def subscribe(request):
     681            self.assertEqual('', request.nodeIdentifier)
     682            return defer.succeed(pubsub.Subscription(request.nodeIdentifier,
     683                                                     request.subscriber,
     684                                                     'subscribed'))
     685
     686        def cb(element):
     687            self.assertFalse(element.subscription.hasAttribute('node'))
     688
     689        self.service.subscribe = subscribe
     690        verify.verifyObject(iwokkel.IPubSubService, self.service)
     691        d = self.handleRequest(xml)
     692        d.addCallback(cb)
     693        return d
     694
     695
     696    def test_onUnsubscribe(self):
     697        """
     698        A successful unsubscription should return an empty response.
     699        """
     700
     701        xml = """
     702        <iq type='set' to='pubsub.example.org'
     703                       from='user@example.org'>
     704          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     705            <unsubscribe node='test' jid='user@example.org/Home'/>
     706          </pubsub>
     707        </iq>
     708        """
     709
     710        def unsubscribe(request):
     711            self.assertEqual(JID('user@example.org'), request.sender)
     712            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     713            self.assertEqual('test', request.nodeIdentifier)
     714            self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     715            return defer.succeed(None)
     716
     717        def cb(element):
     718            self.assertIdentical(None, element)
     719
     720        self.service.unsubscribe = unsubscribe
     721        verify.verifyObject(iwokkel.IPubSubService, self.service)
     722        d = self.handleRequest(xml)
     723        d.addCallback(cb)
     724        return d
     725
     726
    575727    def test_onOptionsGet(self):
    576728        """
    577         Subscription options are not supported.
     729        Getting subscription options is not supported.
    578730        """
    579731
    580732        xml = """
     
    597749        return d
    598750
    599751
     752    def test_onOptionsSet(self):
     753        """
     754        Setting subscription options is not supported.
     755        """
     756
     757        xml = """
     758        <iq type='set' to='pubsub.example.org'
     759                       from='user@example.org'>
     760          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     761            <options/>
     762          </pubsub>
     763        </iq>
     764        """
     765
     766        def cb(result):
     767            self.assertEquals('feature-not-implemented', result.condition)
     768            self.assertEquals('unsupported', result.appCondition.name)
     769            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     770
     771        d = self.handleRequest(xml)
     772        self.assertFailure(d, error.StanzaError)
     773        d.addCallback(cb)
     774        return d
     775
     776
    600777    def test_onSubscriptions(self):
    601778        """
    602779        A subscriptions request should result in
     
    627804            self.assertEqual('subscribed', subscription['subscription'])
    628805
    629806
    630         def subscriptions(requestor, service):
    631             self.assertEqual(JID('user@example.org'), requestor)
    632             self.assertEqual(JID('pubsub.example.org'), service)
     807        def subscriptions(request):
     808            self.assertEqual(JID('user@example.org'), request.sender)
     809            self.assertEqual(JID('pubsub.example.org'), request.recipient)
    633810            subscription = pubsub.Subscription('test', JID('user@example.org'),
    634811                                               'subscribed')
    635812            return defer.succeed([subscription])
    636813
    637814        self.service.subscriptions = subscriptions
     815        verify.verifyObject(iwokkel.IPubSubService, self.service)
     816        d = self.handleRequest(xml)
     817        d.addCallback(cb)
     818        return d
     819
     820
     821    def test_onAffiliations(self):
     822        """
     823        A subscriptions request should result in
     824        L{PubSubService.affiliations} being called and the result prepared
     825        for the response.
     826        """
     827
     828        xml = """
     829        <iq type='get' to='pubsub.example.org'
     830                       from='user@example.org'>
     831          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     832            <affiliations/>
     833          </pubsub>
     834        </iq>
     835        """
     836
     837        def cb(element):
     838            self.assertEqual('pubsub', element.name)
     839            self.assertEqual(NS_PUBSUB, element.uri)
     840            self.assertEqual(NS_PUBSUB, element.affiliations.uri)
     841            children = list(element.affiliations.elements())
     842            self.assertEqual(1, len(children))
     843            affiliation = children[0]
     844            self.assertEqual('affiliation', affiliation.name)
     845            self.assertEqual(NS_PUBSUB, affiliation.uri)
     846            self.assertEqual('test', affiliation['node'])
     847            self.assertEqual('owner', affiliation['affiliation'])
     848
     849
     850        def affiliations(request):
     851            self.assertEqual(JID('user@example.org'), request.sender)
     852            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     853            affiliation = ('test', 'owner')
     854            return defer.succeed([affiliation])
     855
     856        self.service.affiliations = affiliations
     857        verify.verifyObject(iwokkel.IPubSubService, self.service)
     858        d = self.handleRequest(xml)
     859        d.addCallback(cb)
     860        return d
     861
     862
     863    def test_onCreate(self):
     864        """
     865        Replies to create node requests don't return the created node.
     866        """
     867
     868        xml = """
     869        <iq type='set' to='pubsub.example.org'
     870                       from='user@example.org'>
     871          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     872            <create node='mynode'/>
     873          </pubsub>
     874        </iq>
     875        """
     876
     877        def create(request):
     878            self.assertEqual(JID('user@example.org'), request.sender)
     879            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     880            self.assertEqual('mynode', request.nodeIdentifier)
     881            return defer.succeed(request.nodeIdentifier)
     882
     883        def cb(element):
     884            self.assertIdentical(None, element)
     885
     886        self.service.create = create
     887        verify.verifyObject(iwokkel.IPubSubService, self.service)
     888        d = self.handleRequest(xml)
     889        d.addCallback(cb)
     890        return d
     891
     892
     893    def test_onCreateChanged(self):
     894        """
     895        Replies to create node requests return the created node if changed.
     896        """
     897
     898        xml = """
     899        <iq type='set' to='pubsub.example.org'
     900                       from='user@example.org'>
     901          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     902            <create node='mynode'/>
     903          </pubsub>
     904        </iq>
     905        """
     906
     907        def create(request):
     908            return defer.succeed(u'myrenamednode')
     909
     910        def cb(element):
     911            self.assertEqual('pubsub', element.name)
     912            self.assertEqual(NS_PUBSUB, element.uri)
     913            self.assertEqual(NS_PUBSUB, element.create.uri)
     914            self.assertEqual(u'myrenamednode',
     915                             element.create.getAttribute('node'))
     916
     917        self.service.create = create
     918        verify.verifyObject(iwokkel.IPubSubService, self.service)
     919        d = self.handleRequest(xml)
     920        d.addCallback(cb)
     921        return d
     922
     923
     924    def test_onCreateInstant(self):
     925        """
     926        Replies to create instant node requests return the created node.
     927        """
     928
     929        xml = """
     930        <iq type='set' to='pubsub.example.org'
     931                       from='user@example.org'>
     932          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     933            <create/>
     934          </pubsub>
     935        </iq>
     936        """
     937
     938        def create(request):
     939            self.assertIdentical(None, request.nodeIdentifier)
     940            return defer.succeed(u'random')
     941
     942        def cb(element):
     943            self.assertEqual('pubsub', element.name)
     944            self.assertEqual(NS_PUBSUB, element.uri)
     945            self.assertEqual(NS_PUBSUB, element.create.uri)
     946            self.assertEqual(u'random', element.create.getAttribute('node'))
     947
     948        self.service.create = create
     949        verify.verifyObject(iwokkel.IPubSubService, self.service)
    638950        d = self.handleRequest(xml)
    639951        d.addCallback(cb)
    640952        return d
     
    665977                     "label": "Deliver payloads with event notifications"}
    666978                }
    667979
    668         def getDefaultConfiguration(requestor, service, nodeType):
    669             self.assertEqual(JID('user@example.org'), requestor)
    670             self.assertEqual(JID('pubsub.example.org'), service)
    671             self.assertEqual('leaf', nodeType)
     980        def getDefaultConfiguration(request):
     981            self.assertEqual(JID('user@example.org'), request.sender)
     982            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     983            self.assertEqual('leaf', request.nodeType)
    672984            return defer.succeed({})
    673985
    674986        def cb(element):
     
    686998        return d
    687999
    6881000
     1001    def test_onDefaultCollection(self):
     1002        """
     1003        Responses to default requests should depend on passed node type.
     1004        """
     1005
     1006        xml = """
     1007        <iq type='get' to='pubsub.example.org'
     1008                       from='user@example.org'>
     1009          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1010            <default>
     1011              <x xmlns='jabber:x:data' type='submit'>
     1012                <field var='FORM_TYPE' type='hidden'>
     1013                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1014                </field>
     1015                <field var='pubsub#node_type'>
     1016                  <value>collection</value>
     1017                </field>
     1018              </x>
     1019            </default>
     1020
     1021          </pubsub>
     1022        </iq>
     1023        """
     1024
     1025        def getConfigurationOptions():
     1026            return {
     1027                "pubsub#deliver_payloads":
     1028                    {"type": "boolean",
     1029                     "label": "Deliver payloads with event notifications"}
     1030                }
     1031
     1032        def getDefaultConfiguration(request):
     1033            self.assertEqual('collection', request.nodeType)
     1034            return defer.succeed({})
     1035
     1036        def cb(element):
     1037            self.assertEqual('pubsub', element.name)
     1038            self.assertEqual(NS_PUBSUB_OWNER, element.uri)
     1039            self.assertEqual(NS_PUBSUB_OWNER, element.default.uri)
     1040            form = data_form.Form.fromElement(element.default.x)
     1041            self.assertEqual(NS_PUBSUB_CONFIG, form.formNamespace)
     1042
     1043        self.service.getConfigurationOptions = getConfigurationOptions
     1044        self.service.getDefaultConfiguration = getDefaultConfiguration
     1045        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1046        d = self.handleRequest(xml)
     1047        d.addCallback(cb)
     1048        return d
     1049
     1050
     1051    def test_onDefaultUnknownNodeType(self):
     1052        """
     1053        A default request should result in
     1054        L{PubSubService.getDefaultConfiguration} being called.
     1055        """
     1056
     1057        xml = """
     1058        <iq type='get' to='pubsub.example.org'
     1059                       from='user@example.org'>
     1060          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1061            <default>
     1062              <x xmlns='jabber:x:data' type='submit'>
     1063                <field var='FORM_TYPE' type='hidden'>
     1064                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1065                </field>
     1066                <field var='pubsub#node_type'>
     1067                  <value>unknown</value>
     1068                </field>
     1069              </x>
     1070            </default>
     1071
     1072          </pubsub>
     1073        </iq>
     1074        """
     1075
     1076        def getDefaultConfiguration(request):
     1077            self.fail("Unexpected call to getConfiguration")
     1078
     1079        def cb(result):
     1080            self.assertEquals('not-acceptable', result.condition)
     1081
     1082        self.service.getDefaultConfiguration = getDefaultConfiguration
     1083        verify.verifyObject(iwokkel.IPubSubService, self.service)
     1084        d = self.handleRequest(xml)
     1085        self.assertFailure(d, error.StanzaError)
     1086        d.addCallback(cb)
     1087        return d
     1088
     1089
    6891090    def test_onConfigureGet(self):
    6901091        """
    6911092        On a node configuration get request L{PubSubService.getConfiguration}
     
    7141115                     "label": "Owner of the node"}
    7151116                }
    7161117
    717         def getConfiguration(requestor, service, nodeIdentifier):
    718             self.assertEqual(JID('user@example.org'), requestor)
    719             self.assertEqual(JID('pubsub.example.org'), service)
    720             self.assertEqual('test', nodeIdentifier)
     1118        def getConfiguration(request):
     1119            self.assertEqual(JID('user@example.org'), request.sender)
     1120            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1121            self.assertEqual('test', request.nodeIdentifier)
    7211122
    7221123            return defer.succeed({'pubsub#deliver_payloads': '0',
    7231124                                  'pubsub#persist_items': '1',
    724                                   'pubsub#owner': JID('user@example.org')})
     1125                                  'pubsub#owner': JID('user@example.org'),
     1126                                  'x-myfield': ['a', 'b']})
    7251127
    7261128        def cb(element):
    7271129            self.assertEqual('pubsub', element.name)
     
    7491151            field.typeCheck()
    7501152            self.assertEqual(JID('user@example.org'), field.value)
    7511153
     1154            self.assertNotIn('x-myfield', fields)
     1155
     1156
    7521157        self.service.getConfigurationOptions = getConfigurationOptions
    7531158        self.service.getConfiguration = getConfiguration
     1159        verify.verifyObject(iwokkel.IPubSubService, self.service)
    7541160        d = self.handleRequest(xml)
    7551161        d.addCallback(cb)
    7561162        return d
     
    7891195                     "label": "Deliver payloads with event notifications"}
    7901196                }
    7911197
    792         def setConfiguration(requestor, service, nodeIdentifier, options):
    793             self.assertEqual(JID('user@example.org'), requestor)
    794             self.assertEqual(JID('pubsub.example.org'), service)
    795             self.assertEqual('test', nodeIdentifier)
     1198        def setConfiguration(request):
     1199            self.assertEqual(JID('user@example.org'), request.sender)
     1200            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1201            self.assertEqual('test', request.nodeIdentifier)
    7961202            self.assertEqual({'pubsub#deliver_payloads': False,
    797                               'pubsub#persist_items': True}, options)
     1203                              'pubsub#persist_items': True}, request.options)
    7981204            return defer.succeed(None)
    7991205
    8001206        self.service.getConfigurationOptions = getConfigurationOptions
    8011207        self.service.setConfiguration = setConfiguration
     1208        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8021209        return self.handleRequest(xml)
    8031210
    8041211
     
    8231230        </iq>
    8241231        """
    8251232
    826         def setConfiguration(requestor, service, nodeIdentifier, options):
     1233        def setConfiguration(request):
    8271234            self.fail("Unexpected call to setConfiguration")
    8281235
    8291236        self.service.setConfiguration = setConfiguration
     1237        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8301238        return self.handleRequest(xml)
    8311239
    8321240
     
    8621270                     "label": "Deliver payloads with event notifications"}
    8631271                }
    8641272
    865         def setConfiguration(requestor, service, nodeIdentifier, options):
    866             self.assertEquals(['pubsub#deliver_payloads'], options.keys())
     1273        def setConfiguration(request):
     1274            self.assertEquals(['pubsub#deliver_payloads'],
     1275                              request.options.keys())
    8671276
    8681277        self.service.getConfigurationOptions = getConfigurationOptions
    8691278        self.service.setConfiguration = setConfiguration
     1279        verify.verifyObject(iwokkel.IPubSubService, self.service)
    8701280        return self.handleRequest(xml)
    8711281
    8721282
     1283    def test_onConfigureSetBadFormType(self):
     1284        """
     1285        On a node configuration set request unknown fields should be ignored.
     1286        """
     1287
     1288        xml = """
     1289        <iq type='set' to='pubsub.example.org'
     1290                       from='user@example.org'>
     1291          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1292            <configure node='test'>
     1293              <x xmlns='jabber:x:data' type='result'>
     1294                <field var='FORM_TYPE' type='hidden'>
     1295                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1296                </field>
     1297                <field var='pubsub#deliver_payloads'><value>0</value></field>
     1298                <field var='x-myfield'><value>1</value></field>
     1299              </x>
     1300            </configure>
     1301          </pubsub>
     1302        </iq>
     1303        """
     1304
     1305        def cb(result):
     1306            self.assertEquals('bad-request', result.condition)
     1307
     1308        d = self.handleRequest(xml)
     1309        self.assertFailure(d, error.StanzaError)
     1310        d.addCallback(cb)
     1311        return d
     1312
     1313
    8731314    def test_onItems(self):
    8741315        """
    8751316        On a items request, return all items for the given node.
     
    8831324        </iq>
    8841325        """
    8851326
    886         def items(requestor, service, nodeIdentifier, maxItems, items):
    887             self.assertEqual(JID('user@example.org'), requestor)
    888             self.assertEqual(JID('pubsub.example.org'), service)
    889             self.assertEqual('test', nodeIdentifier)
    890             self.assertIdentical(None, maxItems)
    891             self.assertEqual([], items)
     1327        def items(request):
     1328            self.assertEqual(JID('user@example.org'), request.sender)
     1329            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1330            self.assertEqual('test', request.nodeIdentifier)
     1331            self.assertIdentical(None, request.maxItems)
     1332            self.assertEqual([], request.itemIdentifiers)
    8921333            return defer.succeed([pubsub.Item('current')])
    8931334
    8941335        def cb(element):
     
    9251366        </iq>
    9261367        """
    9271368
    928         def retract(requestor, service, nodeIdentifier, itemIdentifiers):
    929             self.assertEqual(JID('user@example.org'), requestor)
    930             self.assertEqual(JID('pubsub.example.org'), service)
    931             self.assertEqual('test', nodeIdentifier)
    932             self.assertEqual(['item1', 'item2'], itemIdentifiers)
     1369        def retract(request):
     1370            self.assertEqual(JID('user@example.org'), request.sender)
     1371            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1372            self.assertEqual('test', request.nodeIdentifier)
     1373            self.assertEqual(['item1', 'item2'], request.itemIdentifiers)
    9331374            return defer.succeed(None)
    9341375
    9351376        self.service.retract = retract
     
    9511392        </iq>
    9521393        """
    9531394
    954         def purge(requestor, service, nodeIdentifier):
    955             self.assertEqual(JID('user@example.org'), requestor)
    956             self.assertEqual(JID('pubsub.example.org'), service)
    957             self.assertEqual('test', nodeIdentifier)
     1395        def purge(request):
     1396            self.assertEqual(JID('user@example.org'), request.sender)
     1397            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1398            self.assertEqual('test', request.nodeIdentifier)
    9581399            return defer.succeed(None)
    9591400
    9601401        self.service.purge = purge
     
    9761417        </iq>
    9771418        """
    9781419
    979         def delete(requestor, service, nodeIdentifier):
    980             self.assertEqual(JID('user@example.org'), requestor)
    981             self.assertEqual(JID('pubsub.example.org'), service)
    982             self.assertEqual('test', nodeIdentifier)
     1420        def delete(request):
     1421            self.assertEqual(JID('user@example.org'), request.sender)
     1422            self.assertEqual(JID('pubsub.example.org'), request.recipient)
     1423            self.assertEqual('test', request.nodeIdentifier)
    9831424            return defer.succeed(None)
    9841425
    9851426        self.service.delete = delete
     
    10311472        self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.redirect.uri)
    10321473        self.assertTrue(message.event.delete.redirect.hasAttribute('uri'))
    10331474        self.assertEqual(redirectURI, message.event.delete.redirect['uri'])
     1475
     1476
     1477    def test_onSubscriptionsGet(self):
     1478        """
     1479        Getting subscription options is not supported.
     1480        """
     1481
     1482        xml = """
     1483        <iq type='get' to='pubsub.example.org'
     1484                       from='user@example.org'>
     1485          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1486            <subscriptions/>
     1487          </pubsub>
     1488        </iq>
     1489        """
     1490
     1491        def cb(result):
     1492            self.assertEquals('feature-not-implemented', result.condition)
     1493            self.assertEquals('unsupported', result.appCondition.name)
     1494            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1495            self.assertEquals('manage-subscriptions',
     1496                              result.appCondition['feature'])
     1497
     1498        d = self.handleRequest(xml)
     1499        self.assertFailure(d, error.StanzaError)
     1500        d.addCallback(cb)
     1501        return d
     1502
     1503
     1504    def test_onSubscriptionsSet(self):
     1505        """
     1506        Setting subscription options is not supported.
     1507        """
     1508
     1509        xml = """
     1510        <iq type='set' to='pubsub.example.org'
     1511                       from='user@example.org'>
     1512          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1513            <subscriptions/>
     1514          </pubsub>
     1515        </iq>
     1516        """
     1517
     1518        def cb(result):
     1519            self.assertEquals('feature-not-implemented', result.condition)
     1520            self.assertEquals('unsupported', result.appCondition.name)
     1521            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1522            self.assertEquals('manage-subscriptions',
     1523                              result.appCondition['feature'])
     1524
     1525        d = self.handleRequest(xml)
     1526        self.assertFailure(d, error.StanzaError)
     1527        d.addCallback(cb)
     1528        return d
     1529
     1530
     1531    def test_onAffiliationsGet(self):
     1532        """
     1533        Getting subscription options is not supported.
     1534        """
     1535
     1536        xml = """
     1537        <iq type='get' to='pubsub.example.org'
     1538                       from='user@example.org'>
     1539          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1540            <affiliations/>
     1541          </pubsub>
     1542        </iq>
     1543        """
     1544
     1545        def cb(result):
     1546            self.assertEquals('feature-not-implemented', result.condition)
     1547            self.assertEquals('unsupported', result.appCondition.name)
     1548            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1549            self.assertEquals('modify-affiliations',
     1550                              result.appCondition['feature'])
     1551
     1552        d = self.handleRequest(xml)
     1553        self.assertFailure(d, error.StanzaError)
     1554        d.addCallback(cb)
     1555        return d
     1556
     1557
     1558    def test_onAffiliationsSet(self):
     1559        """
     1560        Setting subscription options is not supported.
     1561        """
     1562
     1563        xml = """
     1564        <iq type='set' to='pubsub.example.org'
     1565                       from='user@example.org'>
     1566          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1567            <affiliations/>
     1568          </pubsub>
     1569        </iq>
     1570        """
     1571
     1572        def cb(result):
     1573            self.assertEquals('feature-not-implemented', result.condition)
     1574            self.assertEquals('unsupported', result.appCondition.name)
     1575            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1576            self.assertEquals('modify-affiliations',
     1577                              result.appCondition['feature'])
     1578
     1579        d = self.handleRequest(xml)
     1580        self.assertFailure(d, error.StanzaError)
     1581        d.addCallback(cb)
     1582        return d
     1583
     1584
     1585    def test_publish(self):
     1586        """
     1587        Non-overridden L{PubSubService.publish} yields unsupported error.
     1588        """
     1589
     1590        xml = """
     1591        <iq type='set' to='pubsub.example.org'
     1592                       from='user@example.org'>
     1593          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1594            <publish node='mynode'/>
     1595          </pubsub>
     1596        </iq>
     1597        """
     1598
     1599        def cb(result):
     1600            self.assertEquals('feature-not-implemented', result.condition)
     1601            self.assertEquals('unsupported', result.appCondition.name)
     1602            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1603            self.assertEquals('publish', result.appCondition['feature'])
     1604
     1605        d = self.handleRequest(xml)
     1606        self.assertFailure(d, error.StanzaError)
     1607        d.addCallback(cb)
     1608        return d
     1609
     1610
     1611    def test_subscribe(self):
     1612        """
     1613        Non-overridden L{PubSubService.subscribe} yields unsupported error.
     1614        """
     1615
     1616        xml = """
     1617        <iq type='set' to='pubsub.example.org'
     1618                       from='user@example.org'>
     1619          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1620            <subscribe node='test' jid='user@example.org/Home'/>
     1621          </pubsub>
     1622        </iq>
     1623        """
     1624
     1625        def cb(result):
     1626            self.assertEquals('feature-not-implemented', result.condition)
     1627            self.assertEquals('unsupported', result.appCondition.name)
     1628            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1629            self.assertEquals('subscribe', result.appCondition['feature'])
     1630
     1631        d = self.handleRequest(xml)
     1632        self.assertFailure(d, error.StanzaError)
     1633        d.addCallback(cb)
     1634        return d
     1635
     1636
     1637    def test_unsubscribe(self):
     1638        """
     1639        Non-overridden L{PubSubService.unsubscribe} yields unsupported error.
     1640        """
     1641
     1642        xml = """
     1643        <iq type='set' to='pubsub.example.org'
     1644                       from='user@example.org'>
     1645          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1646            <unsubscribe node='test' jid='user@example.org/Home'/>
     1647          </pubsub>
     1648        </iq>
     1649        """
     1650
     1651        def cb(result):
     1652            self.assertEquals('feature-not-implemented', result.condition)
     1653            self.assertEquals('unsupported', result.appCondition.name)
     1654            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1655            self.assertEquals('subscribe', result.appCondition['feature'])
     1656
     1657        d = self.handleRequest(xml)
     1658        self.assertFailure(d, error.StanzaError)
     1659        d.addCallback(cb)
     1660        return d
     1661
     1662
     1663    def test_subscriptions(self):
     1664        """
     1665        Non-overridden L{PubSubService.subscriptions} yields unsupported error.
     1666        """
     1667
     1668        xml = """
     1669        <iq type='get' to='pubsub.example.org'
     1670                       from='user@example.org'>
     1671          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1672            <subscriptions/>
     1673          </pubsub>
     1674        </iq>
     1675        """
     1676
     1677        def cb(result):
     1678            self.assertEquals('feature-not-implemented', result.condition)
     1679            self.assertEquals('unsupported', result.appCondition.name)
     1680            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1681            self.assertEquals('retrieve-subscriptions',
     1682                              result.appCondition['feature'])
     1683
     1684        d = self.handleRequest(xml)
     1685        self.assertFailure(d, error.StanzaError)
     1686        d.addCallback(cb)
     1687        return d
     1688
     1689
     1690    def test_affiliations(self):
     1691        """
     1692        Non-overridden L{PubSubService.affiliations} yields unsupported error.
     1693        """
     1694
     1695        xml = """
     1696        <iq type='get' to='pubsub.example.org'
     1697                       from='user@example.org'>
     1698          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1699            <affiliations/>
     1700          </pubsub>
     1701        </iq>
     1702        """
     1703
     1704        def cb(result):
     1705            self.assertEquals('feature-not-implemented', result.condition)
     1706            self.assertEquals('unsupported', result.appCondition.name)
     1707            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1708            self.assertEquals('retrieve-affiliations',
     1709                              result.appCondition['feature'])
     1710
     1711        d = self.handleRequest(xml)
     1712        self.assertFailure(d, error.StanzaError)
     1713        d.addCallback(cb)
     1714        return d
     1715
     1716
     1717    def test_create(self):
     1718        """
     1719        Non-overridden L{PubSubService.create} yields unsupported error.
     1720        """
     1721
     1722        xml = """
     1723        <iq type='set' to='pubsub.example.org'
     1724                       from='user@example.org'>
     1725          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1726            <create node='mynode'/>
     1727          </pubsub>
     1728        </iq>
     1729        """
     1730
     1731        def cb(result):
     1732            self.assertEquals('feature-not-implemented', result.condition)
     1733            self.assertEquals('unsupported', result.appCondition.name)
     1734            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1735            self.assertEquals('create-nodes', result.appCondition['feature'])
     1736
     1737        d = self.handleRequest(xml)
     1738        self.assertFailure(d, error.StanzaError)
     1739        d.addCallback(cb)
     1740        return d
     1741
     1742
     1743    def test_getDefaultConfiguration(self):
     1744        """
     1745        Non-overridden L{PubSubService.getDefaultConfiguration} yields
     1746        unsupported error.
     1747        """
     1748
     1749        xml = """
     1750        <iq type='get' to='pubsub.example.org'
     1751                       from='user@example.org'>
     1752          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1753            <default/>
     1754          </pubsub>
     1755        </iq>
     1756        """
     1757
     1758        def cb(result):
     1759            self.assertEquals('feature-not-implemented', result.condition)
     1760            self.assertEquals('unsupported', result.appCondition.name)
     1761            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1762            self.assertEquals('retrieve-default', result.appCondition['feature'])
     1763
     1764        d = self.handleRequest(xml)
     1765        self.assertFailure(d, error.StanzaError)
     1766        d.addCallback(cb)
     1767        return d
     1768
     1769
     1770    def test_getConfiguration(self):
     1771        """
     1772        Non-overridden L{PubSubService.getConfiguration} yields unsupported
     1773        error.
     1774        """
     1775
     1776        xml = """
     1777        <iq type='get' to='pubsub.example.org'
     1778                       from='user@example.org'>
     1779          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1780            <configure/>
     1781          </pubsub>
     1782        </iq>
     1783        """
     1784
     1785        def cb(result):
     1786            self.assertEquals('feature-not-implemented', result.condition)
     1787            self.assertEquals('unsupported', result.appCondition.name)
     1788            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1789            self.assertEquals('config-node', result.appCondition['feature'])
     1790
     1791        d = self.handleRequest(xml)
     1792        self.assertFailure(d, error.StanzaError)
     1793        d.addCallback(cb)
     1794        return d
     1795
     1796
     1797    def test_setConfiguration(self):
     1798        """
     1799        Non-overridden L{PubSubService.setConfiguration} yields unsupported
     1800        error.
     1801        """
     1802
     1803        xml = """
     1804        <iq type='set' to='pubsub.example.org'
     1805                       from='user@example.org'>
     1806          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1807            <configure node='test'>
     1808              <x xmlns='jabber:x:data' type='submit'>
     1809                <field var='FORM_TYPE' type='hidden'>
     1810                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1811                </field>
     1812                <field var='pubsub#deliver_payloads'><value>0</value></field>
     1813                <field var='pubsub#persist_items'><value>1</value></field>
     1814              </x>
     1815            </configure>
     1816          </pubsub>
     1817        </iq>
     1818        """
     1819
     1820        def cb(result):
     1821            self.assertEquals('feature-not-implemented', result.condition)
     1822            self.assertEquals('unsupported', result.appCondition.name)
     1823            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1824            self.assertEquals('config-node', result.appCondition['feature'])
     1825
     1826        d = self.handleRequest(xml)
     1827        self.assertFailure(d, error.StanzaError)
     1828        d.addCallback(cb)
     1829        return d
     1830
     1831
     1832    def test_items(self):
     1833        """
     1834        Non-overridden L{PubSubService.items} yields unsupported error.
     1835        """
     1836        xml = """
     1837        <iq type='get' to='pubsub.example.org'
     1838                       from='user@example.org'>
     1839          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1840            <items node='test'/>
     1841          </pubsub>
     1842        </iq>
     1843        """
     1844
     1845        def cb(result):
     1846            self.assertEquals('feature-not-implemented', result.condition)
     1847            self.assertEquals('unsupported', result.appCondition.name)
     1848            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1849            self.assertEquals('retrieve-items', result.appCondition['feature'])
     1850
     1851        d = self.handleRequest(xml)
     1852        self.assertFailure(d, error.StanzaError)
     1853        d.addCallback(cb)
     1854        return d
     1855
     1856
     1857    def test_retract(self):
     1858        """
     1859        Non-overridden L{PubSubService.retract} yields unsupported error.
     1860        """
     1861        xml = """
     1862        <iq type='set' to='pubsub.example.org'
     1863                       from='user@example.org'>
     1864          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1865            <retract node='test'>
     1866              <item id='item1'/>
     1867              <item id='item2'/>
     1868            </retract>
     1869          </pubsub>
     1870        </iq>
     1871        """
     1872
     1873        def cb(result):
     1874            self.assertEquals('feature-not-implemented', result.condition)
     1875            self.assertEquals('unsupported', result.appCondition.name)
     1876            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1877            self.assertEquals('retract-items', result.appCondition['feature'])
     1878
     1879        d = self.handleRequest(xml)
     1880        self.assertFailure(d, error.StanzaError)
     1881        d.addCallback(cb)
     1882        return d
     1883
     1884
     1885    def test_purge(self):
     1886        """
     1887        Non-overridden L{PubSubService.purge} yields unsupported error.
     1888        """
     1889        xml = """
     1890        <iq type='set' to='pubsub.example.org'
     1891                       from='user@example.org'>
     1892          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1893            <purge node='test'/>
     1894          </pubsub>
     1895        </iq>
     1896        """
     1897
     1898        def cb(result):
     1899            self.assertEquals('feature-not-implemented', result.condition)
     1900            self.assertEquals('unsupported', result.appCondition.name)
     1901            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1902            self.assertEquals('purge-nodes', result.appCondition['feature'])
     1903
     1904        d = self.handleRequest(xml)
     1905        self.assertFailure(d, error.StanzaError)
     1906        d.addCallback(cb)
     1907        return d
     1908
     1909
     1910    def test_delete(self):
     1911        """
     1912        Non-overridden L{PubSubService.delete} yields unsupported error.
     1913        """
     1914        xml = """
     1915        <iq type='set' to='pubsub.example.org'
     1916                       from='user@example.org'>
     1917          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     1918            <delete node='test'/>
     1919          </pubsub>
     1920        </iq>
     1921        """
     1922
     1923        def cb(result):
     1924            self.assertEquals('feature-not-implemented', result.condition)
     1925            self.assertEquals('unsupported', result.appCondition.name)
     1926            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     1927            self.assertEquals('delete-nodes', result.appCondition['feature'])
     1928
     1929        d = self.handleRequest(xml)
     1930        self.assertFailure(d, error.StanzaError)
     1931        d.addCallback(cb)
     1932        return d
Note: See TracBrowser for help on using the repository browser.