Skip to content

Commit 1ce3c79

Browse files
committed
chore: Document & test changes
1 parent ab2fc17 commit 1ce3c79

File tree

17 files changed

+740
-48
lines changed

17 files changed

+740
-48
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
default: patch
3+
---
4+
5+
# Always use correct content type for requests
6+
7+
In previous versions, a request body that was similar to a known content type would use that content type in the request. For example `application/json` would be used for `application/vnd.api+json`. This was incorrect and could result in invalid requests being sent.
8+
9+
Now, the content type defined in the OpenAPI document will always be used.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
default: major
3+
---
4+
5+
# Renamed body types and parameters
6+
7+
PR #900 addresses #822.
8+
9+
Where previously there would be one body parameter per supported content type, now there is a single `body` parameter which takes a union of all the possible inputs. This correctly models the fact that only one body can be sent (and ever would be sent) in a request.
10+
11+
For example, when calling a generated endpoint, code which used to look like this:
12+
13+
```python
14+
post_body_multipart.sync_detailed(
15+
client=client,
16+
multipart_data=PostBodyMultipartMultipartData(),
17+
)
18+
```
19+
20+
Will now look like this:
21+
22+
```python
23+
post_body_multipart.sync_detailed(
24+
client=client,
25+
body=PostBodyMultipartBody(),
26+
)
27+
```
28+
29+
Note that both the input parameter name _and_ the class name have changed. This should result in simpler code when there is only a single body type and now produces correct code when there are multiple body types.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
default: minor
3+
---
4+
5+
# Support multiple possible `requestBody`
6+
7+
PR #900 addresses #822.
8+
9+
It is now possible in some circumstances to generate valid code for OpenAPI documents which have multiple possible `requestBody` values. Previously, invalid code could have been generated with no warning (only one body could actually be sent).
10+
11+
Only one content type per "category" is currently supported at a time. The categories are:
12+
13+
- JSON, like `application/json`
14+
- Binary data, like `application/octet-stream`
15+
- Encoded form data, like `application/x-www-form-urlencoded`
16+
- Files, like `multipart/form-data`

end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing import Type
44

5+
from .bodies import BodiesEndpoints
56
from .default import DefaultEndpoints
67
from .location import LocationEndpoints
78
from .naming import NamingEndpoints
@@ -14,6 +15,10 @@
1415

1516

1617
class MyTestApiClientApi:
18+
@classmethod
19+
def bodies(cls) -> Type[BodiesEndpoints]:
20+
return BodiesEndpoints
21+
1722
@classmethod
1823
def tests(cls) -> Type[TestsEndpoints]:
1924
return TestsEndpoints
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
""" Contains methods for accessing the API Endpoints """
2+
3+
import types
4+
5+
from . import json_like, post_bodies_multiple
6+
7+
8+
class BodiesEndpoints:
9+
@classmethod
10+
def post_bodies_multiple(cls) -> types.ModuleType:
11+
"""
12+
Test multiple bodies
13+
"""
14+
return post_bodies_multiple
15+
16+
@classmethod
17+
def json_like(cls) -> types.ModuleType:
18+
"""
19+
A content type that works like json but isn't application/json
20+
"""
21+
return json_like

end_to_end_tests/golden-record/my_test_api_client/api/bodies/__init__.py

Whitespace-only changes.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
from http import HTTPStatus
2+
from typing import Any, Dict, Optional, Union
3+
4+
import httpx
5+
6+
from ... import errors
7+
from ...client import AuthenticatedClient, Client
8+
from ...models.json_like_body import JsonLikeBody
9+
from ...types import Response
10+
11+
12+
def _get_kwargs(
13+
*,
14+
body: JsonLikeBody,
15+
) -> Dict[str, Any]:
16+
headers = {}
17+
18+
_kwargs: Dict[str, Any] = {
19+
"method": "post",
20+
"url": "/bodies/json-like",
21+
}
22+
23+
_body = body.to_dict()
24+
25+
_kwargs["json"] = _body
26+
headers["Content-Type"] = "application/vnd+json"
27+
28+
_kwargs["headers"] = headers
29+
return _kwargs
30+
31+
32+
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
33+
if response.status_code == HTTPStatus.OK:
34+
return None
35+
if client.raise_on_unexpected_status:
36+
raise errors.UnexpectedStatus(response.status_code, response.content)
37+
else:
38+
return None
39+
40+
41+
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Any]:
42+
return Response(
43+
status_code=HTTPStatus(response.status_code),
44+
content=response.content,
45+
headers=response.headers,
46+
parsed=_parse_response(client=client, response=response),
47+
)
48+
49+
50+
def sync_detailed(
51+
*,
52+
client: Union[AuthenticatedClient, Client],
53+
body: JsonLikeBody,
54+
) -> Response[Any]:
55+
"""A content type that works like json but isn't application/json
56+
57+
Args:
58+
body (JsonLikeBody):
59+
60+
Raises:
61+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
62+
httpx.TimeoutException: If the request takes longer than Client.timeout.
63+
64+
Returns:
65+
Response[Any]
66+
"""
67+
68+
kwargs = _get_kwargs(
69+
body=body,
70+
)
71+
72+
response = client.get_httpx_client().request(
73+
**kwargs,
74+
)
75+
76+
return _build_response(client=client, response=response)
77+
78+
79+
async def asyncio_detailed(
80+
*,
81+
client: Union[AuthenticatedClient, Client],
82+
body: JsonLikeBody,
83+
) -> Response[Any]:
84+
"""A content type that works like json but isn't application/json
85+
86+
Args:
87+
body (JsonLikeBody):
88+
89+
Raises:
90+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
91+
httpx.TimeoutException: If the request takes longer than Client.timeout.
92+
93+
Returns:
94+
Response[Any]
95+
"""
96+
97+
kwargs = _get_kwargs(
98+
body=body,
99+
)
100+
101+
response = await client.get_async_httpx_client().request(**kwargs)
102+
103+
return _build_response(client=client, response=response)
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from http import HTTPStatus
2+
from typing import Any, Dict, Optional, Union
3+
4+
import httpx
5+
6+
from ... import errors
7+
from ...client import AuthenticatedClient, Client
8+
from ...models.post_bodies_multiple_data_body import PostBodiesMultipleDataBody
9+
from ...models.post_bodies_multiple_files_body import PostBodiesMultipleFilesBody
10+
from ...models.post_bodies_multiple_json_body import PostBodiesMultipleJsonBody
11+
from ...types import File, Response
12+
13+
14+
def _get_kwargs(
15+
*,
16+
body: Union[
17+
PostBodiesMultipleJsonBody,
18+
File,
19+
PostBodiesMultipleDataBody,
20+
PostBodiesMultipleFilesBody,
21+
],
22+
) -> Dict[str, Any]:
23+
headers = {}
24+
25+
_kwargs: Dict[str, Any] = {
26+
"method": "post",
27+
"url": "/bodies/multiple",
28+
}
29+
30+
if isinstance(body, PostBodiesMultipleJsonBody):
31+
_json_body = body.to_dict()
32+
33+
_kwargs["json"] = _json_body
34+
headers["Content-Type"] = "application/json"
35+
if isinstance(body, File):
36+
_content_body = body.payload
37+
38+
_kwargs["content"] = _content_body
39+
headers["Content-Type"] = "application/octet-stream"
40+
if isinstance(body, PostBodiesMultipleDataBody):
41+
_data_body = body.to_dict()
42+
43+
_kwargs["data"] = _data_body
44+
headers["Content-Type"] = "application/x-www-form-urlencoded"
45+
if isinstance(body, PostBodiesMultipleFilesBody):
46+
_files_body = body.to_multipart()
47+
48+
_kwargs["files"] = _files_body
49+
headers["Content-Type"] = "multipart/form-data"
50+
51+
_kwargs["headers"] = headers
52+
return _kwargs
53+
54+
55+
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
56+
if response.status_code == HTTPStatus.OK:
57+
return None
58+
if client.raise_on_unexpected_status:
59+
raise errors.UnexpectedStatus(response.status_code, response.content)
60+
else:
61+
return None
62+
63+
64+
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Any]:
65+
return Response(
66+
status_code=HTTPStatus(response.status_code),
67+
content=response.content,
68+
headers=response.headers,
69+
parsed=_parse_response(client=client, response=response),
70+
)
71+
72+
73+
def sync_detailed(
74+
*,
75+
client: Union[AuthenticatedClient, Client],
76+
body: Union[
77+
PostBodiesMultipleJsonBody,
78+
File,
79+
PostBodiesMultipleDataBody,
80+
PostBodiesMultipleFilesBody,
81+
],
82+
) -> Response[Any]:
83+
"""Test multiple bodies
84+
85+
Args:
86+
body (PostBodiesMultipleJsonBody):
87+
body (File):
88+
body (PostBodiesMultipleDataBody):
89+
body (PostBodiesMultipleFilesBody):
90+
91+
Raises:
92+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
93+
httpx.TimeoutException: If the request takes longer than Client.timeout.
94+
95+
Returns:
96+
Response[Any]
97+
"""
98+
99+
kwargs = _get_kwargs(
100+
body=body,
101+
)
102+
103+
response = client.get_httpx_client().request(
104+
**kwargs,
105+
)
106+
107+
return _build_response(client=client, response=response)
108+
109+
110+
async def asyncio_detailed(
111+
*,
112+
client: Union[AuthenticatedClient, Client],
113+
body: Union[
114+
PostBodiesMultipleJsonBody,
115+
File,
116+
PostBodiesMultipleDataBody,
117+
PostBodiesMultipleFilesBody,
118+
],
119+
) -> Response[Any]:
120+
"""Test multiple bodies
121+
122+
Args:
123+
body (PostBodiesMultipleJsonBody):
124+
body (File):
125+
body (PostBodiesMultipleDataBody):
126+
body (PostBodiesMultipleFilesBody):
127+
128+
Raises:
129+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
130+
httpx.TimeoutException: If the request takes longer than Client.timeout.
131+
132+
Returns:
133+
Response[Any]
134+
"""
135+
136+
kwargs = _get_kwargs(
137+
body=body,
138+
)
139+
140+
response = await client.get_async_httpx_client().request(**kwargs)
141+
142+
return _build_response(client=client, response=response)

end_to_end_tests/golden-record/my_test_api_client/models/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from .get_location_header_types_string_enum_header import GetLocationHeaderTypesStringEnumHeader
3838
from .http_validation_error import HTTPValidationError
3939
from .import_ import Import
40+
from .json_like_body import JsonLikeBody
4041
from .model_from_all_of import ModelFromAllOf
4142
from .model_name import ModelName
4243
from .model_reference_with_periods import ModelReferenceWithPeriods
@@ -63,6 +64,9 @@
6364
from .model_with_union_property_inlined_fruit_type_0 import ModelWithUnionPropertyInlinedFruitType0
6465
from .model_with_union_property_inlined_fruit_type_1 import ModelWithUnionPropertyInlinedFruitType1
6566
from .none import None_
67+
from .post_bodies_multiple_data_body import PostBodiesMultipleDataBody
68+
from .post_bodies_multiple_files_body import PostBodiesMultipleFilesBody
69+
from .post_bodies_multiple_json_body import PostBodiesMultipleJsonBody
6670
from .post_form_data_inline_body import PostFormDataInlineBody
6771
from .post_naming_property_conflict_with_import_body import PostNamingPropertyConflictWithImportBody
6872
from .post_naming_property_conflict_with_import_response_200 import PostNamingPropertyConflictWithImportResponse200
@@ -106,6 +110,7 @@
106110
"GetLocationHeaderTypesStringEnumHeader",
107111
"HTTPValidationError",
108112
"Import",
113+
"JsonLikeBody",
109114
"ModelFromAllOf",
110115
"ModelName",
111116
"ModelReferenceWithPeriods",
@@ -130,6 +135,9 @@
130135
"ModelWithUnionPropertyInlinedFruitType0",
131136
"ModelWithUnionPropertyInlinedFruitType1",
132137
"None_",
138+
"PostBodiesMultipleDataBody",
139+
"PostBodiesMultipleFilesBody",
140+
"PostBodiesMultipleJsonBody",
133141
"PostFormDataInlineBody",
134142
"PostNamingPropertyConflictWithImportBody",
135143
"PostNamingPropertyConflictWithImportResponse200",

0 commit comments

Comments
 (0)