source: ralphm-patches/pubsub-create-configure.patch @ 19:48bd45f41903

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

Further adjustments on type checking, a first test for parsing subscription
options.

File size: 14.7 KB
  • wokkel/data_form.py

    diff -r 9a749c388cda wokkel/data_form.py
    a b  
    253253            field['var'] = self.var
    254254
    255255        for value in self.values:
    256             if self.fieldType == 'boolean':
     256            if isinstance(value, bool):
    257257                value = unicode(value).lower()
    258             elif self.fieldType in ('jid-single', 'jid-multi'):
    259                 value = value.full()
     258            else:
     259                value = unicode(value)
    260260
    261261            field.addElement('value', content=unicode(value))
    262262
     
    564564            else:
    565565                # Unknown field, ignoring
    566566                pass
     567
     568
     569
     570def findForm(element, formNamespace):
     571    """
     572    Find a Data Form.
     573
     574    Look for an element that represents a Data Form with the specified
     575    form namespace as a child element of the given element.
     576    """
     577    if not element:
     578        return None
     579
     580    for child in element.elements():
     581        if (child.uri, child.name) == ((NS_X_DATA, 'x')):
     582            form = Form.fromElement(child)
     583
     584            if (form.formNamespace == formNamespace or
     585                not form.formNamespace and form.formType=='cancel'):
     586                return form
     587
     588    return None
  • wokkel/pubsub.py

    diff -r 9a749c388cda wokkel/pubsub.py
    a b  
    234234        'optionsSet': ['nodeOrEmpty', 'jid', 'options'],
    235235        'subscriptions': [],
    236236        'affiliations': [],
    237         'create': ['nodeOrNone'],
     237        'create': ['nodeOrNone', 'configureOrNone'],
    238238        'default': ['default'],
    239239        'configureGet': ['nodeOrEmpty'],
    240240        'configureSet': ['nodeOrEmpty', 'configure'],
     
    252252        self.verb = verb
    253253
    254254
    255     @staticmethod
    256     def _findForm(element, formNamespace):
    257         """
    258         Find a Data Form.
    259 
    260         Look for an element that represents a Data Form with the specified
    261         form namespace as a child element of the given element.
    262         """
    263         if not element:
    264             return None
    265 
    266         form = None
    267         for child in element.elements():
    268             try:
    269                 form = data_form.Form.fromElement(child)
    270             except data_form.Error:
    271                 continue
    272 
    273             if form.formNamespace != NS_PUBSUB_NODE_CONFIG:
    274                 continue
    275 
    276         return form
    277 
    278 
    279255    def _parse_node(self, verbElement):
    280256        """
    281257        Parse the required node identifier out of the verbElement.
     
    366342        """
    367343        Parse node type out of a request for the default node configuration.
    368344        """
    369         form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     345        form = data_form.findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
    370346        if form and form.formType == 'submit':
    371347            values = form.getValues()
    372348            self.nodeType = values.get('pubsub#node_type', 'leaf')
     
    378354        """
    379355        Parse options out of a request for setting the node configuration.
    380356        """
    381         form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     357        form = data_form.findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
    382358        if form:
    383359            if form.formType == 'submit':
    384360                self.options = form.getValues()
     
    390366            raise BadRequest(text="Missing configuration form")
    391367
    392368
     369    def _parse_configureOrNone(self, verbElement):
     370        """
     371        Parse optional node configuration form in create request.
     372        """
     373        for element in verbElement.parent.elements():
     374            if element.uri == NS_PUBSUB and element.name == 'configure':
     375                form = data_form.findForm(element, NS_PUBSUB_NODE_CONFIG)
     376                if form:
     377                    if form.formType == 'submit':
     378                        self.options = form.getValues()
     379                    else:
     380                        raise BadRequest(text="Unexpected form type %r" %
     381                                              form.formType)
     382                else:
     383                    self.options = {}
     384
     385
     386    def _render_configureOrNone(self, verbElement):
     387        """
     388        Render optional node configuration form in create request.
     389        """
     390        def makeFields(values):
     391            fields = []
     392            for name, value in values.iteritems():
     393                option = {'var': name}
     394                if isinstance(value, list):
     395                    option['values'] = value
     396                else:
     397                    option['value'] = value
     398                fields.append(data_form.Field.fromDict(option))
     399            return fields
     400
     401        if self.options is not None:
     402            fields = makeFields(self.options)
     403            form = data_form.Form('submit',
     404                                  formNamespace=NS_PUBSUB_NODE_CONFIG,
     405                                  fields=fields)
     406            configure = verbElement.parent.addElement('configure')
     407            configure.addChild(form.toElement())
     408
    393409
    394410    def _parse_itemIdentifiers(self, verbElement):
    395411        """
     
    437453
    438454
    439455    def _parse_options(self, verbElement):
    440         form = PubSubRequest._findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS)
     456        form = data_form.findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS)
    441457        if form:
    442458            if form.formType == 'submit':
    443459                self.options = form.getValues()
     
    468484            self._render_options(optionsElement)
    469485
    470486
     487
    471488    def parseElement(self, element):
    472489        """
    473490        Parse the publish-subscribe verb and parameters out of a request.
     
    663680        pass
    664681
    665682
    666     def createNode(self, service, nodeIdentifier=None, sender=None):
     683    def createNode(self, service, nodeIdentifier=None, options=None,
     684                         sender=None):
    667685        """
    668686        Create a publish subscribe node.
    669687
     
    671689        @type service: L{JID}
    672690        @param nodeIdentifier: Optional suggestion for the id of the node.
    673691        @type nodeIdentifier: C{unicode}
     692        @param options: Optional node configuration options.
     693        @type options: C{dict}
    674694        """
    675695        request = PubSubRequest('create')
    676696        request.recipient = service
    677697        request.nodeIdentifier = nodeIdentifier
    678698        request.sender = sender
     699        request.options = options
    679700
    680701        def cb(iq):
    681702            try:
     
    952973            d = self.getNodes(requestor, target)
    953974        else:
    954975            d = defer.succeed([])
    955            
    956 
    957976
    958977        d.addCallback(lambda nodes: [disco.DiscoItem(target, node)
    959978                                     for node in nodes])
     
    10811100        return processedValues
    10821101
    10831102
     1103    def _preProcess_create(self, resource, request):
     1104        if request.options:
     1105            request.options = self._checkConfiguration(resource,
     1106                                                       request.options)
     1107        return request
     1108
     1109
    10841110    def _preProcess_default(self, resource, request):
    10851111        if request.nodeType not in ('leaf', 'collection'):
    10861112            raise error.StanzaError('not-acceptable')
  • wokkel/test/test_pubsub.py

    diff -r 9a749c388cda wokkel/test/test_pubsub.py
    a b  
    1919from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
    2020
    2121NS_PUBSUB = 'http://jabber.org/protocol/pubsub'
    22 NS_PUBSUB_CONFIG = 'http://jabber.org/protocol/pubsub#node_config'
     22NS_PUBSUB_NODE_CONFIG = 'http://jabber.org/protocol/pubsub#node_config'
    2323NS_PUBSUB_ERRORS = 'http://jabber.org/protocol/pubsub#errors'
    2424NS_PUBSUB_EVENT = 'http://jabber.org/protocol/pubsub#event'
    2525NS_PUBSUB_OWNER = 'http://jabber.org/protocol/pubsub#owner'
     
    271271        return d
    272272
    273273
     274    def test_createNodeWithConfig(self):
     275        """
     276        Test sending create request with configuration options
     277        """
     278
     279        options = {
     280            'pubsub#title': 'Princely Musings (Atom)',
     281            'pubsub#deliver_payloads': True,
     282            'pubsub#persist_items': '1',
     283            'pubsub#max_items': '10',
     284            'pubsub#access_model': 'open',
     285            'pubsub#type': 'http://www.w3.org/2005/Atom',
     286        }
     287
     288        d = self.protocol.createNode(JID('pubsub.example.org'), 'test',
     289                                     sender=JID('user@example.org'),
     290                                     options=options)
     291
     292        iq = self.stub.output[-1]
     293
     294        # check if there is exactly one configure element
     295        children = list(domish.generateElementsQNamed(iq.pubsub.children,
     296                                                      'configure', NS_PUBSUB))
     297        self.assertEqual(1, len(children))
     298
     299        # check that it has a configuration form
     300        form = data_form.findForm(children[0], NS_PUBSUB_NODE_CONFIG)
     301        self.assertNotIdentical(None, form)
     302
     303        response = toResponse(iq, 'result')
     304        self.stub.send(response)
     305        return d
     306
     307
    274308    def test_deleteNode(self):
    275309        """
    276310        Test sending delete request.
     
    9661000        self.assertEqual(JID('user@example.org'), request.sender)
    9671001        self.assertEqual(JID('pubsub.example.org'), request.recipient)
    9681002        self.assertEqual('mynode', request.nodeIdentifier)
     1003        self.assertIdentical(None, request.options)
    9691004
    9701005
    9711006    def test_fromElementCreateInstant(self):
     
    9861021        self.assertIdentical(None, request.nodeIdentifier)
    9871022
    9881023
     1024    def test_fromElementCreateConfigureEmpty(self):
     1025        """
     1026        Test parsing a request to create a node with an empty configuration.
     1027        """
     1028
     1029        xml = """
     1030        <iq type='set' to='pubsub.example.org'
     1031                       from='user@example.org'>
     1032          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1033            <create node='mynode'/>
     1034            <configure/>
     1035          </pubsub>
     1036        </iq>
     1037        """
     1038
     1039        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1040        self.assertEqual({}, request.options)
     1041
     1042
     1043    def test_fromElementCreateConfigureEmptyWrongOrder(self):
     1044        """
     1045        Test parsing a request to create a node and configure, wrong order.
     1046
     1047        The C{configure} element should come after the C{create} request,
     1048        but we should accept both orders.
     1049        """
     1050
     1051        xml = """
     1052        <iq type='set' to='pubsub.example.org'
     1053                       from='user@example.org'>
     1054          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1055            <configure/>
     1056            <create node='mynode'/>
     1057          </pubsub>
     1058        </iq>
     1059        """
     1060
     1061        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1062        self.assertEqual({}, request.options)
     1063
     1064
     1065    def test_fromElementCreateConfigure(self):
     1066        """
     1067        Test parsing a request to create a node.
     1068        """
     1069
     1070        xml = """
     1071        <iq type='set' to='pubsub.example.org'
     1072                       from='user@example.org'>
     1073          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1074            <create node='mynode'/>
     1075            <configure>
     1076              <x xmlns='jabber:x:data' type='submit'>
     1077                <field var='FORM_TYPE' type='hidden'>
     1078                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1079                </field>
     1080                <field var='pubsub#access_model'><value>open</value></field>
     1081                <field var='pubsub#persist_items'><value>0</value></field>
     1082              </x>
     1083            </configure>
     1084          </pubsub>
     1085        </iq>
     1086        """
     1087
     1088        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1089        self.assertIn('pubsub#access_model', request.options)
     1090        self.assertEqual(u'open', request.options['pubsub#access_model'])
     1091        self.assertIn('pubsub#persist_items', request.options)
     1092        self.assertEqual(u'0', request.options['pubsub#persist_items'])
     1093
     1094
    9891095    def test_fromElementDefault(self):
    9901096        """
    9911097        Test parsing a request for the default node configuration.
     
    17711877        return d
    17721878
    17731879
     1880    def test_on_createWithConfig(self):
     1881        """
     1882        On a node create with configuration request the Data Form is parsed and
     1883        L{PubSubResource.create} is called with the passed options.
     1884        """
     1885
     1886        xml = """
     1887        <iq type='set' to='pubsub.example.org'
     1888                       from='user@example.org'>
     1889          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1890            <create node='mynode'/>
     1891            <configure>
     1892              <x xmlns='jabber:x:data' type='submit'>
     1893                <field var='FORM_TYPE' type='hidden'>
     1894                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1895                </field>
     1896                <field var='pubsub#deliver_payloads'><value>0</value></field>
     1897                <field var='pubsub#persist_items'><value>1</value></field>
     1898              </x>
     1899            </configure>
     1900          </pubsub>
     1901        </iq>
     1902        """
     1903
     1904        def getConfigurationOptions():
     1905            return {
     1906                "pubsub#persist_items":
     1907                    {"type": "boolean",
     1908                     "label": "Persist items to storage"},
     1909                "pubsub#deliver_payloads":
     1910                    {"type": "boolean",
     1911                     "label": "Deliver payloads with event notifications"}
     1912                }
     1913
     1914        def create(request):
     1915            self.assertEqual({'pubsub#deliver_payloads': False,
     1916                              'pubsub#persist_items': True}, request.options)
     1917            return defer.succeed(None)
     1918
     1919        self.resource.getConfigurationOptions = getConfigurationOptions
     1920        self.resource.create = create
     1921        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1922        return self.handleRequest(xml)
     1923
     1924
    17741925    def test_on_default(self):
    17751926        """
    17761927        A default request should result in
     
    18041955            self.assertEqual(NS_PUBSUB_OWNER, element.uri)
    18051956            self.assertEqual(NS_PUBSUB_OWNER, element.default.uri)
    18061957            form = data_form.Form.fromElement(element.default.x)
    1807             self.assertEqual(NS_PUBSUB_CONFIG, form.formNamespace)
     1958            self.assertEqual(NS_PUBSUB_NODE_CONFIG, form.formNamespace)
    18081959
    18091960        self.resource.getConfigurationOptions = getConfigurationOptions
    18101961        self.resource.default = default
     
    19332084            self.assertEqual(NS_PUBSUB_OWNER, element.uri)
    19342085            self.assertEqual(NS_PUBSUB_OWNER, element.configure.uri)
    19352086            form = data_form.Form.fromElement(element.configure.x)
    1936             self.assertEqual(NS_PUBSUB_CONFIG, form.formNamespace)
     2087            self.assertEqual(NS_PUBSUB_NODE_CONFIG, form.formNamespace)
    19372088            fields = form.fields
    19382089
    19392090            self.assertIn('pubsub#deliver_payloads', fields)
Note: See TracBrowser for help on using the repository browser.