Ignore:
Timestamp:
Dec 26, 2008, 2:40:11 PM (14 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Convert:
svn:b33ecbfc-034c-dc11-8662-000475d9059e/trunk@140
Message:

If provided, return NodeID in the response for Service Discovery requests.

This also renames the namespace constants to include DISCO in their name
and provides better tests and docstrings.

Author: ralphm.
Reviewer: tofu.
Fixes #7.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wokkel/disco.py

    r31 r46  
    1818from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
    1919
    20 NS = 'http://jabber.org/protocol/disco'
    21 NS_INFO = NS + '#info'
    22 NS_ITEMS = NS + '#items'
     20NS_DISCO = 'http://jabber.org/protocol/disco'
     21NS_DISCO_INFO = NS_DISCO + '#info'
     22NS_DISCO_ITEMS = NS_DISCO + '#items'
    2323
    2424IQ_GET = '/iq[@type="get"]'
    25 DISCO_INFO = IQ_GET + '/query[@xmlns="' + NS_INFO + '"]'
    26 DISCO_ITEMS = IQ_GET + '/query[@xmlns="' + NS_ITEMS + '"]'
     25DISCO_INFO = IQ_GET + '/query[@xmlns="' + NS_DISCO_INFO + '"]'
     26DISCO_ITEMS = IQ_GET + '/query[@xmlns="' + NS_DISCO_ITEMS + '"]'
    2727
    2828class DiscoFeature(domish.Element):
     
    3232
    3333    def __init__(self, feature):
    34         domish.Element.__init__(self, (NS_INFO, 'feature'),
     34        domish.Element.__init__(self, (NS_DISCO_INFO, 'feature'),
    3535                                attribs={'var': feature})
    3636
     
    4242
    4343    def __init__(self, category, type, name = None):
    44         domish.Element.__init__(self, (NS_INFO, 'identity'),
     44        domish.Element.__init__(self, (NS_DISCO_INFO, 'identity'),
    4545                                attribs={'category': category,
    4646                                         'type': type})
     
    5555
    5656    def __init__(self, jid, node='', name=None):
    57         domish.Element.__init__(self, (NS_ITEMS, 'item'),
     57        domish.Element.__init__(self, (NS_DISCO_ITEMS, 'item'),
    5858                                attribs={'jid': jid.full()})
    5959        if node:
     
    6464
    6565
     66
    6667class DiscoHandler(XMPPHandler, IQHandlerMixin):
    6768    """
     
    8081        self.xmlstream.addObserver(DISCO_ITEMS, self.handleRequest)
    8182
    82     def _error(self, failure):
    83         failure.trap(defer.FirstError)
    84         return failure.value.subFailure
    8583
    8684    def _onDiscoInfo(self, iq):
     85        """
     86        Called for incoming disco info requests.
     87
     88        @param iq: The request iq element.
     89        @type iq: L{Element<twisted.words.xish.domish.Element>}
     90        """
    8791        requestor = jid.internJID(iq["from"])
    8892        target = jid.internJID(iq["to"])
    8993        nodeIdentifier = iq.query.getAttribute("node", '')
    9094
    91         def toResponse(results):
    92             info = []
    93             for i in results:
    94                 info.extend(i[1])
    95 
     95        def toResponse(info):
    9696            if nodeIdentifier and not info:
    9797                raise error.StanzaError('item-not-found')
    9898            else:
    99                 response = domish.Element((NS_INFO, 'query'))
     99                response = domish.Element((NS_DISCO_INFO, 'query'))
     100                if nodeIdentifier:
     101                    response['node'] = nodeIdentifier
    100102
    101103                for item in info:
     
    104106            return response
    105107
    106         dl = []
    107         for handler in self.parent:
    108             if IDisco.providedBy(handler):
    109                 dl.append(handler.getDiscoInfo(requestor, target,
    110                                                nodeIdentifier))
    111 
    112         d = defer.DeferredList(dl, fireOnOneErrback=1, consumeErrors=1)
    113         d.addCallbacks(toResponse, self._error)
     108        d = self.info(requestor, target, nodeIdentifier)
     109        d.addCallback(toResponse)
    114110        return d
    115111
     112
    116113    def _onDiscoItems(self, iq):
     114        """
     115        Called for incoming disco items requests.
     116
     117        @param iq: The request iq element.
     118        @type iq: L{Element<twisted.words.xish.domish.Element>}
     119        """
    117120        requestor = jid.internJID(iq["from"])
    118121        target = jid.internJID(iq["to"])
    119122        nodeIdentifier = iq.query.getAttribute("node", '')
    120123
    121         def toResponse(results):
    122             items = []
    123             for i in results:
    124                 items.extend(i[1])
    125 
    126             response = domish.Element((NS_ITEMS, 'query'))
     124        def toResponse(items):
     125            response = domish.Element((NS_DISCO_ITEMS, 'query'))
     126            if nodeIdentifier:
     127                response['node'] = nodeIdentifier
    127128
    128129            for item in items:
     
    131132            return response
    132133
    133         dl = []
    134         for handler in self.parent:
    135             if IDisco.providedBy(handler):
    136                 dl.append(handler.getDiscoItems(requestor, target,
    137                                                 nodeIdentifier))
    138 
    139         d = defer.DeferredList(dl, fireOnOneErrback=1, consumeErrors=1)
    140         d.addCallbacks(toResponse, self._error)
     134        d = self.items(requestor, target, nodeIdentifier)
     135        d.addCallback(toResponse)
    141136        return d
     137
     138
     139    def _gatherResults(self, deferredList):
     140        """
     141        Gather results from a list of deferreds.
     142
     143        Similar to L{defer.gatherResults}, but flattens the returned results,
     144        consumes errors after the first one and fires the errback of the
     145        returned deferred with the failure of the first deferred that fires its
     146        errback.
     147
     148        @param deferredList: List of deferreds for which the results should be
     149                             gathered.
     150        @type deferredList: C{list}
     151        @return: Deferred that fires with a list of gathered results.
     152        @rtype: L{defer.Deferred}
     153        """
     154        def cb(resultList):
     155            results = []
     156            for success, value in resultList:
     157                results.extend(value)
     158            return results
     159
     160        def eb(failure):
     161            failure.trap(defer.FirstError)
     162            return failure.value.subFailure
     163
     164        d = defer.DeferredList(deferredList, fireOnOneErrback=1,
     165                                             consumeErrors=1)
     166        d.addCallbacks(cb, eb)
     167        return d
     168
     169
     170    def info(self, requestor, target, nodeIdentifier):
     171        """
     172        Inspect all sibling protocol handlers for disco info.
     173
     174        Calls the L{getDiscoInfo<IDisco.getDiscoInfo>} method on all child
     175        handlers of the parent, that provide L{IDisco}.
     176
     177        @param requestor: The entity that sent the request.
     178        @type requestor: L{JID<twisted.words.protocols.jabber.jid.JID>}
     179        @param target: The entity the request was sent to.
     180        @type target: L{JID<twisted.words.protocols.jabber.jid.JID>}
     181        @param nodeIdentifier: The optional node being queried, or C{''}.
     182        @type nodeIdentifier: C{unicode}
     183        @return: Deferred with the gathered results from sibling handlers.
     184        @rtype: L{defer.Deferred}
     185        """
     186        dl = [handler.getDiscoInfo(requestor, target, nodeIdentifier)
     187              for handler in self.parent
     188              if IDisco.providedBy(handler)]
     189        return self._gatherResults(dl)
     190
     191
     192    def items(self, requestor, target, nodeIdentifier):
     193        """
     194        Inspect all sibling protocol handlers for disco items.
     195
     196        Calls the L{getDiscoItems<IDisco.getDiscoItems>} method on all child
     197        handlers of the parent, that provide L{IDisco}.
     198
     199        @param requestor: The entity that sent the request.
     200        @type requestor: L{JID<twisted.words.protocols.jabber.jid.JID>}
     201        @param target: The entity the request was sent to.
     202        @type target: L{JID<twisted.words.protocols.jabber.jid.JID>}
     203        @param nodeIdentifier: The optional node being queried, or C{''}.
     204        @type nodeIdentifier: C{unicode}
     205        @return: Deferred with the gathered results from sibling handlers.
     206        @rtype: L{defer.Deferred}
     207        """
     208        dl = [handler.getDiscoItems(requestor, target, nodeIdentifier)
     209              for handler in self.parent
     210              if IDisco.providedBy(handler)]
     211        return self._gatherResults(dl)
Note: See TracChangeset for help on using the changeset viewer.