forked from loafle/openapi-generator-original
[python-experimental] Allow response media types to omit schema (#12135)
* Adds issue spec file and attemts to generate code from it * Adds missing schema definitions * Skips fromProperty invocation if the passed in schema is none in getContent * Makes MediaType.schema optional * Adds checking that the content type is in self.content * Sets ApiResponse body type as Unset if there is no schema for it * Handles schema = None case * Adds endpoint without response schema * Reverts version files * Adds test_response_without_schema
This commit is contained in:
parent
7851dfe148
commit
1deaaa88fe
@ -6753,7 +6753,10 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
String contentType = contentEntry.getKey();
|
String contentType = contentEntry.getKey();
|
||||||
CodegenProperty schemaProp = fromProperty(toMediaTypeSchemaName(contentType, mediaTypeSchemaSuffix), mt.getSchema());
|
CodegenProperty schemaProp = null;
|
||||||
|
if (mt.getSchema() != null) {
|
||||||
|
schemaProp = fromProperty(toMediaTypeSchemaName(contentType, mediaTypeSchemaSuffix), mt.getSchema());
|
||||||
|
}
|
||||||
CodegenMediaType codegenMt = new CodegenMediaType(schemaProp, ceMap);
|
CodegenMediaType codegenMt = new CodegenMediaType(schemaProp, ceMap);
|
||||||
cmtContent.put(contentType, codegenMt);
|
cmtContent.put(contentType, codegenMt);
|
||||||
if (schemaProp != null) {
|
if (schemaProp != null) {
|
||||||
|
@ -673,6 +673,7 @@ class Encoding:
|
|||||||
self.allow_reserved = allow_reserved
|
self.allow_reserved = allow_reserved
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class MediaType:
|
class MediaType:
|
||||||
"""
|
"""
|
||||||
Used to store request and response body schema information
|
Used to store request and response body schema information
|
||||||
@ -682,14 +683,8 @@ class MediaType:
|
|||||||
The encoding object SHALL only apply to requestBody objects when the media type is
|
The encoding object SHALL only apply to requestBody objects when the media type is
|
||||||
multipart or application/x-www-form-urlencoded.
|
multipart or application/x-www-form-urlencoded.
|
||||||
"""
|
"""
|
||||||
|
schema: typing.Optional[typing.Type[Schema]] = None
|
||||||
def __init__(
|
encoding: typing.Optional[typing.Dict[str, Encoding]] = None
|
||||||
self,
|
|
||||||
schema: typing.Type[Schema],
|
|
||||||
encoding: typing.Optional[typing.Dict[str, Encoding]] = None,
|
|
||||||
):
|
|
||||||
self.schema = schema
|
|
||||||
self.encoding = encoding
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -808,7 +803,27 @@ class OpenApiResponse(JSONDetector):
|
|||||||
content_type = response.getheader('content-type')
|
content_type = response.getheader('content-type')
|
||||||
deserialized_body = unset
|
deserialized_body = unset
|
||||||
streamed = response.supports_chunked_reads()
|
streamed = response.supports_chunked_reads()
|
||||||
|
|
||||||
|
deserialized_headers = unset
|
||||||
|
if self.headers is not None:
|
||||||
|
# TODO add header deserialiation here
|
||||||
|
pass
|
||||||
|
|
||||||
if self.content is not None:
|
if self.content is not None:
|
||||||
|
if content_type not in self.content:
|
||||||
|
raise ApiValueError(
|
||||||
|
f'Invalid content_type={content_type} returned for response with '
|
||||||
|
'status_code={str(response.status)}'
|
||||||
|
)
|
||||||
|
body_schema = self.content[content_type].schema
|
||||||
|
if body_schema is None:
|
||||||
|
# some specs do not define response content media type schemas
|
||||||
|
return self.response_cls(
|
||||||
|
response=response,
|
||||||
|
headers=deserialized_headers,
|
||||||
|
body=unset
|
||||||
|
)
|
||||||
|
|
||||||
if self.content_type_is_json(content_type):
|
if self.content_type_is_json(content_type):
|
||||||
body_data = self.__deserialize_json(response)
|
body_data = self.__deserialize_json(response)
|
||||||
elif content_type == 'application/octet-stream':
|
elif content_type == 'application/octet-stream':
|
||||||
@ -818,16 +833,11 @@ class OpenApiResponse(JSONDetector):
|
|||||||
content_type = 'multipart/form-data'
|
content_type = 'multipart/form-data'
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError('Deserialization of {} has not yet been implemented'.format(content_type))
|
raise NotImplementedError('Deserialization of {} has not yet been implemented'.format(content_type))
|
||||||
body_schema = self.content[content_type].schema
|
|
||||||
deserialized_body = body_schema._from_openapi_data(
|
deserialized_body = body_schema._from_openapi_data(
|
||||||
body_data, _configuration=configuration)
|
body_data, _configuration=configuration)
|
||||||
elif streamed:
|
elif streamed:
|
||||||
response.release_conn()
|
response.release_conn()
|
||||||
|
|
||||||
deserialized_headers = unset
|
|
||||||
if self.headers is not None:
|
|
||||||
deserialized_headers = unset
|
|
||||||
|
|
||||||
return self.response_cls(
|
return self.response_cls(
|
||||||
response=response,
|
response=response,
|
||||||
headers=deserialized_headers,
|
headers=deserialized_headers,
|
||||||
|
@ -175,7 +175,7 @@ default | ApiResponseForDefault | {{message}}
|
|||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------- | ------------- | ------------- | -------------
|
------------- | ------------- | ------------- | -------------
|
||||||
response | urllib3.HTTPResponse | Raw response |
|
response | urllib3.HTTPResponse | Raw response |
|
||||||
body | {{#unless content}}Unset{{else}}typing.Union[{{#each content}}{{this.schema.baseName}}, {{/each}}]{{/unless}} | {{#unless content}}body was not defined{{/unless}} |
|
body | {{#unless content}}Unset{{else}}typing.Union[{{#each content}}{{#if this.schema}}{{this.schema.baseName}}{{else}}Unset{{/if}}, {{/each}}]{{/unless}} | {{#unless content}}body was not defined{{/unless}} |
|
||||||
headers | {{#unless responseHeaders}}Unset{{else}}ResponseHeadersFor{{code}}{{/unless}} | {{#unless responseHeaders}}headers were not defined{{/unless}} |
|
headers | {{#unless responseHeaders}}Unset{{else}}ResponseHeadersFor{{code}}{{/unless}} | {{#unless responseHeaders}}headers were not defined{{/unless}} |
|
||||||
{{#each content}}
|
{{#each content}}
|
||||||
{{#with this.schema}}
|
{{#with this.schema}}
|
||||||
|
@ -182,8 +182,8 @@ class RequestCookieParams(RequestRequiredCookieParams, RequestOptionalCookiePara
|
|||||||
request_body_{{paramName}} = api_client.RequestBody(
|
request_body_{{paramName}} = api_client.RequestBody(
|
||||||
content={
|
content={
|
||||||
{{#each content}}
|
{{#each content}}
|
||||||
'{{{@key}}}': api_client.MediaType(
|
'{{{@key}}}': api_client.MediaType({{#if this.schema}}
|
||||||
schema={{this.schema.baseName}}),
|
schema={{this.schema.baseName}}{{/if}}),
|
||||||
{{/each}}
|
{{/each}}
|
||||||
},
|
},
|
||||||
{{#if required}}
|
{{#if required}}
|
||||||
@ -285,7 +285,11 @@ class ApiResponseFor{{code}}(api_client.ApiResponse):
|
|||||||
{{#and responseHeaders content}}
|
{{#and responseHeaders content}}
|
||||||
body: typing.Union[
|
body: typing.Union[
|
||||||
{{#each content}}
|
{{#each content}}
|
||||||
|
{{#if this.schema}}
|
||||||
{{this.schema.baseName}},
|
{{this.schema.baseName}},
|
||||||
|
{{else}}
|
||||||
|
Unset,
|
||||||
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
]
|
]
|
||||||
headers: ResponseHeadersFor{{code}}
|
headers: ResponseHeadersFor{{code}}
|
||||||
@ -297,7 +301,11 @@ class ApiResponseFor{{code}}(api_client.ApiResponse):
|
|||||||
{{else}}
|
{{else}}
|
||||||
body: typing.Union[
|
body: typing.Union[
|
||||||
{{#each content}}
|
{{#each content}}
|
||||||
|
{{#if this.schema}}
|
||||||
{{this.schema.baseName}},
|
{{this.schema.baseName}},
|
||||||
|
{{else}}
|
||||||
|
Unset,
|
||||||
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
]
|
]
|
||||||
headers: Unset = unset
|
headers: Unset = unset
|
||||||
@ -323,8 +331,8 @@ _response_for_{{code}} = api_client.OpenApiResponse(
|
|||||||
{{#if @first}}
|
{{#if @first}}
|
||||||
content={
|
content={
|
||||||
{{/if}}
|
{{/if}}
|
||||||
'{{{@key}}}': api_client.MediaType(
|
'{{{@key}}}': api_client.MediaType({{#if this.schema}}
|
||||||
schema={{this.schema.baseName}}),
|
schema={{this.schema.baseName}}{{/if}}),
|
||||||
{{#if @last}}
|
{{#if @last}}
|
||||||
},
|
},
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -1546,6 +1546,18 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json; charset=utf-8:
|
application/json; charset=utf-8:
|
||||||
schema: {}
|
schema: {}
|
||||||
|
"/fake/responseWithoutSchema":
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- fake
|
||||||
|
summary: receives a response without schema
|
||||||
|
operationId: responseWithoutSchema
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: contents without schema definition
|
||||||
|
content:
|
||||||
|
application/json: {}
|
||||||
|
application/xml: {}
|
||||||
servers:
|
servers:
|
||||||
- url: 'http://{server}.swagger.io:{port}/v2'
|
- url: 'http://{server}.swagger.io:{port}/v2'
|
||||||
description: petstore server
|
description: petstore server
|
||||||
|
@ -108,6 +108,7 @@ Class | Method | HTTP request | Description
|
|||||||
*FakeApi* | [**parameter_collisions**](docs/FakeApi.md#parameter_collisions) | **POST** /fake/parameterCollisions/{1}/{aB}/{Ab}/{self}/{A-B}/ | parameter collision case
|
*FakeApi* | [**parameter_collisions**](docs/FakeApi.md#parameter_collisions) | **POST** /fake/parameterCollisions/{1}/{aB}/{Ab}/{self}/{A-B}/ | parameter collision case
|
||||||
*FakeApi* | [**query_parameter_collection_format**](docs/FakeApi.md#query_parameter_collection_format) | **PUT** /fake/test-query-paramters |
|
*FakeApi* | [**query_parameter_collection_format**](docs/FakeApi.md#query_parameter_collection_format) | **PUT** /fake/test-query-paramters |
|
||||||
*FakeApi* | [**ref_object_in_query**](docs/FakeApi.md#ref_object_in_query) | **GET** /fake/refObjInQuery | user list
|
*FakeApi* | [**ref_object_in_query**](docs/FakeApi.md#ref_object_in_query) | **GET** /fake/refObjInQuery | user list
|
||||||
|
*FakeApi* | [**response_without_schema**](docs/FakeApi.md#response_without_schema) | **GET** /fake/responseWithoutSchema | receives a response without schema
|
||||||
*FakeApi* | [**string**](docs/FakeApi.md#string) | **POST** /fake/refs/string |
|
*FakeApi* | [**string**](docs/FakeApi.md#string) | **POST** /fake/refs/string |
|
||||||
*FakeApi* | [**string_enum**](docs/FakeApi.md#string_enum) | **POST** /fake/refs/enum |
|
*FakeApi* | [**string_enum**](docs/FakeApi.md#string_enum) | **POST** /fake/refs/enum |
|
||||||
*FakeApi* | [**upload_download_file**](docs/FakeApi.md#upload_download_file) | **POST** /fake/uploadDownloadFile | uploads a file and downloads a file using application/octet-stream
|
*FakeApi* | [**upload_download_file**](docs/FakeApi.md#upload_download_file) | **POST** /fake/uploadDownloadFile | uploads a file and downloads a file using application/octet-stream
|
||||||
|
@ -28,6 +28,7 @@ Method | HTTP request | Description
|
|||||||
[**parameter_collisions**](FakeApi.md#parameter_collisions) | **POST** /fake/parameterCollisions/{1}/{aB}/{Ab}/{self}/{A-B}/ | parameter collision case
|
[**parameter_collisions**](FakeApi.md#parameter_collisions) | **POST** /fake/parameterCollisions/{1}/{aB}/{Ab}/{self}/{A-B}/ | parameter collision case
|
||||||
[**query_parameter_collection_format**](FakeApi.md#query_parameter_collection_format) | **PUT** /fake/test-query-paramters |
|
[**query_parameter_collection_format**](FakeApi.md#query_parameter_collection_format) | **PUT** /fake/test-query-paramters |
|
||||||
[**ref_object_in_query**](FakeApi.md#ref_object_in_query) | **GET** /fake/refObjInQuery | user list
|
[**ref_object_in_query**](FakeApi.md#ref_object_in_query) | **GET** /fake/refObjInQuery | user list
|
||||||
|
[**response_without_schema**](FakeApi.md#response_without_schema) | **GET** /fake/responseWithoutSchema | receives a response without schema
|
||||||
[**string**](FakeApi.md#string) | **POST** /fake/refs/string |
|
[**string**](FakeApi.md#string) | **POST** /fake/refs/string |
|
||||||
[**string_enum**](FakeApi.md#string_enum) | **POST** /fake/refs/enum |
|
[**string_enum**](FakeApi.md#string_enum) | **POST** /fake/refs/enum |
|
||||||
[**upload_download_file**](FakeApi.md#upload_download_file) | **POST** /fake/uploadDownloadFile | uploads a file and downloads a file using application/octet-stream
|
[**upload_download_file**](FakeApi.md#upload_download_file) | **POST** /fake/uploadDownloadFile | uploads a file and downloads a file using application/octet-stream
|
||||||
@ -2547,6 +2548,61 @@ body | Unset | body was not defined |
|
|||||||
headers | Unset | headers were not defined |
|
headers | Unset | headers were not defined |
|
||||||
|
|
||||||
|
|
||||||
|
void (empty response body)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
No authorization required
|
||||||
|
|
||||||
|
[[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)
|
||||||
|
|
||||||
|
# **response_without_schema**
|
||||||
|
> response_without_schema()
|
||||||
|
|
||||||
|
receives a response without schema
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```python
|
||||||
|
import petstore_api
|
||||||
|
from petstore_api.api import fake_api
|
||||||
|
from pprint import pprint
|
||||||
|
# Defining the host is optional and defaults to http://petstore.swagger.io:80/v2
|
||||||
|
# See configuration.py for a list of all supported configuration parameters.
|
||||||
|
configuration = petstore_api.Configuration(
|
||||||
|
host = "http://petstore.swagger.io:80/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Enter a context with an instance of the API client
|
||||||
|
with petstore_api.ApiClient(configuration) as api_client:
|
||||||
|
# Create an instance of the API class
|
||||||
|
api_instance = fake_api.FakeApi(api_client)
|
||||||
|
|
||||||
|
# example, this endpoint has no required or optional parameters
|
||||||
|
try:
|
||||||
|
# receives a response without schema
|
||||||
|
api_response = api_instance.response_without_schema()
|
||||||
|
except petstore_api.ApiException as e:
|
||||||
|
print("Exception when calling FakeApi->response_without_schema: %s\n" % e)
|
||||||
|
```
|
||||||
|
### Parameters
|
||||||
|
This endpoint does not need any parameter.
|
||||||
|
|
||||||
|
### Return Types, Responses
|
||||||
|
|
||||||
|
Code | Class | Description
|
||||||
|
------------- | ------------- | -------------
|
||||||
|
n/a | api_client.ApiResponseWithoutDeserialization | When skip_deserialization is True this response is returned
|
||||||
|
200 | ApiResponseFor200 | contents without schema definition
|
||||||
|
|
||||||
|
#### ApiResponseFor200
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------- | ------------- | ------------- | -------------
|
||||||
|
response | urllib3.HTTPResponse | Raw response |
|
||||||
|
body | typing.Union[Unset, Unset, ] | |
|
||||||
|
headers | Unset | headers were not defined |
|
||||||
|
|
||||||
|
|
||||||
void (empty response body)
|
void (empty response body)
|
||||||
|
|
||||||
### Authorization
|
### Authorization
|
||||||
|
@ -34,6 +34,7 @@ from petstore_api.api.fake_api_endpoints.object_model_with_ref_props import Obje
|
|||||||
from petstore_api.api.fake_api_endpoints.parameter_collisions import ParameterCollisions
|
from petstore_api.api.fake_api_endpoints.parameter_collisions import ParameterCollisions
|
||||||
from petstore_api.api.fake_api_endpoints.query_parameter_collection_format import QueryParameterCollectionFormat
|
from petstore_api.api.fake_api_endpoints.query_parameter_collection_format import QueryParameterCollectionFormat
|
||||||
from petstore_api.api.fake_api_endpoints.ref_object_in_query import RefObjectInQuery
|
from petstore_api.api.fake_api_endpoints.ref_object_in_query import RefObjectInQuery
|
||||||
|
from petstore_api.api.fake_api_endpoints.response_without_schema import ResponseWithoutSchema
|
||||||
from petstore_api.api.fake_api_endpoints.string import String
|
from petstore_api.api.fake_api_endpoints.string import String
|
||||||
from petstore_api.api.fake_api_endpoints.string_enum import StringEnum
|
from petstore_api.api.fake_api_endpoints.string_enum import StringEnum
|
||||||
from petstore_api.api.fake_api_endpoints.upload_download_file import UploadDownloadFile
|
from petstore_api.api.fake_api_endpoints.upload_download_file import UploadDownloadFile
|
||||||
@ -66,6 +67,7 @@ class FakeApi(
|
|||||||
ParameterCollisions,
|
ParameterCollisions,
|
||||||
QueryParameterCollectionFormat,
|
QueryParameterCollectionFormat,
|
||||||
RefObjectInQuery,
|
RefObjectInQuery,
|
||||||
|
ResponseWithoutSchema,
|
||||||
String,
|
String,
|
||||||
StringEnum,
|
StringEnum,
|
||||||
UploadDownloadFile,
|
UploadDownloadFile,
|
||||||
|
@ -0,0 +1,139 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
Generated by: https://openapi-generator.tech
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import re # noqa: F401
|
||||||
|
import sys # noqa: F401
|
||||||
|
import typing
|
||||||
|
import urllib3
|
||||||
|
from urllib3._collections import HTTPHeaderDict
|
||||||
|
|
||||||
|
from petstore_api import api_client, exceptions
|
||||||
|
import decimal # noqa: F401
|
||||||
|
from datetime import date, datetime # noqa: F401
|
||||||
|
from frozendict import frozendict # noqa: F401
|
||||||
|
|
||||||
|
from petstore_api.schemas import ( # noqa: F401
|
||||||
|
AnyTypeSchema,
|
||||||
|
ComposedSchema,
|
||||||
|
DictSchema,
|
||||||
|
ListSchema,
|
||||||
|
StrSchema,
|
||||||
|
IntSchema,
|
||||||
|
Int32Schema,
|
||||||
|
Int64Schema,
|
||||||
|
Float32Schema,
|
||||||
|
Float64Schema,
|
||||||
|
NumberSchema,
|
||||||
|
DateSchema,
|
||||||
|
DateTimeSchema,
|
||||||
|
DecimalSchema,
|
||||||
|
BoolSchema,
|
||||||
|
BinarySchema,
|
||||||
|
NoneSchema,
|
||||||
|
none_type,
|
||||||
|
Configuration,
|
||||||
|
Unset,
|
||||||
|
unset,
|
||||||
|
ComposedBase,
|
||||||
|
ListBase,
|
||||||
|
DictBase,
|
||||||
|
NoneBase,
|
||||||
|
StrBase,
|
||||||
|
IntBase,
|
||||||
|
Int32Base,
|
||||||
|
Int64Base,
|
||||||
|
Float32Base,
|
||||||
|
Float64Base,
|
||||||
|
NumberBase,
|
||||||
|
DateBase,
|
||||||
|
DateTimeBase,
|
||||||
|
BoolBase,
|
||||||
|
BinaryBase,
|
||||||
|
Schema,
|
||||||
|
_SchemaValidator,
|
||||||
|
_SchemaTypeChecker,
|
||||||
|
_SchemaEnumMaker
|
||||||
|
)
|
||||||
|
|
||||||
|
_path = '/fake/responseWithoutSchema'
|
||||||
|
_method = 'GET'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ApiResponseFor200(api_client.ApiResponse):
|
||||||
|
response: urllib3.HTTPResponse
|
||||||
|
body: typing.Union[
|
||||||
|
Unset,
|
||||||
|
Unset,
|
||||||
|
]
|
||||||
|
headers: Unset = unset
|
||||||
|
|
||||||
|
|
||||||
|
_response_for_200 = api_client.OpenApiResponse(
|
||||||
|
response_cls=ApiResponseFor200,
|
||||||
|
content={
|
||||||
|
'application/json': api_client.MediaType(),
|
||||||
|
'application/xml': api_client.MediaType(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
_status_code_to_response = {
|
||||||
|
'200': _response_for_200,
|
||||||
|
}
|
||||||
|
_all_accept_content_types = (
|
||||||
|
'application/json',
|
||||||
|
'application/xml',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseWithoutSchema(api_client.Api):
|
||||||
|
|
||||||
|
def response_without_schema(
|
||||||
|
self: api_client.Api,
|
||||||
|
accept_content_types: typing.Tuple[str] = _all_accept_content_types,
|
||||||
|
stream: bool = False,
|
||||||
|
timeout: typing.Optional[typing.Union[int, typing.Tuple]] = None,
|
||||||
|
skip_deserialization: bool = False,
|
||||||
|
) -> typing.Union[
|
||||||
|
ApiResponseFor200,
|
||||||
|
api_client.ApiResponseWithoutDeserialization
|
||||||
|
]:
|
||||||
|
"""
|
||||||
|
receives a response without schema
|
||||||
|
:param skip_deserialization: If true then api_response.response will be set but
|
||||||
|
api_response.body and api_response.headers will not be deserialized into schema
|
||||||
|
class instances
|
||||||
|
"""
|
||||||
|
|
||||||
|
_headers = HTTPHeaderDict()
|
||||||
|
# TODO add cookie handling
|
||||||
|
if accept_content_types:
|
||||||
|
for accept_content_type in accept_content_types:
|
||||||
|
_headers.add('Accept', accept_content_type)
|
||||||
|
|
||||||
|
response = self.api_client.call_api(
|
||||||
|
resource_path=_path,
|
||||||
|
method=_method,
|
||||||
|
headers=_headers,
|
||||||
|
stream=stream,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
if skip_deserialization:
|
||||||
|
api_response = api_client.ApiResponseWithoutDeserialization(response=response)
|
||||||
|
else:
|
||||||
|
response_for_status = _status_code_to_response.get(str(response.status))
|
||||||
|
if response_for_status:
|
||||||
|
api_response = response_for_status.deserialize(response, self.api_client.configuration)
|
||||||
|
else:
|
||||||
|
api_response = api_client.ApiResponseWithoutDeserialization(response=response)
|
||||||
|
|
||||||
|
if not 200 <= response.status <= 299:
|
||||||
|
raise exceptions.ApiException(api_response=api_response)
|
||||||
|
|
||||||
|
return api_response
|
@ -677,6 +677,7 @@ class Encoding:
|
|||||||
self.allow_reserved = allow_reserved
|
self.allow_reserved = allow_reserved
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class MediaType:
|
class MediaType:
|
||||||
"""
|
"""
|
||||||
Used to store request and response body schema information
|
Used to store request and response body schema information
|
||||||
@ -686,14 +687,8 @@ class MediaType:
|
|||||||
The encoding object SHALL only apply to requestBody objects when the media type is
|
The encoding object SHALL only apply to requestBody objects when the media type is
|
||||||
multipart or application/x-www-form-urlencoded.
|
multipart or application/x-www-form-urlencoded.
|
||||||
"""
|
"""
|
||||||
|
schema: typing.Optional[typing.Type[Schema]] = None
|
||||||
def __init__(
|
encoding: typing.Optional[typing.Dict[str, Encoding]] = None
|
||||||
self,
|
|
||||||
schema: typing.Type[Schema],
|
|
||||||
encoding: typing.Optional[typing.Dict[str, Encoding]] = None,
|
|
||||||
):
|
|
||||||
self.schema = schema
|
|
||||||
self.encoding = encoding
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -812,7 +807,27 @@ class OpenApiResponse(JSONDetector):
|
|||||||
content_type = response.getheader('content-type')
|
content_type = response.getheader('content-type')
|
||||||
deserialized_body = unset
|
deserialized_body = unset
|
||||||
streamed = response.supports_chunked_reads()
|
streamed = response.supports_chunked_reads()
|
||||||
|
|
||||||
|
deserialized_headers = unset
|
||||||
|
if self.headers is not None:
|
||||||
|
# TODO add header deserialiation here
|
||||||
|
pass
|
||||||
|
|
||||||
if self.content is not None:
|
if self.content is not None:
|
||||||
|
if content_type not in self.content:
|
||||||
|
raise ApiValueError(
|
||||||
|
f'Invalid content_type={content_type} returned for response with '
|
||||||
|
'status_code={str(response.status)}'
|
||||||
|
)
|
||||||
|
body_schema = self.content[content_type].schema
|
||||||
|
if body_schema is None:
|
||||||
|
# some specs do not define response content media type schemas
|
||||||
|
return self.response_cls(
|
||||||
|
response=response,
|
||||||
|
headers=deserialized_headers,
|
||||||
|
body=unset
|
||||||
|
)
|
||||||
|
|
||||||
if self.content_type_is_json(content_type):
|
if self.content_type_is_json(content_type):
|
||||||
body_data = self.__deserialize_json(response)
|
body_data = self.__deserialize_json(response)
|
||||||
elif content_type == 'application/octet-stream':
|
elif content_type == 'application/octet-stream':
|
||||||
@ -822,16 +837,11 @@ class OpenApiResponse(JSONDetector):
|
|||||||
content_type = 'multipart/form-data'
|
content_type = 'multipart/form-data'
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError('Deserialization of {} has not yet been implemented'.format(content_type))
|
raise NotImplementedError('Deserialization of {} has not yet been implemented'.format(content_type))
|
||||||
body_schema = self.content[content_type].schema
|
|
||||||
deserialized_body = body_schema._from_openapi_data(
|
deserialized_body = body_schema._from_openapi_data(
|
||||||
body_data, _configuration=configuration)
|
body_data, _configuration=configuration)
|
||||||
elif streamed:
|
elif streamed:
|
||||||
response.release_conn()
|
response.release_conn()
|
||||||
|
|
||||||
deserialized_headers = unset
|
|
||||||
if self.headers is not None:
|
|
||||||
deserialized_headers = unset
|
|
||||||
|
|
||||||
return self.response_cls(
|
return self.response_cls(
|
||||||
response=response,
|
response=response,
|
||||||
headers=deserialized_headers,
|
headers=deserialized_headers,
|
||||||
|
@ -70,29 +70,34 @@ class TestFakeApi(unittest.TestCase):
|
|||||||
def __assert_request_called_with(
|
def __assert_request_called_with(
|
||||||
mock_request,
|
mock_request,
|
||||||
url: str,
|
url: str,
|
||||||
|
method: str = 'POST',
|
||||||
body: typing.Optional[bytes] = None,
|
body: typing.Optional[bytes] = None,
|
||||||
content_type: str = 'application/json',
|
content_type: typing.Optional[str] = 'application/json',
|
||||||
fields: typing.Optional[tuple[api_client.RequestField, ...]] = None,
|
fields: typing.Optional[tuple[api_client.RequestField, ...]] = None,
|
||||||
accept_content_type: str = 'application/json',
|
accept_content_type: str = 'application/json',
|
||||||
stream: bool = False,
|
stream: bool = False,
|
||||||
query_params: typing.Optional[typing.Tuple[typing.Tuple[str, str], ...]] = None
|
query_params: typing.Optional[typing.Tuple[typing.Tuple[str, str], ...]] = None
|
||||||
):
|
):
|
||||||
mock_request.assert_called_with(
|
headers = {
|
||||||
'POST',
|
|
||||||
url,
|
|
||||||
headers=HTTPHeaderDict(
|
|
||||||
{
|
|
||||||
'Accept': accept_content_type,
|
'Accept': accept_content_type,
|
||||||
'Content-Type': content_type,
|
|
||||||
'User-Agent': 'OpenAPI-Generator/1.0.0/python'
|
'User-Agent': 'OpenAPI-Generator/1.0.0/python'
|
||||||
}
|
}
|
||||||
),
|
if content_type:
|
||||||
body=body,
|
headers['Content-Type'] = content_type
|
||||||
|
kwargs = dict(
|
||||||
|
headers=HTTPHeaderDict(headers),
|
||||||
query_params=query_params,
|
query_params=query_params,
|
||||||
fields=fields,
|
fields=fields,
|
||||||
stream=stream,
|
stream=stream,
|
||||||
timeout=None,
|
timeout=None,
|
||||||
)
|
)
|
||||||
|
if method != 'GET':
|
||||||
|
kwargs['body'] = body
|
||||||
|
mock_request.assert_called_with(
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def test_array_model(self):
|
def test_array_model(self):
|
||||||
from petstore_api.model import animal_farm, animal
|
from petstore_api.model import animal_farm, animal
|
||||||
@ -702,6 +707,37 @@ class TestFakeApi(unittest.TestCase):
|
|||||||
assert isinstance(api_response.body, schemas.NoneClass)
|
assert isinstance(api_response.body, schemas.NoneClass)
|
||||||
assert api_response.body.is_none()
|
assert api_response.body.is_none()
|
||||||
|
|
||||||
|
def test_response_without_schema(self):
|
||||||
|
# received response is not loaded into body because there is no deserialization schema defined
|
||||||
|
with patch.object(RESTClientObject, 'request') as mock_request:
|
||||||
|
body = None
|
||||||
|
content_type = 'application/json'
|
||||||
|
mock_request.return_value = self.__response(
|
||||||
|
self.__json_bytes(body),
|
||||||
|
)
|
||||||
|
|
||||||
|
api_response = self.api.response_without_schema()
|
||||||
|
self.__assert_request_called_with(
|
||||||
|
mock_request,
|
||||||
|
'http://petstore.swagger.io:80/v2/fake/responseWithoutSchema',
|
||||||
|
method='GET',
|
||||||
|
accept_content_type='application/json, application/xml',
|
||||||
|
content_type=None
|
||||||
|
)
|
||||||
|
|
||||||
|
assert isinstance(api_response.body, schemas.Unset)
|
||||||
|
|
||||||
|
|
||||||
|
with patch.object(RESTClientObject, 'request') as mock_request:
|
||||||
|
mock_request.return_value = self.__response(
|
||||||
|
'blah',
|
||||||
|
content_type='text/plain'
|
||||||
|
)
|
||||||
|
|
||||||
|
# when an incorrect content-type is sent back, and exception is raised
|
||||||
|
with self.assertRaises(exceptions.ApiValueError):
|
||||||
|
self.api.response_without_schema()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user