Index: third_party/google-endpoints/endpoints/parameter_converter.py |
diff --git a/third_party/google-endpoints/endpoints/parameter_converter.py b/third_party/google-endpoints/endpoints/parameter_converter.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5a765cf893f4d4a83ec29f803ea0f759ed724043 |
--- /dev/null |
+++ b/third_party/google-endpoints/endpoints/parameter_converter.py |
@@ -0,0 +1,199 @@ |
+# Copyright 2016 Google Inc. All Rights Reserved. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+ |
+"""Helper that converts parameter values to the type expected by the API. |
+ |
+Parameter values that appear in the URL and the query string are usually |
+converted to native types before being passed to the backend. This code handles |
+that conversion and some validation. |
+""" |
+ |
+# pylint: disable=g-bad-name |
+import errors |
+ |
+ |
+__all__ = ['transform_parameter_value'] |
+ |
+ |
+def _check_enum(parameter_name, value, parameter_config): |
+ """Checks if an enum value is valid. |
+ |
+ This is called by the transform_parameter_value function and shouldn't be |
+ called directly. |
+ |
+ This verifies that the value of an enum parameter is valid. |
+ |
+ Args: |
+ parameter_name: A string containing the name of the parameter, which is |
+ either just a variable name or the name with the index appended. For |
+ example 'var' or 'var[2]'. |
+ value: A string containing the value passed in for the parameter. |
+ parameter_config: The dictionary containing information specific to the |
+ parameter in question. This is retrieved from request.parameters in |
+ the method config. |
+ |
+ Raises: |
+ EnumRejectionError: If the given value is not among the accepted |
+ enum values in the field parameter. |
+ """ |
+ enum_values = [enum['backendValue'] |
+ for enum in parameter_config['enum'].values() |
+ if 'backendValue' in enum] |
+ if value not in enum_values: |
+ raise errors.EnumRejectionError(parameter_name, value, enum_values) |
+ |
+ |
+def _check_boolean(parameter_name, value, parameter_config): |
+ """Checks if a boolean value is valid. |
+ |
+ This is called by the transform_parameter_value function and shouldn't be |
+ called directly. |
+ |
+ This checks that the string value passed in can be converted to a valid |
+ boolean value. |
+ |
+ Args: |
+ parameter_name: A string containing the name of the parameter, which is |
+ either just a variable name or the name with the index appended. For |
+ example 'var' or 'var[2]'. |
+ value: A string containing the value passed in for the parameter. |
+ parameter_config: The dictionary containing information specific to the |
+ parameter in question. This is retrieved from request.parameters in |
+ the method config. |
+ |
+ Raises: |
+ BasicTypeParameterError: If the given value is not a valid boolean |
+ value. |
+ """ |
+ if parameter_config.get('type') != 'boolean': |
+ return |
+ |
+ if value.lower() not in ('1', 'true', '0', 'false'): |
+ raise errors.BasicTypeParameterError(parameter_name, value, 'boolean') |
+ |
+ |
+def _convert_boolean(value): |
+ """Convert a string to a boolean value the same way the server does. |
+ |
+ This is called by the transform_parameter_value function and shouldn't be |
+ called directly. |
+ |
+ Args: |
+ value: A string value to be converted to a boolean. |
+ |
+ Returns: |
+ True or False, based on whether the value in the string would be interpreted |
+ as true or false by the server. In the case of an invalid entry, this |
+ returns False. |
+ """ |
+ if value.lower() in ('1', 'true'): |
+ return True |
+ return False |
+ |
+ |
+# Map to convert parameters from strings to their desired back-end format. |
+# Anything not listed here will remain a string. Note that the server |
+# keeps int64 and uint64 as strings when passed to the backend. |
+# This maps a type name from the .api method configuration to a (validation |
+# function, conversion function, descriptive type name) tuple. The |
+# descriptive type name is only used in conversion error messages, and the |
+# names here are chosen to match the error messages from the server. |
+# Note that the 'enum' entry is special cased. Enums have 'type': 'string', |
+# so we have special case code to recognize them and use the 'enum' map |
+# entry. |
+_PARAM_CONVERSION_MAP = {'boolean': (_check_boolean, |
+ _convert_boolean, |
+ 'boolean'), |
+ 'int32': (None, int, 'integer'), |
+ 'uint32': (None, int, 'integer'), |
+ 'float': (None, float, 'float'), |
+ 'double': (None, float, 'double'), |
+ 'enum': (_check_enum, None, None)} |
+ |
+ |
+def _get_parameter_conversion_entry(parameter_config): |
+ """Get information needed to convert the given parameter to its API type. |
+ |
+ Args: |
+ parameter_config: The dictionary containing information specific to the |
+ parameter in question. This is retrieved from request.parameters in the |
+ method config. |
+ |
+ Returns: |
+ The entry from _PARAM_CONVERSION_MAP with functions/information needed to |
+ validate and convert the given parameter from a string to the type expected |
+ by the API. |
+ """ |
+ entry = _PARAM_CONVERSION_MAP.get(parameter_config.get('type')) |
+ |
+ # Special handling for enum parameters. An enum's type is 'string', so we |
+ # need to detect them by the presence of an 'enum' property in their |
+ # configuration. |
+ if entry is None and 'enum' in parameter_config: |
+ entry = _PARAM_CONVERSION_MAP['enum'] |
+ |
+ return entry |
+ |
+ |
+def transform_parameter_value(parameter_name, value, parameter_config): |
+ """Validates and transforms parameters to the type expected by the API. |
+ |
+ If the value is a list this will recursively call _transform_parameter_value |
+ on the values in the list. Otherwise, it checks all parameter rules for the |
+ the current value and converts its type from a string to whatever format |
+ the API expects. |
+ |
+ In the list case, '[index-of-value]' is appended to the parameter name for |
+ error reporting purposes. |
+ |
+ Args: |
+ parameter_name: A string containing the name of the parameter, which is |
+ either just a variable name or the name with the index appended, in the |
+ recursive case. For example 'var' or 'var[2]'. |
+ value: A string or list of strings containing the value(s) passed in for |
+ the parameter. These are the values from the request, to be validated, |
+ transformed, and passed along to the backend. |
+ parameter_config: The dictionary containing information specific to the |
+ parameter in question. This is retrieved from request.parameters in the |
+ method config. |
+ |
+ Returns: |
+ The converted parameter value(s). Not all types are converted, so this |
+ may be the same string that's passed in. |
+ """ |
+ if isinstance(value, list): |
+ # We're only expecting to handle path and query string parameters here. |
+ # The way path and query string parameters are passed in, they'll likely |
+ # only be single values or singly-nested lists (no lists nested within |
+ # lists). But even if there are nested lists, we'd want to preserve that |
+ # structure. These recursive calls should preserve it and convert all |
+ # parameter values. See the docstring for information about the parameter |
+ # renaming done here. |
+ return [transform_parameter_value('%s[%d]' % (parameter_name, index), |
+ element, parameter_config) |
+ for index, element in enumerate(value)] |
+ |
+ # Validate and convert the parameter value. |
+ entry = _get_parameter_conversion_entry(parameter_config) |
+ if entry: |
+ validation_func, conversion_func, type_name = entry |
+ if validation_func: |
+ validation_func(parameter_name, value, parameter_config) |
+ if conversion_func: |
+ try: |
+ return conversion_func(value) |
+ except ValueError: |
+ raise errors.BasicTypeParameterError(parameter_name, value, type_name) |
+ |
+ return value |