source: ralphm-patches/pubsub-create-configure.patch @ 20:9e1c2535b74e

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

Add filterUnknown for typeCheck, request.options is a form, per docstring.

File size: 14.8 KB
  • wokkel/data_form.py

    diff -r 26ba5a2ff579 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
     
    616616
    617617        for field in filtered:
    618618            self.removeField(field)
     619
     620
     621def findForm(element, formNamespace):
     622    """
     623    Find a Data Form.
     624
     625    Look for an element that represents a Data Form with the specified
     626    form namespace as a child element of the given element.
     627    """
     628    if not element:
     629        return None
     630
     631    for child in element.elements():
     632        if (child.uri, child.name) == ((NS_X_DATA, 'x')):
     633            form = Form.fromElement(child)
     634
     635            if (form.formNamespace == formNamespace or
     636                not form.formNamespace and form.formType=='cancel'):
     637                return form
     638
     639    return None
  • wokkel/pubsub.py

    diff -r 26ba5a2ff579 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 in ('submit', 'cancel'):
    384360                self.options = form
     
    388364            raise BadRequest(text="Missing configuration form")
    389365
    390366
     367    def _parse_configureOrNone(self, verbElement):
     368        """
     369        Parse optional node configuration form in create request.
     370        """
     371        for element in verbElement.parent.elements():
     372            if element.uri == NS_PUBSUB and element.name == 'configure':
     373                form = data_form.findForm(element, NS_PUBSUB_NODE_CONFIG)
     374                if form:
     375                    if form.formType == 'submit':
     376                        self.options = form
     377                    else:
     378                        raise BadRequest(text="Unexpected form type %r" %
     379                                              form.formType)
     380                else:
     381                    form = data_form.Form('submit',
     382                                          formNamespace=NS_PUBSUB_NODE_CONFIG)
     383                    self.options = form
     384
     385
     386    def _render_configureOrNone(self, verbElement):
     387        """
     388        Render optional node configuration form in create request.
     389        """
     390        if self.options is not None:
     391            configure = verbElement.parent.addElement('configure')
     392            configure.addChild(self.options.toElement())
     393
    391394
    392395    def _parse_itemIdentifiers(self, verbElement):
    393396        """
     
    435438
    436439
    437440    def _parse_options(self, verbElement):
    438         form = PubSubRequest._findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS)
     441        form = data_form.findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS)
    439442        if form:
    440443            if form.formType not in ('submit', 'cancel'):
    441444                raise BadRequest(text="Unexpected form type %r" % form.formType)
     
    451454    def _parse_optionsWithSubscribe(self, verbElement):
    452455        for element in verbElement.parent.elements():
    453456            if element.name == 'options' and element.uri == NS_PUBSUB:
    454                 form = PubSubRequest._findForm(element,
    455                                                NS_PUBSUB_SUBSCRIBE_OPTIONS)
     457                form = data_form.findForm(element,
     458                                          NS_PUBSUB_SUBSCRIBE_OPTIONS)
    456459                if not form:
    457460                    continue
    458461
     
    662665        pass
    663666
    664667
    665     def createNode(self, service, nodeIdentifier=None, sender=None):
     668    def createNode(self, service, nodeIdentifier=None, options=None,
     669                         sender=None):
    666670        """
    667671        Create a publish subscribe node.
    668672
     
    670674        @type service: L{JID}
    671675        @param nodeIdentifier: Optional suggestion for the id of the node.
    672676        @type nodeIdentifier: C{unicode}
     677        @param options: Optional node configuration options.
     678        @type options: C{dict}
    673679        """
    674680        request = PubSubRequest('create')
    675681        request.recipient = service
    676682        request.nodeIdentifier = nodeIdentifier
    677683        request.sender = sender
    678684
     685        if options:
     686            form = data_form.Form(formType='result',
     687                                  formNamespace=NS_PUBSUB_NODE_CONFIG)
     688            form.makeFields(options)
     689            request.options = form
     690
    679691        def cb(iq):
    680692            try:
    681693                new_node = iq.pubsub.create["node"]
     
    956968            d = self.getNodes(requestor, target)
    957969        else:
    958970            d = defer.succeed([])
    959            
    960 
    961971
    962972        d.addCallback(lambda nodes: [disco.DiscoItem(target, node)
    963973                                     for node in nodes])
     
    10671077        form.typeCheck(fieldDefs, filterUnknown=True)
    10681078
    10691079
     1080    def _preProcess_create(self, resource, request):
     1081        if request.options:
     1082            self._checkConfiguration(resource, request.options)
     1083        return request
     1084
     1085
    10701086    def _preProcess_default(self, resource, request):
    10711087        if request.nodeType not in ('leaf', 'collection'):
    10721088            raise error.StanzaError('not-acceptable')
  • wokkel/test/test_pubsub.py

    diff -r 26ba5a2ff579 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.
     
    9981032        self.assertEqual(JID('user@example.org'), request.sender)
    9991033        self.assertEqual(JID('pubsub.example.org'), request.recipient)
    10001034        self.assertEqual('mynode', request.nodeIdentifier)
     1035        self.assertIdentical(None, request.options)
    10011036
    10021037
    10031038    def test_fromElementCreateInstant(self):
     
    10181053        self.assertIdentical(None, request.nodeIdentifier)
    10191054
    10201055
     1056    def test_fromElementCreateConfigureEmpty(self):
     1057        """
     1058        Test parsing a request to create a node with an empty configuration.
     1059        """
     1060
     1061        xml = """
     1062        <iq type='set' to='pubsub.example.org'
     1063                       from='user@example.org'>
     1064          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1065            <create node='mynode'/>
     1066            <configure/>
     1067          </pubsub>
     1068        </iq>
     1069        """
     1070
     1071        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1072        self.assertEqual({}, request.options.getValues())
     1073
     1074
     1075    def test_fromElementCreateConfigureEmptyWrongOrder(self):
     1076        """
     1077        Test parsing a request to create a node and configure, wrong order.
     1078
     1079        The C{configure} element should come after the C{create} request,
     1080        but we should accept both orders.
     1081        """
     1082
     1083        xml = """
     1084        <iq type='set' to='pubsub.example.org'
     1085                       from='user@example.org'>
     1086          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1087            <configure/>
     1088            <create node='mynode'/>
     1089          </pubsub>
     1090        </iq>
     1091        """
     1092
     1093        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1094        self.assertEqual({}, request.options.getValues())
     1095
     1096
     1097    def test_fromElementCreateConfigure(self):
     1098        """
     1099        Test parsing a request to create a node.
     1100        """
     1101
     1102        xml = """
     1103        <iq type='set' to='pubsub.example.org'
     1104                       from='user@example.org'>
     1105          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1106            <create node='mynode'/>
     1107            <configure>
     1108              <x xmlns='jabber:x:data' type='submit'>
     1109                <field var='FORM_TYPE' type='hidden'>
     1110                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1111                </field>
     1112                <field var='pubsub#access_model'><value>open</value></field>
     1113                <field var='pubsub#persist_items'><value>0</value></field>
     1114              </x>
     1115            </configure>
     1116          </pubsub>
     1117        </iq>
     1118        """
     1119
     1120        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     1121        values = request.options.getValues()
     1122        self.assertIn('pubsub#access_model', values)
     1123        self.assertEqual(u'open', values['pubsub#access_model'])
     1124        self.assertIn('pubsub#persist_items', values)
     1125        self.assertEqual(u'0', values['pubsub#persist_items'])
     1126
     1127
    10211128    def test_fromElementDefault(self):
    10221129        """
    10231130        Test parsing a request for the default node configuration.
     
    18041911        return d
    18051912
    18061913
     1914    def test_on_createWithConfig(self):
     1915        """
     1916        On a node create with configuration request the Data Form is parsed and
     1917        L{PubSubResource.create} is called with the passed options.
     1918        """
     1919
     1920        xml = """
     1921        <iq type='set' to='pubsub.example.org'
     1922                       from='user@example.org'>
     1923          <pubsub xmlns='http://jabber.org/protocol/pubsub'>
     1924            <create node='mynode'/>
     1925            <configure>
     1926              <x xmlns='jabber:x:data' type='submit'>
     1927                <field var='FORM_TYPE' type='hidden'>
     1928                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     1929                </field>
     1930                <field var='pubsub#deliver_payloads'><value>0</value></field>
     1931                <field var='pubsub#persist_items'><value>1</value></field>
     1932              </x>
     1933            </configure>
     1934          </pubsub>
     1935        </iq>
     1936        """
     1937
     1938        def getConfigurationOptions():
     1939            return {
     1940                "pubsub#persist_items":
     1941                    {"type": "boolean",
     1942                     "label": "Persist items to storage"},
     1943                "pubsub#deliver_payloads":
     1944                    {"type": "boolean",
     1945                     "label": "Deliver payloads with event notifications"}
     1946                }
     1947
     1948        def create(request):
     1949            self.assertEqual({'pubsub#deliver_payloads': False,
     1950                              'pubsub#persist_items': True},
     1951                             request.options.getValues())
     1952            return defer.succeed(None)
     1953
     1954        self.resource.getConfigurationOptions = getConfigurationOptions
     1955        self.resource.create = create
     1956        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
     1957        return self.handleRequest(xml)
     1958
     1959
    18071960    def test_on_default(self):
    18081961        """
    18091962        A default request should result in
     
    18371990            self.assertEqual(NS_PUBSUB_OWNER, element.uri)
    18381991            self.assertEqual(NS_PUBSUB_OWNER, element.default.uri)
    18391992            form = data_form.Form.fromElement(element.default.x)
    1840             self.assertEqual(NS_PUBSUB_CONFIG, form.formNamespace)
     1993            self.assertEqual(NS_PUBSUB_NODE_CONFIG, form.formNamespace)
    18411994
    18421995        self.resource.getConfigurationOptions = getConfigurationOptions
    18431996        self.resource.default = default
     
    19662119            self.assertEqual(NS_PUBSUB_OWNER, element.uri)
    19672120            self.assertEqual(NS_PUBSUB_OWNER, element.configure.uri)
    19682121            form = data_form.Form.fromElement(element.configure.x)
    1969             self.assertEqual(NS_PUBSUB_CONFIG, form.formNamespace)
     2122            self.assertEqual(NS_PUBSUB_NODE_CONFIG, form.formNamespace)
    19702123            fields = form.fields
    19712124
    19722125            self.assertIn('pubsub#deliver_payloads', fields)
Note: See TracBrowser for help on using the repository browser.