source: ralphm-patches/pubsub-subscription-options.patch @ 29:c7380726f583

Last change on this file since 29:c7380726f583 was 29:c7380726f583, checked in by Ralph Meijer <ralphm@…>, 10 years ago

Remove unneeded parameter to typeCheck.

File size: 16.3 KB
  • wokkel/pubsub.py

    Add support for subscription options in subscribe requests
    
    diff -r 4ae065e9ba2b wokkel/pubsub.py
    a b  
    228228    # Map request verb to parameter handler names
    229229    _parameters = {
    230230        'publish': ['node', 'items'],
    231         'subscribe': ['nodeOrEmpty', 'jid'],
     231        'subscribe': ['nodeOrEmpty', 'jid', 'optionsWithSubscribe'],
    232232        'unsubscribe': ['nodeOrEmpty', 'jid'],
    233233        'optionsGet': ['nodeOrEmpty', 'jid'],
    234234        'optionsSet': ['nodeOrEmpty', 'jid', 'options'],
     
    372372            if element.uri == NS_PUBSUB and element.name == 'configure':
    373373                form = data_form.findForm(element, NS_PUBSUB_NODE_CONFIG)
    374374                if form:
    375                     if form.formType == 'submit':
    376                         self.options = form
    377                     else:
     375                    if form.formType != 'submit':
    378376                        raise BadRequest(text=u"Unexpected form type '%s'" %
    379377                                              form.formType)
    380378                else:
    381379                    form = data_form.Form('submit',
    382380                                          formNamespace=NS_PUBSUB_NODE_CONFIG)
    383                     self.options = form
     381                self.options = form
    384382
    385383
    386384    def _render_configureOrNone(self, verbElement):
     
    451449            raise BadRequest(text="Missing options form")
    452450
    453451
     452
     453    def _render_options(self, verbElement):
     454        verbElement.addChild(self.options.toElement())
     455
     456
     457    def _parse_optionsWithSubscribe(self, verbElement):
     458        for element in verbElement.parent.elements():
     459            if element.name == 'options' and element.uri == NS_PUBSUB:
     460                form = data_form.findForm(element,
     461                                          NS_PUBSUB_SUBSCRIBE_OPTIONS)
     462                if form:
     463                    if form.formType != 'submit':
     464                        raise BadRequest(text=u"Unexpected form type '%s'" %
     465                                              form.formType)
     466                else:
     467                    form = data_form.Form('submit',
     468                                          formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
     469                self.options = form
     470
     471
     472    def _render_optionsWithSubscribe(self, verbElement):
     473        if self.options:
     474            optionsElement = verbElement.parent.addElement('options')
     475            self._render_options(optionsElement)
     476
     477
    454478    def parseElement(self, element):
    455479        """
    456480        Parse the publish-subscribe verb and parameters out of a request.
    457481        """
    458482        generic.Stanza.parseElement(self, element)
    459483
     484        verbs = []
     485        children = []
    460486        for child in element.pubsub.elements():
    461487            key = (self.stanzaType, child.uri, child.name)
    462488            try:
    463489                verb = self._requestVerbMap[key]
    464490            except KeyError:
    465491                continue
    466             else:
    467                 self.verb = verb
    468                 break
    469492
    470         if not self.verb:
     493            verbs.append(verb)
     494            children.append(child)
     495
     496        if not verbs:
    471497            raise NotImplementedError()
    472498
    473         for parameter in self._parameters[verb]:
     499        if len(verbs) > 1:
     500            if 'optionsSet' in verbs and 'subscribe' in verbs:
     501                self.verb = 'subscribe'
     502                child = children[verbs.index('subscribe')]
     503            else:
     504                raise NotImplementedError()
     505        else:
     506            self.verb = verbs[0]
     507
     508        for parameter in self._parameters[self.verb]:
    474509            getattr(self, '_parse_%s' % parameter)(child)
    475510
    476511
     512
    477513    def send(self, xs):
    478514        """
    479515        Send this request to its recipient.
     
    686722        return request.send(self.xmlstream)
    687723
    688724
    689     def subscribe(self, service, nodeIdentifier, subscriber, sender=None):
     725    def subscribe(self, service, nodeIdentifier, subscriber,
     726                        options=None, sender=None):
    690727        """
    691728        Subscribe to a publish subscribe node.
    692729
     
    697734        @param subscriber: The entity to subscribe to the node. This entity
    698735                           will get notifications of new published items.
    699736        @type subscriber: L{JID}
     737        @param options: Subscription options.
     738        @type options: C{dict}.
    700739        """
    701740        request = PubSubRequest('subscribe')
    702741        request.recipient = service
     
    704743        request.subscriber = subscriber
    705744        request.sender = sender
    706745
     746        if options:
     747            form = data_form.Form(formType='submit',
     748                                  formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
     749            form.makeFields(options)
     750            request.options = form
     751
    707752        def cb(iq):
    708753            subscription = iq.pubsub.subscription["subscription"]
    709754
     
    789834        d.addCallback(cb)
    790835        return d
    791836
     837    def getOptions(self, service, nodeIdentifier, subscriber, sender=None):
     838        """
     839        Get subscription options.
     840
     841        @param service: The publish subscribe service that keeps the node.
     842        @type service: L{JID}
     843
     844        @param nodeIdentifier: The identifier of the node.
     845        @type nodeIdentifier: C{unicode}
     846
     847        @param subscriber: The entity subscribed to the node.
     848        @type subscriber: L{JID}
     849
     850        @rtype: L{data_form.Form}
     851        """
     852        request = PubSubRequest('optionsGet')
     853        request.recipient = service
     854        request.nodeIdentifier = nodeIdentifier
     855        request.subscriber = subscriber
     856        request.sender = sender
     857
     858        def cb(iq):
     859            form = data_form.findForm(iq.pubsub.options,
     860                                      NS_PUBSUB_SUBSCRIBE_OPTIONS)
     861            form.typeCheck()
     862            return form
     863
     864        d = request.send(self.xmlstream)
     865        d.addCallback(cb)
     866        return d
     867
     868
     869    def setOptions(self, service, nodeIdentifier, subscriber,
     870                         options, sender=None):
     871        """
     872        Set subscription options.
     873
     874        @param service: The publish subscribe service that keeps the node.
     875        @type service: L{JID}
     876
     877        @param nodeIdentifier: The identifier of the node.
     878        @type nodeIdentifier: C{unicode}
     879
     880        @param subscriber: The entity subscribed to the node.
     881        @type subscriber: L{JID}
     882
     883        @param options: Subscription options.
     884        @type options: C{dict}.
     885        """
     886        request = PubSubRequest('optionsSet')
     887        request.recipient = service
     888        request.nodeIdentifier = nodeIdentifier
     889        request.subscriber = subscriber
     890        request.sender = sender
     891
     892        form = data_form.Form(formType='submit',
     893                              formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
     894        form.makeFields(options)
     895        request.options = form
     896
     897        d = request.send(self.xmlstream)
     898        return d
     899
    792900
    793901
    794902class PubSubService(XMPPHandler, IQHandlerMixin):
  • wokkel/test/test_pubsub.py

    diff -r 4ae065e9ba2b wokkel/test/test_pubsub.py
    a b  
    2424NS_PUBSUB_EVENT = 'http://jabber.org/protocol/pubsub#event'
    2525NS_PUBSUB_OWNER = 'http://jabber.org/protocol/pubsub#owner'
    2626NS_PUBSUB_META_DATA = 'http://jabber.org/protocol/pubsub#meta-data'
     27NS_PUBSUB_SUBSCRIBE_OPTIONS = 'http://jabber.org/protocol/pubsub#subscribe_options'
    2728
    2829def calledAsync(fn):
    2930    """
     
    482483        return d
    483484
    484485
     486    def test_subscribeWithOptions(self):
     487        options = {'pubsub#deliver': False}
     488
     489        d = self.protocol.subscribe(JID('pubsub.example.org'), 'test',
     490                                    JID('user@example.org'),
     491                                    options=options)
     492        iq = self.stub.output[-1]
     493
     494        # Check options present
     495        childNames = []
     496        for element in iq.pubsub.elements():
     497            if element.uri == NS_PUBSUB:
     498                childNames.append(element.name)
     499
     500        self.assertEqual(['subscribe', 'options'], childNames)
     501        form = data_form.findForm(iq.pubsub.options,
     502                                  NS_PUBSUB_SUBSCRIBE_OPTIONS)
     503        self.assertEqual('submit', form.formType)
     504        form.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
     505        self.assertEqual(options, form.getValues())
     506
     507        # Send response
     508        response = toResponse(iq, 'result')
     509        pubsub = response.addElement((NS_PUBSUB, 'pubsub'))
     510        subscription = pubsub.addElement('subscription')
     511        subscription['node'] = 'test'
     512        subscription['jid'] = 'user@example.org'
     513        subscription['subscription'] = 'subscribed'
     514        self.stub.send(response)
     515
     516        return d
     517
     518
    485519    def test_subscribeWithSender(self):
    486520        """
    487521        Test sending subscription request from a specific JID.
     
    625659        return d
    626660
    627661
     662    def test_getOptions(self):
     663        def cb(form):
     664            self.assertEqual('form', form.formType)
     665            self.assertEqual(NS_PUBSUB_SUBSCRIBE_OPTIONS, form.formNamespace)
     666            field = form.fields['pubsub#deliver']
     667            self.assertEqual('boolean', field.fieldType)
     668            self.assertIdentical(True, field.value)
     669            self.assertEqual('Enable delivery?', field.label)
     670
     671        d = self.protocol.getOptions(JID('pubsub.example.org'), 'test',
     672                                     JID('user@example.org'),
     673                                     sender=JID('user@example.org'))
     674        d.addCallback(cb)
     675
     676        iq = self.stub.output[-1]
     677        self.assertEqual('pubsub.example.org', iq.getAttribute('to'))
     678        self.assertEqual('get', iq.getAttribute('type'))
     679        self.assertEqual('pubsub', iq.pubsub.name)
     680        self.assertEqual(NS_PUBSUB, iq.pubsub.uri)
     681        children = list(domish.generateElementsQNamed(iq.pubsub.children,
     682                                                      'options', NS_PUBSUB))
     683        self.assertEqual(1, len(children))
     684        child = children[0]
     685        self.assertEqual('test', child['node'])
     686
     687        self.assertEqual(0, len(child.children))
     688
     689        # Send response
     690        form = data_form.Form('form', formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
     691        form.addField(data_form.Field('boolean', var='pubsub#deliver',
     692                                                 label='Enable delivery?',
     693                                                 value=True))
     694        response = toResponse(iq, 'result')
     695        response.addElement((NS_PUBSUB, 'pubsub'))
     696        response.pubsub.addElement('options')
     697        response.pubsub.options.addChild(form.toElement())
     698        self.stub.send(response)
     699
     700        return d
     701
     702
     703    def test_setOptions(self):
     704        """
     705        setOptions should send out a options-set request.
     706        """
     707        options = {'pubsub#deliver': False}
     708
     709        d = self.protocol.setOptions(JID('pubsub.example.org'), 'test',
     710                                     JID('user@example.org'),
     711                                     options,
     712                                     sender=JID('user@example.org'))
     713
     714        iq = self.stub.output[-1]
     715        self.assertEqual('pubsub.example.org', iq.getAttribute('to'))
     716        self.assertEqual('set', iq.getAttribute('type'))
     717        self.assertEqual('pubsub', iq.pubsub.name)
     718        self.assertEqual(NS_PUBSUB, iq.pubsub.uri)
     719        children = list(domish.generateElementsQNamed(iq.pubsub.children,
     720                                                      'options', NS_PUBSUB))
     721        self.assertEqual(1, len(children))
     722        child = children[0]
     723        self.assertEqual('test', child['node'])
     724
     725        form = data_form.findForm(child, NS_PUBSUB_SUBSCRIBE_OPTIONS)
     726        self.assertEqual('submit', form.formType)
     727        form.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
     728        self.assertEqual(options, form.getValues())
     729
     730        response = toResponse(iq, 'result')
     731        self.stub.send(response)
     732
     733        return d
     734
    628735
    629736class PubSubRequestTest(unittest.TestCase):
    630737
     738    def test_fromElementUnknown(self):
     739        """
     740        An unknown verb raises NotImplementedError.
     741        """
     742
     743        xml = """
     744        <iq type='set' to='pubsub.example.org'
     745                       from='user@example.org'>
     746          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     747            <non-existing-verb/>
     748          </pubsub>
     749        </iq>
     750        """
     751
     752        self.assertRaises(NotImplementedError,
     753                          pubsub.PubSubRequest.fromElement, parseXml(xml))
     754
     755
     756    def test_fromElementKnownBadCombination(self):
     757        """
     758        Multiple verbs in an unknown configuration raises NotImplementedError.
     759        """
     760
     761        xml = """
     762        <iq type='set' to='pubsub.example.org'
     763                       from='user@example.org'>
     764          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     765             <publish/>
     766             <create/>
     767          </pubsub>
     768        </iq>
     769        """
     770
     771        self.assertRaises(NotImplementedError,
     772                          pubsub.PubSubRequest.fromElement, parseXml(xml))
     773
    631774    def test_fromElementPublish(self):
    632775        """
    633776        Test parsing a publish request.
     
    753896        self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
    754897        self.assertEqual('jid-required', err.appCondition.name)
    755898
     899
     900    def test_fromElementSubscribeWithOptions(self):
     901        """
     902        Test parsing a subscription request.
     903        """
     904
     905        xml = """
     906        <iq type='set' to='pubsub.example.org'
     907                       from='user@example.org'>
     908          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     909            <subscribe node='test' jid='user@example.org/Home'/>
     910            <options>
     911              <x xmlns="jabber:x:data" type='submit'>
     912                <field var='FORM_TYPE' type='hidden'>
     913                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
     914                </field>
     915                <field var='pubsub#deliver' type='boolean'
     916                       label='Enable delivery?'>
     917                  <value>1</value>
     918                </field>
     919              </x>
     920            </options>
     921          </pubsub>
     922        </iq>
     923        """
     924
     925        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     926        self.assertEqual('subscribe', request.verb)
     927        request.options.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
     928        self.assertEqual({'pubsub#deliver': True}, request.options.getValues())
     929
     930
     931    def test_fromElementSubscribeWithOptionsBadFormType(self):
     932        """
     933        The options form should have the right type.
     934        """
     935
     936        xml = """
     937        <iq type='set' to='pubsub.example.org'
     938                       from='user@example.org'>
     939          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     940            <subscribe node='test' jid='user@example.org/Home'/>
     941            <options>
     942              <x xmlns="jabber:x:data" type='result'>
     943                <field var='FORM_TYPE' type='hidden'>
     944                  <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
     945                </field>
     946                <field var='pubsub#deliver' type='boolean'
     947                       label='Enable delivery?'>
     948                  <value>1</value>
     949                </field>
     950              </x>
     951            </options>
     952          </pubsub>
     953        </iq>
     954        """
     955
     956        err = self.assertRaises(error.StanzaError,
     957                                pubsub.PubSubRequest.fromElement,
     958                                parseXml(xml))
     959        self.assertEqual('bad-request', err.condition)
     960        self.assertEqual("Unexpected form type 'result'", err.text)
     961        self.assertEqual(None, err.appCondition)
     962
     963
     964    def test_fromElementSubscribeWithOptionsEmpty(self):
     965        """
     966        When no (suitable) form is found, the options are empty.
     967        """
     968
     969        xml = """
     970        <iq type='set' to='pubsub.example.org'
     971                       from='user@example.org'>
     972          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     973            <subscribe node='test' jid='user@example.org/Home'/>
     974            <options/>
     975          </pubsub>
     976        </iq>
     977        """
     978
     979        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     980        self.assertEqual('subscribe', request.verb)
     981        self.assertEqual({}, request.options.getValues())
     982
     983
    756984    def test_fromElementUnsubscribe(self):
    757985        """
    758986        Test parsing an unsubscription request.
Note: See TracBrowser for help on using the repository browser.