Changeset 22

Show
Ignore:
Timestamp:
03/01/07 13:09:19 (2 years ago)
Author:
kevin
Message:

shuffled some code around to better manage when input validation
occurs. clears up and simplifies some things.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk

    • Property svn:ignore set to
      TGWebServices.egg-info
  • trunk/tgwebservices/controllers.py

    r21 r22  
    1414import cElementTree as et 
    1515from genshi.builder import tag, Element 
    16 import simplejson 
    1716 
    1817from turbogears import controllers, expose as tgexpose 
     
    2120from turbogears import validators 
    2221 
    23 from tgwebservices import soap 
     22from tgwebservices import soap, iconv 
    2423from tgwebservices.runtime import register, primitives, \ 
    2524                                  FunctionInfo, register_complex_type, \ 
     
    6160                          allow_json=True)(dict_wrapped_result(func)) 
    6261         
    63         def newfunc(*args, **kw): 
     62        def newfunc(self, **kw): 
    6463            if "tg_format" in kw: 
    6564                if kw["tg_format"] == "json": 
     
    6867                    cherrypy.request.headers["Accept"] = "text/xml" 
    6968                del kw["tg_format"] 
     69             
     70            request = cherrypy.request 
     71            input_types = fi.input_types 
     72             
     73            # the _tgws keyword is used when a web service system, such as 
     74            # SOAP, is calling the function rather than a call that is being 
     75            # made via direct URL traversal. 
     76            if "_tgws" in kw: 
     77                webservice = True 
     78                del kw["_tgws"] 
     79            else: 
     80                webservice = False 
     81             
     82            if "xml_body" in kw: 
     83                kw = iconv.handle_xml_params(kw["xml_body"], input_types) 
     84            elif request.headers.get("Content-Type", "") \ 
     85                 .startswith("text/xml"): 
     86                try: 
     87                    clen = int(request.headers.get('Content-Length')) or 0 
     88                    data = request.body.read(clen) 
     89                    body = et.fromstring(data) 
     90                except SyntaxError: 
     91                    raise validators.Invalid("Request XML is invalid", "",  
     92                                             None) 
     93                kw = iconv.handle_xml_params(body, input_types) 
     94            elif request.headers.get("Content-Type", "") \ 
     95                .startswith("application/json"): 
     96                clen = int(request.headers.get('Content-Length')) or 0 
     97                data = request.body.read(clen) 
     98                kw = iconv.handle_json_params(data, input_types) 
     99            else: 
     100                iconv.handle_keyword_params(kw, input_types) 
     101             
    70102            try: 
    71                 # the _tgws keyword is used when a web service system, such as 
    72                 # SOAP, is calling the function rather than a call that is being 
    73                 # made via direct URL traversal. 
    74                 if "_tgws" in kw: 
    75                     del kw["_tgws"] 
    76                     return func(*args, **kw) 
     103                if webservice: 
     104                    return func(self, **kw) 
    77105                else: 
    78106                    # when direct traversal is used, call the standard 
    79107                    # TurboGears expose mechanism. 
    80                     return tgfunc(*args, **kw) 
     108                    return tgfunc(self, **kw) 
    81109            except TypeError: 
    82110                # handle the case where a bogus parameter was sent in to the 
     
    110138    return entangle 
    111139 
    112 def boolean_converter(value): 
    113     value = value.lower() 
    114     if value in ["", "false", "0", "no", "off"]: 
    115         return False 
    116     if value in ["true", "1", "yes", "on"]: 
    117         return True 
    118     raise validators.Invalid("%s is not a legal boolean value" % (value), 
    119                              value, None) 
    120  
    121 def _get_single_value(elem, itype): 
    122     """Converts a single XML element into the given type""" 
    123     if isinstance(itype, list): 
    124         itemtype = itype[0] 
    125         items = [] 
    126         for subelem in elem.getchildren(): 
    127             items.append(_get_single_value(subelem, itemtype)) 
    128         return items 
    129     if not isinstance(itype, type): 
    130         itype = type(itype) 
    131     if itype not in primitives: 
    132         return _xml_to_instance(elem, itype) 
    133     if itype == bool: 
    134         itype = boolean_converter 
    135          
    136     if elem.text is None: 
    137         text = "" 
    138     else: 
    139         text = elem.text 
    140          
    141     try: 
    142         return itype(text) 
    143     except ValueError: 
    144         raise validators.Invalid( 
    145             "%s value for the '%s' parameter is not a " 
    146             "valid %s" % (text, soap.namespace_expr.sub("", elem.tag), 
    147                          itype.__name__),  
    148             text, None) 
    149      
    150 def _xml_to_instance(input, cls): 
    151     """Converts an input element into a new instance of cls.""" 
    152     instance = cls() 
    153     for elem in input.getchildren(): 
    154         tag = soap.namespace_expr.sub("", elem.tag) 
    155         try: 
    156             itype = getattr(cls, tag) 
    157         except AttributeError: 
    158             raise validators.Invalid("%s is an unknown tag for a %s"  
    159                                      % (tag, cls.__name__), 
    160                 tag, None) 
    161         setattr(instance, tag, _get_single_value(elem, itype)) 
    162     return instance 
    163  
    164 def _handle_xml_params(body, input_types): 
    165     kw = {} 
    166     for elem in body.getchildren(): 
    167         param = soap.namespace_expr.sub("", elem.tag) 
    168         try: 
    169             itype = input_types[param] 
    170         except KeyError: 
    171             raise validators.Invalid( 
    172                 "%s is not a valid parameter (valid values are: %s)" 
    173                 % (param, input_types.keys()), param, None 
    174             ) 
    175         kw[param] = _get_single_value(elem, itype) 
    176     return kw 
    177  
    178 def _handle_keyword_params(kw, input_types): 
    179     # convert the input parameters to appropriate types 
    180     for key in kw: 
    181         if key in input_types: 
    182             try: 
    183                 converter = input_types[key] 
    184                 if converter == bool: 
    185                     converter = boolean_converter 
    186                 kw[key] = converter(kw[key]) 
    187             except ValueError: 
    188                 raise validators.Invalid( 
    189                     "%s value for the '%s' parameter is not a " 
    190                     "valid %s" % (kw[key], key,  
    191                                  input_types[key].__name__),  
    192                     kw[key], None) 
    193         else: 
    194             raise validators.Invalid( 
    195                 "%s is not a valid parameter (valid values are: %s)" 
    196                 % (key, input_types.keys()), key, None 
    197             ) 
    198  
    199 def _get_json_value(value, itype): 
    200     if isinstance(itype, list): 
    201         itemtype = itype[0] 
    202         return [_get_json_value(item, itemtype) for item in value] 
    203     elif not isinstance(itype, type): 
    204         return _get_json_value(value, type(itype)) 
    205     elif itype not in primitives and isinstance(value, dict): 
    206         return _create_instance_from_json(value, itype) 
    207     return itype(value) 
    208  
    209 def _create_instance_from_json(value, cls): 
    210     instance = cls() 
    211     for key in value: 
    212         try: 
    213             itype = getattr(cls, key) 
    214         except AttributeError: 
    215             raise validators.Invalid("%s is an unknown parameter for a %s"  
    216                                      % (key, cls.__name__), 
    217                 key, None) 
    218         setattr(instance, str(key), _get_json_value(value[key], itype)) 
    219     return instance 
    220  
    221 def _handle_json_params(input, input_types): 
    222     if isinstance(input, basestring): 
    223         try: 
    224             input = simplejson.loads(input) 
    225         except ValueError: 
    226             raise validators.Invalid("Invalid JSON input") 
    227     kw = {} 
    228     for key in input: 
    229         if key in input_types: 
    230             kw[str(key)] = _get_json_value(input[key], input_types[key]) 
    231         else: 
    232             raise validators.Invalid( 
    233                 "%s is not a valid parameter (valid values are: %s)" 
    234                 % (key, input_types.keys()), key, None 
    235             ) 
    236     return kw 
    237          
    238  
    239140def wsvalidate(*args, **kw): 
    240141    """Validates and converts incoming parameters. Also registers the  
     
    256157        input_types.update(kw) 
    257158        fi.input_types = input_types 
    258          
    259         def newfunc(self, **kw): 
    260             request = cherrypy.request 
    261             if "xml_body" in kw and len(kw) == 1: 
    262                 body = kw.pop("xml_body") 
    263                 kw = _handle_xml_params(body, input_types) 
    264             elif request.headers.get("Content-Type", "") \ 
    265                  .startswith("text/xml"): 
    266                 try: 
    267                     clen = int(request.headers.get('Content-Length')) or 0 
    268                     data = request.body.read(clen) 
    269                     body = et.fromstring(data) 
    270                 except SyntaxError: 
    271                     raise validators.Invalid("Request XML is invalid", "",  
    272                                              None) 
    273                 kw = _handle_xml_params(body, input_types) 
    274             elif request.headers.get("Content-Type", "") \ 
    275                 .startswith("application/json"): 
    276                 clen = int(request.headers.get('Content-Length')) or 0 
    277                 data = request.body.read(clen) 
    278                 kw = _handle_json_params(data, input_types) 
    279             else: 
    280                 _handle_keyword_params(kw, input_types) 
    281                  
    282             return func(self, **kw) 
    283         newfunc.__name__ = func.__name__ 
    284         newfunc.__doc__ = func.__doc__ 
    285         newfunc._ws_func_info = func._ws_func_info 
    286         return newfunc 
     159        return func 
    287160    return entangle 
    288161 
  • trunk/tgwebservices/soap.py

    r19 r22  
    66import traceback 
    77import time 
     8import logging 
    89 
    910import dispatch 
     
    1617 
    1718from tgwebservices.runtime import primitives, ctvalues, typedproperty, unsigned 
     19 
     20log = logging.getLogger("tgwebservices.soap") 
    1821 
    1922namespace_expr = re.compile(r'^\{.*\}') 
     
    200203         
    201204        params = {"_tgws" : True} 
    202         if len(body.getchildren()): 
    203             params["xml_body"] = body 
     205        params["xml_body"] = body 
    204206         
    205207        # ensure that the requested method has actually been exposed 
  • trunk/tgwebservices/tests/test_controllers.py

    r21 r22  
    11import cElementTree as et 
    22 
    3 from tgwebservices.controllers import _get_single_value, _handle_json_params 
     3from tgwebservices.iconv import _get_single_value, handle_json_params 
    44 
    55class Person(object): 
     
    110110    input_types = {"person" : Person} 
    111111    data = {"person" : {"name" : "Fred", "age" : 99}} 
    112     kw = _handle_json_params(data, input_types) 
     112    kw = handle_json_params(data, input_types) 
    113113    assert isinstance(kw["person"], Person) 
    114114    print kw["person"].age 
     
    121121        [{"name" : "Mike", "age" : 42}, {"name" : "Carol", "age" : 40}] 
    122122}} 
    123     kw = _handle_json_params(data, input_types) 
     123    kw = handle_json_params(data, input_types) 
    124124    assert isinstance(kw["family"], Family2) 
    125125    assert len(kw["family"].members) == 2 
  • trunk/tgwebservices/tests/test_json.py

    r21 r22  
    4040    output = cherrypy.response.body[0] 
    4141    print output 
    42     assert output == """{"faultcode": "Client", "faultstring": "Unexpected parameter in function call (somestrings() got an unexpected keyword argument 'foo')"}""" 
     42    assert output == """{"faultcode": "Client", "faultstring": "foo is not a valid parameter (valid values are: [])"}""" 
    4343 
    4444class BooleanInput(WebServicesRoot):