OLD | NEW |
(Empty) | |
| 1 # Copyright 2016 Google Inc. All Rights Reserved. |
| 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at |
| 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. |
| 14 |
| 15 """Helper that converts parameter values to the type expected by the API. |
| 16 |
| 17 Parameter values that appear in the URL and the query string are usually |
| 18 converted to native types before being passed to the backend. This code handles |
| 19 that conversion and some validation. |
| 20 """ |
| 21 |
| 22 # pylint: disable=g-bad-name |
| 23 import errors |
| 24 |
| 25 |
| 26 __all__ = ['transform_parameter_value'] |
| 27 |
| 28 |
| 29 def _check_enum(parameter_name, value, parameter_config): |
| 30 """Checks if an enum value is valid. |
| 31 |
| 32 This is called by the transform_parameter_value function and shouldn't be |
| 33 called directly. |
| 34 |
| 35 This verifies that the value of an enum parameter is valid. |
| 36 |
| 37 Args: |
| 38 parameter_name: A string containing the name of the parameter, which is |
| 39 either just a variable name or the name with the index appended. For |
| 40 example 'var' or 'var[2]'. |
| 41 value: A string containing the value passed in for the parameter. |
| 42 parameter_config: The dictionary containing information specific to the |
| 43 parameter in question. This is retrieved from request.parameters in |
| 44 the method config. |
| 45 |
| 46 Raises: |
| 47 EnumRejectionError: If the given value is not among the accepted |
| 48 enum values in the field parameter. |
| 49 """ |
| 50 enum_values = [enum['backendValue'] |
| 51 for enum in parameter_config['enum'].values() |
| 52 if 'backendValue' in enum] |
| 53 if value not in enum_values: |
| 54 raise errors.EnumRejectionError(parameter_name, value, enum_values) |
| 55 |
| 56 |
| 57 def _check_boolean(parameter_name, value, parameter_config): |
| 58 """Checks if a boolean value is valid. |
| 59 |
| 60 This is called by the transform_parameter_value function and shouldn't be |
| 61 called directly. |
| 62 |
| 63 This checks that the string value passed in can be converted to a valid |
| 64 boolean value. |
| 65 |
| 66 Args: |
| 67 parameter_name: A string containing the name of the parameter, which is |
| 68 either just a variable name or the name with the index appended. For |
| 69 example 'var' or 'var[2]'. |
| 70 value: A string containing the value passed in for the parameter. |
| 71 parameter_config: The dictionary containing information specific to the |
| 72 parameter in question. This is retrieved from request.parameters in |
| 73 the method config. |
| 74 |
| 75 Raises: |
| 76 BasicTypeParameterError: If the given value is not a valid boolean |
| 77 value. |
| 78 """ |
| 79 if parameter_config.get('type') != 'boolean': |
| 80 return |
| 81 |
| 82 if value.lower() not in ('1', 'true', '0', 'false'): |
| 83 raise errors.BasicTypeParameterError(parameter_name, value, 'boolean') |
| 84 |
| 85 |
| 86 def _convert_boolean(value): |
| 87 """Convert a string to a boolean value the same way the server does. |
| 88 |
| 89 This is called by the transform_parameter_value function and shouldn't be |
| 90 called directly. |
| 91 |
| 92 Args: |
| 93 value: A string value to be converted to a boolean. |
| 94 |
| 95 Returns: |
| 96 True or False, based on whether the value in the string would be interpreted |
| 97 as true or false by the server. In the case of an invalid entry, this |
| 98 returns False. |
| 99 """ |
| 100 if value.lower() in ('1', 'true'): |
| 101 return True |
| 102 return False |
| 103 |
| 104 |
| 105 # Map to convert parameters from strings to their desired back-end format. |
| 106 # Anything not listed here will remain a string. Note that the server |
| 107 # keeps int64 and uint64 as strings when passed to the backend. |
| 108 # This maps a type name from the .api method configuration to a (validation |
| 109 # function, conversion function, descriptive type name) tuple. The |
| 110 # descriptive type name is only used in conversion error messages, and the |
| 111 # names here are chosen to match the error messages from the server. |
| 112 # Note that the 'enum' entry is special cased. Enums have 'type': 'string', |
| 113 # so we have special case code to recognize them and use the 'enum' map |
| 114 # entry. |
| 115 _PARAM_CONVERSION_MAP = {'boolean': (_check_boolean, |
| 116 _convert_boolean, |
| 117 'boolean'), |
| 118 'int32': (None, int, 'integer'), |
| 119 'uint32': (None, int, 'integer'), |
| 120 'float': (None, float, 'float'), |
| 121 'double': (None, float, 'double'), |
| 122 'enum': (_check_enum, None, None)} |
| 123 |
| 124 |
| 125 def _get_parameter_conversion_entry(parameter_config): |
| 126 """Get information needed to convert the given parameter to its API type. |
| 127 |
| 128 Args: |
| 129 parameter_config: The dictionary containing information specific to the |
| 130 parameter in question. This is retrieved from request.parameters in the |
| 131 method config. |
| 132 |
| 133 Returns: |
| 134 The entry from _PARAM_CONVERSION_MAP with functions/information needed to |
| 135 validate and convert the given parameter from a string to the type expected |
| 136 by the API. |
| 137 """ |
| 138 entry = _PARAM_CONVERSION_MAP.get(parameter_config.get('type')) |
| 139 |
| 140 # Special handling for enum parameters. An enum's type is 'string', so we |
| 141 # need to detect them by the presence of an 'enum' property in their |
| 142 # configuration. |
| 143 if entry is None and 'enum' in parameter_config: |
| 144 entry = _PARAM_CONVERSION_MAP['enum'] |
| 145 |
| 146 return entry |
| 147 |
| 148 |
| 149 def transform_parameter_value(parameter_name, value, parameter_config): |
| 150 """Validates and transforms parameters to the type expected by the API. |
| 151 |
| 152 If the value is a list this will recursively call _transform_parameter_value |
| 153 on the values in the list. Otherwise, it checks all parameter rules for the |
| 154 the current value and converts its type from a string to whatever format |
| 155 the API expects. |
| 156 |
| 157 In the list case, '[index-of-value]' is appended to the parameter name for |
| 158 error reporting purposes. |
| 159 |
| 160 Args: |
| 161 parameter_name: A string containing the name of the parameter, which is |
| 162 either just a variable name or the name with the index appended, in the |
| 163 recursive case. For example 'var' or 'var[2]'. |
| 164 value: A string or list of strings containing the value(s) passed in for |
| 165 the parameter. These are the values from the request, to be validated, |
| 166 transformed, and passed along to the backend. |
| 167 parameter_config: The dictionary containing information specific to the |
| 168 parameter in question. This is retrieved from request.parameters in the |
| 169 method config. |
| 170 |
| 171 Returns: |
| 172 The converted parameter value(s). Not all types are converted, so this |
| 173 may be the same string that's passed in. |
| 174 """ |
| 175 if isinstance(value, list): |
| 176 # We're only expecting to handle path and query string parameters here. |
| 177 # The way path and query string parameters are passed in, they'll likely |
| 178 # only be single values or singly-nested lists (no lists nested within |
| 179 # lists). But even if there are nested lists, we'd want to preserve that |
| 180 # structure. These recursive calls should preserve it and convert all |
| 181 # parameter values. See the docstring for information about the parameter |
| 182 # renaming done here. |
| 183 return [transform_parameter_value('%s[%d]' % (parameter_name, index), |
| 184 element, parameter_config) |
| 185 for index, element in enumerate(value)] |
| 186 |
| 187 # Validate and convert the parameter value. |
| 188 entry = _get_parameter_conversion_entry(parameter_config) |
| 189 if entry: |
| 190 validation_func, conversion_func, type_name = entry |
| 191 if validation_func: |
| 192 validation_func(parameter_name, value, parameter_config) |
| 193 if conversion_func: |
| 194 try: |
| 195 return conversion_func(value) |
| 196 except ValueError: |
| 197 raise errors.BasicTypeParameterError(parameter_name, value, type_name) |
| 198 |
| 199 return value |
OLD | NEW |