Changeset 226:2532cfb12c7c


Ignore:
Timestamp:
Oct 4, 2016, 9:53:27 AM (4 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Parents:
225:58dd11c3ddd5 (diff), 224:16fca7e4806f (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
hg-git-rename-source:
git
Message:

Merge branch 'master' into item-retract

Location:
wokkel
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • wokkel/pubsub.py

    r223 r226  
    4343                    'pubsub[@xmlns="' + NS_PUBSUB + '" or ' + \
    4444                           '@xmlns="' + NS_PUBSUB_OWNER + '"]'
     45
     46BOOL_TRUE = ('1','true')
     47BOOL_FALSE = ('0','false')
    4548
    4649class SubscriptionPending(Exception):
     
    253256    subscriptions = None
    254257    affiliations = None
     258    notify = None
    255259
    256260    # Map request iq type and subelement name to request verb
     
    294298        'configureSet': ['nodeOrEmpty', 'configure'],
    295299        'items': ['node', 'maxItems', 'itemIdentifiers', 'subidOrNone'],
    296         'retract': ['node', 'itemIdentifiers'],
     300        'retract': ['node', 'notify', 'itemIdentifiers'],
    297301        'purge': ['node'],
    298302        'delete': ['node'],
     
    567571
    568572
     573    def _parse_notify(self, verbElement):
     574        value = verbElement.getAttribute('notify')
     575
     576        if value:
     577            if value in BOOL_TRUE:
     578                self.notify = True
     579            elif value in BOOL_FALSE:
     580                self.notify = False
     581            else:
     582                raise BadRequest(text="Field notify must be a boolean value")
     583
     584
     585    def _render_notify(self, verbElement):
     586        if self.notify is not None:
     587            verbElement['notify'] = "true" if self.notify else "false"
     588
     589
    569590    def parseElement(self, element):
    570591        """
     
    956977        return d
    957978
     979    def retractItems(self, service, nodeIdentifier, itemIdentifiers, notify=None, sender=None):
     980        """
     981        Retract items from a publish subscribe node.
     982
     983        @param service: The publish subscribe service to delete the node from.
     984        @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
     985        @param nodeIdentifier: The identifier of the node.
     986        @type nodeIdentifier: C{unicode}
     987        @param itemIdentifiers: Identifiers of the items to be retracted.
     988        @type itemIdentifiers: C{set}
     989        @param notify: True if notification is required
     990        @type notify: C{unicode}
     991        """
     992        request = self._request_class('retract')
     993        request.recipient = service
     994        request.nodeIdentifier = nodeIdentifier
     995        request.itemIdentifiers = itemIdentifiers
     996        request.notify = notify
     997        request.sender = sender
     998        return request.send(self.xmlstream)
    958999
    9591000    def getOptions(self, service, nodeIdentifier, subscriber,
     
    13781419
    13791420
     1421    def notifyRetract(self, service, nodeIdentifier, notifications):
     1422        for subscriber, subscriptions, items in notifications:
     1423            message = self._createNotification('items', service,
     1424                                               nodeIdentifier, subscriber,
     1425                                               subscriptions)
     1426            for item in items:
     1427                retract = domish.Element((None, "retract"))
     1428                retract['id'] = item['id']
     1429                message.event.items.addChild(retract)
     1430            self.send(message)
     1431
     1432
    13801433    def notifyDelete(self, service, nodeIdentifier, subscribers,
    13811434                           redirectURI=None):
  • wokkel/pubsub.py

    r225 r226  
    1111"""
    1212
    13 from zope.interface import implements
     13from __future__ import division, absolute_import
     14
     15from zope.interface import implementer
    1416
    1517from twisted.internet import defer
    1618from twisted.python import log
     19from twisted.python.compat import StringType, iteritems, unicode
    1720from twisted.words.protocols.jabber import jid, error
    1821from twisted.words.xish import domish
     
    107110
    108111    @ivar nodeIdentifier: The identifier of the node subscribed to.  The root
    109         node is denoted by C{None}.
    110     @type nodeIdentifier: C{unicode}
     112        node is denoted by L{None}.
     113    @type nodeIdentifier: L{unicode}
    111114
    112115    @ivar subscriber: The subscribing entity.
     
    115118    @ivar state: The subscription state. One of C{'subscribed'}, C{'pending'},
    116119                 C{'unconfigured'}.
    117     @type state: C{unicode}
     120    @type state: L{unicode}
    118121
    119122    @ivar options: Optional list of subscription options.
    120     @type options: C{dict}
     123    @type options: L{dict}
    121124
    122125    @ivar subscriptionIdentifier: Optional subscription identifier.
    123     @type subscriptionIdentifier: C{unicode}
     126    @type subscriptionIdentifier: L{unicode}
    124127    """
    125128
     
    172175        """
    173176        @param id: optional item identifier
    174         @type id: C{unicode}
     177        @type id: L{unicode}
    175178        @param payload: optional item payload. Either as a domish element, or
    176179                        as serialized XML.
    177         @type payload: object providing L{domish.IElement} or C{unicode}.
     180        @type payload: object providing L{domish.IElement} or L{unicode}.
    178181        """
    179182
     
    182185            self['id'] = id
    183186        if payload is not None:
    184             if isinstance(payload, basestring):
     187            if isinstance(payload, StringType):
    185188                self.addRawXml(payload)
    186189            else:
     
    195198    The set of instance variables used depends on the type of request. If
    196199    a variable is not applicable or not passed in the request, its value is
    197     C{None}.
     200    L{None}.
    198201
    199202    @ivar verb: The type of publish-subscribe request. See C{_requestVerbMap}.
    200     @type verb: C{str}.
     203    @type verb: L{str}.
    201204
    202205    @ivar affiliations: Affiliations to be modified.
    203     @type affiliations: C{set}
     206    @type affiliations: L{set}
    204207
    205208    @ivar items: The items to be published, as L{domish.Element}s.
    206     @type items: C{list}
     209    @type items: L{list}
    207210
    208211    @ivar itemIdentifiers: Identifiers of the items to be retrieved or
    209212                           retracted.
    210     @type itemIdentifiers: C{set}
     213    @type itemIdentifiers: L{set}
    211214
    212215    @ivar maxItems: Maximum number of items to retrieve.
    213     @type maxItems: C{int}.
     216    @type maxItems: L{int}.
    214217
    215218    @ivar nodeIdentifier: Identifier of the node the request is about.
    216     @type nodeIdentifier: C{unicode}
     219    @type nodeIdentifier: L{unicode}
    217220
    218221    @ivar nodeType: The type of node that should be created, or for which the
    219222                    configuration is retrieved. C{'leaf'} or C{'collection'}.
    220     @type nodeType: C{str}
     223    @type nodeType: L{str}
    221224
    222225    @ivar options: Configurations options for nodes, subscriptions and publish
     
    228231
    229232    @ivar subscriptionIdentifier: Identifier for a specific subscription.
    230     @type subscriptionIdentifier: C{unicode}
     233    @type subscriptionIdentifier: L{unicode}
    231234
    232235    @ivar subscriptions: Subscriptions to be modified, as a set of
    233236        L{Subscription}.
    234     @type subscriptions: C{set}
     237    @type subscriptions: L{set}
    235238
    236239    @ivar affiliations: Affiliations to be modified, as a dictionary of entity
    237240        (L{JID<twisted.words.protocols.jabber.jid.JID>} to affiliation
    238         (C{unicode}).
    239     @type affiliations: C{dict}
     241        (L{unicode}).
     242    @type affiliations: L{dict}
    240243    """
    241244
     
    279282
    280283    # Map request verb to request iq type and subelement name
    281     _verbRequestMap = dict(((v, k) for k, v in _requestVerbMap.iteritems()))
     284    _verbRequestMap = dict(((v, k) for k, v in iteritems(_requestVerbMap)))
    282285
    283286    # Map request verb to parameter handler names
     
    667670    @type recipient: L{wokkel.pubsub.ItemsEvent}
    668671    @param nodeIdentifier: Identifier of the node the event pertains to.
    669     @type nodeIdentifier: C{unicode}
     672    @type nodeIdentifier: L{unicode}
    670673    @param headers: SHIM headers, see L{wokkel.shim.extractHeaders}.
    671     @type headers: C{dict}
     674    @type headers: L{dict}
    672675    """
    673676
     
    685688
    686689    @param items: List of received items as domish elements.
    687     @type items: C{list} of L{domish.Element}
     690    @type items: L{list} of L{domish.Element}
    688691    """
    689692
     
    710713
    711714
     715@implementer(IPubSubClient)
    712716class PubSubClient(XMPPHandler):
    713717    """
    714718    Publish subscribe client protocol.
    715719    """
    716 
    717     implements(IPubSubClient)
    718720
    719721    def connectionInitialized(self):
     
    792794        @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
    793795        @param nodeIdentifier: Optional suggestion for the id of the node.
    794         @type nodeIdentifier: C{unicode}
     796        @type nodeIdentifier: L{unicode}
    795797        @param options: Optional node configuration options.
    796         @type options: C{dict}
     798        @type options: L{dict}
    797799        """
    798800        request = PubSubRequest('create')
     
    827829        @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
    828830        @param nodeIdentifier: The identifier of the node.
    829         @type nodeIdentifier: C{unicode}
     831        @type nodeIdentifier: L{unicode}
    830832        """
    831833        request = PubSubRequest('delete')
     
    845847
    846848        @param nodeIdentifier: The identifier of the node.
    847         @type nodeIdentifier: C{unicode}
     849        @type nodeIdentifier: L{unicode}
    848850
    849851        @param subscriber: The entity to subscribe to the node. This entity
     
    852854
    853855        @param options: Subscription options.
    854         @type options: C{dict}
     856        @type options: L{dict}
    855857
    856858        @return: Deferred that fires with L{Subscription} or errbacks with
     
    897899
    898900        @param nodeIdentifier: The identifier of the node.
    899         @type nodeIdentifier: C{unicode}
     901        @type nodeIdentifier: L{unicode}
    900902
    901903        @param subscriber: The entity to unsubscribe from the node.
     
    903905
    904906        @param subscriptionIdentifier: Optional subscription identifier.
    905         @type subscriptionIdentifier: C{unicode}
     907        @type subscriptionIdentifier: L{unicode}
    906908        """
    907909        request = PubSubRequest('unsubscribe')
     
    921923        @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
    922924        @param nodeIdentifier: The identifier of the node.
    923         @type nodeIdentifier: C{unicode}
     925        @type nodeIdentifier: L{unicode}
    924926        @param items: Optional list of L{Item}s to publish.
    925         @type items: C{list}
     927        @type items: L{list}
    926928        """
    927929        request = PubSubRequest('publish')
     
    934936
    935937    def items(self, service, nodeIdentifier, maxItems=None,
    936               subscriptionIdentifier=None, sender=None):
     938              subscriptionIdentifier=None, sender=None, itemIdentifiers=None):
    937939        """
    938940        Retrieve previously published items from a publish subscribe node.
     
    942944
    943945        @param nodeIdentifier: The identifier of the node.
    944         @type nodeIdentifier: C{unicode}
     946        @type nodeIdentifier: L{unicode}
    945947
    946948        @param maxItems: Optional limit on the number of retrieved items.
    947         @type maxItems: C{int}
     949        @type maxItems: L{int}
    948950
    949951        @param subscriptionIdentifier: Optional subscription identifier. In
    950952            case the node has been subscribed to multiple times, this narrows
    951953            the results to the specific subscription.
    952         @type subscriptionIdentifier: C{unicode}
     954        @type subscriptionIdentifier: L{unicode}
     955
     956        @param itemIdentifiers: Identifiers of the items to be retrieved.
     957        @type itemIdentifiers: L{set} of L{unicode}
    953958        """
    954959        request = PubSubRequest('items')
     
    959964        request.subscriptionIdentifier = subscriptionIdentifier
    960965        request.sender = sender
     966        request.itemIdentifiers = itemIdentifiers
    961967
    962968        def cb(iq):
     
    10011007
    10021008        @param nodeIdentifier: The identifier of the node.
    1003         @type nodeIdentifier: C{unicode}
     1009        @type nodeIdentifier: L{unicode}
    10041010
    10051011        @param subscriber: The entity subscribed to the node.
     
    10071013
    10081014        @param subscriptionIdentifier: Optional subscription identifier.
    1009         @type subscriptionIdentifier: C{unicode}
     1015        @type subscriptionIdentifier: L{unicode}
    10101016
    10111017        @rtype: L{data_form.Form}
     
    10381044
    10391045        @param nodeIdentifier: The identifier of the node.
    1040         @type nodeIdentifier: C{unicode}
     1046        @type nodeIdentifier: L{unicode}
    10411047
    10421048        @param subscriber: The entity subscribed to the node.
     
    10441050
    10451051        @param options: Subscription options.
    1046         @type options: C{dict}.
     1052        @type options: L{dict}.
    10471053
    10481054        @param subscriptionIdentifier: Optional subscription identifier.
    1049         @type subscriptionIdentifier: C{unicode}
     1055        @type subscriptionIdentifier: L{unicode}
    10501056        """
    10511057        request = PubSubRequest('optionsSet')
     
    10661072
    10671073
     1074@implementer(IPubSubService, disco.IDisco)
    10681075class PubSubService(XMPPHandler, IQHandlerMixin):
    10691076    """
     
    10901097                         keys C{'category'}, C{'type'} and C{'name'}.
    10911098    @ivar pubSubFeatures: List of supported publish-subscribe features for
    1092                           service discovery, as C{str}.
    1093     @type pubSubFeatures: C{list} or C{None}
    1094     """
    1095 
    1096     implements(IPubSubService, disco.IDisco)
     1099                          service discovery, as L{str}.
     1100    @type pubSubFeatures: L{list} or L{None}
     1101    """
    10971102
    10981103    iqHandlers = {
     
    13931398            affiliations['node'] = request.nodeIdentifier
    13941399
    1395         for entity, affiliation in result.iteritems():
     1400        for entity, affiliation in iteritems(result):
    13961401            item = affiliations.addElement('affiliation')
    13971402            item['jid'] = entity.full()
     
    15041509
    15051510
     1511@implementer(IPubSubResource)
    15061512class PubSubResource(object):
    1507 
    1508     implements(IPubSubResource)
    15091513
    15101514    features = []
  • wokkel/test/test_pubsub.py

    r222 r226  
    907907
    908908
     909    def test_retractItems(self):
     910        """
     911        Test sending items retraction.
     912        """
     913        d = self.protocol.retractItems(JID('pubsub.example.org'), 'test',
     914                                       itemIdentifiers=['item1', 'item2'])
     915
     916        iq = self.stub.output[-1]
     917        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
     918        self.assertEquals('set', iq.getAttribute('type'))
     919        self.assertEquals('pubsub', iq.pubsub.name)
     920        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
     921        children = list(domish.generateElementsQNamed(iq.pubsub.children,
     922                                                      'retract', NS_PUBSUB))
     923        self.assertEquals(1, len(children))
     924        child = children[0]
     925        self.assertEquals('test', child['node'])
     926        itemIdentifiers = [item.getAttribute('id') for item in
     927                           domish.generateElementsQNamed(child.children, 'item',
     928                                                         NS_PUBSUB)]
     929        self.assertEquals(['item1', 'item2'], itemIdentifiers)
     930
     931        self.stub.send(toResponse(iq, 'result'))
     932        return d
     933
     934
     935    def test_retractItemsWithSender(self):
     936        """
     937        Test retracting items request from a specific JID.
     938        """
     939        d = self.protocol.retractItems(JID('pubsub.example.org'), 'test',
     940                                       itemIdentifiers=['item1', 'item2'],
     941                                       sender=JID('user@example.org'))
     942
     943        iq = self.stub.output[-1]
     944        self.assertEquals('user@example.org', iq['from'])
     945
     946        self.stub.send(toResponse(iq, 'result'))
     947        return d
     948
     949
    909950    def test_getOptions(self):
    910951        def cb(form):
  • wokkel/test/test_pubsub.py

    r225 r226  
    55Tests for L{wokkel.pubsub}
    66"""
     7
     8from __future__ import division, absolute_import
    79
    810from zope.interface import verify
     
    113115        element = subscription.toElement()
    114116        self.assertEqual('1234', element.getAttribute('subid'))
     117
     118
     119
     120class ItemTests(unittest.TestCase):
     121    """
     122    Tests for L{pubsub.Item}.
     123    """
     124
     125    def test_payloadRaw(self):
     126        """
     127        Adding a payload as a string assumes serialized XML.
     128        """
     129        payload = "<test xmlns='foo'/>"
     130        item = pubsub.Item(payload=payload)
     131        self.assertEqual(payload, item.children[0])
     132
     133
     134    def test_payloadElement(self):
     135        """
     136        Adding a payload as an domish Element, just adds that element as child.
     137        """
     138        payload = domish.Element(('foo', 'test'))
     139        item = pubsub.Item(payload=payload)
     140        self.assertIs(payload, item.children[0])
    115141
    116142
     
    802828
    803829
     830    def test_itemsWithItemIdentifiers(self):
     831        """
     832        Test sending items request with item identifiers.
     833        """
     834        def cb(items):
     835            self.assertEquals(2, len(items))
     836            self.assertEquals([item1, item2], items)
     837
     838        d = self.protocol.items(JID('pubsub.example.org'), 'test',
     839                                itemIdentifiers=['item1', 'item2'])
     840        d.addCallback(cb)
     841
     842        iq = self.stub.output[-1]
     843        self.assertEquals('pubsub.example.org', iq.getAttribute('to'))
     844        self.assertEquals('get', iq.getAttribute('type'))
     845        self.assertEquals('pubsub', iq.pubsub.name)
     846        self.assertEquals(NS_PUBSUB, iq.pubsub.uri)
     847        children = list(domish.generateElementsQNamed(iq.pubsub.children,
     848                                                      'items', NS_PUBSUB))
     849        self.assertEquals(1, len(children))
     850        child = children[0]
     851        self.assertEquals('test', child['node'])
     852        itemIdentifiers = [item.getAttribute('id') for item in
     853                           domish.generateElementsQNamed(child.children, 'item',
     854                                                         NS_PUBSUB)]
     855        self.assertEquals(['item1', 'item2'], itemIdentifiers)
     856
     857        response = toResponse(iq, 'result')
     858        items = response.addElement((NS_PUBSUB, 'pubsub')).addElement('items')
     859        items['node'] = 'test'
     860        item1 = items.addElement('item')
     861        item1['id'] = 'item1'
     862        item2 = items.addElement('item')
     863        item2['id'] = 'item2'
     864
     865        self.stub.send(response)
     866
     867        return d
     868
     869
    804870    def test_itemsWithSubscriptionIdentifier(self):
    805871        """
     
    29553021        def configureSet(request):
    29563022            self.assertEquals(['pubsub#deliver_payloads'],
    2957                               request.options.keys())
     3023                              list(request.options.keys()))
    29583024
    29593025        self.resource.getConfigurationOptions = getConfigurationOptions
Note: See TracChangeset for help on using the changeset viewer.