Changeset 20:9e1c2535b74e in ralphm-patches for pubsub-subscription-options.patch


Ignore:
Timestamp:
Dec 30, 2009, 9:10:15 PM (11 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pubsub-subscription-options.patch

    r19 r20  
    1 diff -r 2919e60d3588 wokkel/data_form.py
    2 --- a/wokkel/data_form.py       Mon Dec 28 12:13:34 2009 +0100
    3 +++ b/wokkel/data_form.py       Tue Dec 29 15:34:35 2009 +0100
     1diff -r 3ef9bc7a0d70 wokkel/data_form.py
     2--- a/wokkel/data_form.py       Wed Dec 30 16:53:05 2009 +0100
     3+++ b/wokkel/data_form.py       Wed Dec 30 21:05:59 2009 +0100
    44@@ -76,6 +76,7 @@
    55             option['label'] = self.label
     
    5959             kwargs['options'] = options
    6060 
    61 @@ -416,6 +418,44 @@
     61@@ -343,7 +345,7 @@
     62     """
     63     Data Form.
     64 
     65-    There are two similarly named properties of forms. The L{formType} is the
     66+    There are two similarly named properties of forms. The C{formType} is the
     67     the so-called type of the form, and is set as the C{'type'} attribute
     68     on the form's root element.
     69 
     70@@ -351,19 +353,25 @@
     71     provide a context for the field names used in this form, by setting a
     72     special hidden field named C{'FORM_TYPE'}, to put the names of all
     73     other fields in the namespace of the value of that field. This namespace
     74-    is recorded in the L{formNamespace} instance variable.
     75+    is recorded in the C{formNamespace} instance variable.
     76 
     77     @ivar formType: Type of form. One of C{'form'}, C{'submit'}, {'cancel'},
     78                     or {'result'}.
     79-    @type formType: C{str}.
     80+    @type formType: C{str}
     81+
     82     @ivar formNamespace: The optional namespace of the field names for this
     83-                         form. This goes in the special field named
     84-                         C{'FORM_TYPE'}, if set.
     85-    @type formNamespace: C{str}.
     86-    @ivar fields: Dictionary of fields that have a name. Note that this is
     87-                  meant to be used for reading, only. One should use
     88-                  L{addField} for adding fields.
     89+        form. This goes in the special field named C{'FORM_TYPE'}, if set.
     90+    @type formNamespace: C{str}
     91+
     92+    @ivar fields: Dictionary of named fields. Note that this is meant to be
     93+        used for reading, only. One should use L{addField} or L{makeFields} and
     94+        L{removeField} for adding and removing fields.
     95     @type fields: C{dict}
     96+
     97+    @ivar fieldList: List of all fields, in the order they are added. Like
     98+        C{fields}, this is meant to be used for reading, only.
     99+    @type fieldList: C{list}
     100+
     101     """
     102 
     103     def __init__(self, formType, title=None, instructions=None,
     104@@ -403,7 +411,7 @@
     105         """
     106         Add a field to this form.
     107 
     108-        Fields are added in order, and L{fields} is a dictionary of the
     109+        Fields are added in order, and C{fields} is a dictionary of the
     110         named fields, that is kept in sync only if this method is used for
     111         adding new fields. Multiple fields with the same name are disallowed.
     112         """
     113@@ -416,7 +424,71 @@
    62114         self.fieldList.append(field)
    63115 
    64116 
     117+    def removeField(self, field):
     118+        """
     119+        Remove a field from this form.
     120+        """
     121+        self.fieldList.remove(field)
     122+
     123+        if field.var is not None:
     124+            del self.fields[field.var]
     125+
     126+
    65127+    def makeFields(self, values, fieldDefs=None, filterUnknown=True):
    66128+        """
     
    73135+        not C{None}, fields will only be created from C{values} with a
    74136+        corresponding entry in C{fieldDefs}.
     137+
     138+        @param values: Values to create fields from.
     139+        @type values: C{dict}
     140+
     141+        @param fieldDefs: Field definitions as a dictionary. See
     142+            L{wokkel.iwokkel.IPubSubService.getConfigurationOptions}
     143+        @type fieldDefs: C{dict}
     144+
     145+        @param filterUnknown: If C{True}, ignore fields that are not in
     146+            C{fieldDefs}.
     147+        @type filterUnknown: C{bool}
    75148+        """
    76149+        for name, value in values.iteritems():
     
    102175+
    103176     def toElement(self):
     177+        """
     178+        Return the DOM representation of this Form.
     179+
     180+        @rtype: L{domish.Element}
     181+        """
    104182         form = domish.Element((NS_X_DATA, 'x'))
    105183         form['type'] = self.formType
    106 @@ -489,3 +529,38 @@
     184 
     185@@ -477,7 +549,17 @@
     186 
     187         return form
     188 
     189+
     190     def getValues(self):
     191+        """
     192+        Extract values from the named form fields.
     193+
     194+        For all named fields, the corresponding value or values are
     195+        returned in a dictionary keyed by the field name. For multi-value
     196+        fields, the dictionary value is a list, otherwise a single value.
     197+
     198+        @rtype: C{dict}
     199+        """
     200         values = {}
     201 
     202         for name, field in self.fields.iteritems():
     203@@ -489,3 +571,48 @@
    107204             values[name] = value
    108205 
     
    110207+
    111208+
    112 +    def typeCheck(self, fieldDefs):
     209+    def typeCheck(self, fieldDefs, filterUnknown=False):
    113210+        """
    114211+        Check values of fields according to the field definition.
     
    119216+
    120217+        If the field type is C{None} (when not set by the receiving entity),
    121 +        the field definition is used to set the field's type before checking
    122 +        the values.
     218+        C{'text-single'} is assumed, as this is the default value.
    123219+
    124220+        @param fieldDefs: Field definitions as a dictionary. See
    125221+            L{wokkel.iwokkel.IPubSubService.getConfigurationOptions}
    126 +        @type fieldDefs: C{dict}.
    127 +        """
     222+        @type fieldDefs: C{dict}
     223+
     224+        @param filterUnknown: If C{True}, remove fields that are not in
     225+            C{fieldDefs}.
     226+        @type filterUnknown: C{bool}
     227+        """
     228+
     229+        filtered = []
    128230+
    129231+        for name, field in self.fields.iteritems():
     
    140242+                    pass
    141243+                field.typeCheck()
     244+            elif filterUnknown:
     245+                filtered.append(field)
    142246+            else:
    143 +                # Unknown field, ignoring
     247+                # Unknown field, not filtering
    144248+                pass
    145 diff -r 2919e60d3588 wokkel/pubsub.py
    146 --- a/wokkel/pubsub.py  Mon Dec 28 12:13:34 2009 +0100
    147 +++ b/wokkel/pubsub.py  Tue Dec 29 15:34:35 2009 +0100
     249+
     250+        for field in filtered:
     251+            self.removeField(field)
     252diff -r 3ef9bc7a0d70 wokkel/pubsub.py
     253--- a/wokkel/pubsub.py  Wed Dec 30 16:53:05 2009 +0100
     254+++ b/wokkel/pubsub.py  Wed Dec 30 21:05:59 2009 +0100
    148255@@ -228,7 +228,7 @@
    149256     # Map request verb to parameter handler names
     
    155262         'optionsGet': ['nodeOrEmpty', 'jid'],
    156263         'optionsSet': ['nodeOrEmpty', 'jid', 'options'],
    157 @@ -445,32 +445,71 @@
    158                  self.options = {}
     264@@ -380,10 +380,8 @@
     265         """
     266         form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
     267         if form:
     268-            if form.formType == 'submit':
     269-                self.options = form.getValues()
     270-            elif form.formType == 'cancel':
     271-                self.options = {}
     272+            if form.formType in ('submit', 'cancel'):
     273+                self.options = form
    159274             else:
    160275                 raise BadRequest(text="Unexpected form type %r" % form.formType)
    161 -        else:
    162 +        elif self.verb == 'optionsSet':
     276         else:
     277@@ -439,38 +437,71 @@
     278     def _parse_options(self, verbElement):
     279         form = PubSubRequest._findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS)
     280         if form:
     281-            if form.formType == 'submit':
     282-                self.options = form.getValues()
     283-            elif form.formType == 'cancel':
     284-                self.options = {}
     285-            else:
     286+            if form.formType not in ('submit', 'cancel'):
     287                 raise BadRequest(text="Unexpected form type %r" % form.formType)
     288+            self.options = form
     289         else:
    163290             raise BadRequest(text="Missing options form")
    164291 
     292+
     293+    def _render_options(self, verbElement):
     294+        verbElement.addChild(self.options.toElement())
     295+
    165296+
    166297+    def _parse_optionsWithSubscribe(self, verbElement):
     
    169300+                form = PubSubRequest._findForm(element,
    170301+                                               NS_PUBSUB_SUBSCRIBE_OPTIONS)
    171 +                if form:
    172 +                    self.optionsForm = form
    173 +                    if form.formType == 'submit':
    174 +                        self.options = form.getValues()
    175 +                    else:
    176 +                        BadRequest(text="Unexpected form type %r" % form.formType)
    177 +
    178 +
    179 +    def _render_options(self, verbElement):
    180 +        form = data_form.Form(formType='submit',
    181 +                              formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
    182 +        form.makeFields(values=self.options)
    183 +        verbElement.addChild(form.toElement())
     302+                if not form:
     303+                    continue
     304+
     305+                self.options = form
     306+                if form.formType != 'submit':
     307+                    BadRequest(text="Unexpected form type %r" % form.formType)
    184308+
    185309+
     
    233357         """
    234358         Send this request to its recipient.
    235 @@ -674,7 +713,8 @@
     359@@ -674,7 +705,8 @@
    236360         return request.send(self.xmlstream)
    237361 
     
    243367         Subscribe to a publish subscribe node.
    244368 
    245 @@ -685,11 +725,14 @@
     369@@ -685,6 +717,8 @@
    246370         @param subscriber: The entity to subscribe to the node. This entity
    247371                            will get notifications of new published items.
     
    252376         request = PubSubRequest('subscribe')
    253377         request.recipient = service
    254          request.nodeIdentifier = nodeIdentifier
     378@@ -692,6 +726,12 @@
    255379         request.subscriber = subscriber
    256 +        request.options = options
    257380         request.sender = sender
    258381 
     382+        if options:
     383+            form = data_form.Form(formType='result',
     384+                                  formNamespace=NS_PUBSUB_SUBSCRIBE_OPTIONS)
     385+            form.makeFields(options)
     386+            request.options = form
     387+
    259388         def cb(iq):
    260 @@ -1012,29 +1055,11 @@
     389             subscription = iq.pubsub.subscription["subscription"]
     390 
     391@@ -956,6 +996,8 @@
     392             handlerName, argNames = self._legacyHandlers[request.verb]
     393             handler = getattr(self, handlerName)
     394             args = [getattr(request, arg) for arg in argNames]
     395+            if 'options' in argNames:
     396+                args[argNames.index('options')] = request.options.getValues()
     397             d = handler(*args)
     398 
     399         # If needed, translate the result into a response
     400@@ -1012,55 +1054,17 @@
    261401             return None
    262402 
     
    291431 
    292432 
    293 diff -r 2919e60d3588 wokkel/test/test_pubsub.py
    294 --- a/wokkel/test/test_pubsub.py        Mon Dec 28 12:13:34 2009 +0100
    295 +++ b/wokkel/test/test_pubsub.py        Tue Dec 29 15:34:35 2009 +0100
     433-    def _checkConfiguration(self, resource, values):
     434-        options = resource.getConfigurationOptions()
     435-        processedValues = {}
     436-
     437-        for key, value in values.iteritems():
     438-            if key not in options:
     439-                continue
     440-
     441-            option = {'var': key}
     442-            option.update(options[key])
     443-            field = data_form.Field.fromDict(option)
     444-            if isinstance(value, list):
     445-                field.values = value
     446-            else:
     447-                field.value = value
     448-            field.typeCheck()
     449-
     450-            if isinstance(value, list):
     451-                processedValues[key] = field.values
     452-            else:
     453-                processedValues[key] = field.value
     454-
     455-        return processedValues
     456+    def _checkConfiguration(self, resource, form):
     457+        fieldDefs = resource.getConfigurationOptions()
     458+        form.typeCheck(fieldDefs, filterUnknown=True)
     459 
     460 
     461     def _preProcess_default(self, resource, request):
     462@@ -1091,12 +1095,11 @@
     463 
     464 
     465     def _preProcess_configureSet(self, resource, request):
     466-        if request.options:
     467-            request.options = self._checkConfiguration(resource,
     468-                                                       request.options)
     469+        if request.options.formType == 'cancel':
     470+            return None
     471+        else:
     472+            self._checkConfiguration(resource, request.options)
     473             return request
     474-        else:
     475-            return None
     476 
     477 
     478     def _toResponse_items(self, result, resource, request):
     479diff -r 3ef9bc7a0d70 wokkel/test/test_pubsub.py
     480--- a/wokkel/test/test_pubsub.py        Wed Dec 30 16:53:05 2009 +0100
     481+++ b/wokkel/test/test_pubsub.py        Wed Dec 30 21:05:59 2009 +0100
    296482@@ -447,6 +447,37 @@
    297483         return d
     
    332518         """
    333519         Test sending subscription request from a specific JID.
    334 @@ -718,6 +749,39 @@
     520@@ -718,6 +749,38 @@
    335521         self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri)
    336522         self.assertEqual('jid-required', err.appCondition.name)
     
    364550+        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
    365551+        self.assertEqual('subscribe', request.verb)
    366 +        request.optionsForm.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
    367 +        request.options = request.optionsForm.getValues()
    368 +        self.assertEqual({'pubsub#deliver': True}, request.options)
     552+        request.options.typeCheck({'pubsub#deliver': {'type': 'boolean'}})
     553+        self.assertEqual({'pubsub#deliver': True}, request.options.getValues())
    369554+
    370555+
     
    372557         """
    373558         Test parsing an unsubscription request.
     559@@ -805,7 +868,7 @@
     560         self.assertEqual(JID('pubsub.example.org'), request.recipient)
     561         self.assertEqual('test', request.nodeIdentifier)
     562         self.assertEqual(JID('user@example.org/Home'), request.subscriber)
     563-        self.assertEqual({'pubsub#deliver': '1'}, request.options)
     564+        self.assertEqual({'pubsub#deliver': '1'}, request.options.getValues())
     565 
     566 
     567     def test_fromElementOptionsSetCancel(self):
     568@@ -825,7 +888,7 @@
     569         """
     570 
     571         request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     572-        self.assertEqual({}, request.options)
     573+        self.assertEqual('cancel', request.options.formType)
     574 
     575 
     576     def test_fromElementOptionsSetBadFormType(self):
     577@@ -1053,7 +1116,8 @@
     578         self.assertEqual(JID('pubsub.example.org'), request.recipient)
     579         self.assertEqual('test', request.nodeIdentifier)
     580         self.assertEqual({'pubsub#deliver_payloads': '0',
     581-                          'pubsub#persist_items': '1'}, request.options)
     582+                          'pubsub#persist_items': '1'},
     583+                         request.options.getValues())
     584 
     585 
     586     def test_fromElementConfigureSetCancel(self):
     587@@ -1073,7 +1137,7 @@
     588         """
     589 
     590         request = pubsub.PubSubRequest.fromElement(parseXml(xml))
     591-        self.assertEqual({}, request.options)
     592+        self.assertEqual('cancel', request.options.formType)
     593 
     594 
     595     def test_fromElementConfigureSetBadFormType(self):
    374596@@ -1895,7 +1959,7 @@
    375597             return defer.succeed({'pubsub#deliver_payloads': '0',
     
    381603         def cb(element):
    382604             self.assertEqual('pubsub', element.name)
     605@@ -1968,7 +2032,8 @@
     606 
     607         def configureSet(request):
     608             self.assertEqual({'pubsub#deliver_payloads': False,
     609-                              'pubsub#persist_items': True}, request.options)
     610+                              'pubsub#persist_items': True},
     611+                             request.options.getValues())
     612             return defer.succeed(None)
     613 
     614         self.resource.getConfigurationOptions = getConfigurationOptions
     615@@ -2040,7 +2105,7 @@
     616 
     617         def configureSet(request):
     618             self.assertEquals(['pubsub#deliver_payloads'],
     619-                              request.options.keys())
     620+                              request.options.fields.keys())
     621 
     622         self.resource.getConfigurationOptions = getConfigurationOptions
     623         self.resource.configureSet = configureSet
     624@@ -2595,6 +2660,48 @@
     625         return d
     626 
     627 
     628+    def test_setConfigurationOptionsDict(self):
     629+        """
     630+        Options should be passed as a dictionary, not a form.
     631+        """
     632+
     633+        xml = """
     634+        <iq type='set' to='pubsub.example.org'
     635+                       from='user@example.org'>
     636+          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
     637+            <configure node='test'>
     638+              <x xmlns='jabber:x:data' type='submit'>
     639+                <field var='FORM_TYPE' type='hidden'>
     640+                  <value>http://jabber.org/protocol/pubsub#node_config</value>
     641+                </field>
     642+                <field var='pubsub#deliver_payloads'><value>0</value></field>
     643+                <field var='pubsub#persist_items'><value>1</value></field>
     644+              </x>
     645+            </configure>
     646+          </pubsub>
     647+        </iq>
     648+        """
     649+
     650+        def getConfigurationOptions():
     651+            return {
     652+                "pubsub#persist_items":
     653+                    {"type": "boolean",
     654+                     "label": "Persist items to storage"},
     655+                "pubsub#deliver_payloads":
     656+                    {"type": "boolean",
     657+                     "label": "Deliver payloads with event notifications"}
     658+                }
     659+
     660+        def setConfiguration(requestor, service, nodeIdentifier, options):
     661+            self.assertEquals({'pubsub#deliver_payloads': False,
     662+                               'pubsub#persist_items': True}, options)
     663+
     664+
     665+        self.service.getConfigurationOptions = getConfigurationOptions
     666+        self.service.setConfiguration = setConfiguration
     667+        return self.handleRequest(xml)
     668+
     669+
     670     def test_items(self):
     671         """
     672         Non-overridden L{PubSubService.items} yields unsupported error.
Note: See TracChangeset for help on using the changeset viewer.