From 58a031003525243d246f24d4049bf837a1aa4d16 Mon Sep 17 00:00:00 2001 From: Laurent Mazuel Date: Tue, 7 Jul 2020 14:59:26 -0700 Subject: [PATCH 1/5] Introduce explode into autorest --- README.md | 2 +- autorest/codegen/models/operation.py | 33 ++++++++++++++----- autorest/codegen/models/parameter.py | 3 ++ .../_queries_operations_async.py | 6 ++-- .../operations/_queries_operations.py | 6 ++-- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index eb5e7dd1d03..04c18e3b06c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ pass-thru: - subset-reducer # version: 3.0.6258 use-extension: - "@autorest/modelerfour": "4.15.378" + "@autorest/modelerfour": "4.15.381" modelerfour: group-parameters: true diff --git a/autorest/codegen/models/operation.py b/autorest/codegen/models/operation.py index aebce7e0e17..5327999e42a 100644 --- a/autorest/codegen/models/operation.py +++ b/autorest/codegen/models/operation.py @@ -15,6 +15,7 @@ from .schema_request import SchemaRequest from .object_schema import ObjectSchema from .constant_schema import ConstantSchema +from .list_schema import ListSchema _LOGGER = logging.getLogger(__name__) @@ -197,7 +198,7 @@ def build_serialize_data_call(parameter: Parameter, function_name: str) -> str: if parameter.skip_url_encoding: optional_parameters.append("skip_quote=True") - if parameter.style: + if parameter.style and not parameter.explode: if parameter.style in [ParameterStyle.simple, ParameterStyle.form]: div_char = "," elif parameter.style in [ParameterStyle.spaceDelimited]: @@ -210,17 +211,33 @@ def build_serialize_data_call(parameter: Parameter, function_name: str) -> str: raise ValueError(f"Do not support {parameter.style} yet") optional_parameters.append(f"div='{div_char}'") - serialization_constraints = parameter.schema.serialization_constraints - optional_parameters += serialization_constraints if serialization_constraints else "" + if parameter.explode: + if not isinstance(parameter.schema, ListSchema): + raise ValueError("Got a explode boolean on a non-array schema") + serialization_schema = parameter.schema.element_type + else: + serialization_schema = parameter.schema - optional_parameters_string = "" if not optional_parameters else ", " + ", ".join(optional_parameters) + serialization_constraints = serialization_schema.serialization_constraints + if serialization_constraints: + optional_parameters += serialization_constraints origin_name = parameter.full_serialized_name - return ( - f"""self._serialize.{function_name}("{origin_name.lstrip('_')}", {origin_name}, """ - + f"""'{parameter.schema.serialization_type}'{optional_parameters_string})""" - ) + parameters = [ + f'"{origin_name.lstrip("_")}"', + "q" if parameter.explode else origin_name, + f"'{serialization_schema.serialization_type}'", + *optional_parameters + ] + parameters_line = ', '.join(parameters) + + serialize_line = f'self._serialize.{function_name}({parameters_line})' + + if parameter.explode: + return f"[{serialize_line} for q in {origin_name}]" + else: + return serialize_line @property def serialization_context(self) -> str: diff --git a/autorest/codegen/models/parameter.py b/autorest/codegen/models/parameter.py index 44f80898b05..9bd65c5e97e 100644 --- a/autorest/codegen/models/parameter.py +++ b/autorest/codegen/models/parameter.py @@ -54,6 +54,7 @@ def __init__( constraints: List[Any], target_property_name: Optional[Union[int, str]] = None, # first uses id as placeholder style: Optional[ParameterStyle] = None, + explode: Optional[bool] = False, *, flattened: bool = False, grouped_by: Optional["Parameter"] = None, @@ -72,6 +73,7 @@ def __init__( self.constraints = constraints self.target_property_name = target_property_name self.style = style + self.explode = explode self.flattened = flattened self.grouped_by = grouped_by self.original_parameter = original_parameter @@ -194,6 +196,7 @@ def from_yaml(cls, yaml_data: Dict[str, Any]) -> "Parameter": constraints=[], # FIXME constraints target_property_name=id(yaml_data["targetProperty"]) if yaml_data.get("targetProperty") else None, style=ParameterStyle(http_protocol["style"]) if "style" in http_protocol else None, + explode=http_protocol["explode"] if "explode" in http_protocol else False, grouped_by=yaml_data.get("groupedBy", None), original_parameter=yaml_data.get("originalParameter", None), flattened=yaml_data.get("flattened", False), diff --git a/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/aio/operations_async/_queries_operations_async.py b/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/aio/operations_async/_queries_operations_async.py index 52ba17b0b0b..33a12a9df6d 100644 --- a/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/aio/operations_async/_queries_operations_async.py +++ b/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/aio/operations_async/_queries_operations_async.py @@ -65,7 +65,7 @@ async def array_string_multi_null( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = self._serialize.query("array_query", array_query, '[str]', div=',') + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] @@ -109,7 +109,7 @@ async def array_string_multi_empty( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = self._serialize.query("array_query", array_query, '[str]', div=',') + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] @@ -155,7 +155,7 @@ async def array_string_multi_valid( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = self._serialize.query("array_query", array_query, '[str]', div=',') + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] diff --git a/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/operations/_queries_operations.py b/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/operations/_queries_operations.py index 78e9b705cfe..737c802725e 100644 --- a/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/operations/_queries_operations.py +++ b/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/operations/_queries_operations.py @@ -70,7 +70,7 @@ def array_string_multi_null( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = self._serialize.query("array_query", array_query, '[str]', div=',') + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] @@ -115,7 +115,7 @@ def array_string_multi_empty( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = self._serialize.query("array_query", array_query, '[str]', div=',') + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] @@ -162,7 +162,7 @@ def array_string_multi_valid( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = self._serialize.query("array_query", array_query, '[str]', div=',') + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] From 1e4127d3b444acbd4ee17a34f9fe90c5a17b776d Mon Sep 17 00:00:00 2001 From: Laurent Mazuel Date: Tue, 7 Jul 2020 16:03:49 -0700 Subject: [PATCH 2/5] Model null element of a multi array as empty string --- autorest/codegen/models/operation.py | 2 +- .../_queries_operations_async.py | 16 ++++++++++------ .../operations/_queries_operations.py | 16 ++++++++++------ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/autorest/codegen/models/operation.py b/autorest/codegen/models/operation.py index 5327999e42a..bc5f626c374 100644 --- a/autorest/codegen/models/operation.py +++ b/autorest/codegen/models/operation.py @@ -235,7 +235,7 @@ def build_serialize_data_call(parameter: Parameter, function_name: str) -> str: serialize_line = f'self._serialize.{function_name}({parameters_line})' if parameter.explode: - return f"[{serialize_line} for q in {origin_name}]" + return f"[{serialize_line} if q is not None else '' for q in {origin_name}]" else: return serialize_line diff --git a/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/aio/operations_async/_queries_operations_async.py b/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/aio/operations_async/_queries_operations_async.py index 33a12a9df6d..d1c2645b6ff 100644 --- a/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/aio/operations_async/_queries_operations_async.py +++ b/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/aio/operations_async/_queries_operations_async.py @@ -65,7 +65,7 @@ async def array_string_multi_null( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') if q is not None else '' for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] @@ -92,6 +92,8 @@ async def array_string_multi_empty( ) -> None: """Get an empty array [] of string using the multi-array format. + No query parameter should be sent, since the array is empty. + :param array_query: an empty array [] of string using the multi-array format. :type array_query: list[str] :keyword callable cls: A custom type or function that will be passed the direct response @@ -109,7 +111,7 @@ async def array_string_multi_empty( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') if q is not None else '' for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] @@ -134,11 +136,13 @@ async def array_string_multi_valid( array_query: Optional[List[str]] = None, **kwargs ) -> None: - """Get an array of string ['ArrayQuery1', 'begin!*'();:@ &=+$,/?#[]end' , null, ''] using the - mult-array format. + """Get an array of string using the multi-array format. + + Parameter is ['ArrayQuery1', 'begin!*'();:@ &=+$,/?#[]end' , null, '']. null is sent as empty + string. :param array_query: an array of string ['ArrayQuery1', 'begin!*'();:@ &=+$,/?#[]end' , null, - ''] using the mult-array format. + ''] using the multi-array format. :type array_query: list[str] :keyword callable cls: A custom type or function that will be passed the direct response :return: None, or the result of cls(response) @@ -155,7 +159,7 @@ async def array_string_multi_valid( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') if q is not None else '' for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] diff --git a/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/operations/_queries_operations.py b/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/operations/_queries_operations.py index 737c802725e..e68accf5139 100644 --- a/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/operations/_queries_operations.py +++ b/test/vanilla/Expected/AcceptanceTests/UrlMultiCollectionFormat/urlmulticollectionformat/operations/_queries_operations.py @@ -70,7 +70,7 @@ def array_string_multi_null( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') if q is not None else '' for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] @@ -98,6 +98,8 @@ def array_string_multi_empty( # type: (...) -> None """Get an empty array [] of string using the multi-array format. + No query parameter should be sent, since the array is empty. + :param array_query: an empty array [] of string using the multi-array format. :type array_query: list[str] :keyword callable cls: A custom type or function that will be passed the direct response @@ -115,7 +117,7 @@ def array_string_multi_empty( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') if q is not None else '' for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] @@ -141,11 +143,13 @@ def array_string_multi_valid( **kwargs # type: Any ): # type: (...) -> None - """Get an array of string ['ArrayQuery1', 'begin!*'();:@ &=+$,/?#[]end' , null, ''] using the - mult-array format. + """Get an array of string using the multi-array format. + + Parameter is ['ArrayQuery1', 'begin!*'();:@ &=+$,/?#[]end' , null, '']. null is sent as empty + string. :param array_query: an array of string ['ArrayQuery1', 'begin!*'();:@ &=+$,/?#[]end' , null, - ''] using the mult-array format. + ''] using the multi-array format. :type array_query: list[str] :keyword callable cls: A custom type or function that will be passed the direct response :return: None, or the result of cls(response) @@ -162,7 +166,7 @@ def array_string_multi_valid( # Construct parameters query_parameters = {} # type: Dict[str, Any] if array_query is not None: - query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') for q in array_query] + query_parameters['arrayQuery'] = [self._serialize.query("array_query", q, 'str') if q is not None else '' for q in array_query] # Construct headers header_parameters = {} # type: Dict[str, Any] From 404f5b161adbbfe21d75bba89fe9fbc4dd041955 Mon Sep 17 00:00:00 2001 From: Laurent Mazuel Date: Wed, 8 Jul 2020 13:38:42 -0700 Subject: [PATCH 3/5] Update parameter.py --- autorest/codegen/models/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autorest/codegen/models/parameter.py b/autorest/codegen/models/parameter.py index 9bd65c5e97e..041738e88d0 100644 --- a/autorest/codegen/models/parameter.py +++ b/autorest/codegen/models/parameter.py @@ -196,7 +196,7 @@ def from_yaml(cls, yaml_data: Dict[str, Any]) -> "Parameter": constraints=[], # FIXME constraints target_property_name=id(yaml_data["targetProperty"]) if yaml_data.get("targetProperty") else None, style=ParameterStyle(http_protocol["style"]) if "style" in http_protocol else None, - explode=http_protocol["explode"] if "explode" in http_protocol else False, + explode=http_protocol.get("explode", False), grouped_by=yaml_data.get("groupedBy", None), original_parameter=yaml_data.get("originalParameter", None), flattened=yaml_data.get("flattened", False), From 6d766ded4a3381f8615a0a37741934481ddf2325 Mon Sep 17 00:00:00 2001 From: Laurent Mazuel Date: Mon, 10 Aug 2020 11:30:29 -0700 Subject: [PATCH 4/5] Pylint --- autorest/codegen/models/operation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autorest/codegen/models/operation.py b/autorest/codegen/models/operation.py index 65546d4fa27..bf753f76b42 100644 --- a/autorest/codegen/models/operation.py +++ b/autorest/codegen/models/operation.py @@ -236,8 +236,7 @@ def build_serialize_data_call(parameter: Parameter, function_name: str) -> str: if parameter.explode: return f"[{serialize_line} if q is not None else '' for q in {origin_name}]" - else: - return serialize_line + return serialize_line @property def serialization_context(self) -> str: From fa40209856e6fca44da12fde28a8665dd478ff8f Mon Sep 17 00:00:00 2001 From: Laurent Mazuel Date: Mon, 10 Aug 2020 14:04:02 -0700 Subject: [PATCH 5/5] ChangeLog --- ChangeLog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index ad2409bd3fb..c2e1de3901c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -9,6 +9,10 @@ Modelerfour version: 4.15.400 - Updated minimum `azure-core` version to 1.8.0 #747 - Updated minimum `msrest` version to 0.6.18 #747 +**Bug fixes** + +- Fix "multi" in Swagger (will generate correctly multiple query param now) + ### 2020-08-07 - 5.1.0-preview.7 Autorest Core version: 3.0.6302 Modelerfour version: 4.15.400