diff --git a/README.md b/README.md index cea385093..07e0d5145 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ _Be forewarned, this is a beta-level feature in the sense that the API exposed i 1. A `pyproject.toml` file with some basic metadata intended to be used with [Poetry]. 1. A `README.md` you'll most definitely need to update with your project's details 1. A Python module named just like the auto-generated project name (e.g. "my_api_client") which contains: - 1. A `client` module which will have both a `Client` class and an `AuthenticatedClient` class. You'll need these + 1. A `client` module which will have a `Client` class. You'll need it for calling the functions in the `api` module. 1. An `api` module which will contain one module for each tag in your OpenAPI spec, as well as a `default` module for endpoints without a tag. Each of these modules in turn contains one function for calling each endpoint. 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 13120943a..645570bda 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 @@ -18,7 +18,6 @@ post_form_data_inline, post_tests_json_body_string, test_inline_objects, - token_with_cookie_auth_token_with_cookie_get, unsupported_content_tests_unsupported_content_get, upload_file_tests_upload_post, upload_multiple_files_tests_upload_post, @@ -145,13 +144,6 @@ def test_inline_objects(cls) -> types.ModuleType: """ return test_inline_objects - @classmethod - def token_with_cookie_auth_token_with_cookie_get(cls) -> types.ModuleType: - """ - Test optional cookie parameters - """ - return token_with_cookie_auth_token_with_cookie_get - @classmethod def callback_test(cls) -> types.ModuleType: """ diff --git a/end_to_end_tests/golden-record/README.md b/end_to_end_tests/golden-record/README.md index 3def2172e..1320205a7 100644 --- a/end_to_end_tests/golden-record/README.md +++ b/end_to_end_tests/golden-record/README.md @@ -10,14 +10,6 @@ from my_test_api_client import Client client = Client(base_url="https://api.example.com") ``` -If the endpoints you're going to hit require authentication, use `AuthenticatedClient` instead: - -```python -from my_test_api_client import AuthenticatedClient - -client = AuthenticatedClient(base_url="https://api.example.com", token="SuperSecretToken") -``` - Now call your endpoint and use your models: ```python @@ -44,8 +36,8 @@ response: Response[MyDataModel] = await get_my_data_model.asyncio_detailed(clien By default, when you're calling an HTTPS API it will attempt to verify that SSL is working correctly. Using certificate verification is highly recommended most of the time, but sometimes you may need to authenticate to a server (especially an internal server) using a custom certificate bundle. ```python -client = AuthenticatedClient( - base_url="https://internal_api.example.com", +client = Client( + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -54,9 +46,9 @@ client = AuthenticatedClient( You can also disable certificate validation altogether, but beware that **this is a security risk**. ```python -client = AuthenticatedClient( - base_url="https://internal_api.example.com", - token="SuperSecretToken", +client = Client( + base_url="https://internal_api.example.com", + token="SuperSecretToken", verify_ssl=False ) ``` diff --git a/end_to_end_tests/golden-record/my_test_api_client/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/__init__.py index 530928e7a..379bfaaed 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/__init__.py +++ b/end_to_end_tests/golden-record/my_test_api_client/__init__.py @@ -1,7 +1,4 @@ """ A client library for accessing My Test API """ -from .client import AuthenticatedClient, Client +from .client import Client -__all__ = ( - "AuthenticatedClient", - "Client", -) +__all__ = ("Client",) 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 eea2a39cb..46e29bacb 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 @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union import httpx @@ -16,7 +16,6 @@ def _get_kwargs( url = "{}/common_parameters".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} params["common"] = common @@ -27,19 +26,16 @@ def _get_kwargs( "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -61,7 +57,6 @@ def sync_detailed( common (Union[Unset, None, str]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -91,7 +86,6 @@ async def asyncio_detailed( common (Union[Unset, None, str]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: 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 54f11f8dc..27f2d52fb 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 @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union import httpx @@ -16,7 +16,6 @@ def _get_kwargs( url = "{}/common_parameters".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} params["common"] = common @@ -27,19 +26,16 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -61,7 +57,6 @@ def sync_detailed( common (Union[Unset, None, str]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -91,7 +86,6 @@ async def asyncio_detailed( common (Union[Unset, None, str]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_header_types.py b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_header_types.py index ab6ae3180..010fc00d4 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_header_types.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_header_types.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union import httpx @@ -23,7 +23,6 @@ def _get_kwargs( url = "{}/location/header/types".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() if not isinstance(boolean_header, Unset): headers["Boolean-Header"] = "true" if boolean_header else "false" @@ -47,18 +46,15 @@ def _get_kwargs( "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -90,7 +86,6 @@ def sync_detailed( string_enum_header (Union[Unset, GetLocationHeaderTypesStringEnumHeader]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -135,7 +130,6 @@ async def asyncio_detailed( string_enum_header (Union[Unset, GetLocationHeaderTypesStringEnumHeader]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: 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 index 427cf04dc..79875c5c6 100644 --- 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 @@ -1,6 +1,6 @@ import datetime from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union import httpx @@ -20,7 +20,6 @@ def _get_kwargs( url = "{}/location/query/optionality".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} json_not_null_required = not_null_required.isoformat() @@ -51,19 +50,16 @@ def _get_kwargs( "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -91,7 +87,6 @@ def sync_detailed( not_null_not_required (Union[Unset, None, datetime.datetime]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -130,7 +125,6 @@ async def asyncio_detailed( not_null_not_required (Union[Unset, None, datetime.datetime]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameter_references/get_parameter_references_path_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameter_references/get_parameter_references_path_param.py index bdb518de5..a926bd055 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameter_references/get_parameter_references_path_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameter_references/get_parameter_references_path_param.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -15,17 +15,13 @@ def _get_kwargs( string_param: str, integer_param: int = 0, header_param: str, - cookie_param: str, ) -> Dict[str, Any]: url = "{}/parameter-references/{path_param}".format(client.base_url, path_param=path_param) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() headers["header param"] = header_param - cookies["cookie param"] = cookie_param - params: Dict[str, Any] = {} params["string param"] = string_param @@ -37,19 +33,16 @@ def _get_kwargs( "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -68,7 +61,6 @@ def sync_detailed( string_param: str, integer_param: int = 0, header_param: str, - cookie_param: str, ) -> Response[Any]: """Test different types of parameter references @@ -77,10 +69,8 @@ def sync_detailed( string_param (str): integer_param (int): header_param (str): - cookie_param (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -93,7 +83,6 @@ def sync_detailed( string_param=string_param, integer_param=integer_param, header_param=header_param, - cookie_param=cookie_param, ) response = httpx.request( @@ -111,7 +100,6 @@ async def asyncio_detailed( string_param: str, integer_param: int = 0, header_param: str, - cookie_param: str, ) -> Response[Any]: """Test different types of parameter references @@ -120,10 +108,8 @@ async def asyncio_detailed( string_param (str): integer_param (int): header_param (str): - cookie_param (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -136,7 +122,6 @@ async def asyncio_detailed( string_param=string_param, integer_param=integer_param, header_param=header_param, - cookie_param=cookie_param, ) async with httpx.AsyncClient(verify=client.verify_ssl) as _client: 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 6ddea0265..e8e7c3e5d 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 @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union import httpx @@ -17,7 +17,6 @@ def _get_kwargs( url = "{}/common_parameters_overriding/{param}".format(client.base_url, param=param_path) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} params["param"] = param_query @@ -28,19 +27,16 @@ def _get_kwargs( "method": "delete", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -64,7 +60,6 @@ def sync_detailed( param_query (Union[Unset, None, str]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -97,7 +92,6 @@ async def asyncio_detailed( param_query (Union[Unset, None, str]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_common_parameters_overriding_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_common_parameters_overriding_param.py index 2089e9c59..11bc6c46d 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_common_parameters_overriding_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/get_common_parameters_overriding_param.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -17,7 +17,6 @@ def _get_kwargs( url = "{}/common_parameters_overriding/{param}".format(client.base_url, param=param_path) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} params["param"] = param_query @@ -28,19 +27,16 @@ def _get_kwargs( "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -66,7 +62,6 @@ def sync_detailed( 'overridden_in_GET'. Example: an example string. Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -101,7 +96,6 @@ async def asyncio_detailed( 'overridden_in_GET'. Example: an example string. Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: 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 c6e0f4736..69fa0d4bd 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 @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union import httpx @@ -14,19 +14,14 @@ def _get_kwargs( client: Client, param_query: Union[Unset, None, str] = UNSET, param_header: Union[Unset, str] = UNSET, - param_cookie: Union[Unset, str] = UNSET, ) -> Dict[str, Any]: url = "{}/same-name-multiple-locations/{param}".format(client.base_url, param=param_path) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() if not isinstance(param_header, Unset): headers["param"] = param_header - if param_cookie is not UNSET: - cookies["param"] = param_cookie - params: Dict[str, Any] = {} params["param"] = param_query @@ -36,19 +31,16 @@ def _get_kwargs( "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -66,17 +58,14 @@ def sync_detailed( client: Client, param_query: Union[Unset, None, str] = UNSET, param_header: Union[Unset, str] = UNSET, - param_cookie: Union[Unset, str] = UNSET, ) -> Response[Any]: """ Args: param_path (str): param_query (Union[Unset, None, str]): param_header (Union[Unset, str]): - param_cookie (Union[Unset, str]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -88,7 +77,6 @@ def sync_detailed( client=client, param_query=param_query, param_header=param_header, - param_cookie=param_cookie, ) response = httpx.request( @@ -105,17 +93,14 @@ async def asyncio_detailed( client: Client, param_query: Union[Unset, None, str] = UNSET, param_header: Union[Unset, str] = UNSET, - param_cookie: Union[Unset, str] = UNSET, ) -> Response[Any]: """ Args: param_path (str): param_query (Union[Unset, None, str]): param_header (Union[Unset, str]): - param_cookie (Union[Unset, str]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -127,7 +112,6 @@ async def asyncio_detailed( client=client, param_query=param_query, param_header=param_header, - param_cookie=param_cookie, ) async with httpx.AsyncClient(verify=client.verify_ssl) as _client: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py index a005a85ac..8e69d36d6 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/parameters/multiple_path_parameters.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -21,24 +21,20 @@ def _get_kwargs( ) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -66,7 +62,6 @@ def sync_detailed( param3 (int): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -105,7 +100,6 @@ async def asyncio_detailed( param3 (int): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/responses/post_responses_unions_simple_before_complex.py b/end_to_end_tests/golden-record/my_test_api_client/api/responses/post_responses_unions_simple_before_complex.py index 811633348..1d520a9a8 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/responses/post_responses_unions_simple_before_complex.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/responses/post_responses_unions_simple_before_complex.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -18,28 +18,22 @@ def _get_kwargs( url = "{}/responses/unions/simple_before_complex".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response( - *, client: Client, response: httpx.Response -) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]: +def _parse_response(*, client: Client, response: httpx.Response) -> PostResponsesUnionsSimpleBeforeComplexResponse200: if response.status_code == HTTPStatus.OK: response_200 = PostResponsesUnionsSimpleBeforeComplexResponse200.from_dict(response.json()) return response_200 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response( @@ -60,7 +54,6 @@ def sync_detailed( """Regression test for #603 Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -82,11 +75,10 @@ def sync_detailed( def sync( *, client: Client, -) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]: +) -> PostResponsesUnionsSimpleBeforeComplexResponse200: """Regression test for #603 Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -105,7 +97,6 @@ async def asyncio_detailed( """Regression test for #603 Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -125,11 +116,10 @@ async def asyncio_detailed( async def asyncio( *, client: Client, -) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]: +) -> PostResponsesUnionsSimpleBeforeComplexResponse200: """Regression test for #603 Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py b/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py index 5df86a828..01288a9a6 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -15,24 +15,20 @@ def _get_kwargs( url = "{}/tag_with_number".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -50,7 +46,6 @@ def sync_detailed( ) -> Response[Any]: """ Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -75,7 +70,6 @@ async def asyncio_detailed( ) -> Response[Any]: """ Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py index ca87484c4..24a98e712 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Dict, Union, cast import httpx @@ -18,7 +18,6 @@ def _get_kwargs( url = "{}/tests/callback".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() json_json_body = json_body.to_dict() @@ -26,13 +25,12 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "json": json_json_body, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Union[Any, HTTPValidationError]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[Any, HTTPValidationError]: if response.status_code == HTTPStatus.OK: response_200 = cast(Any, response.json()) return response_200 @@ -40,10 +38,8 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Uni response_422 = HTTPValidationError.from_dict(response.json()) return response_422 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Union[Any, HTTPValidationError]]: @@ -68,7 +64,6 @@ def sync_detailed( json_body (AModel): A Model for testing all the ways custom objects can be used Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -92,7 +87,7 @@ def sync( *, client: Client, json_body: AModel, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Path with callback Try sending a request related to a callback @@ -101,7 +96,6 @@ def sync( json_body (AModel): A Model for testing all the ways custom objects can be used Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -127,7 +121,6 @@ async def asyncio_detailed( json_body (AModel): A Model for testing all the ways custom objects can be used Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -149,7 +142,7 @@ async def asyncio( *, client: Client, json_body: AModel, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Path with callback Try sending a request related to a callback @@ -158,7 +151,6 @@ async def asyncio( json_body (AModel): A Model for testing all the ways custom objects can be used Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: 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 44a2cc859..5fa2a44da 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 @@ -1,6 +1,6 @@ import datetime from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Dict, List, Union, cast import httpx from dateutil.parser import isoparse @@ -31,7 +31,6 @@ def _get_kwargs( url = "{}/tests/defaults".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} params["string_prop"] = string_prop @@ -93,13 +92,12 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Union[Any, HTTPValidationError]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[Any, HTTPValidationError]: if response.status_code == HTTPStatus.OK: response_200 = cast(Any, response.json()) return response_200 @@ -107,10 +105,8 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Uni response_422 = HTTPValidationError.from_dict(response.json()) return response_422 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Union[Any, HTTPValidationError]]: @@ -153,7 +149,6 @@ def sync_detailed( required_model_prop (ModelWithUnionProperty): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -197,7 +192,7 @@ def sync( enum_prop: AnEnum, model_prop: "ModelWithUnionProperty", required_model_prop: "ModelWithUnionProperty", -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Defaults Args: @@ -214,7 +209,6 @@ def sync( required_model_prop (ModelWithUnionProperty): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -268,7 +262,6 @@ async def asyncio_detailed( required_model_prop (ModelWithUnionProperty): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -310,7 +303,7 @@ async def asyncio( enum_prop: AnEnum, model_prop: "ModelWithUnionProperty", required_model_prop: "ModelWithUnionProperty", -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Defaults Args: @@ -327,7 +320,6 @@ async def asyncio( required_model_prop (ModelWithUnionProperty): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_booleans.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_booleans.py index ce71633d9..22749ffff 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_booleans.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_booleans.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, cast +from typing import Any, Dict, List, cast import httpx @@ -15,26 +15,22 @@ def _get_kwargs( url = "{}/tests/basic_lists/booleans".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[List[bool]]: +def _parse_response(*, client: Client, response: httpx.Response) -> List[bool]: if response.status_code == HTTPStatus.OK: response_200 = cast(List[bool], response.json()) return response_200 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[List[bool]]: @@ -55,7 +51,6 @@ def sync_detailed( Get a list of booleans Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -77,13 +72,12 @@ def sync_detailed( def sync( *, client: Client, -) -> Optional[List[bool]]: +) -> List[bool]: """Get Basic List Of Booleans Get a list of booleans Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -104,7 +98,6 @@ async def asyncio_detailed( Get a list of booleans Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -124,13 +117,12 @@ async def asyncio_detailed( async def asyncio( *, client: Client, -) -> Optional[List[bool]]: +) -> List[bool]: """Get Basic List Of Booleans Get a list of booleans Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_floats.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_floats.py index dcb97ade5..a4d89b8c7 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_floats.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_floats.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, cast +from typing import Any, Dict, List, cast import httpx @@ -15,26 +15,22 @@ def _get_kwargs( url = "{}/tests/basic_lists/floats".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[List[float]]: +def _parse_response(*, client: Client, response: httpx.Response) -> List[float]: if response.status_code == HTTPStatus.OK: response_200 = cast(List[float], response.json()) return response_200 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[List[float]]: @@ -55,7 +51,6 @@ def sync_detailed( Get a list of floats Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -77,13 +72,12 @@ def sync_detailed( def sync( *, client: Client, -) -> Optional[List[float]]: +) -> List[float]: """Get Basic List Of Floats Get a list of floats Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -104,7 +98,6 @@ async def asyncio_detailed( Get a list of floats Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -124,13 +117,12 @@ async def asyncio_detailed( async def asyncio( *, client: Client, -) -> Optional[List[float]]: +) -> List[float]: """Get Basic List Of Floats Get a list of floats Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_integers.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_integers.py index 800c29608..65a1e4ddd 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_integers.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_integers.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, cast +from typing import Any, Dict, List, cast import httpx @@ -15,26 +15,22 @@ def _get_kwargs( url = "{}/tests/basic_lists/integers".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[List[int]]: +def _parse_response(*, client: Client, response: httpx.Response) -> List[int]: if response.status_code == HTTPStatus.OK: response_200 = cast(List[int], response.json()) return response_200 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[List[int]]: @@ -55,7 +51,6 @@ def sync_detailed( Get a list of integers Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -77,13 +72,12 @@ def sync_detailed( def sync( *, client: Client, -) -> Optional[List[int]]: +) -> List[int]: """Get Basic List Of Integers Get a list of integers Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -104,7 +98,6 @@ async def asyncio_detailed( Get a list of integers Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -124,13 +117,12 @@ async def asyncio_detailed( async def asyncio( *, client: Client, -) -> Optional[List[int]]: +) -> List[int]: """Get Basic List Of Integers Get a list of integers Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_strings.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_strings.py index 2a84b2b5e..c65de6734 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_strings.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_basic_list_of_strings.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, cast +from typing import Any, Dict, List, cast import httpx @@ -15,26 +15,22 @@ def _get_kwargs( url = "{}/tests/basic_lists/strings".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[List[str]]: +def _parse_response(*, client: Client, response: httpx.Response) -> List[str]: if response.status_code == HTTPStatus.OK: response_200 = cast(List[str], response.json()) return response_200 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[List[str]]: @@ -55,7 +51,6 @@ def sync_detailed( Get a list of strings Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -77,13 +72,12 @@ def sync_detailed( def sync( *, client: Client, -) -> Optional[List[str]]: +) -> List[str]: """Get Basic List Of Strings Get a list of strings Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -104,7 +98,6 @@ async def asyncio_detailed( Get a list of strings Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -124,13 +117,12 @@ async def asyncio_detailed( async def asyncio( *, client: Client, -) -> Optional[List[str]]: +) -> List[str]: """Get Basic List Of Strings Get a list of strings Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py index 6cffd7741..f604b017d 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py @@ -24,7 +24,6 @@ def _get_kwargs( url = "{}/tests/".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} json_an_enum_value = [] @@ -64,15 +63,12 @@ def _get_kwargs( "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response( - *, client: Client, response: httpx.Response -) -> Optional[Union[HTTPValidationError, List["AModel"]]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[HTTPValidationError, List["AModel"]]: if response.status_code == HTTPStatus.OK: response_200 = [] _response_200 = response.json() @@ -90,10 +86,8 @@ def _parse_response( response_423 = HTTPValidationError.from_dict(response.json()) return response_423 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response( @@ -126,7 +120,6 @@ def sync_detailed( some_date (Union[datetime.date, datetime.datetime]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -156,7 +149,7 @@ def sync( an_enum_value_with_null: List[Optional[AnEnumWithNull]], an_enum_value_with_only_null: List[None], some_date: Union[datetime.date, datetime.datetime], -) -> Optional[Union[HTTPValidationError, List["AModel"]]]: +) -> Union[HTTPValidationError, List["AModel"]]: """Get List Get a list of things @@ -168,7 +161,6 @@ def sync( some_date (Union[datetime.date, datetime.datetime]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -203,7 +195,6 @@ async def asyncio_detailed( some_date (Union[datetime.date, datetime.datetime]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -231,7 +222,7 @@ async def asyncio( an_enum_value_with_null: List[Optional[AnEnumWithNull]], an_enum_value_with_only_null: List[None], some_date: Union[datetime.date, datetime.datetime], -) -> Optional[Union[HTTPValidationError, List["AModel"]]]: +) -> Union[HTTPValidationError, List["AModel"]]: """Get List Get a list of things @@ -243,7 +234,6 @@ async def asyncio( some_date (Union[datetime.date, datetime.datetime]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/int_enum_tests_int_enum_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/int_enum_tests_int_enum_post.py index 4e23476f5..20866b841 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/int_enum_tests_int_enum_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/int_enum_tests_int_enum_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Dict, Union, cast import httpx @@ -18,7 +18,6 @@ def _get_kwargs( url = "{}/tests/int_enum".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} json_int_enum = int_enum.value @@ -31,13 +30,12 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Union[Any, HTTPValidationError]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[Any, HTTPValidationError]: if response.status_code == HTTPStatus.OK: response_200 = cast(Any, response.json()) return response_200 @@ -45,10 +43,8 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Uni response_422 = HTTPValidationError.from_dict(response.json()) return response_422 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Union[Any, HTTPValidationError]]: @@ -71,7 +67,6 @@ def sync_detailed( int_enum (AnIntEnum): An enumeration. Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -95,14 +90,13 @@ def sync( *, client: Client, int_enum: AnIntEnum, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Int Enum Args: int_enum (AnIntEnum): An enumeration. Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -126,7 +120,6 @@ async def asyncio_detailed( int_enum (AnIntEnum): An enumeration. Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -148,14 +141,13 @@ async def asyncio( *, client: Client, int_enum: AnIntEnum, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Int Enum Args: int_enum (AnIntEnum): An enumeration. Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py index 383522958..47ac27903 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Dict, Union, cast import httpx @@ -18,7 +18,6 @@ def _get_kwargs( url = "{}/tests/json_body".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() json_json_body = json_body.to_dict() @@ -26,13 +25,12 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "json": json_json_body, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Union[Any, HTTPValidationError]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[Any, HTTPValidationError]: if response.status_code == HTTPStatus.OK: response_200 = cast(Any, response.json()) return response_200 @@ -40,10 +38,8 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Uni response_422 = HTTPValidationError.from_dict(response.json()) return response_422 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Union[Any, HTTPValidationError]]: @@ -68,7 +64,6 @@ def sync_detailed( json_body (AModel): A Model for testing all the ways custom objects can be used Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -92,7 +87,7 @@ def sync( *, client: Client, json_body: AModel, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Json Body Try sending a JSON body @@ -101,7 +96,6 @@ def sync( json_body (AModel): A Model for testing all the ways custom objects can be used Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -127,7 +121,6 @@ async def asyncio_detailed( json_body (AModel): A Model for testing all the ways custom objects can be used Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -149,7 +142,7 @@ async def asyncio( *, client: Client, json_body: AModel, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Json Body Try sending a JSON body @@ -158,7 +151,6 @@ async def asyncio( json_body (AModel): A Model for testing all the ways custom objects can be used Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/no_response_tests_no_response_get.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/no_response_tests_no_response_get.py index 933184d3f..668ff64b1 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/no_response_tests_no_response_get.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/no_response_tests_no_response_get.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -15,24 +15,20 @@ def _get_kwargs( url = "{}/tests/no_response".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -51,7 +47,6 @@ def sync_detailed( """No Response Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -77,7 +72,6 @@ async def asyncio_detailed( """No Response Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_get.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_get.py index 99859332e..52f736430 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_get.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/octet_stream_tests_octet_stream_get.py @@ -1,6 +1,6 @@ from http import HTTPStatus from io import BytesIO -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -16,26 +16,22 @@ def _get_kwargs( url = "{}/tests/octet_stream".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[File]: +def _parse_response(*, client: Client, response: httpx.Response) -> File: if response.status_code == HTTPStatus.OK: response_200 = File(payload=BytesIO(response.content)) return response_200 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[File]: @@ -54,7 +50,6 @@ def sync_detailed( """Octet Stream Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -76,11 +71,10 @@ def sync_detailed( def sync( *, client: Client, -) -> Optional[File]: +) -> File: """Octet Stream Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -99,7 +93,6 @@ async def asyncio_detailed( """Octet Stream Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -119,11 +112,10 @@ async def asyncio_detailed( async def asyncio( *, client: Client, -) -> Optional[File]: +) -> File: """Octet Stream Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py index ff9e887f4..5da08834a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -17,25 +17,21 @@ def _get_kwargs( url = "{}/tests/post_form_data".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "data": form_data.to_dict(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -57,7 +53,6 @@ def sync_detailed( Post form data Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -87,7 +82,6 @@ async def asyncio_detailed( Post form data Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py index 46536a27a..e32adb529 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -17,25 +17,21 @@ def _get_kwargs( url = "{}/tests/post_form_data_inline".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "data": form_data.to_dict(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -57,7 +53,6 @@ def sync_detailed( Post form data (inline schema) Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -87,7 +82,6 @@ async def asyncio_detailed( Post form data (inline schema) Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_tests_json_body_string.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_tests_json_body_string.py index 6a1d178dc..0a20b3a43 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_tests_json_body_string.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_tests_json_body_string.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Dict, Union, cast import httpx @@ -17,7 +17,6 @@ def _get_kwargs( url = "{}/tests/json_body/string".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() json_json_body = json_body @@ -25,13 +24,12 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "json": json_json_body, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Union[HTTPValidationError, str]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[HTTPValidationError, str]: if response.status_code == HTTPStatus.OK: response_200 = cast(str, response.json()) return response_200 @@ -39,10 +37,8 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Uni response_422 = HTTPValidationError.from_dict(response.json()) return response_422 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Union[HTTPValidationError, str]]: @@ -65,7 +61,6 @@ def sync_detailed( json_body (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -89,14 +84,13 @@ def sync( *, client: Client, json_body: str, -) -> Optional[Union[HTTPValidationError, str]]: +) -> Union[HTTPValidationError, str]: """Json Body Which is String Args: json_body (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -120,7 +114,6 @@ async def asyncio_detailed( json_body (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -142,14 +135,13 @@ async def asyncio( *, client: Client, json_body: str, -) -> Optional[Union[HTTPValidationError, str]]: +) -> Union[HTTPValidationError, str]: """Json Body Which is String Args: json_body (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/test_inline_objects.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/test_inline_objects.py index d1533c0e1..4bcce73ec 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/test_inline_objects.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/test_inline_objects.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -18,7 +18,6 @@ def _get_kwargs( url = "{}/tests/inline_objects".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() json_json_body = json_body.to_dict() @@ -26,21 +25,18 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "json": json_json_body, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[TestInlineObjectsResponse200]: +def _parse_response(*, client: Client, response: httpx.Response) -> TestInlineObjectsResponse200: if response.status_code == HTTPStatus.OK: response_200 = TestInlineObjectsResponse200.from_dict(response.json()) return response_200 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[TestInlineObjectsResponse200]: @@ -63,7 +59,6 @@ def sync_detailed( json_body (TestInlineObjectsJsonBody): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -87,14 +82,13 @@ def sync( *, client: Client, json_body: TestInlineObjectsJsonBody, -) -> Optional[TestInlineObjectsResponse200]: +) -> TestInlineObjectsResponse200: """Test Inline Objects Args: json_body (TestInlineObjectsJsonBody): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -118,7 +112,6 @@ async def asyncio_detailed( json_body (TestInlineObjectsJsonBody): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -140,14 +133,13 @@ async def asyncio( *, client: Client, json_body: TestInlineObjectsJsonBody, -) -> Optional[TestInlineObjectsResponse200]: +) -> TestInlineObjectsResponse200: """Test Inline Objects Args: json_body (TestInlineObjectsJsonBody): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/token_with_cookie_auth_token_with_cookie_get.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/token_with_cookie_auth_token_with_cookie_get.py deleted file mode 100644 index 85d53d9da..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/token_with_cookie_auth_token_with_cookie_get.py +++ /dev/null @@ -1,113 +0,0 @@ -from http import HTTPStatus -from typing import Any, Dict, Optional - -import httpx - -from ... import errors -from ...client import Client -from ...types import Response - - -def _get_kwargs( - *, - client: Client, - my_token: str, -) -> Dict[str, Any]: - url = "{}/auth/token_with_cookie".format(client.base_url) - - headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() - - cookies["MyToken"] = my_token - - return { - "method": "get", - "url": url, - "headers": headers, - "cookies": cookies, - "timeout": client.get_timeout(), - } - - -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: - if response.status_code == HTTPStatus.OK: - return None - if response.status_code == HTTPStatus.UNAUTHORIZED: - return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") - else: - return None - - -def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: - return Response( - status_code=HTTPStatus(response.status_code), - content=response.content, - headers=response.headers, - parsed=_parse_response(client=client, response=response), - ) - - -def sync_detailed( - *, - client: Client, - my_token: str, -) -> Response[Any]: - """TOKEN_WITH_COOKIE - - Test optional cookie parameters - - Args: - my_token (str): - - Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. - httpx.TimeoutException: If the request takes longer than Client.timeout. - - Returns: - Response[Any] - """ - - kwargs = _get_kwargs( - client=client, - my_token=my_token, - ) - - response = httpx.request( - verify=client.verify_ssl, - **kwargs, - ) - - return _build_response(client=client, response=response) - - -async def asyncio_detailed( - *, - client: Client, - my_token: str, -) -> Response[Any]: - """TOKEN_WITH_COOKIE - - Test optional cookie parameters - - Args: - my_token (str): - - Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. - httpx.TimeoutException: If the request takes longer than Client.timeout. - - Returns: - Response[Any] - """ - - kwargs = _get_kwargs( - client=client, - my_token=my_token, - ) - - async with httpx.AsyncClient(verify=client.verify_ssl) as _client: - response = await _client.request(**kwargs) - - return _build_response(client=client, response=response) diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/unsupported_content_tests_unsupported_content_get.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/unsupported_content_tests_unsupported_content_get.py index 2daec1319..51dd62ba0 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/unsupported_content_tests_unsupported_content_get.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/unsupported_content_tests_unsupported_content_get.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -15,24 +15,20 @@ def _get_kwargs( url = "{}/tests/unsupported_content".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() return { "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -51,7 +47,6 @@ def sync_detailed( """Unsupported Content Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -77,7 +72,6 @@ async def asyncio_detailed( """Unsupported Content Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py index d00ec5e40..21d6488c8 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union, cast +from typing import Any, Dict, Union, cast import httpx @@ -18,7 +18,6 @@ def _get_kwargs( url = "{}/tests/upload".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() multipart_multipart_data = multipart_data.to_multipart() @@ -26,13 +25,12 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "files": multipart_multipart_data, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Union[Any, HTTPValidationError]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[Any, HTTPValidationError]: if response.status_code == HTTPStatus.OK: response_200 = cast(Any, response.json()) return response_200 @@ -40,10 +38,8 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Uni response_422 = HTTPValidationError.from_dict(response.json()) return response_422 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Union[Any, HTTPValidationError]]: @@ -68,7 +64,6 @@ def sync_detailed( multipart_data (BodyUploadFileTestsUploadPost): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -92,7 +87,7 @@ def sync( *, client: Client, multipart_data: BodyUploadFileTestsUploadPost, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Upload File Upload a file @@ -101,7 +96,6 @@ def sync( multipart_data (BodyUploadFileTestsUploadPost): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -127,7 +121,6 @@ async def asyncio_detailed( multipart_data (BodyUploadFileTestsUploadPost): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -149,7 +142,7 @@ async def asyncio( *, client: Client, multipart_data: BodyUploadFileTestsUploadPost, -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Upload File Upload a file @@ -158,7 +151,6 @@ async def asyncio( multipart_data (BodyUploadFileTestsUploadPost): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py index 34add0e4e..969ee8e3c 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Dict, List, Union, cast import httpx @@ -17,7 +17,6 @@ def _get_kwargs( url = "{}/tests/upload/multiple".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() multipart_multipart_data = [] for multipart_data_item_data in multipart_data: @@ -29,13 +28,12 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "files": multipart_multipart_data, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Union[Any, HTTPValidationError]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[Any, HTTPValidationError]: if response.status_code == HTTPStatus.OK: response_200 = cast(Any, response.json()) return response_200 @@ -43,10 +41,8 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Uni response_422 = HTTPValidationError.from_dict(response.json()) return response_422 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Union[Any, HTTPValidationError]]: @@ -71,7 +67,6 @@ def sync_detailed( multipart_data (List[File]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -95,7 +90,7 @@ def sync( *, client: Client, multipart_data: List[File], -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Upload multiple files Upload several files in the same request @@ -104,7 +99,6 @@ def sync( multipart_data (List[File]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -130,7 +124,6 @@ async def asyncio_detailed( multipart_data (List[File]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -152,7 +145,7 @@ async def asyncio( *, client: Client, multipart_data: List[File], -) -> Optional[Union[Any, HTTPValidationError]]: +) -> Union[Any, HTTPValidationError]: """Upload multiple files Upload several files in the same request @@ -161,7 +154,6 @@ async def asyncio( multipart_data (List[File]): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/true_/false_.py b/end_to_end_tests/golden-record/my_test_api_client/api/true_/false_.py index f8332a87a..0b615127b 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/true_/false_.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/true_/false_.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional +from typing import Any, Dict import httpx @@ -16,7 +16,6 @@ def _get_kwargs( url = "{}/naming/keywords".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() params: Dict[str, Any] = {} params["import"] = import_ @@ -27,19 +26,16 @@ def _get_kwargs( "method": "get", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "params": params, } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]: +def _parse_response(*, client: Client, response: httpx.Response) -> Any: if response.status_code == HTTPStatus.OK: return None - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]: @@ -61,7 +57,6 @@ def sync_detailed( import_ (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -91,7 +86,6 @@ async def asyncio_detailed( import_ (str): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/end_to_end_tests/golden-record/my_test_api_client/client.py b/end_to_end_tests/golden-record/my_test_api_client/client.py index 99aeffa53..b4bbf3cb3 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/client.py +++ b/end_to_end_tests/golden-record/my_test_api_client/client.py @@ -3,6 +3,8 @@ import attr +from .jwt import JwtGenerator + @attr.s(auto_attribs=True) class Client: @@ -10,55 +12,40 @@ class Client: Attributes: base_url: The base URL for the API, all requests are made to a relative path to this URL - cookies: A dictionary of cookies to be sent with every request headers: A dictionary of headers to be sent with every request timeout: The maximum amount of a time in seconds a request can take. API functions will raise httpx.TimeoutException if this is exceeded. verify_ssl: Whether or not to verify the SSL certificate of the API server. This should be True in production, but can be set to False for testing purposes. - raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a - status code that was not documented in the source OpenAPI document. + key: The private key used to sign the JWT encoded with ES256. + key_fingerprint: Key ID or fingerprint. + jwt_expiration: Controls the expiration time for a JWT. 60 seconds by default. """ base_url: str - cookies: Dict[str, str] = attr.ib(factory=dict, kw_only=True) headers: Dict[str, str] = attr.ib(factory=dict, kw_only=True) timeout: float = attr.ib(5.0, kw_only=True) verify_ssl: Union[str, bool, ssl.SSLContext] = attr.ib(True, kw_only=True) - raise_on_unexpected_status: bool = attr.ib(False, kw_only=True) + + key: str = attr.ib(kw_only=True) + key_fingerprint: str = attr.ib(kw_only=True) + jwt_expiration: int = attr.ib(60, kw_only=True) + + def __attrs_post_init__(self) -> None: + self._jwt_generator = JwtGenerator(key=self.key, kid=self.key_fingerprint, exp=self.jwt_expiration) def get_headers(self) -> Dict[str, str]: - """Get headers to be used in all endpoints""" - return {**self.headers} + """Get headers to be used in authenticated endpoints""" + token = self._jwt_generator.generate() + return {"Authorization": f"Bearer {token}", **self.headers} def with_headers(self, headers: Dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" return attr.evolve(self, headers={**self.headers, **headers}) - def get_cookies(self) -> Dict[str, str]: - return {**self.cookies} - - def with_cookies(self, cookies: Dict[str, str]) -> "Client": - """Get a new client matching this one with additional cookies""" - return attr.evolve(self, cookies={**self.cookies, **cookies}) - def get_timeout(self) -> float: return self.timeout def with_timeout(self, timeout: float) -> "Client": """Get a new client matching this one with a new timeout (in seconds)""" return attr.evolve(self, timeout=timeout) - - -@attr.s(auto_attribs=True) -class AuthenticatedClient(Client): - """A Client which has been authenticated for use on secured endpoints""" - - token: str - prefix: str = "Bearer" - auth_header_name: str = "Authorization" - - def get_headers(self) -> Dict[str, str]: - """Get headers to be used in authenticated endpoints""" - auth_header_value = f"{self.prefix} {self.token}" if self.prefix else self.token - return {self.auth_header_name: auth_header_value, **self.headers} diff --git a/end_to_end_tests/golden-record/my_test_api_client/errors.py b/end_to_end_tests/golden-record/my_test_api_client/errors.py index a508e1360..8458dd44e 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/errors.py +++ b/end_to_end_tests/golden-record/my_test_api_client/errors.py @@ -1,10 +1,15 @@ """ Contains shared errors types that can be raised from API functions """ +import httpx class UnexpectedStatus(Exception): - """Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True""" + """Raised by api functions when the response status is an undocumented status.""" - ... + response: httpx.Response + + def __init__(self, *args: object, response: httpx.Response) -> None: + super().__init__(*args) + self.response = response __all__ = ["UnexpectedStatus"] diff --git a/end_to_end_tests/golden-record/my_test_api_client/jwt.py b/end_to_end_tests/golden-record/my_test_api_client/jwt.py new file mode 100644 index 000000000..d3aac3f51 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/jwt.py @@ -0,0 +1,46 @@ +import secrets +import time +from datetime import datetime, timedelta, timezone +from threading import Lock + +import jwt + + +class JwtGenerator: + def __init__(self, key: str, kid: str, exp: int): + self.key = key + self.kid = kid + self.exp = exp + + self._expires: float = 0 + self._jwt: str = "" + self._lock = Lock() + + # generate a new token 10 seconds earlier than the 'exp' header to give + # enough time for the request to be made + self._expires_leeway = 10 + + def generate(self) -> str: + if self._needs_refresh(): + with self._lock: + self._generate() + + return self._jwt + + def _needs_refresh(self) -> bool: + return not self._jwt or self._expires < time.monotonic() + + def _generate(self) -> None: + jti = secrets.token_hex(16) + now = datetime.now(tz=timezone.utc) + exp = now + timedelta(seconds=self.exp) + + payload = {"jti": jti, "nbf": now, "exp": exp, "iat": now, "aud": "api"} + headers = {"kid": self.kid} + + self._expires = time.monotonic() + self.exp - self._expires_leeway + + if self._expires < 0: + self._expires = 0 + + self._jwt = jwt.encode(payload=payload, headers=headers, algorithm="ES256", key=self.key) diff --git a/end_to_end_tests/golden-record/my_test_api_client/types.py b/end_to_end_tests/golden-record/my_test_api_client/types.py index 230efea92..2e9f7b08c 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/types.py +++ b/end_to_end_tests/golden-record/my_test_api_client/types.py @@ -38,7 +38,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T __all__ = ["File", "Response", "FileJsonType"] diff --git a/end_to_end_tests/golden-record/pyproject.toml b/end_to_end_tests/golden-record/pyproject.toml index 71fa00d62..59bfbf1ed 100644 --- a/end_to_end_tests/golden-record/pyproject.toml +++ b/end_to_end_tests/golden-record/pyproject.toml @@ -16,6 +16,7 @@ python = "^3.7" httpx = ">=0.15.4,<0.24.0" attrs = ">=21.3.0" python-dateutil = "^2.8.0" +pyjwt = {extras = ["crypto"], version = "^2.6.0"} [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index 803bedfeb..573d6d0b9 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -781,40 +781,6 @@ } } }, - "/auth/token_with_cookie": { - "get": { - "tags": [ - "tests" - ], - "summary": "TOKEN_WITH_COOKIE", - "description": "Test optional cookie parameters", - "operationId": "token_with_cookie_auth_token_with_cookie_get", - "parameters": [ - { - "required": true, - "schema": { - "title": "Token", - "type": "string" - }, - "name": "MyToken", - "in": "cookie" - } - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {} - } - } - }, - "401": { - "description": "Unauthorized" - } - } - } - }, "/common_parameters": { "parameters": [ { @@ -914,13 +880,6 @@ "type": "string" } }, - { - "name": "param", - "in": "cookie", - "schema": { - "type": "string" - } - }, { "name": "param", "in": "path", @@ -1164,7 +1123,6 @@ "$ref": "#/components/parameters/integer-param" }, {"$ref": "#/components/parameters/header-param"}, - {"$ref": "#/components/parameters/cookie-param"}, {"$ref": "#/components/parameters/path-param"} ], "responses": { @@ -2272,14 +2230,6 @@ "type": "string" } }, - "cookie-param": { - "name": "cookie param", - "in": "cookie", - "required": false, - "schema": { - "type": "string" - } - }, "path-param": { "name": "path_param", "in": "path", diff --git a/integration-tests/README.md b/integration-tests/README.md index f7e1cdfc6..66e91a48a 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -10,14 +10,6 @@ from integration_tests import Client client = Client(base_url="https://api.example.com") ``` -If the endpoints you're going to hit require authentication, use `AuthenticatedClient` instead: - -```python -from integration_tests import AuthenticatedClient - -client = AuthenticatedClient(base_url="https://api.example.com", token="SuperSecretToken") -``` - Now call your endpoint and use your models: ```python @@ -44,8 +36,8 @@ response: Response[MyDataModel] = await get_my_data_model.asyncio_detailed(clien By default, when you're calling an HTTPS API it will attempt to verify that SSL is working correctly. Using certificate verification is highly recommended most of the time, but sometimes you may need to authenticate to a server (especially an internal server) using a custom certificate bundle. ```python -client = AuthenticatedClient( - base_url="https://internal_api.example.com", +client = Client( + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -54,9 +46,9 @@ client = AuthenticatedClient( You can also disable certificate validation altogether, but beware that **this is a security risk**. ```python -client = AuthenticatedClient( - base_url="https://internal_api.example.com", - token="SuperSecretToken", +client = Client( + base_url="https://internal_api.example.com", + token="SuperSecretToken", verify_ssl=False ) ``` @@ -84,4 +76,4 @@ If you want to install this client into another project without publishing it (e 1. If that project **is using Poetry**, you can simply do `poetry add ` from that project 1. If that project is not using Poetry: 1. Build a wheel with `poetry build -f wheel` - 1. Install that wheel from the other project `pip install ` \ No newline at end of file + 1. Install that wheel from the other project `pip install ` diff --git a/integration-tests/integration_tests/__init__.py b/integration-tests/integration_tests/__init__.py index 48f0fb8da..2a9be04ea 100644 --- a/integration-tests/integration_tests/__init__.py +++ b/integration-tests/integration_tests/__init__.py @@ -1,7 +1,4 @@ """ A client library for accessing OpenAPI Test Server """ -from .client import AuthenticatedClient, Client +from .client import Client -__all__ = ( - "AuthenticatedClient", - "Client", -) +__all__ = ("Client",) diff --git a/integration-tests/integration_tests/api/body/post_body_multipart.py b/integration-tests/integration_tests/api/body/post_body_multipart.py index 303443af7..d26f6983e 100644 --- a/integration-tests/integration_tests/api/body/post_body_multipart.py +++ b/integration-tests/integration_tests/api/body/post_body_multipart.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union import httpx @@ -19,7 +19,6 @@ def _get_kwargs( url = "{}/body/multipart".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() multipart_multipart_data = multipart_data.to_multipart() @@ -27,15 +26,12 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), "files": multipart_multipart_data, } -def _parse_response( - *, client: Client, response: httpx.Response -) -> Optional[Union[PostBodyMultipartResponse200, PublicError]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[PostBodyMultipartResponse200, PublicError]: if response.status_code == HTTPStatus.OK: response_200 = PostBodyMultipartResponse200.from_dict(response.json()) @@ -44,10 +40,8 @@ def _parse_response( response_400 = PublicError.from_dict(response.json()) return response_400 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response( @@ -71,7 +65,6 @@ def sync_detailed( multipart_data (PostBodyMultipartMultipartData): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -95,13 +88,12 @@ def sync( *, client: Client, multipart_data: PostBodyMultipartMultipartData, -) -> Optional[Union[PostBodyMultipartResponse200, PublicError]]: +) -> Union[PostBodyMultipartResponse200, PublicError]: """ Args: multipart_data (PostBodyMultipartMultipartData): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -124,7 +116,6 @@ async def asyncio_detailed( multipart_data (PostBodyMultipartMultipartData): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -146,13 +137,12 @@ async def asyncio( *, client: Client, multipart_data: PostBodyMultipartMultipartData, -) -> Optional[Union[PostBodyMultipartResponse200, PublicError]]: +) -> Union[PostBodyMultipartResponse200, PublicError]: """ Args: multipart_data (PostBodyMultipartMultipartData): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/integration-tests/integration_tests/api/parameters/post_parameters_header.py b/integration-tests/integration_tests/api/parameters/post_parameters_header.py index c89c4c304..5db518a97 100644 --- a/integration-tests/integration_tests/api/parameters/post_parameters_header.py +++ b/integration-tests/integration_tests/api/parameters/post_parameters_header.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union import httpx @@ -21,7 +21,6 @@ def _get_kwargs( url = "{}/parameters/header".format(client.base_url) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() headers["Boolean-Header"] = "true" if boolean_header else "false" @@ -35,14 +34,11 @@ def _get_kwargs( "method": "post", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), } -def _parse_response( - *, client: Client, response: httpx.Response -) -> Optional[Union[PostParametersHeaderResponse200, PublicError]]: +def _parse_response(*, client: Client, response: httpx.Response) -> Union[PostParametersHeaderResponse200, PublicError]: if response.status_code == HTTPStatus.OK: response_200 = PostParametersHeaderResponse200.from_dict(response.json()) @@ -51,10 +47,8 @@ def _parse_response( response_400 = PublicError.from_dict(response.json()) return response_400 - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response( @@ -84,7 +78,6 @@ def sync_detailed( integer_header (int): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -114,7 +107,7 @@ def sync( string_header: str, number_header: float, integer_header: int, -) -> Optional[Union[PostParametersHeaderResponse200, PublicError]]: +) -> Union[PostParametersHeaderResponse200, PublicError]: """ Args: boolean_header (bool): @@ -123,7 +116,6 @@ def sync( integer_header (int): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -155,7 +147,6 @@ async def asyncio_detailed( integer_header (int): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: @@ -183,7 +174,7 @@ async def asyncio( string_header: str, number_header: float, integer_header: int, -) -> Optional[Union[PostParametersHeaderResponse200, PublicError]]: +) -> Union[PostParametersHeaderResponse200, PublicError]: """ Args: boolean_header (bool): @@ -192,7 +183,6 @@ async def asyncio( integer_header (int): Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/integration-tests/integration_tests/client.py b/integration-tests/integration_tests/client.py index 99aeffa53..b4bbf3cb3 100644 --- a/integration-tests/integration_tests/client.py +++ b/integration-tests/integration_tests/client.py @@ -3,6 +3,8 @@ import attr +from .jwt import JwtGenerator + @attr.s(auto_attribs=True) class Client: @@ -10,55 +12,40 @@ class Client: Attributes: base_url: The base URL for the API, all requests are made to a relative path to this URL - cookies: A dictionary of cookies to be sent with every request headers: A dictionary of headers to be sent with every request timeout: The maximum amount of a time in seconds a request can take. API functions will raise httpx.TimeoutException if this is exceeded. verify_ssl: Whether or not to verify the SSL certificate of the API server. This should be True in production, but can be set to False for testing purposes. - raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a - status code that was not documented in the source OpenAPI document. + key: The private key used to sign the JWT encoded with ES256. + key_fingerprint: Key ID or fingerprint. + jwt_expiration: Controls the expiration time for a JWT. 60 seconds by default. """ base_url: str - cookies: Dict[str, str] = attr.ib(factory=dict, kw_only=True) headers: Dict[str, str] = attr.ib(factory=dict, kw_only=True) timeout: float = attr.ib(5.0, kw_only=True) verify_ssl: Union[str, bool, ssl.SSLContext] = attr.ib(True, kw_only=True) - raise_on_unexpected_status: bool = attr.ib(False, kw_only=True) + + key: str = attr.ib(kw_only=True) + key_fingerprint: str = attr.ib(kw_only=True) + jwt_expiration: int = attr.ib(60, kw_only=True) + + def __attrs_post_init__(self) -> None: + self._jwt_generator = JwtGenerator(key=self.key, kid=self.key_fingerprint, exp=self.jwt_expiration) def get_headers(self) -> Dict[str, str]: - """Get headers to be used in all endpoints""" - return {**self.headers} + """Get headers to be used in authenticated endpoints""" + token = self._jwt_generator.generate() + return {"Authorization": f"Bearer {token}", **self.headers} def with_headers(self, headers: Dict[str, str]) -> "Client": """Get a new client matching this one with additional headers""" return attr.evolve(self, headers={**self.headers, **headers}) - def get_cookies(self) -> Dict[str, str]: - return {**self.cookies} - - def with_cookies(self, cookies: Dict[str, str]) -> "Client": - """Get a new client matching this one with additional cookies""" - return attr.evolve(self, cookies={**self.cookies, **cookies}) - def get_timeout(self) -> float: return self.timeout def with_timeout(self, timeout: float) -> "Client": """Get a new client matching this one with a new timeout (in seconds)""" return attr.evolve(self, timeout=timeout) - - -@attr.s(auto_attribs=True) -class AuthenticatedClient(Client): - """A Client which has been authenticated for use on secured endpoints""" - - token: str - prefix: str = "Bearer" - auth_header_name: str = "Authorization" - - def get_headers(self) -> Dict[str, str]: - """Get headers to be used in authenticated endpoints""" - auth_header_value = f"{self.prefix} {self.token}" if self.prefix else self.token - return {self.auth_header_name: auth_header_value, **self.headers} diff --git a/integration-tests/integration_tests/errors.py b/integration-tests/integration_tests/errors.py index a508e1360..8458dd44e 100644 --- a/integration-tests/integration_tests/errors.py +++ b/integration-tests/integration_tests/errors.py @@ -1,10 +1,15 @@ """ Contains shared errors types that can be raised from API functions """ +import httpx class UnexpectedStatus(Exception): - """Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True""" + """Raised by api functions when the response status is an undocumented status.""" - ... + response: httpx.Response + + def __init__(self, *args: object, response: httpx.Response) -> None: + super().__init__(*args) + self.response = response __all__ = ["UnexpectedStatus"] diff --git a/integration-tests/integration_tests/jwt.py b/integration-tests/integration_tests/jwt.py new file mode 100644 index 000000000..d3aac3f51 --- /dev/null +++ b/integration-tests/integration_tests/jwt.py @@ -0,0 +1,46 @@ +import secrets +import time +from datetime import datetime, timedelta, timezone +from threading import Lock + +import jwt + + +class JwtGenerator: + def __init__(self, key: str, kid: str, exp: int): + self.key = key + self.kid = kid + self.exp = exp + + self._expires: float = 0 + self._jwt: str = "" + self._lock = Lock() + + # generate a new token 10 seconds earlier than the 'exp' header to give + # enough time for the request to be made + self._expires_leeway = 10 + + def generate(self) -> str: + if self._needs_refresh(): + with self._lock: + self._generate() + + return self._jwt + + def _needs_refresh(self) -> bool: + return not self._jwt or self._expires < time.monotonic() + + def _generate(self) -> None: + jti = secrets.token_hex(16) + now = datetime.now(tz=timezone.utc) + exp = now + timedelta(seconds=self.exp) + + payload = {"jti": jti, "nbf": now, "exp": exp, "iat": now, "aud": "api"} + headers = {"kid": self.kid} + + self._expires = time.monotonic() + self.exp - self._expires_leeway + + if self._expires < 0: + self._expires = 0 + + self._jwt = jwt.encode(payload=payload, headers=headers, algorithm="ES256", key=self.key) diff --git a/integration-tests/integration_tests/types.py b/integration-tests/integration_tests/types.py index 230efea92..2e9f7b08c 100644 --- a/integration-tests/integration_tests/types.py +++ b/integration-tests/integration_tests/types.py @@ -38,7 +38,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T __all__ = ["File", "Response", "FileJsonType"] diff --git a/integration-tests/poetry.lock b/integration-tests/poetry.lock index e8ae9c4d9..ac67d9d20 100644 --- a/integration-tests/poetry.lock +++ b/integration-tests/poetry.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + [[package]] name = "anyio" version = "3.5.0" @@ -5,6 +7,10 @@ description = "High level compatibility layer for multiple asynchronous event lo category = "main" optional = false python-versions = ">=3.6.2" +files = [ + {file = "anyio-3.5.0-py3-none-any.whl", hash = "sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e"}, + {file = "anyio-3.5.0.tar.gz", hash = "sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6"}, +] [package.dependencies] idna = ">=2.8" @@ -23,6 +29,10 @@ description = "Atomic file writes." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] [[package]] name = "attrs" @@ -31,6 +41,10 @@ description = "Classes Without Boilerplate" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] [package.extras] dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] @@ -45,6 +59,87 @@ description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" [[package]] name = "colorama" @@ -53,6 +148,56 @@ description = "Cross-platform colored terminal text." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] + +[[package]] +name = "cryptography" +version = "39.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06"}, + {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536"}, + {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5"}, + {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0"}, + {file = "cryptography-39.0.2-cp36-abi3-win32.whl", hash = "sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480"}, + {file = "cryptography-39.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9"}, + {file = "cryptography-39.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac"}, + {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:50cadb9b2f961757e712a9737ef33d89b8190c3ea34d0fb6675e00edbe35d074"}, + {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:103e8f7155f3ce2ffa0049fe60169878d47a4364b277906386f8de21c9234aa1"}, + {file = "cryptography-39.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3"}, + {file = "cryptography-39.0.2.tar.gz", hash = "sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f"}, +] + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "check-manifest", "mypy", "ruff", "types-pytz", "types-requests"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist", "pytz"] +test-randomorder = ["pytest-randomly"] +tox = ["tox"] [[package]] name = "h11" @@ -61,6 +206,10 @@ description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, + {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, +] [[package]] name = "httpcore" @@ -69,6 +218,10 @@ description = "A minimal low-level HTTP client." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "httpcore-0.15.0-py3-none-any.whl", hash = "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6"}, + {file = "httpcore-0.15.0.tar.gz", hash = "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"}, +] [package.dependencies] anyio = ">=3.0.0,<4.0.0" @@ -87,6 +240,10 @@ description = "The next generation HTTP client." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "httpx-0.23.0-py3-none-any.whl", hash = "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b"}, + {file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"}, +] [package.dependencies] certifi = "*" @@ -107,6 +264,10 @@ description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] [[package]] name = "importlib-metadata" @@ -115,6 +276,10 @@ description = "Read metadata from Python packages" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"}, + {file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"}, +] [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} @@ -132,6 +297,10 @@ description = "iniconfig: brain-dead simple config-ini parsing" category = "dev" optional = false python-versions = "*" +files = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] [[package]] name = "packaging" @@ -140,6 +309,10 @@ description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" @@ -151,6 +324,10 @@ description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} @@ -166,6 +343,43 @@ description = "library with cross-python path, ini-parsing, io, code, log facili category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pyjwt" +version = "2.6.0" +description = "JSON Web Token implementation in Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.6.0-py3-none-any.whl", hash = "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"}, + {file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"}, +] + +[package.dependencies] +cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pyparsing" @@ -174,6 +388,10 @@ description = "Python parsing module" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, + {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, +] [package.extras] diagrams = ["jinja2", "railroad-diagrams"] @@ -185,6 +403,10 @@ description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pytest-7.0.0-py3-none-any.whl", hash = "sha256:42901e6bd4bd4a0e533358a86e848427a49005a3256f657c5c8f8dd35ef137a9"}, + {file = "pytest-7.0.0.tar.gz", hash = "sha256:dad48ffda394e5ad9aa3b7d7ddf339ed502e5e365b1350e0af65f4a602344b11"}, +] [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} @@ -207,6 +429,10 @@ description = "Extensions to the standard Python datetime module" category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] [package.dependencies] six = ">=1.5" @@ -218,6 +444,10 @@ description = "Validating URI References per RFC 3986" category = "main" optional = false python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] [package.dependencies] idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} @@ -232,6 +462,10 @@ description = "Python 2 and 3 compatibility utilities" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] [[package]] name = "sniffio" @@ -240,6 +474,10 @@ description = "Sniff out which async library your code is running under" category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, + {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, +] [[package]] name = "tomli" @@ -248,6 +486,10 @@ description = "A lil' TOML parser" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"}, + {file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, +] [[package]] name = "typing-extensions" @@ -256,6 +498,10 @@ description = "Backported and Experimental Type Hints for Python 3.6+" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, + {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, +] [[package]] name = "zipp" @@ -264,106 +510,16 @@ description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, +] [package.extras] docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] [metadata] -lock-version = "1.1" +lock-version = "2.0" python-versions = "^3.7" -content-hash = "cc9c6bc8724192810d28f6dcfec43fe6d6a552036bcf45938e0aaa9b50bacf8d" - -[metadata.files] -anyio = [ - {file = "anyio-3.5.0-py3-none-any.whl", hash = "sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e"}, - {file = "anyio-3.5.0.tar.gz", hash = "sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] -certifi = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] -h11 = [ - {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, - {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, -] -httpcore = [ - {file = "httpcore-0.15.0-py3-none-any.whl", hash = "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6"}, - {file = "httpcore-0.15.0.tar.gz", hash = "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"}, -] -httpx = [ - {file = "httpx-0.23.0-py3-none-any.whl", hash = "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b"}, - {file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"}, -] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"}, - {file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pyparsing = [ - {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, - {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, -] -pytest = [ - {file = "pytest-7.0.0-py3-none-any.whl", hash = "sha256:42901e6bd4bd4a0e533358a86e848427a49005a3256f657c5c8f8dd35ef137a9"}, - {file = "pytest-7.0.0.tar.gz", hash = "sha256:dad48ffda394e5ad9aa3b7d7ddf339ed502e5e365b1350e0af65f4a602344b11"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -rfc3986 = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -sniffio = [ - {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, - {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, -] -tomli = [ - {file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"}, - {file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, -] -typing-extensions = [ - {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, - {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, -] -zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, -] +content-hash = "bc76ed8d2c248989d7844e0b6dcb6dea0bc20fe4f5694e21a2a4e8df5bcea21c" diff --git a/integration-tests/pyproject.toml b/integration-tests/pyproject.toml index 49f0f134d..8dd7f4ddc 100644 --- a/integration-tests/pyproject.toml +++ b/integration-tests/pyproject.toml @@ -14,6 +14,7 @@ python = "^3.7" httpx = ">=0.15.4,<0.24.0" attrs = ">=21.3.0" python-dateutil = "^2.8.0" +pyjwt = {extras = ["crypto"], version = "^2.6.0"} [tool.poetry.dev-dependencies] pytest = "^7.0.0" @@ -37,4 +38,4 @@ exclude = ''' [tool.isort] line_length = 120 -profile = "black" \ No newline at end of file +profile = "black" diff --git a/integration-tests/tests/conftest.py b/integration-tests/tests/conftest.py index 9cdd679fa..de7e71a5f 100644 --- a/integration-tests/tests/conftest.py +++ b/integration-tests/tests/conftest.py @@ -5,4 +5,4 @@ @pytest.fixture(scope="session") def client() -> Client: - return Client("http://localhost:3000") + return Client("http://localhost:3000", key="foo", key_fingerprint="bar") diff --git a/openapi_python_client/__init__.py b/openapi_python_client/__init__.py index 109ce84c7..27aeb343e 100644 --- a/openapi_python_client/__init__.py +++ b/openapi_python_client/__init__.py @@ -265,6 +265,11 @@ def _build_api(self) -> None: client_template = self.env.get_template("client.py.jinja") client_path.write_text(client_template.render(), encoding=self.file_encoding) + # Generate jwt + jwt_path = self.package_dir / "jwt.py" + jwt_template = self.env.get_template("jwt.py.jinja") + jwt_path.write_text(jwt_template.render(), encoding=self.file_encoding) + # Generate included Errors errors_path = self.package_dir / "errors.py" errors_template = self.env.get_template("errors.py.jinja") diff --git a/openapi_python_client/templates/README.md.jinja b/openapi_python_client/templates/README.md.jinja index 1d50c8d2a..7f1b10cce 100644 --- a/openapi_python_client/templates/README.md.jinja +++ b/openapi_python_client/templates/README.md.jinja @@ -10,14 +10,6 @@ from {{ package_name }} import Client client = Client(base_url="https://api.example.com") ``` -If the endpoints you're going to hit require authentication, use `AuthenticatedClient` instead: - -```python -from {{ package_name }} import AuthenticatedClient - -client = AuthenticatedClient(base_url="https://api.example.com", token="SuperSecretToken") -``` - Now call your endpoint and use your models: ```python @@ -44,8 +36,8 @@ response: Response[MyDataModel] = await get_my_data_model.asyncio_detailed(clien By default, when you're calling an HTTPS API it will attempt to verify that SSL is working correctly. Using certificate verification is highly recommended most of the time, but sometimes you may need to authenticate to a server (especially an internal server) using a custom certificate bundle. ```python -client = AuthenticatedClient( - base_url="https://internal_api.example.com", +client = Client( + base_url="https://internal_api.example.com", token="SuperSecretToken", verify_ssl="/path/to/certificate_bundle.pem", ) @@ -54,9 +46,9 @@ client = AuthenticatedClient( You can also disable certificate validation altogether, but beware that **this is a security risk**. ```python -client = AuthenticatedClient( - base_url="https://internal_api.example.com", - token="SuperSecretToken", +client = Client( + base_url="https://internal_api.example.com", + token="SuperSecretToken", verify_ssl=False ) ``` diff --git a/openapi_python_client/templates/client.py.jinja b/openapi_python_client/templates/client.py.jinja index 3155f30bf..44692a1db 100644 --- a/openapi_python_client/templates/client.py.jinja +++ b/openapi_python_client/templates/client.py.jinja @@ -2,60 +2,49 @@ import ssl from typing import Dict, Union import attr +from .jwt import JwtGenerator + + @attr.s(auto_attribs=True) class Client: - """ A class for keeping track of data related to the API + """A class for keeping track of data related to the API Attributes: base_url: The base URL for the API, all requests are made to a relative path to this URL - cookies: A dictionary of cookies to be sent with every request headers: A dictionary of headers to be sent with every request timeout: The maximum amount of a time in seconds a request can take. API functions will raise httpx.TimeoutException if this is exceeded. verify_ssl: Whether or not to verify the SSL certificate of the API server. This should be True in production, but can be set to False for testing purposes. - raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a - status code that was not documented in the source OpenAPI document. + key: The private key used to sign the JWT encoded with ES256. + key_fingerprint: Key ID or fingerprint. + jwt_expiration: Controls the expiration time for a JWT. 60 seconds by default. """ base_url: str - cookies: Dict[str, str] = attr.ib(factory=dict, kw_only=True) headers: Dict[str, str] = attr.ib(factory=dict, kw_only=True) timeout: float = attr.ib(5.0, kw_only=True) verify_ssl: Union[str, bool, ssl.SSLContext] = attr.ib(True, kw_only=True) - raise_on_unexpected_status: bool = attr.ib(False, kw_only=True) + + key: str = attr.ib(kw_only=True) + key_fingerprint: str = attr.ib(kw_only=True) + jwt_expiration: int = attr.ib(60, kw_only=True) + + def __attrs_post_init__(self) -> None: + self._jwt_generator = JwtGenerator(key=self.key, kid=self.key_fingerprint, exp=self.jwt_expiration) def get_headers(self) -> Dict[str, str]: - """ Get headers to be used in all endpoints """ - return {**self.headers} + """Get headers to be used in authenticated endpoints""" + token = self._jwt_generator.generate() + return {"Authorization": f"Bearer {token}", **self.headers} def with_headers(self, headers: Dict[str, str]) -> "Client": - """ Get a new client matching this one with additional headers """ + """Get a new client matching this one with additional headers""" return attr.evolve(self, headers={**self.headers, **headers}) - def get_cookies(self) -> Dict[str, str]: - return {**self.cookies} - - def with_cookies(self, cookies: Dict[str, str]) -> "Client": - """ Get a new client matching this one with additional cookies """ - return attr.evolve(self, cookies={**self.cookies, **cookies}) - def get_timeout(self) -> float: return self.timeout def with_timeout(self, timeout: float) -> "Client": - """ Get a new client matching this one with a new timeout (in seconds) """ + """Get a new client matching this one with a new timeout (in seconds)""" return attr.evolve(self, timeout=timeout) - -@attr.s(auto_attribs=True) -class AuthenticatedClient(Client): - """ A Client which has been authenticated for use on secured endpoints """ - - token: str - prefix: str = "Bearer" - auth_header_name: str = "Authorization" - - def get_headers(self) -> Dict[str, str]: - """Get headers to be used in authenticated endpoints""" - auth_header_value = f"{self.prefix} {self.token}" if self.prefix else self.token - return {self.auth_header_name: auth_header_value, **self.headers} diff --git a/openapi_python_client/templates/endpoint_macros.py.jinja b/openapi_python_client/templates/endpoint_macros.py.jinja index 4dc0575f9..8b13e1e8f 100644 --- a/openapi_python_client/templates/endpoint_macros.py.jinja +++ b/openapi_python_client/templates/endpoint_macros.py.jinja @@ -15,19 +15,6 @@ {% endif %} {% endmacro %} -{% macro cookie_params(endpoint) %} -{% if endpoint.cookie_parameters %} - {% for parameter in endpoint.cookie_parameters.values() %} - {% if parameter.required %} -cookies["{{ parameter.name}}"] = {{ parameter.python_name }} - {% else %} -if {{ parameter.python_name }} is not UNSET: - cookies["{{ parameter.name}}"] = {{ parameter.python_name }} - {% endif %} - {% endfor %} -{% endif %} -{% endmacro %} - {% macro query_params(endpoint) %} {% if endpoint.query_parameters %} @@ -83,12 +70,7 @@ params = {k: v for k, v in params.items() if v is not UNSET and v is not None} {{ parameter.to_string() }}, {% endfor %} *, -{# Proper client based on whether or not the endpoint requires authentication #} -{% if endpoint.requires_security %} -client: AuthenticatedClient, -{% else %} client: Client, -{% endif %} {# Form data if any #} {% if endpoint.form_body %} form_data: {{ endpoint.form_body.get_type_string() }}, @@ -108,10 +90,6 @@ json_body: {{ endpoint.json_body.get_type_string() }}, {% for parameter in endpoint.header_parameters.values() %} {{ parameter.to_string() }}, {% endfor %} -{# cookie parameters #} -{% for parameter in endpoint.cookie_parameters.values() %} -{{ parameter.to_string() }}, -{% endfor %} {% endmacro %} {# Just lists all kwargs to endpoints as name=name for passing to other functions #} @@ -135,9 +113,6 @@ json_body=json_body, {% for parameter in endpoint.header_parameters.values() %} {{ parameter.python_name }}={{ parameter.python_name }}, {% endfor %} -{% for parameter in endpoint.cookie_parameters.values() %} -{{ parameter.python_name }}={{ parameter.python_name }}, -{% endfor %} {% endmacro %} {% macro docstring(endpoint, return_string) %} @@ -160,7 +135,6 @@ Args: {% endif %} Raises: - errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: diff --git a/openapi_python_client/templates/endpoint_module.py.jinja b/openapi_python_client/templates/endpoint_module.py.jinja index 26d313f16..a1e711a68 100644 --- a/openapi_python_client/templates/endpoint_module.py.jinja +++ b/openapi_python_client/templates/endpoint_module.py.jinja @@ -1,9 +1,9 @@ from http import HTTPStatus -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Dict, List, Union, cast import httpx -from ...client import AuthenticatedClient, Client +from ...client import Client from ...types import Response, UNSET from ... import errors @@ -11,7 +11,7 @@ from ... import errors {{ relative }} {% endfor %} -{% from "endpoint_macros.py.jinja" import header_params, cookie_params, query_params, json_body, multipart_body, +{% from "endpoint_macros.py.jinja" import header_params, query_params, json_body, multipart_body, arguments, client, kwargs, parse_response, docstring %} {% set return_string = endpoint.response_type() %} @@ -28,12 +28,9 @@ def _get_kwargs( ) headers: Dict[str, str] = client.get_headers() - cookies: Dict[str, Any] = client.get_cookies() {{ header_params(endpoint) | indent(4) }} - {{ cookie_params(endpoint) | indent(4) }} - {{ query_params(endpoint) | indent(4) }} {{ json_body(endpoint) | indent(4) }} @@ -44,7 +41,6 @@ def _get_kwargs( "method": "{{ endpoint.method }}", "url": url, "headers": headers, - "cookies": cookies, "timeout": client.get_timeout(), {% if endpoint.form_body %} "data": form_data.to_dict(), @@ -59,7 +55,7 @@ def _get_kwargs( } -def _parse_response(*, client: Client, response: httpx.Response) -> Optional[{{ return_string }}]: +def _parse_response(*, client: Client, response: httpx.Response) -> {{ return_string }}: {% for response in endpoint.responses %} if response.status_code == HTTPStatus.{{ response.status_code.name }}: {% if parsed_responses %}{% import "property_templates/" + response.prop.template as prop_template %} @@ -73,10 +69,8 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[{{ return None {% endif %} {% endfor %} - if client.raise_on_unexpected_status: - raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}") else: - return None + raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}", response=response) def _build_response(*, client: Client, response: httpx.Response) -> Response[{{ return_string }}]: @@ -107,7 +101,7 @@ def sync_detailed( {% if parsed_responses %} def sync( {{ arguments(endpoint) | indent(4) }} -) -> Optional[{{ return_string }}]: +) -> {{ return_string }}: {{ docstring(endpoint, return_string) | indent(4) }} return sync_detailed( @@ -134,11 +128,10 @@ async def asyncio_detailed( {% if parsed_responses %} async def asyncio( {{ arguments(endpoint) | indent(4) }} -) -> Optional[{{ return_string }}]: +) -> {{ return_string }}: {{ docstring(endpoint, return_string) | indent(4) }} return (await asyncio_detailed( {{ kwargs(endpoint) }} )).parsed {% endif %} - diff --git a/openapi_python_client/templates/errors.py.jinja b/openapi_python_client/templates/errors.py.jinja index 7445a2dad..8458dd44e 100644 --- a/openapi_python_client/templates/errors.py.jinja +++ b/openapi_python_client/templates/errors.py.jinja @@ -1,7 +1,15 @@ """ Contains shared errors types that can be raised from API functions """ +import httpx + class UnexpectedStatus(Exception): - """ Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True """ - ... + """Raised by api functions when the response status is an undocumented status.""" + + response: httpx.Response + + def __init__(self, *args: object, response: httpx.Response) -> None: + super().__init__(*args) + self.response = response + __all__ = ["UnexpectedStatus"] diff --git a/openapi_python_client/templates/jwt.py.jinja b/openapi_python_client/templates/jwt.py.jinja new file mode 100644 index 000000000..50290c1a6 --- /dev/null +++ b/openapi_python_client/templates/jwt.py.jinja @@ -0,0 +1,46 @@ +import secrets +import time +from datetime import datetime, timedelta, timezone +from threading import Lock + +import jwt + + +class JwtGenerator: + def __init__(self, key: str, kid: str, exp: int): + self.key = key + self.kid = kid + self.exp = exp + + self._expires: float = 0 + self._jwt: str = '' + self._lock = Lock() + + # generate a new token 10 seconds earlier than the 'exp' header to give + # enough time for the request to be made + self._expires_leeway = 10 + + def generate(self) -> str: + if self._needs_refresh(): + with self._lock: + self._generate() + + return self._jwt + + def _needs_refresh(self) -> bool: + return not self._jwt or self._expires < time.monotonic() + + def _generate(self) -> None: + jti = secrets.token_hex(16) + now = datetime.now(tz=timezone.utc) + exp = now + timedelta(seconds=self.exp) + + payload = {"jti": jti, "nbf": now, "exp": exp, "iat": now, "aud": "api"} + headers = {"kid": self.kid} + + self._expires = time.monotonic() + self.exp - self._expires_leeway + + if self._expires < 0: + self._expires = 0 + + self._jwt = jwt.encode(payload=payload, headers=headers, algorithm="ES256", key=self.key) diff --git a/openapi_python_client/templates/package_init.py.jinja b/openapi_python_client/templates/package_init.py.jinja index 366a7e508..362266c25 100644 --- a/openapi_python_client/templates/package_init.py.jinja +++ b/openapi_python_client/templates/package_init.py.jinja @@ -1,7 +1,6 @@ """ {{ package_description }} """ -from .client import AuthenticatedClient, Client +from .client import Client __all__ = ( - "AuthenticatedClient", "Client", ) diff --git a/openapi_python_client/templates/pyproject.toml.jinja b/openapi_python_client/templates/pyproject.toml.jinja index 410d1ebc4..e39011db4 100644 --- a/openapi_python_client/templates/pyproject.toml.jinja +++ b/openapi_python_client/templates/pyproject.toml.jinja @@ -17,6 +17,7 @@ python = "^3.7" httpx = ">=0.15.4,<0.24.0" attrs = ">=21.3.0" python-dateutil = "^2.8.0" +pyjwt = {extras = ["crypto"], version = "^2.6.0"} [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/openapi_python_client/templates/types.py.jinja b/openapi_python_client/templates/types.py.jinja index c746db6e1..82a522c6f 100644 --- a/openapi_python_client/templates/types.py.jinja +++ b/openapi_python_client/templates/types.py.jinja @@ -39,7 +39,7 @@ class Response(Generic[T]): status_code: HTTPStatus content: bytes headers: MutableMapping[str, str] - parsed: Optional[T] + parsed: T __all__ = ["File", "Response", "FileJsonType"]