source: ralphm-patches/pubsub_request.patch @ 1:7fc86463b39f

Last change on this file since 1:7fc86463b39f was 1:7fc86463b39f, checked in by Ralph Meijer <ralphm@…>, 12 years ago

Add patches.

File size: 76.2 KB
  • wokkel/iwokkel.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 fafe0148a94c 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        """
     371
    393372
    394373    def getConfigurationOptions():
    395374        """
     
    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 fafe0148a94c wokkel/pubsub.py
    a b  
    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    """
     
    466434
    467435
    468436
     437class Request(object):
     438    """
     439    An IQ request.
     440
     441    @ivar sender: The entity from which the request was received.
     442    @type sender: L{jid.JID}
     443    @ivar recipient: The entity to which the request was sent.
     444    @type recipient: L{jid.JID}
     445    """
     446
     447    sender = None
     448    recipient = None
     449    stanzaType = None
     450
     451    @classmethod
     452    def fromElement(Class, element):
     453        request = Class()
     454        request.parseElement(element)
     455        return request
     456
     457
     458    def parseElement(self, element):
     459        self.sender = jid.internJID(element['from'])
     460        if element.hasAttribute('from'):
     461            self.sender = jid.internJID(element['from'])
     462        if element.hasAttribute('to'):
     463            self.recipient = jid.internJID(element['to'])
     464        self.stanzaType = element.getAttribute('type')
     465
     466
     467
     468class PubSubRequest(Request):
     469    """
     470    A publish-subscribe request.
     471
     472    The set of instance variables used depends on the type of request. If
     473    a variable is not applicable or not passed in the request, its value is
     474    C{None}.
     475
     476    @ivar affiliations: Affiliations to be modified.
     477    @type affiliations: C{set}
     478    @ivar items: The items to be published, as L{domish.Element}s.
     479    @type items: C{list}
     480    @ivar itemIdentifiers: Identifiers of the items to be retrieved or
     481                           retracted.
     482    @type itemIdentifiers: C{set}
     483    @ivar maxItems: Maximum number of items to retrieve.
     484    @type maxItems: C{int}.
     485    @ivar nodeIdentifier: Identifier of the node the request is about.
     486    @type nodeIdentifier: C{unicode}
     487    @ivar nodeType: The type of node that should be created, or for which the
     488                    configuration is retrieved. C{'leaf'} or C{'collection'}.
     489    @type nodeType: C{str}
     490    @ivar options: Configurations options for nodes, subscriptions and publish
     491                   requests.
     492    @type options: L{data_form.Form}
     493    @ivar subscriber: The subscribing entity.
     494    @type subscriber: L{JID}
     495    @ivar subscriptionIdentifier: Identifier for a specific subscription.
     496    @type subscriptionIdentifier: C{unicode}
     497    @ivar subscriptions: Subscriptions to be modified, as a set of
     498                         L{Subscription}.
     499    @type subscriptions: C{set}
     500    """
     501
     502    verb = None
     503
     504    affiliations = None
     505    items = None
     506    itemIdentifiers = None
     507    maxItems = None
     508    nodeIdentifier = None
     509    nodeType = None
     510    options = None
     511    subscriber = None
     512    subscriptionIdentifier = None
     513    subscriptions = None
     514
     515
     516    _verbs = {
     517        ('set', NS_PUBSUB, 'publish'): ('publish', ['node', 'items']),
     518        ('set', NS_PUBSUB, 'subscribe'): ('subscribe', ['nodeOrEmpty', 'jid']),
     519        ('set', NS_PUBSUB, 'unsubscribe'): ('unsubscribe', ['node', 'jid']),
     520        ('get', NS_PUBSUB, 'options'): ('optionsGet', []),
     521        ('set', NS_PUBSUB, 'options'): ('optionsSet', []),
     522        ('get', NS_PUBSUB, 'subscriptions'): ('subscriptions', []),
     523        ('get', NS_PUBSUB, 'affiliations'): ('affiliations', []),
     524        ('set', NS_PUBSUB, 'create'): ('create', ['nodeOrNone']),
     525        ('get', NS_PUBSUB_OWNER, 'default'): ('default', ['default']),
     526        ('get', NS_PUBSUB_OWNER, 'configure'): ('configureGet',
     527                                                ['nodeOrEmpty']),
     528        ('set', NS_PUBSUB_OWNER, 'configure'): ('configureSet',
     529                                                ['nodeOrEmpty', 'configure']),
     530        ('get', NS_PUBSUB, 'items'): ('items', ['node', 'maxItems',
     531                                                'itemIdentifiers']),
     532        ('set', NS_PUBSUB, 'retract'): ('retract', ['node',
     533                                                    'itemIdentifiers']),
     534        ('set', NS_PUBSUB_OWNER, 'purge'): ('purge', ['node']),
     535        ('set', NS_PUBSUB_OWNER, 'delete'): ('delete', ['node']),
     536        ('get', NS_PUBSUB_OWNER, 'affiliations'): ('affiliationsGet', []),
     537        ('set', NS_PUBSUB_OWNER, 'affiliations'): ('affiliationsSet', []),
     538        ('get', NS_PUBSUB_OWNER, 'subscriptions'): ('subscriptionsGet', []),
     539        ('set', NS_PUBSUB_OWNER, 'subscriptions'): ('subscriptionsSet', []),
     540    }
     541
     542    @staticmethod
     543    def _findForm(element, formNamespace):
     544        if not element:
     545            return None
     546
     547        form = None
     548        for child in element.elements():
     549            try:
     550                form = data_form.Form.fromElement(child)
     551            except data_form.Error:
     552                continue
     553
     554            if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
     555                continue
     556
     557        return form
     558
     559
     560    def _parse_node(self, verbElement):
     561        try:
     562            self.nodeIdentifier = verbElement["node"]
     563        except KeyError:
     564            raise BadRequest('nodeid-required')
     565
     566
     567    def _parse_nodeOrEmpty(self, verbElement):
     568        self.nodeIdentifier = verbElement.getAttribute("node", '')
     569
     570
     571    def _parse_nodeOrNone(self, verbElement):
     572        self.nodeIdentifier = verbElement.getAttribute("node")
     573
     574
     575    def _parse_items(self, verbElement):
     576        self.items = []
     577        for element in verbElement.elements():
     578            if element.uri == NS_PUBSUB and element.name == 'item':
     579                self.items.append(element)
     580
     581
     582    def _parse_jid(self, verbElement):
     583        try:
     584            self.subscriber = jid.internJID(verbElement["jid"])
     585        except KeyError:
     586            raise BadRequest('jid-required')
     587
     588
     589    def _parse_default(self, verbElement):
     590        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     591        if form and form.formType == 'submit':
     592            values = form.getValues()
     593            self.nodeType = values.get('pubsub#node_type', 'leaf')
     594        else:
     595            self.nodeType = 'leaf'
     596
     597
     598    def _parse_configure(self, verbElement):
     599        form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     600        if form:
     601            if form.formType == 'submit':
     602                self.options = form.getValues()
     603            elif form.formType == 'cancel':
     604                self.options = {}
     605            else:
     606                raise BadRequest("Unexpected form type %r" % form.formType)
     607        else:
     608            raise BadRequest("Missing configuration form")
     609
     610
     611
     612    def _parse_itemIdentifiers(self, verbElement):
     613        self.itemIdentifiers = []
     614        for element in verbElement.elements():
     615            if element.uri == NS_PUBSUB and element.name == 'item':
     616                try:
     617                    self.itemIdentifiers.append(element["id"])
     618                except KeyError:
     619                    raise BadRequest()
     620
     621
     622    def _parse_maxItems(self, verbElement):
     623        value = verbElement.getAttribute('max_items')
     624
     625        if value:
     626            try:
     627                self.maxItems = int(value)
     628            except ValueError:
     629                raise BadRequest(text="Field max_items requires a positive " +
     630                                      "integer value")
     631
     632
     633    def parseElement(self, element):
     634        Request.parseElement(self, element)
     635
     636        for child in element.pubsub.elements():
     637            key = (self.stanzaType, child.uri, child.name)
     638            self.verb, parsers = PubSubRequest._verbs.get(key)
     639            if self.verb:
     640                break
     641
     642        if not self.verb:
     643            raise NotImplementedError()
     644
     645        for parser in parsers:
     646            getattr(self, '_parse_%s' % parser)(child)
     647
     648
     649
    469650class PubSubService(XMPPHandler, IQHandlerMixin):
    470651    """
    471652    Protocol implementation for a XMPP Publish Subscribe Service.
     
    497678    implements(IPubSubService)
    498679
    499680    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 
     681            '/*': '_onPubSubRequest',
    521682            }
    522683
    523684
     
    530691
    531692
    532693    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)
     694        self.xmlstream.addObserver(PUBSUB_REQUEST, self.handleRequest)
    537695
    538696
    539697    def getDiscoInfo(self, requestor, target, nodeIdentifier):
     
    585743        return d
    586744
    587745
    588     def _findForm(self, element, formNamespace):
    589         if not element:
    590             return None
     746    def _onPubSubRequest(self, iq):
     747        request = PubSubRequest.fromElement(iq)
     748        handler = getattr(self, '_on_%s' % (request.verb))
     749        return handler(request)
    591750
    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
    598751
    599             if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
    600                 continue
     752    def _on_publish(self, request):
     753        return self.publish(request)
    601754
    602         return form
    603755
    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')
     756    def _on_subscribe(self, request):
    674757
    675758        def toResponse(result):
    676759            response = domish.Element((NS_PUBSUB, "pubsub"))
     
    681764            subscription["subscription"] = result.state
    682765            return response
    683766
    684         d = self.subscribe(requestor, service, nodeIdentifier, subscriber)
     767        d = self.subscribe(request)
    685768        d.addCallback(toResponse)
    686769        return d
    687770
    688771
    689     def _onUnsubscribe(self, iq):
    690         requestor, service, nodeIdentifier, subscriber = self._getParameters(
    691                 iq, 'unsubscribe', 'nodeOrEmpty', 'jid')
     772    def _on_unsubscribe(self, request):
     773        return self.unsubscribe(request)
    692774
    693         return self.unsubscribe(requestor, service, nodeIdentifier, subscriber)
    694775
    695 
    696     def _onOptionsGet(self, iq):
     776    def _on_optionsGet(self, request):
    697777        raise Unsupported('subscription-options')
    698778
    699779
    700     def _onOptionsSet(self, iq):
     780    def _on_optionsSet(self, request):
    701781        raise Unsupported('subscription-options')
    702782
    703783
    704     def _onSubscriptions(self, iq):
    705         requestor, service = self._getParameters(iq)
     784    def _on_subscriptions(self, request):
    706785
    707786        def toResponse(result):
    708787            response = domish.Element((NS_PUBSUB, 'pubsub'))
     
    714793                item['subscription'] = subscription.state
    715794            return response
    716795
    717         d = self.subscriptions(requestor, service)
     796        d = self.subscriptions(request)
    718797        d.addCallback(toResponse)
    719798        return d
    720799
    721800
    722     def _onAffiliations(self, iq):
    723         requestor, service = self._getParameters(iq)
     801    def _on_affiliations(self, request):
    724802
    725803        def toResponse(result):
    726804            response = domish.Element((NS_PUBSUB, 'pubsub'))
     
    733811
    734812            return response
    735813
    736         d = self.affiliations(requestor, service)
     814        d = self.affiliations(request)
    737815        d.addCallback(toResponse)
    738816        return d
    739817
    740818
    741     def _onCreate(self, iq):
    742         requestor, service = self._getParameters(iq)
    743         nodeIdentifier = iq.pubsub.create.getAttribute("node")
     819    def _on_create(self, request):
    744820
    745821        def toResponse(result):
    746             if not nodeIdentifier or nodeIdentifier != result:
     822            if not request.nodeIdentifier or request.nodeIdentifier != result:
    747823                response = domish.Element((NS_PUBSUB, 'pubsub'))
    748824                create = response.addElement('create')
    749825                create['node'] = result
     
    751827            else:
    752828                return None
    753829
    754         d = self.create(requestor, service, nodeIdentifier)
     830        d = self.create(request)
    755831        d.addCallback(toResponse)
    756832        return d
    757833
     
    771847            fields.append(data_form.Field.fromDict(option))
    772848        return fields
    773849
     850
    774851    def _formFromConfiguration(self, values):
    775852        options = self.getConfigurationOptions()
    776853        fields = self._makeFields(options, values)
     
    779856                              fields=fields)
    780857
    781858        return form
     859
    782860
    783861    def _checkConfiguration(self, values):
    784862        options = self.getConfigurationOptions()
     
    805883        return processedValues
    806884
    807885
    808     def _onDefault(self, iq):
    809         requestor, service = self._getParameters(iq)
     886    def _on_default(self, request):
    810887
    811888        def toResponse(options):
    812889            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
     
    814891            default.addChild(self._formFromConfiguration(options).toElement())
    815892            return response
    816893
    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'):
     894        if request.nodeType not in ('leaf', 'collection'):
    822895            return defer.fail(error.StanzaError('not-acceptable'))
    823896
    824         d = self.getDefaultConfiguration(requestor, service, nodeType)
     897        d = self.getDefaultConfiguration(request)
    825898        d.addCallback(toResponse)
    826899        return d
    827900
    828901
    829     def _onConfigureGet(self, iq):
    830         requestor, service, nodeIdentifier = self._getParameters(
    831                 iq, 'configure', 'nodeOrEmpty')
    832 
     902    def _on_configureGet(self, request):
    833903        def toResponse(options):
    834904            response = domish.Element((NS_PUBSUB_OWNER, "pubsub"))
    835905            configure = response.addElement("configure")
    836             configure.addChild(self._formFromConfiguration(options).toElement())
     906            form = self._formFromConfiguration(options)
     907            configure.addChild(form.toElement())
    837908
    838             if nodeIdentifier:
    839                 configure["node"] = nodeIdentifier
     909            if request.nodeIdentifier:
     910                configure["node"] = request.nodeIdentifier
    840911
    841912            return response
    842913
    843         d = self.getConfiguration(requestor, service, nodeIdentifier)
     914        d = self.getConfiguration(request)
    844915        d.addCallback(toResponse)
    845916        return d
    846917
    847918
    848     def _onConfigureSet(self, iq):
    849         requestor, service, nodeIdentifier = self._getParameters(
    850                 iq, 'configure', 'nodeOrEmpty')
     919    def _on_configureSet(self, request):
     920        if request.options:
     921            request.options = self._checkConfiguration(request.options)
     922            return self.setConfiguration(request)
     923        else:
     924            return None
    851925
    852         # Search configuration form with correct FORM_TYPE and process it
    853926
    854         form = self._findForm(iq.pubsub.configure, NS_PUBSUB_NODE_CONFIG)
    855927
    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()
     928    def _on_items(self, request):
    879929
    880930        def toResponse(result):
    881931            response = domish.Element((NS_PUBSUB, 'pubsub'))
    882932            items = response.addElement('items')
    883             if nodeIdentifier:
    884                 items["node"] = nodeIdentifier
     933            items["node"] = request.nodeIdentifier
    885934
    886935            for item in result:
    887936                items.addChild(item)
    888937
    889938            return response
    890939
    891         d = self.items(requestor, service, nodeIdentifier, maxItems,
    892                        itemIdentifiers)
     940        d = self.items(request)
    893941        d.addCallback(toResponse)
    894942        return d
    895943
    896944
    897     def _onRetract(self, iq):
    898         requestor, service, nodeIdentifier = self._getParameters(
    899                 iq, 'retract', 'node')
     945    def _on_retract(self, request):
     946        return self.retract(request)
    900947
    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()
    908948
    909         return self.retract(requestor, service, nodeIdentifier,
    910                             itemIdentifiers)
     949    def _on_purge(self, request):
     950        return self.purge(request)
    911951
    912952
    913     def _onPurge(self, iq):
    914         requestor, service, nodeIdentifier = self._getParameters(
    915                 iq, 'purge', 'node')
    916         return self.purge(requestor, service, nodeIdentifier)
     953    def _on_delete(self, request):
     954        return self.delete(request)
    917955
    918956
    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):
     957    def _on_affiliationsGet(self, iq):
    926958        raise Unsupported('modify-affiliations')
    927959
    928960
    929     def _onAffiliationsSet(self, iq):
     961    def _on_affiliationsSet(self, iq):
    930962        raise Unsupported('modify-affiliations')
    931963
    932964
    933     def _onSubscriptionsGet(self, iq):
     965    def _on_subscriptionsGet(self, iq):
    934966        raise Unsupported('manage-subscriptions')
    935967
    936968
    937     def _onSubscriptionsSet(self, iq):
     969    def _on_subscriptionsSet(self, iq):
    938970        raise Unsupported('manage-subscriptions')
    939971
    940972    # public methods
     
    9901022        return []
    9911023
    9921024
    993     def publish(self, requestor, service, nodeIdentifier, items):
     1025    def publish(self, request):
    9941026        raise Unsupported('publish')
    9951027
    9961028
    997     def subscribe(self, requestor, service, nodeIdentifier, subscriber):
     1029    def subscribe(self, request):
    9981030        raise Unsupported('subscribe')
    9991031
    10001032
    1001     def unsubscribe(self, requestor, service, nodeIdentifier, subscriber):
     1033    def unsubscribe(self, request):
    10021034        raise Unsupported('subscribe')
    10031035
    10041036
    1005     def subscriptions(self, requestor, service):
     1037    def subscriptions(self, request):
    10061038        raise Unsupported('retrieve-subscriptions')
    10071039
    10081040
    1009     def affiliations(self, requestor, service):
     1041    def affiliations(self, request):
    10101042        raise Unsupported('retrieve-affiliations')
    10111043
    10121044
    1013     def create(self, requestor, service, nodeIdentifier):
     1045    def create(self, request):
    10141046        raise Unsupported('create-nodes')
    10151047
    10161048
     
    10181050        return {}
    10191051
    10201052
    1021     def getDefaultConfiguration(self, requestor, service, nodeType):
     1053    def getDefaultConfiguration(self, request):
    10221054        raise Unsupported('retrieve-default')
    10231055
    10241056
    1025     def getConfiguration(self, requestor, service, nodeIdentifier):
     1057    def getConfiguration(self, request):
    10261058        raise Unsupported('config-node')
    10271059
    10281060
    1029     def setConfiguration(self, requestor, service, nodeIdentifier, options):
     1061    def setConfiguration(self, request):
    10301062        raise Unsupported('config-node')
    10311063
    10321064
    1033     def items(self, requestor, service, nodeIdentifier, maxItems,
    1034                     itemIdentifiers):
     1065    def items(self, request):
    10351066        raise Unsupported('retrieve-items')
    10361067
    10371068
    1038     def retract(self, requestor, service, nodeIdentifier, itemIdentifiers):
     1069    def retract(self, request):
    10391070        raise Unsupported('retract-items')
    10401071
    10411072
    1042     def purge(self, requestor, service, nodeIdentifier):
     1073    def purge(self, request):
    10431074        raise Unsupported('purge-nodes')
    10441075
    10451076
    1046     def delete(self, requestor, service, nodeIdentifier):
     1077    def delete(self, request):
    10471078        raise Unsupported('delete-nodes')
  • wokkel/test/test_pubsub.py

    diff -r fafe0148a94c 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)
     597
     598
     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
    573725
    574726
    575727    def test_onOptionsGet(self):
    576728        """
    577         Subscription options are not supported.
     729        Getting subscription options is not supported.
    578730        """
    579731
    580732        xml = """
    581733        <iq type='get' to='pubsub.example.org'
     734                       from='user@example.org'>
     735          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     736            <options/>
     737          </pubsub>
     738        </iq>
     739        """
     740
     741        def cb(result):
     742            self.assertEquals('feature-not-implemented', result.condition)
     743            self.assertEquals('unsupported', result.appCondition.name)
     744            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
     745
     746        d = self.handleRequest(xml)
     747        self.assertFailure(d, error.StanzaError)
     748        d.addCallback(cb)
     749        return d
     750
     751
     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'
    582759                       from='user@example.org'>
    583760          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
    584761            <options/>
     
    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):
     
    682994        self.service.getDefaultConfiguration = getDefaultConfiguration
    683995        verify.verifyObject(iwokkel.IPubSubService, self.service)
    684996        d = self.handleRequest(xml)
     997        d.addCallback(cb)
     998        return d
     999
     1000
     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)
    6851086        d.addCallback(cb)
    6861087        return d
    6871088
     
    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)
     1281
     1282
     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
    8711312
    8721313
    8731314    def test_onItems(self):
     
    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.