diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py index f516aefcf..a71148e05 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py @@ -3,6 +3,7 @@ from typing import Type from .default import DefaultEndpoints +from .location import LocationEndpoints from .parameters import ParametersEndpoints from .tag1 import Tag1Endpoints from .tests import TestsEndpoints @@ -24,3 +25,7 @@ def parameters(cls) -> Type[ParametersEndpoints]: @classmethod def tag1(cls) -> Type[Tag1Endpoints]: return Tag1Endpoints + + @classmethod + def location(cls) -> Type[LocationEndpoints]: + return LocationEndpoints diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/location/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/location/__init__.py new file mode 100644 index 000000000..b7e42ea57 --- /dev/null +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/location/__init__.py @@ -0,0 +1,11 @@ +""" Contains methods for accessing the API Endpoints """ + +import types + +from . import get_location_query_optionality + + +class LocationEndpoints: + @classmethod + def get_location_query_optionality(cls) -> types.ModuleType: + return get_location_query_optionality diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py index 8fc547a66..5455c2c70 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py @@ -13,7 +13,6 @@ json_body_tests_json_body_post, no_response_tests_no_response_get, octet_stream_tests_octet_stream_get, - optional_value_tests_optional_query_param, post_form_data, test_inline_objects, token_with_cookie_auth_token_with_cookie_get, @@ -129,13 +128,6 @@ def test_inline_objects(cls) -> types.ModuleType: """ return test_inline_objects - @classmethod - def optional_value_tests_optional_query_param(cls) -> types.ModuleType: - """ - Test optional query parameters - """ - return optional_value_tests_optional_query_param - @classmethod def token_with_cookie_auth_token_with_cookie_get(cls) -> types.ModuleType: """ diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py index 96487587d..a3eea040e 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/default/get_common_parameters.py @@ -9,7 +9,7 @@ def _get_kwargs( *, client: Client, - common: Union[Unset, str] = UNSET, + common: Union[Unset, None, str] = UNSET, ) -> Dict[str, Any]: url = "{}/common_parameters".format(client.base_url) @@ -42,7 +42,7 @@ def _build_response(*, response: httpx.Response) -> Response[Any]: def sync_detailed( *, client: Client, - common: Union[Unset, str] = UNSET, + common: Union[Unset, None, str] = UNSET, ) -> Response[Any]: kwargs = _get_kwargs( client=client, @@ -59,7 +59,7 @@ def sync_detailed( async def asyncio_detailed( *, client: Client, - common: Union[Unset, str] = UNSET, + common: Union[Unset, None, str] = UNSET, ) -> Response[Any]: kwargs = _get_kwargs( client=client, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/default/post_common_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/default/post_common_parameters.py index 3283f4830..d84b5772f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/default/post_common_parameters.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/default/post_common_parameters.py @@ -9,7 +9,7 @@ def _get_kwargs( *, client: Client, - common: Union[Unset, str] = UNSET, + common: Union[Unset, None, str] = UNSET, ) -> Dict[str, Any]: url = "{}/common_parameters".format(client.base_url) @@ -42,7 +42,7 @@ def _build_response(*, response: httpx.Response) -> Response[Any]: def sync_detailed( *, client: Client, - common: Union[Unset, str] = UNSET, + common: Union[Unset, None, str] = UNSET, ) -> Response[Any]: kwargs = _get_kwargs( client=client, @@ -59,7 +59,7 @@ def sync_detailed( async def asyncio_detailed( *, client: Client, - common: Union[Unset, str] = UNSET, + common: Union[Unset, None, str] = UNSET, ) -> Response[Any]: kwargs = _get_kwargs( client=client, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/location/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/api/location/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py new file mode 100644 index 000000000..b1a7bc14d --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py @@ -0,0 +1,105 @@ +import datetime +from typing import Any, Dict, Union + +import httpx + +from ...client import Client +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + *, + client: Client, + not_null_required: datetime.datetime, + null_required: Union[Unset, None, datetime.datetime] = UNSET, + null_not_required: Union[Unset, None, datetime.datetime] = UNSET, + not_null_not_required: Union[Unset, None, datetime.datetime] = UNSET, +) -> Dict[str, Any]: + url = "{}/location/query/optionality".format(client.base_url) + + headers: Dict[str, Any] = client.get_headers() + cookies: Dict[str, Any] = client.get_cookies() + + json_not_null_required = not_null_required.isoformat() + + json_null_required: Union[Unset, None, str] = UNSET + if not isinstance(null_required, Unset): + json_null_required = null_required.isoformat() if null_required else None + + json_null_not_required: Union[Unset, None, str] = UNSET + if not isinstance(null_not_required, Unset): + json_null_not_required = null_not_required.isoformat() if null_not_required else None + + json_not_null_not_required: Union[Unset, None, str] = UNSET + if not isinstance(not_null_not_required, Unset): + json_not_null_not_required = not_null_not_required.isoformat() if not_null_not_required else None + + params: Dict[str, Any] = { + "not_null_required": json_not_null_required, + "null_required": json_null_required, + "null_not_required": json_null_not_required, + "not_null_not_required": json_not_null_not_required, + } + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + + return { + "url": url, + "headers": headers, + "cookies": cookies, + "timeout": client.get_timeout(), + "params": params, + } + + +def _build_response(*, response: httpx.Response) -> Response[Any]: + return Response( + status_code=response.status_code, + content=response.content, + headers=response.headers, + parsed=None, + ) + + +def sync_detailed( + *, + client: Client, + not_null_required: datetime.datetime, + null_required: Union[Unset, None, datetime.datetime] = UNSET, + null_not_required: Union[Unset, None, datetime.datetime] = UNSET, + not_null_not_required: Union[Unset, None, datetime.datetime] = UNSET, +) -> Response[Any]: + kwargs = _get_kwargs( + client=client, + not_null_required=not_null_required, + null_required=null_required, + null_not_required=null_not_required, + not_null_not_required=not_null_not_required, + ) + + response = httpx.get( + **kwargs, + ) + + return _build_response(response=response) + + +async def asyncio_detailed( + *, + client: Client, + not_null_required: datetime.datetime, + null_required: Union[Unset, None, datetime.datetime] = UNSET, + null_not_required: Union[Unset, None, datetime.datetime] = UNSET, + not_null_not_required: Union[Unset, None, datetime.datetime] = UNSET, +) -> Response[Any]: + kwargs = _get_kwargs( + client=client, + not_null_required=not_null_required, + null_required=null_required, + null_not_required=null_not_required, + not_null_not_required=not_null_not_required, + ) + + async with httpx.AsyncClient() as _client: + response = await _client.get(**kwargs) + + return _build_response(response=response) diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/delete_common_parameters_overriding_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/delete_common_parameters_overriding_param.py index 91ea6fd5f..5c6efb5d9 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/delete_common_parameters_overriding_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/delete_common_parameters_overriding_param.py @@ -10,7 +10,7 @@ def _get_kwargs( *, client: Client, param_path: str, - param_query: Union[Unset, str] = UNSET, + param_query: Union[Unset, None, str] = UNSET, ) -> Dict[str, Any]: url = "{}/common_parameters_overriding/{param}".format(client.base_url, param=param_path) @@ -44,7 +44,7 @@ def sync_detailed( *, client: Client, param_path: str, - param_query: Union[Unset, str] = UNSET, + param_query: Union[Unset, None, str] = UNSET, ) -> Response[Any]: kwargs = _get_kwargs( client=client, @@ -63,7 +63,7 @@ async def asyncio_detailed( *, client: Client, param_path: str, - param_query: Union[Unset, str] = UNSET, + param_query: Union[Unset, None, str] = UNSET, ) -> Response[Any]: kwargs = _get_kwargs( client=client, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_same_name_multiple_locations_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_same_name_multiple_locations_param.py index 3f01bc592..4bd74d91a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_same_name_multiple_locations_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_same_name_multiple_locations_param.py @@ -10,7 +10,7 @@ def _get_kwargs( *, client: Client, param_path: str, - param_query: Union[Unset, str] = UNSET, + param_query: Union[Unset, None, str] = UNSET, param_header: Union[Unset, str] = UNSET, param_cookie: Union[Unset, str] = UNSET, ) -> Dict[str, Any]: @@ -52,7 +52,7 @@ def sync_detailed( *, client: Client, param_path: str, - param_query: Union[Unset, str] = UNSET, + param_query: Union[Unset, None, str] = UNSET, param_header: Union[Unset, str] = UNSET, param_cookie: Union[Unset, str] = UNSET, ) -> Response[Any]: @@ -75,7 +75,7 @@ async def asyncio_detailed( *, client: Client, param_path: str, - param_query: Union[Unset, str] = UNSET, + param_query: Union[Unset, None, str] = UNSET, param_header: Union[Unset, str] = UNSET, param_cookie: Union[Unset, str] = UNSET, ) -> Response[Any]: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py index f22ec0153..5bf24d582 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py @@ -14,66 +14,37 @@ def _get_kwargs( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), - required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, + string_prop: str = "the default string", + date_prop: datetime.date = isoparse("1010-10-10").date(), + float_prop: float = 3.14, + int_prop: int = 7, + boolean_prop: bool = False, + list_prop: List[AnEnum], + union_prop: Union[float, str] = "not a float", + union_prop_with_ref: Union[AnEnum, None, Unset, float] = 0.6, + enum_prop: AnEnum, + model_prop: ModelWithUnionProperty, required_model_prop: ModelWithUnionProperty, - nullable_model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, - nullable_required_model_prop: Optional[ModelWithUnionProperty], ) -> Dict[str, Any]: url = "{}/tests/defaults".format(client.base_url) headers: Dict[str, Any] = client.get_headers() cookies: Dict[str, Any] = client.get_cookies() - json_not_required_not_nullable_datetime_prop: Union[Unset, str] = UNSET - if not isinstance(not_required_not_nullable_datetime_prop, Unset): - json_not_required_not_nullable_datetime_prop = not_required_not_nullable_datetime_prop.isoformat() + json_date_prop = date_prop.isoformat() + json_list_prop = [] + for list_prop_item_data in list_prop: + list_prop_item = list_prop_item_data.value - json_not_required_nullable_datetime_prop: Union[Unset, None, str] = UNSET - if not isinstance(not_required_nullable_datetime_prop, Unset): - json_not_required_nullable_datetime_prop = ( - not_required_nullable_datetime_prop.isoformat() if not_required_nullable_datetime_prop else None - ) - - json_required_not_nullable_datetime_prop = required_not_nullable_datetime_prop.isoformat() - - json_required_nullable_datetime_prop = ( - required_nullable_datetime_prop.isoformat() if required_nullable_datetime_prop else None - ) - - json_date_prop: Union[Unset, str] = UNSET - if not isinstance(date_prop, Unset): - json_date_prop = date_prop.isoformat() + json_list_prop.append(list_prop_item) - json_list_prop: Union[Unset, List[str]] = UNSET - if not isinstance(list_prop, Unset): - json_list_prop = [] - for list_prop_item_data in list_prop: - list_prop_item = list_prop_item_data.value + json_union_prop = union_prop - json_list_prop.append(list_prop_item) - - json_union_prop: Union[Unset, float, str] - if isinstance(union_prop, Unset): - json_union_prop = UNSET - else: - json_union_prop = union_prop - - json_union_prop_with_ref: Union[Unset, float, str] + json_union_prop_with_ref: Union[None, Unset, float, str] if isinstance(union_prop_with_ref, Unset): json_union_prop_with_ref = UNSET + elif union_prop_with_ref is None: + json_union_prop_with_ref = None elif isinstance(union_prop_with_ref, AnEnum): json_union_prop_with_ref = UNSET if not isinstance(union_prop_with_ref, Unset): @@ -82,28 +53,14 @@ def _get_kwargs( else: json_union_prop_with_ref = union_prop_with_ref - json_enum_prop: Union[Unset, str] = UNSET - if not isinstance(enum_prop, Unset): - json_enum_prop = enum_prop.value + json_enum_prop = enum_prop.value - json_model_prop: Union[Unset, Dict[str, Any]] = UNSET - if not isinstance(model_prop, Unset): - json_model_prop = model_prop.to_dict() + json_model_prop = model_prop.to_dict() json_required_model_prop = required_model_prop.to_dict() - json_nullable_model_prop: Union[Unset, None, Dict[str, Any]] = UNSET - if not isinstance(nullable_model_prop, Unset): - json_nullable_model_prop = nullable_model_prop.to_dict() if nullable_model_prop else None - - json_nullable_required_model_prop = nullable_required_model_prop.to_dict() if nullable_required_model_prop else None - params: Dict[str, Any] = { "string_prop": string_prop, - "not_required_not_nullable_datetime_prop": json_not_required_not_nullable_datetime_prop, - "not_required_nullable_datetime_prop": json_not_required_nullable_datetime_prop, - "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, - "required_nullable_datetime_prop": json_required_nullable_datetime_prop, "date_prop": json_date_prop, "float_prop": float_prop, "int_prop": int_prop, @@ -113,13 +70,8 @@ def _get_kwargs( "union_prop_with_ref": json_union_prop_with_ref, "enum_prop": json_enum_prop, } - if not isinstance(json_model_prop, Unset): - params.update(json_model_prop) + params.update(json_model_prop) params.update(json_required_model_prop) - if not isinstance(json_nullable_model_prop, Unset) and json_nullable_model_prop is not None: - params.update(json_nullable_model_prop) - if json_nullable_required_model_prop is not None: - params.update(json_nullable_required_model_prop) params = {k: v for k, v in params.items() if v is not UNSET and v is not None} return { @@ -155,31 +107,21 @@ def _build_response(*, response: httpx.Response) -> Response[Union[Any, HTTPVali def sync_detailed( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), - required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, + string_prop: str = "the default string", + date_prop: datetime.date = isoparse("1010-10-10").date(), + float_prop: float = 3.14, + int_prop: int = 7, + boolean_prop: bool = False, + list_prop: List[AnEnum], + union_prop: Union[float, str] = "not a float", + union_prop_with_ref: Union[AnEnum, None, Unset, float] = 0.6, + enum_prop: AnEnum, + model_prop: ModelWithUnionProperty, required_model_prop: ModelWithUnionProperty, - nullable_model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, - nullable_required_model_prop: Optional[ModelWithUnionProperty], ) -> Response[Union[Any, HTTPValidationError]]: kwargs = _get_kwargs( client=client, string_prop=string_prop, - not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, - not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, - required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, - required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -190,8 +132,6 @@ def sync_detailed( enum_prop=enum_prop, model_prop=model_prop, required_model_prop=required_model_prop, - nullable_model_prop=nullable_model_prop, - nullable_required_model_prop=nullable_required_model_prop, ) response = httpx.post( @@ -204,33 +144,23 @@ def sync_detailed( def sync( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), - required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, + string_prop: str = "the default string", + date_prop: datetime.date = isoparse("1010-10-10").date(), + float_prop: float = 3.14, + int_prop: int = 7, + boolean_prop: bool = False, + list_prop: List[AnEnum], + union_prop: Union[float, str] = "not a float", + union_prop_with_ref: Union[AnEnum, None, Unset, float] = 0.6, + enum_prop: AnEnum, + model_prop: ModelWithUnionProperty, required_model_prop: ModelWithUnionProperty, - nullable_model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, - nullable_required_model_prop: Optional[ModelWithUnionProperty], ) -> Optional[Union[Any, HTTPValidationError]]: """ """ return sync_detailed( client=client, string_prop=string_prop, - not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, - not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, - required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, - required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -241,39 +171,27 @@ def sync( enum_prop=enum_prop, model_prop=model_prop, required_model_prop=required_model_prop, - nullable_model_prop=nullable_model_prop, - nullable_required_model_prop=nullable_required_model_prop, ).parsed async def asyncio_detailed( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), - required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, + string_prop: str = "the default string", + date_prop: datetime.date = isoparse("1010-10-10").date(), + float_prop: float = 3.14, + int_prop: int = 7, + boolean_prop: bool = False, + list_prop: List[AnEnum], + union_prop: Union[float, str] = "not a float", + union_prop_with_ref: Union[AnEnum, None, Unset, float] = 0.6, + enum_prop: AnEnum, + model_prop: ModelWithUnionProperty, required_model_prop: ModelWithUnionProperty, - nullable_model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, - nullable_required_model_prop: Optional[ModelWithUnionProperty], ) -> Response[Union[Any, HTTPValidationError]]: kwargs = _get_kwargs( client=client, string_prop=string_prop, - not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, - not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, - required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, - required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -284,8 +202,6 @@ async def asyncio_detailed( enum_prop=enum_prop, model_prop=model_prop, required_model_prop=required_model_prop, - nullable_model_prop=nullable_model_prop, - nullable_required_model_prop=nullable_required_model_prop, ) async with httpx.AsyncClient() as _client: @@ -297,23 +213,17 @@ async def asyncio_detailed( async def asyncio( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), - required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, + string_prop: str = "the default string", + date_prop: datetime.date = isoparse("1010-10-10").date(), + float_prop: float = 3.14, + int_prop: int = 7, + boolean_prop: bool = False, + list_prop: List[AnEnum], + union_prop: Union[float, str] = "not a float", + union_prop_with_ref: Union[AnEnum, None, Unset, float] = 0.6, + enum_prop: AnEnum, + model_prop: ModelWithUnionProperty, required_model_prop: ModelWithUnionProperty, - nullable_model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, - nullable_required_model_prop: Optional[ModelWithUnionProperty], ) -> Optional[Union[Any, HTTPValidationError]]: """ """ @@ -321,10 +231,6 @@ async def asyncio( await asyncio_detailed( client=client, string_prop=string_prop, - not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, - not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, - required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, - required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -335,7 +241,5 @@ async def asyncio( enum_prop=enum_prop, model_prop=model_prop, required_model_prop=required_model_prop, - nullable_model_prop=nullable_model_prop, - nullable_required_model_prop=nullable_required_model_prop, ) ).parsed diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py deleted file mode 100644 index 370ba1d45..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py +++ /dev/null @@ -1,117 +0,0 @@ -from typing import Any, Dict, List, Optional, Union - -import httpx - -from ...client import Client -from ...models.http_validation_error import HTTPValidationError -from ...types import UNSET, Response, Unset - - -def _get_kwargs( - *, - client: Client, - query_param: Union[Unset, List[str]] = UNSET, -) -> Dict[str, Any]: - url = "{}/tests/optional_query_param/".format(client.base_url) - - headers: Dict[str, Any] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() - - json_query_param: Union[Unset, List[str]] = UNSET - if not isinstance(query_param, Unset): - json_query_param = query_param - - params: Dict[str, Any] = { - "query_param": json_query_param, - } - params = {k: v for k, v in params.items() if v is not UNSET and v is not None} - - return { - "url": url, - "headers": headers, - "cookies": cookies, - "timeout": client.get_timeout(), - "params": params, - } - - -def _parse_response(*, response: httpx.Response) -> Optional[Union[Any, HTTPValidationError]]: - if response.status_code == 200: - response_200 = response.json() - - return response_200 - if response.status_code == 422: - response_422 = HTTPValidationError.from_dict(response.json()) - - return response_422 - return None - - -def _build_response(*, response: httpx.Response) -> Response[Union[Any, HTTPValidationError]]: - return Response( - status_code=response.status_code, - content=response.content, - headers=response.headers, - parsed=_parse_response(response=response), - ) - - -def sync_detailed( - *, - client: Client, - query_param: Union[Unset, List[str]] = UNSET, -) -> Response[Union[Any, HTTPValidationError]]: - kwargs = _get_kwargs( - client=client, - query_param=query_param, - ) - - response = httpx.get( - **kwargs, - ) - - return _build_response(response=response) - - -def sync( - *, - client: Client, - query_param: Union[Unset, List[str]] = UNSET, -) -> Optional[Union[Any, HTTPValidationError]]: - """Test optional query parameters""" - - return sync_detailed( - client=client, - query_param=query_param, - ).parsed - - -async def asyncio_detailed( - *, - client: Client, - query_param: Union[Unset, List[str]] = UNSET, -) -> Response[Union[Any, HTTPValidationError]]: - kwargs = _get_kwargs( - client=client, - query_param=query_param, - ) - - async with httpx.AsyncClient() as _client: - response = await _client.get(**kwargs) - - return _build_response(response=response) - - -async def asyncio( - *, - client: Client, - query_param: Union[Unset, List[str]] = UNSET, -) -> Optional[Union[Any, HTTPValidationError]]: - """Test optional query parameters""" - - return ( - await asyncio_detailed( - client=client, - query_param=query_param, - ) - ).parsed diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index 093752212..8ae566b5d 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -373,7 +373,7 @@ "operationId": "defaults_tests_defaults_post", "parameters": [ { - "required": false, + "required": true, "schema": { "title": "String Prop", "type": "string", @@ -382,56 +382,8 @@ "name": "string_prop", "in": "query" }, - { - "required": false, - "schema": { - "title": "Not Required, Not Nullable Datetime Prop", - "nullable": false, - "type": "string", - "format": "date-time", - "default": "1010-10-10T00:00:00" - }, - "name": "not_required_not_nullable_datetime_prop", - "in": "query" - }, - { - "required": false, - "schema": { - "title": "Not Required, Nullable Datetime Prop", - "nullable": true, - "type": "string", - "format": "date-time", - "default": "1010-10-10T00:00:00" - }, - "name": "not_required_nullable_datetime_prop", - "in": "query" - }, - { - "required": true, - "schema": { - "title": "Required, Not Nullable Datetime Prop", - "nullable": false, - "type": "string", - "format": "date-time", - "default": "1010-10-10T00:00:00" - }, - "name": "required_not_nullable_datetime_prop", - "in": "query" - }, { "required": true, - "schema": { - "title": "Required, Nullable Datetime Prop", - "nullable": true, - "type": "string", - "format": "date-time", - "default": "1010-10-10T00:00:00" - }, - "name": "required_nullable_datetime_prop", - "in": "query" - }, - { - "required": false, "schema": { "title": "Date Prop", "type": "string", @@ -442,7 +394,7 @@ "in": "query" }, { - "required": false, + "required": true, "schema": { "title": "Float Prop", "type": "number", @@ -452,7 +404,7 @@ "in": "query" }, { - "required": false, + "required": true, "schema": { "title": "Int Prop", "type": "integer", @@ -462,7 +414,7 @@ "in": "query" }, { - "required": false, + "required": true, "schema": { "title": "Boolean Prop", "type": "boolean", @@ -472,7 +424,7 @@ "in": "query" }, { - "required": false, + "required": true, "schema": { "title": "List Prop", "type": "array", @@ -488,7 +440,7 @@ "in": "query" }, { - "required": false, + "required": true, "schema": { "title": "Union Prop", "anyOf": [ @@ -522,7 +474,7 @@ "in": "query" }, { - "required": false, + "required": true, "schema": { "$ref": "#/components/schemas/AnEnum" }, @@ -530,7 +482,7 @@ "in": "query" }, { - "required": false, + "required": true, "schema": { "$ref": "#/components/schemas/ModelWithUnionProperty" }, @@ -544,32 +496,6 @@ }, "name": "required_model_prop", "in": "query" - }, - { - "required": false, - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/ModelWithUnionProperty" - } - ], - "nullable": true - }, - "name": "nullable_model_prop", - "in": "query" - }, - { - "required": true, - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/ModelWithUnionProperty" - } - ], - "nullable": true - }, - "name": "nullable_required_model_prop", - "in": "query" } ], "responses": { @@ -743,54 +669,6 @@ } } }, - "/tests/optional_query_param/": { - "get": { - "tags": [ - "tests" - ], - "summary": "Optional Query Params test", - "description": "Test optional query parameters", - "operationId": "optional_value_tests_optional_query_param", - "parameters": [ - { - "required": false, - "schema": { - "title": "Query Param", - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "string1", - "string2" - ] - }, - "name": "query_param", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {} - } - } - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - } - } - } - } - }, "/auth/token_with_cookie": { "get": { "tags": [ @@ -901,44 +779,6 @@ } ] }, - "/same_identifier_conflict/{param}/{param_path}": { - "description": "Test that if you have properties with the same Python identifier, it not produces code", - "get": { - "tags": [ - "parameters" - ], - "parameters": [ - { - "name": "param_path", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "param", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "param", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "" - } - } - } - }, "/same-name-multiple-locations/{param}": { "description": "Test that if you have a property of the same name in multiple locations, it produces valid code", "get": { @@ -992,6 +832,57 @@ } } } + }, + "/location/query/optionality": { + "description": "Test what happens with various combinations of required and nullable in query parameters.", + "get": { + "tags": [ + "location" + ], + "parameters": [ + { + "name": "not_null_required", + "required": true, + "schema": { + "type": "string", + "format": "date-time", + "nullable": false + }, + "in": "query" + }, + { + "name": "null_required", + "required": true, + "schema": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "in": "query" + }, + { + "name": "null_not_required", + "required": false, + "schema": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "in": "query" + }, + { + "name": "not_null_not_required", + "required": false, + "schema": { + "type": "string", + "format": "date-time", + "nullable": false + }, + "in": "query" + } + ], + "responses": {} + } } }, "components": { diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index 3d143224a..fdf2245d7 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -301,8 +301,6 @@ def _add_parameters( endpoint.used_python_identifiers.add(existing_prop.python_name) prop.set_python_name(new_name=f"{param.name}_{param.param_in}", config=config) - endpoint.relative_imports.update(prop.get_imports(prefix="...")) - if prop.python_name in endpoint.used_python_identifiers: return ( ParseError( @@ -310,6 +308,11 @@ def _add_parameters( ), schemas, ) + if param.param_in == oai.ParameterLocation.QUERY and (prop.nullable or not prop.required): + # There is no NULL for query params, so nullable and not required are the same. + prop = attr.evolve(prop, required=False, nullable=True) + + endpoint.relative_imports.update(prop.get_imports(prefix="...")) endpoint.used_python_identifiers.add(prop.python_name) parameters_by_location[param.param_in][prop.name] = prop diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index c3b1fe289..7a1fe6099 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -4,8 +4,10 @@ import pytest import openapi_python_client.schema as oai -from openapi_python_client import GeneratorError +from openapi_python_client import Config, GeneratorError from openapi_python_client.parser.errors import ParseError +from openapi_python_client.parser.openapi import Endpoint, EndpointCollection +from openapi_python_client.parser.properties import IntProperty, Schemas MODULE_NAME = "openapi_python_client.parser.openapi" @@ -628,163 +630,113 @@ def test__add_parameters_with_location_postfix_conflict2(self, mocker): assert isinstance(result, ParseError) assert result.detail == "Parameters with same Python identifier `prop_name_path` detected" - def test__add_parameters_happy(self, mocker): - from openapi_python_client.parser.openapi import Endpoint - from openapi_python_client.parser.properties import Property - + def test__add_parameters_skips_references(self): + """References are not supported as direct params yet""" endpoint = self.make_endpoint() - path_prop_name = "path_prop_name" - path_prop = mocker.MagicMock(autospec=Property) - path_prop_import = mocker.MagicMock() - path_prop.get_imports = mocker.MagicMock(return_value={path_prop_import}) - path_prop.python_name = "path_prop_name" - - query_prop_name = "query_prop_name" - query_prop = mocker.MagicMock(autospec=Property) - query_prop_import = mocker.MagicMock() - query_prop.get_imports = mocker.MagicMock(return_value={query_prop_import}) - query_prop.python_name = "query_prop_name" - - header_prop_name = "header_prop_name" - header_prop_operation = mocker.MagicMock(autospec=Property) - header_prop_operation.name = header_prop_name - header_prop_operation.required = False - header_prop_operation_import = mocker.MagicMock() - header_prop_operation.get_imports = mocker.MagicMock(return_value={header_prop_operation_import}) - header_prop_operation.python_name = "header_prop_name" - - header_prop_path = mocker.MagicMock(autospec=Property) - header_prop_path.name = header_prop_name - header_prop_path.required = True - header_prop_path_import = mocker.MagicMock() - header_prop_path.get_imports = mocker.MagicMock(return_value={header_prop_path_import}) - header_prop_path.python_name = "header_prop_name" - - cookie_prop_name = "cookie_prop_name" - cookie_prop = mocker.MagicMock(autospec=Property) - cookie_prop_import = mocker.MagicMock() - cookie_prop.get_imports = mocker.MagicMock(return_value={cookie_prop_import}) - cookie_prop.python_name = "cookie_prop_name" + data = oai.Operation.construct( + parameters=[ + oai.Reference.construct(ref="blah"), + ] + ) - schemas_1 = mocker.MagicMock() - schemas_2 = mocker.MagicMock() - schemas_3 = mocker.MagicMock() - schemas_4 = mocker.MagicMock() - schemas_5 = mocker.MagicMock() - property_from_data = mocker.patch( - f"{MODULE_NAME}.property_from_data", - side_effect=[ - (path_prop, schemas_1), - (query_prop, schemas_2), - (header_prop_operation, schemas_3), - (header_prop_path, schemas_4), - (cookie_prop, schemas_5), - ], + (endpoint, _) = endpoint._add_parameters(endpoint=endpoint, data=data, schemas=Schemas(), config=Config()) + + assert isinstance(endpoint, Endpoint) + assert ( + len(endpoint.path_parameters) + + len(endpoint.query_parameters) + + len(endpoint.cookie_parameters) + + len(endpoint.header_parameters) + == 0 ) - path_schema = mocker.MagicMock() - query_schema = mocker.MagicMock() - header_operation_schema = mocker.MagicMock() - cookie_schema = mocker.MagicMock() - header_pathitem_schema = mocker.MagicMock() - operation_data = oai.Operation.construct( + def test__add_parameters_skips_params_without_schemas(self): + """Params without schemas are allowed per spec, but the any type doesn't make sense as a parameter""" + endpoint = self.make_endpoint() + data = oai.Operation.construct( parameters=[ - oai.Parameter.construct(name=path_prop_name, required=True, param_schema=path_schema, param_in="path"), oai.Parameter.construct( - name=query_prop_name, required=False, param_schema=query_schema, param_in="query" + name="param", + param_in="path", ), - oai.Parameter.construct( - name=header_prop_name, required=False, param_schema=header_operation_schema, param_in="header" - ), - oai.Reference.construct(), # Should be ignored - oai.Parameter.construct(), # Should be ignored ] ) - initial_schemas = mocker.MagicMock() - config = MagicMock() - (endpoint, schemas) = Endpoint._add_parameters( - endpoint=endpoint, data=operation_data, schemas=initial_schemas, config=config - ) - path_item_data = oai.PathItem.construct( + (endpoint, _) = endpoint._add_parameters(endpoint=endpoint, data=data, schemas=Schemas(), config=Config()) + + assert isinstance(endpoint, Endpoint) + assert len(endpoint.path_parameters) == 0 + + def test__add_parameters_same_identifier_conflict(self): + endpoint = self.make_endpoint() + data = oai.Operation.construct( parameters=[ oai.Parameter.construct( - name=header_prop_name, required=True, param_schema=header_pathitem_schema, param_in="header" + name="param", + param_in="path", + param_schema=oai.Schema.construct(nullable=False, type="string"), ), oai.Parameter.construct( - name=cookie_prop_name, required=False, param_schema=cookie_schema, param_in="cookie" + name="param_path", + param_in="path", + param_schema=oai.Schema.construct(nullable=False, type="string"), + ), + oai.Parameter.construct( + name="param", + param_in="query", + param_schema=oai.Schema.construct(nullable=False, type="string"), ), - oai.Reference.construct(), # Should be ignored - oai.Parameter.construct(), # Should be ignored ] ) - (endpoint, schemas) = Endpoint._add_parameters( - endpoint=endpoint, data=path_item_data, schemas=schemas, config=config - ) - property_from_data.assert_has_calls( - [ - mocker.call( - name=path_prop_name, - required=True, - data=path_schema, - schemas=initial_schemas, - parent_name="name", - config=config, - ), - mocker.call( - name=query_prop_name, - required=False, - data=query_schema, - schemas=schemas_1, - parent_name="name", - config=config, - ), - mocker.call( - name=header_prop_name, + (err, _) = endpoint._add_parameters(endpoint=endpoint, data=data, schemas=Schemas(), config=Config()) + + assert isinstance(err, ParseError) + assert "param_path" in err.detail + + def test__add_parameters_query_optionality(self): + endpoint = self.make_endpoint() + data = oai.Operation.construct( + parameters=[ + oai.Parameter.construct( + name="not_null_not_required", required=False, - data=header_operation_schema, - schemas=schemas_2, - parent_name="name", - config=config, + param_schema=oai.Schema.construct(nullable=False, type="string"), + param_in="query", ), - mocker.call( - name=header_prop_name, + oai.Parameter.construct( + name="not_null_required", required=True, - data=header_pathitem_schema, - schemas=schemas_3, - parent_name="name", - config=config, + param_schema=oai.Schema.construct(nullable=False, type="string"), + param_in="query", ), - mocker.call( - name=cookie_prop_name, + oai.Parameter.construct( + name="null_not_required", required=False, - data=cookie_schema, - schemas=schemas_4, - parent_name="name", - config=config, + param_schema=oai.Schema.construct(nullable=True, type="string"), + param_in="query", + ), + oai.Parameter.construct( + name="null_required", + required=True, + param_schema=oai.Schema.construct(nullable=True, type="string"), + param_in="query", ), ] ) - path_prop.get_imports.assert_called_once_with(prefix="...") - query_prop.get_imports.assert_called_once_with(prefix="...") - header_prop_operation.get_imports.assert_called_once_with(prefix="...") - cookie_prop.get_imports.assert_called_once_with(prefix="...") - assert endpoint.relative_imports == { - "import_3", - path_prop_import, - query_prop_import, - header_prop_operation_import, - cookie_prop_import, - } - assert endpoint.path_parameters == {path_prop.name: path_prop} - assert endpoint.query_parameters == {query_prop.name: query_prop} - assert endpoint.header_parameters == {header_prop_operation.name: header_prop_operation} - assert endpoint.header_parameters[header_prop_operation.name].required is False - assert endpoint.cookie_parameters == {cookie_prop.name: cookie_prop} - assert schemas == schemas_5 - - def test__add_parameters_duplicate_properties(self, mocker): + + (endpoint, _) = endpoint._add_parameters(endpoint=endpoint, data=data, schemas=Schemas(), config=Config()) + + assert len(endpoint.query_parameters) == 4, "Not all query params were added" + for param in endpoint.query_parameters.values(): + if param.name == "not_null_required": + assert not param.nullable + assert param.required + else: + assert param.nullable + assert not param.required + + def test__add_parameters_duplicate_properties(self): from openapi_python_client.parser.openapi import Endpoint, Schemas endpoint = self.make_endpoint() @@ -1114,8 +1066,35 @@ def test_from_data(self, mocker): schemas_3, ) + def test_from_data_overrides_path_item_params_with_operation_params(self): + data = { + "/": oai.PathItem.construct( + parameters=[ + oai.Parameter.construct( + name="param", param_in="query", param_schema=oai.Schema.construct(type="string") + ), + ], + get=oai.Operation.construct( + parameters=[ + oai.Parameter.construct( + name="param", param_in="query", param_schema=oai.Schema.construct(type="integer") + ) + ], + responses={"200": oai.Response.construct(description="blah")}, + ), + ) + } + + collections, schemas = EndpointCollection.from_data( + data=data, + schemas=Schemas(), + config=Config(), + ) + collection: EndpointCollection = collections["default"] + assert isinstance(collection.endpoints[0].query_parameters["param"], IntProperty) + def test_from_data_errors(self, mocker): - from openapi_python_client.parser.openapi import Endpoint, EndpointCollection, ParseError + from openapi_python_client.parser.openapi import ParseError path_1_put = oai.Operation.construct() path_1_post = oai.Operation.construct(tags=["tag_2", "tag_3"])