Skip to content

Commit 83af2ca

Browse files
spacetherrk0n
authored andcommitted
[python-experimental] fixes json + charset use case (OpenAPITools#12114)
* Adds code to detect json content type when charset is also set * Updates template to properly render content type, regenerates samples * Adds test_json_with_charset * Reverts version file * Fixes typo
1 parent ce2dbdc commit 83af2ca

File tree

9 files changed

+327
-16
lines changed

9 files changed

+327
-16
lines changed

modules/openapi-generator/src/main/resources/python-experimental/api_client.handlebars

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,20 @@ class ApiResponseWithoutDeserialization(ApiResponse):
719719
headers: typing.Union[Unset, typing.List[HeaderParameter]] = unset
720720

721721

722-
class OpenApiResponse:
722+
class JSONDetector:
723+
@staticmethod
724+
def content_type_is_json(content_type: str) -> bool:
725+
"""
726+
for when content_type strings also include charset info like:
727+
application/json; charset=UTF-8
728+
"""
729+
content_type_piece = content_type.split(';')[0]
730+
if content_type_piece == 'application/json':
731+
return True
732+
return False
733+
734+
735+
class OpenApiResponse(JSONDetector):
723736
def __init__(
724737
self,
725738
response_cls: typing.Type[ApiResponse] = ApiResponse,
@@ -734,8 +747,8 @@ class OpenApiResponse:
734747

735748
@staticmethod
736749
def __deserialize_json(response: urllib3.HTTPResponse) -> typing.Any:
737-
decoded_data = response.data.decode("utf-8")
738-
return json.loads(decoded_data)
750+
# python must be >= 3.9 so we can pass in bytes into json.loads
751+
return json.loads(response.data)
739752

740753
@staticmethod
741754
def __file_name_from_content_disposition(content_disposition: typing.Optional[str]) -> typing.Optional[str]:
@@ -796,7 +809,7 @@ class OpenApiResponse:
796809
deserialized_body = unset
797810
streamed = response.supports_chunked_reads()
798811
if self.content is not None:
799-
if content_type == 'application/json':
812+
if self.content_type_is_json(content_type):
800813
body_data = self.__deserialize_json(response)
801814
elif content_type == 'application/octet-stream':
802815
body_data = self.__deserialize_application_octet_stream(response)
@@ -1245,7 +1258,7 @@ class SerializedRequestBody(typing.TypedDict, total=False):
12451258
fields: typing.Tuple[typing.Union[RequestField, tuple[str, str]], ...]
12461259

12471260

1248-
class RequestBody(StyleFormSerializer):
1261+
class RequestBody(StyleFormSerializer, JSONDetector):
12491262
"""
12501263
A request body parameter
12511264
content: content_type to MediaType Schema info
@@ -1382,7 +1395,7 @@ class RequestBody(StyleFormSerializer):
13821395
cast_in_data = media_type.schema(in_data)
13831396
# TODO check for and use encoding if it exists
13841397
# and content_type is multipart or application/x-www-form-urlencoded
1385-
if content_type == 'application/json':
1398+
if self.content_type_is_json(content_type):
13861399
return self.__serialize_json(cast_in_data)
13871400
elif content_type == 'text/plain':
13881401
return self.__serialize_text_plain(cast_in_data)

modules/openapi-generator/src/main/resources/python-experimental/endpoint.handlebars

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class RequestCookieParams(RequestRequiredCookieParams, RequestOptionalCookiePara
182182
request_body_{{paramName}} = api_client.RequestBody(
183183
content={
184184
{{#each content}}
185-
'{{@key}}': api_client.MediaType(
185+
'{{{@key}}}': api_client.MediaType(
186186
schema={{this.schema.baseName}}),
187187
{{/each}}
188188
},
@@ -323,7 +323,7 @@ _response_for_{{code}} = api_client.OpenApiResponse(
323323
{{#if @first}}
324324
content={
325325
{{/if}}
326-
'{{@key}}': api_client.MediaType(
326+
'{{{@key}}}': api_client.MediaType(
327327
schema={{this.schema.baseName}}),
328328
{{#if @last}}
329329
},
@@ -351,7 +351,7 @@ _status_code_to_response = {
351351
{{#if @first}}
352352
_all_accept_content_types = (
353353
{{/if}}
354-
'{{this.mediaType}}',
354+
'{{{this.mediaType}}}',
355355
{{#if @last}}
356356
)
357357
{{/if}}
@@ -382,7 +382,7 @@ class {{operationIdCamelCase}}(api_client.Api):
382382
{{#with bodyParam}}
383383
{{#each content}}
384384
{{#if @first}}
385-
content_type: str = '{{@key}}',
385+
content_type: str = '{{{@key}}}',
386386
{{/if}}
387387
{{/each}}
388388
{{/with}}

modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,22 @@ paths:
15301530
responses:
15311531
'200':
15321532
description: ok
1533+
'/fake/jsonWithCharset':
1534+
post:
1535+
tags:
1536+
- fake
1537+
summary: json with charset tx and rx
1538+
operationId: jsonWithCharset
1539+
requestBody:
1540+
content:
1541+
application/json; charset=utf-8:
1542+
schema: {}
1543+
responses:
1544+
200:
1545+
description: success
1546+
content:
1547+
application/json; charset=utf-8:
1548+
schema: {}
15331549
servers:
15341550
- url: 'http://{server}.swagger.io:{port}/v2'
15351551
description: petstore server

samples/openapi3/client/petstore/python-experimental/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ Class | Method | HTTP request | Description
100100
*FakeApi* | [**inline_additional_properties**](docs/FakeApi.md#inline_additional_properties) | **POST** /fake/inline-additionalProperties | test inline additionalProperties
101101
*FakeApi* | [**inline_composition**](docs/FakeApi.md#inline_composition) | **POST** /fake/inlineComposition/ | testing composed schemas at inline locations
102102
*FakeApi* | [**json_form_data**](docs/FakeApi.md#json_form_data) | **GET** /fake/jsonFormData | test json serialization of form data
103+
*FakeApi* | [**json_with_charset**](docs/FakeApi.md#json_with_charset) | **POST** /fake/jsonWithCharset | json with charset tx and rx
103104
*FakeApi* | [**mammal**](docs/FakeApi.md#mammal) | **POST** /fake/refs/mammal |
104105
*FakeApi* | [**number_with_validations**](docs/FakeApi.md#number_with_validations) | **POST** /fake/refs/number |
105106
*FakeApi* | [**object_in_query**](docs/FakeApi.md#object_in_query) | **GET** /fake/objInQuery | user list

samples/openapi3/client/petstore/python-experimental/docs/FakeApi.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Method | HTTP request | Description
2020
[**inline_additional_properties**](FakeApi.md#inline_additional_properties) | **POST** /fake/inline-additionalProperties | test inline additionalProperties
2121
[**inline_composition**](FakeApi.md#inline_composition) | **POST** /fake/inlineComposition/ | testing composed schemas at inline locations
2222
[**json_form_data**](FakeApi.md#json_form_data) | **GET** /fake/jsonFormData | test json serialization of form data
23+
[**json_with_charset**](FakeApi.md#json_with_charset) | **POST** /fake/jsonWithCharset | json with charset tx and rx
2324
[**mammal**](FakeApi.md#mammal) | **POST** /fake/refs/mammal |
2425
[**number_with_validations**](FakeApi.md#number_with_validations) | **POST** /fake/refs/number |
2526
[**object_in_query**](FakeApi.md#object_in_query) | **GET** /fake/objInQuery | user list
@@ -1618,6 +1619,87 @@ No authorization required
16181619

16191620
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
16201621

1622+
# **json_with_charset**
1623+
> bool, date, datetime, dict, float, int, list, str, none_type json_with_charset()
1624+
1625+
json with charset tx and rx
1626+
1627+
### Example
1628+
1629+
```python
1630+
import petstore_api
1631+
from petstore_api.api import fake_api
1632+
from pprint import pprint
1633+
# Defining the host is optional and defaults to http://petstore.swagger.io:80/v2
1634+
# See configuration.py for a list of all supported configuration parameters.
1635+
configuration = petstore_api.Configuration(
1636+
host = "http://petstore.swagger.io:80/v2"
1637+
)
1638+
1639+
# Enter a context with an instance of the API client
1640+
with petstore_api.ApiClient(configuration) as api_client:
1641+
# Create an instance of the API class
1642+
api_instance = fake_api.FakeApi(api_client)
1643+
1644+
# example passing only optional values
1645+
body = None
1646+
try:
1647+
# json with charset tx and rx
1648+
api_response = api_instance.json_with_charset(
1649+
body=body,
1650+
)
1651+
pprint(api_response)
1652+
except petstore_api.ApiException as e:
1653+
print("Exception when calling FakeApi->json_with_charset: %s\n" % e)
1654+
```
1655+
### Parameters
1656+
1657+
Name | Type | Description | Notes
1658+
------------- | ------------- | ------------- | -------------
1659+
body | typing.Union[SchemaForRequestBodyApplicationJsonCharsetutf8, Unset] | optional, default is unset |
1660+
content_type | str | optional, default is 'application/json; charset=utf-8' | Selects the schema and serialization of the request body
1661+
accept_content_types | typing.Tuple[str] | default is ('application/json; charset=utf-8', ) | Tells the server the content type(s) that are accepted by the client
1662+
stream | bool | default is False | if True then the response.content will be streamed and loaded from a file like object. When downloading a file, set this to True to force the code to deserialize the content to a FileSchema file
1663+
timeout | typing.Optional[typing.Union[int, typing.Tuple]] | default is None | the timeout used by the rest client
1664+
skip_deserialization | bool | default is False | when True, headers and body will be unset and an instance of api_client.ApiResponseWithoutDeserialization will be returned
1665+
1666+
### body
1667+
1668+
#### SchemaForRequestBodyApplicationJsonCharsetutf8
1669+
1670+
Type | Description | Notes
1671+
------------- | ------------- | -------------
1672+
typing.Union[dict, frozendict, str, date, datetime, int, float, bool, Decimal, None, list, tuple, bytes] | |
1673+
1674+
### Return Types, Responses
1675+
1676+
Code | Class | Description
1677+
------------- | ------------- | -------------
1678+
n/a | api_client.ApiResponseWithoutDeserialization | When skip_deserialization is True this response is returned
1679+
200 | ApiResponseFor200 | success
1680+
1681+
#### ApiResponseFor200
1682+
Name | Type | Description | Notes
1683+
------------- | ------------- | ------------- | -------------
1684+
response | urllib3.HTTPResponse | Raw response |
1685+
body | typing.Union[SchemaFor200ResponseBodyApplicationJsonCharsetutf8, ] | |
1686+
headers | Unset | headers were not defined |
1687+
1688+
#### SchemaFor200ResponseBodyApplicationJsonCharsetutf8
1689+
1690+
Type | Description | Notes
1691+
------------- | ------------- | -------------
1692+
typing.Union[dict, frozendict, str, date, datetime, int, float, bool, Decimal, None, list, tuple, bytes] | |
1693+
1694+
1695+
**bool, date, datetime, dict, float, int, list, str, none_type**
1696+
1697+
### Authorization
1698+
1699+
No authorization required
1700+
1701+
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
1702+
16211703
# **mammal**
16221704
> Mammal mammal(mammal)
16231705

samples/openapi3/client/petstore/python-experimental/petstore_api/api/fake_api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from petstore_api.api.fake_api_endpoints.inline_additional_properties import InlineAdditionalProperties
2727
from petstore_api.api.fake_api_endpoints.inline_composition import InlineComposition
2828
from petstore_api.api.fake_api_endpoints.json_form_data import JsonFormData
29+
from petstore_api.api.fake_api_endpoints.json_with_charset import JsonWithCharset
2930
from petstore_api.api.fake_api_endpoints.mammal import Mammal
3031
from petstore_api.api.fake_api_endpoints.number_with_validations import NumberWithValidations
3132
from petstore_api.api.fake_api_endpoints.object_in_query import ObjectInQuery
@@ -57,6 +58,7 @@ class FakeApi(
5758
InlineAdditionalProperties,
5859
InlineComposition,
5960
JsonFormData,
61+
JsonWithCharset,
6062
Mammal,
6163
NumberWithValidations,
6264
ObjectInQuery,

0 commit comments

Comments
 (0)