diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonExperimentalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonExperimentalClientCodegen.java index 5424080f0d7..3eca491d66f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonExperimentalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonExperimentalClientCodegen.java @@ -919,6 +919,7 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen { * With this customization, we ensure that when schemas are passed to getSchemaType * - if they have ref in them they are a model * - if they do not have ref in them they are not a model + * and code is also customized to allow anyType request body schemas * * @param codegenParameter the body parameter * @param name model schema ref key in components @@ -936,7 +937,75 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen { forceSimpleRef = true; } } - super.addBodyModelSchema(codegenParameter, name, schema, imports, bodyParameterName, forceSimpleRef); + + CodegenModel codegenModel = null; + if (StringUtils.isNotBlank(name)) { + schema.setName(name); + codegenModel = fromModel(name, schema); + } + if (codegenModel != null) { + codegenParameter.isModel = true; + } + + if (codegenModel != null && (codegenModel.hasVars || forceSimpleRef)) { + if (StringUtils.isEmpty(bodyParameterName)) { + codegenParameter.baseName = codegenModel.classname; + } else { + codegenParameter.baseName = bodyParameterName; + } + codegenParameter.paramName = toParamName(codegenParameter.baseName); + codegenParameter.baseType = codegenModel.classname; + codegenParameter.dataType = getTypeDeclaration(codegenModel.classname); + codegenParameter.description = codegenModel.description; + codegenParameter.isNullable = codegenModel.isNullable; + imports.add(codegenParameter.baseType); + } else { + CodegenProperty codegenProperty = fromProperty("property", schema); + + if (codegenProperty != null && codegenProperty.getComplexType() != null && codegenProperty.getComplexType().contains(" | ")) { + List parts = Arrays.asList(codegenProperty.getComplexType().split(" \\| ")); + imports.addAll(parts); + String codegenModelName = codegenProperty.getComplexType(); + codegenParameter.baseName = codegenModelName; + codegenParameter.paramName = toParamName(codegenParameter.baseName); + codegenParameter.baseType = codegenParameter.baseName; + codegenParameter.dataType = getTypeDeclaration(codegenModelName); + codegenParameter.description = codegenProperty.getDescription(); + codegenParameter.isNullable = codegenProperty.isNullable; + } else { + if (ModelUtils.isMapSchema(schema)) {// http body is map + LOGGER.error("Map should be supported. Please report to openapi-generator github repo about the issue."); + } else if (codegenProperty != null) { + String codegenModelName, codegenModelDescription; + + if (codegenModel != null) { + codegenModelName = codegenModel.classname; + codegenModelDescription = codegenModel.description; + } else { + codegenModelName = "anyType"; + codegenModelDescription = ""; + } + + if (StringUtils.isEmpty(bodyParameterName)) { + codegenParameter.baseName = codegenModelName; + } else { + codegenParameter.baseName = bodyParameterName; + } + + codegenParameter.paramName = toParamName(codegenParameter.baseName); + codegenParameter.baseType = codegenModelName; + codegenParameter.dataType = getTypeDeclaration(codegenModelName); + codegenParameter.description = codegenModelDescription; + + if (codegenProperty.complexType != null) { + imports.add(codegenProperty.complexType); + } + } + } + + // set nullable + setParameterNullable(codegenParameter, codegenProperty); + } } diff --git a/modules/openapi-generator/src/main/resources/python-experimental/api_client.handlebars b/modules/openapi-generator/src/main/resources/python-experimental/api_client.handlebars index 4dfc613aae2..ac1ed1743a5 100644 --- a/modules/openapi-generator/src/main/resources/python-experimental/api_client.handlebars +++ b/modules/openapi-generator/src/main/resources/python-experimental/api_client.handlebars @@ -4,6 +4,7 @@ from dataclasses import dataclass from decimal import Decimal import enum +import email import json import os import io @@ -777,6 +778,20 @@ class OpenApiResponse: else: return response.data + @staticmethod + def __deserialize_multipart_form_data( + response: urllib3.HTTPResponse + ) -> typing.Dict[str, typing.Any]: + msg = email.message_from_bytes(response.data) + return { + part.get_param("name", header="Content-Disposition"): part.get_payload( + decode=True + ).decode(part.get_content_charset()) + if part.get_content_charset() + else part.get_payload() + for part in msg.get_payload() + } + def deserialize(self, response: urllib3.HTTPResponse, configuration: Configuration) -> ApiResponse: content_type = response.getheader('content-type') deserialized_body = unset @@ -786,6 +801,9 @@ class OpenApiResponse: body_data = self.__deserialize_json(response) elif content_type == 'application/octet-stream': body_data = self.__deserialize_application_octet_stream(response) + elif content_type.startswith('multipart/form-data'): + body_data = self.__deserialize_multipart_form_data(response) + content_type = 'multipart/form-data' else: raise NotImplementedError('Deserialization of {} has not yet been implemented'.format(content_type)) body_schema = self.content[content_type].schema diff --git a/modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml b/modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml index 7fcdb42c7b3..5db730b756a 100644 --- a/modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml @@ -1436,6 +1436,60 @@ paths: application/json: schema: $ref: '#/components/schemas/HealthCheckResult' + /fake/inlineComposition/: + post: + tags: + - fake + summary: testing composed schemas at inline locations + operationId: inlineComposition + parameters: + - name: compositionAtRoot + in: query + schema: + allOf: + - type: string + minLength: 1 + - name: compositionInProperty + in: query + schema: + type: object + properties: + someProp: + allOf: + - type: string + minLength: 1 + requestBody: + content: + application/json: + schema: + allOf: + - type: string + minLength: 1 + multipart/form-data: + schema: + type: object + properties: + someProp: + allOf: + - type: string + minLength: 1 + responses: + 200: + description: success + content: + application/json: + schema: + allOf: + - type: string + minLength: 1 + multipart/form-data: + schema: + type: object + properties: + someProp: + allOf: + - type: string + minLength: 1 servers: - url: 'http://{server}.swagger.io:{port}/v2' description: petstore server @@ -2681,4 +2735,11 @@ components: type: string format: number cost: - $ref: '#/components/schemas/Money' \ No newline at end of file + $ref: '#/components/schemas/Money' + ObjectWithInlineCompositionProperty: + type: object + properties: + someProp: + allOf: + - type: string + minLength: 1 diff --git a/samples/openapi3/client/petstore/python-experimental/.openapi-generator/FILES b/samples/openapi3/client/petstore/python-experimental/.openapi-generator/FILES index fdc3984d899..48127260f21 100644 --- a/samples/openapi3/client/petstore/python-experimental/.openapi-generator/FILES +++ b/samples/openapi3/client/petstore/python-experimental/.openapi-generator/FILES @@ -41,6 +41,7 @@ docs/ComposedNumber.md docs/ComposedObject.md docs/ComposedOneOfDifferentTypes.md docs/ComposedString.md +docs/CompositionInProperty.md docs/Currency.md docs/DanishPig.md docs/DateTimeTest.md @@ -95,6 +96,7 @@ docs/ObjectInterface.md docs/ObjectModelWithRefProps.md docs/ObjectWithDecimalProperties.md docs/ObjectWithDifficultlyNamedProps.md +docs/ObjectWithInlineCompositionProperty.md docs/ObjectWithValidations.md docs/Order.md docs/ParentPet.md @@ -179,6 +181,7 @@ petstore_api/model/composed_number.py petstore_api/model/composed_object.py petstore_api/model/composed_one_of_different_types.py petstore_api/model/composed_string.py +petstore_api/model/composition_in_property.py petstore_api/model/currency.py petstore_api/model/danish_pig.py petstore_api/model/date_time_test.py @@ -230,6 +233,7 @@ petstore_api/model/object_interface.py petstore_api/model/object_model_with_ref_props.py petstore_api/model/object_with_decimal_properties.py petstore_api/model/object_with_difficultly_named_props.py +petstore_api/model/object_with_inline_composition_property.py petstore_api/model/object_with_validations.py petstore_api/model/order.py petstore_api/model/parent_pet.py diff --git a/samples/openapi3/client/petstore/python-experimental/README.md b/samples/openapi3/client/petstore/python-experimental/README.md index a849a36ae20..eaf9a932d09 100644 --- a/samples/openapi3/client/petstore/python-experimental/README.md +++ b/samples/openapi3/client/petstore/python-experimental/README.md @@ -98,6 +98,7 @@ Class | Method | HTTP request | Description *FakeApi* | [**fake_health_get**](docs/FakeApi.md#fake_health_get) | **GET** /fake/health | Health check endpoint *FakeApi* | [**group_parameters**](docs/FakeApi.md#group_parameters) | **DELETE** /fake | Fake endpoint to test group parameters (optional) *FakeApi* | [**inline_additional_properties**](docs/FakeApi.md#inline_additional_properties) | **POST** /fake/inline-additionalProperties | test inline additionalProperties +*FakeApi* | [**inline_composition**](docs/FakeApi.md#inline_composition) | **POST** /fake/inlineComposition/ | testing composed schemas at inline locations *FakeApi* | [**json_form_data**](docs/FakeApi.md#json_form_data) | **GET** /fake/jsonFormData | test json serialization of form data *FakeApi* | [**mammal**](docs/FakeApi.md#mammal) | **POST** /fake/refs/mammal | *FakeApi* | [**number_with_validations**](docs/FakeApi.md#number_with_validations) | **POST** /fake/refs/number | @@ -172,6 +173,7 @@ Class | Method | HTTP request | Description - [ComposedObject](docs/ComposedObject.md) - [ComposedOneOfDifferentTypes](docs/ComposedOneOfDifferentTypes.md) - [ComposedString](docs/ComposedString.md) + - [CompositionInProperty](docs/CompositionInProperty.md) - [Currency](docs/Currency.md) - [DanishPig](docs/DanishPig.md) - [DateTimeTest](docs/DateTimeTest.md) @@ -223,6 +225,7 @@ Class | Method | HTTP request | Description - [ObjectModelWithRefProps](docs/ObjectModelWithRefProps.md) - [ObjectWithDecimalProperties](docs/ObjectWithDecimalProperties.md) - [ObjectWithDifficultlyNamedProps](docs/ObjectWithDifficultlyNamedProps.md) + - [ObjectWithInlineCompositionProperty](docs/ObjectWithInlineCompositionProperty.md) - [ObjectWithValidations](docs/ObjectWithValidations.md) - [Order](docs/Order.md) - [ParentPet](docs/ParentPet.md) diff --git a/samples/openapi3/client/petstore/python-experimental/docs/CompositionInProperty.md b/samples/openapi3/client/petstore/python-experimental/docs/CompositionInProperty.md new file mode 100644 index 00000000000..12441fe99ec --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/CompositionInProperty.md @@ -0,0 +1,10 @@ +# CompositionInProperty + +#### Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**someProp** | **object** | | [optional] +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/samples/openapi3/client/petstore/python-experimental/docs/FakeApi.md b/samples/openapi3/client/petstore/python-experimental/docs/FakeApi.md index 45fdc4a5c1c..ddb0b6f2c8e 100644 --- a/samples/openapi3/client/petstore/python-experimental/docs/FakeApi.md +++ b/samples/openapi3/client/petstore/python-experimental/docs/FakeApi.md @@ -18,6 +18,7 @@ Method | HTTP request | Description [**fake_health_get**](FakeApi.md#fake_health_get) | **GET** /fake/health | Health check endpoint [**group_parameters**](FakeApi.md#group_parameters) | **DELETE** /fake | Fake endpoint to test group parameters (optional) [**inline_additional_properties**](FakeApi.md#inline_additional_properties) | **POST** /fake/inline-additionalProperties | test inline additionalProperties +[**inline_composition**](FakeApi.md#inline_composition) | **POST** /fake/inlineComposition/ | testing composed schemas at inline locations [**json_form_data**](FakeApi.md#json_form_data) | **GET** /fake/jsonFormData | test json serialization of form data [**mammal**](FakeApi.md#mammal) | **POST** /fake/refs/mammal | [**number_with_validations**](FakeApi.md#number_with_validations) | **POST** /fake/refs/number | @@ -1408,6 +1409,136 @@ 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) +# **inline_composition** +> object inline_composition() + +testing composed schemas at inline locations + +### Example + +```python +import petstore_api +from petstore_api.api import fake_api +from petstore_api.model.composition_in_property import CompositionInProperty +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 passing only optional values + query_params = { + 'compositionAtRoot': , + 'compositionInProperty': dict( + some_prop=, + ), + } + body = + try: + # testing composed schemas at inline locations + api_response = api_instance.inline_composition( + query_params=query_params, + body=body, + ) + pprint(api_response) + except petstore_api.ApiException as e: + print("Exception when calling FakeApi->inline_composition: %s\n" % e) +``` +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +body | typing.Union[SchemaForRequestBodyApplicationJson, SchemaForRequestBodyMultipartFormData, Unset] | optional, default is unset | +query_params | RequestQueryParams | | +content_type | str | optional, default is 'application/json' | Selects the schema and serialization of the request body +accept_content_types | typing.Tuple[str] | default is ('application/json', 'multipart/form-data', ) | Tells the server the content type(s) that are accepted by the client +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 +timeout | typing.Optional[typing.Union[int, typing.Tuple]] | default is None | the timeout used by the rest client +skip_deserialization | bool | default is False | when True, headers and body will be unset and an instance of api_client.ApiResponseWithoutDeserialization will be returned + +### body + +#### SchemaForRequestBodyApplicationJson + +#### Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +#### SchemaForRequestBodyMultipartFormData + +#### Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**someProp** | **object** | | [optional] +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +### query_params +#### RequestQueryParams + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +compositionAtRoot | CompositionAtRootSchema | | optional +compositionInProperty | CompositionInPropertySchema | | optional + + +#### CompositionAtRootSchema + +#### Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +#### CompositionInPropertySchema +Type | Description | Notes +------------- | ------------- | ------------- +[**{str: (bool, date, datetime, dict, float, int, list, str, none_type)}**](CompositionInProperty.md) | | + + +### Return Types, Responses + +Code | Class | Description +------------- | ------------- | ------------- +n/a | api_client.ApiResponseWithoutDeserialization | When skip_deserialization is True this response is returned +200 | ApiResponseFor200 | success + +#### ApiResponseFor200 +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +response | urllib3.HTTPResponse | Raw response | +body | typing.Union[SchemaFor200ResponseBodyApplicationJson, SchemaFor200ResponseBodyMultipartFormData, ] | | +headers | Unset | headers were not defined | + +#### SchemaFor200ResponseBodyApplicationJson + +#### Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +#### SchemaFor200ResponseBodyMultipartFormData + +#### Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**someProp** | **object** | | [optional] +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + + +**object** + +### 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) + # **json_form_data** > json_form_data() diff --git a/samples/openapi3/client/petstore/python-experimental/docs/ObjectWithInlineCompositionProperty.md b/samples/openapi3/client/petstore/python-experimental/docs/ObjectWithInlineCompositionProperty.md new file mode 100644 index 00000000000..f83f69a567d --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/ObjectWithInlineCompositionProperty.md @@ -0,0 +1,10 @@ +# ObjectWithInlineCompositionProperty + +#### Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**someProp** | **object** | | [optional] +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/api/fake_api.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/api/fake_api.py index 7a81465ef0f..521ac4a0817 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/api/fake_api.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/api/fake_api.py @@ -24,6 +24,7 @@ from petstore_api.api.fake_api_endpoints.enum_parameters import EnumParameters from petstore_api.api.fake_api_endpoints.fake_health_get import FakeHealthGet from petstore_api.api.fake_api_endpoints.group_parameters import GroupParameters from petstore_api.api.fake_api_endpoints.inline_additional_properties import InlineAdditionalProperties +from petstore_api.api.fake_api_endpoints.inline_composition import InlineComposition from petstore_api.api.fake_api_endpoints.json_form_data import JsonFormData from petstore_api.api.fake_api_endpoints.mammal import Mammal from petstore_api.api.fake_api_endpoints.number_with_validations import NumberWithValidations @@ -52,6 +53,7 @@ class FakeApi( FakeHealthGet, GroupParameters, InlineAdditionalProperties, + InlineComposition, JsonFormData, Mammal, NumberWithValidations, diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/api/fake_api_endpoints/inline_composition.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/api/fake_api_endpoints/inline_composition.py new file mode 100644 index 00000000000..5a595a3fb3e --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/api/fake_api_endpoints/inline_composition.py @@ -0,0 +1,554 @@ +# 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, + InstantiationMetadata, + Unset, + unset, + ComposedBase, + ListBase, + DictBase, + NoneBase, + StrBase, + IntBase, + NumberBase, + DateBase, + DateTimeBase, + BoolBase, + BinaryBase, + Schema, + _SchemaValidator, + _SchemaTypeChecker, + _SchemaEnumMaker +) + +from petstore_api.model.composition_in_property import CompositionInProperty + +# query params + + +class CompositionAtRootSchema( + ComposedSchema +): + + @classmethod + @property + def _composed_schemas(cls): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + + + class allOf_0( + _SchemaValidator( + min_length=1, + ), + StrSchema + ): + pass + return { + 'allOf': [ + allOf_0, + ], + 'oneOf': [ + ], + 'anyOf': [ + ], + } + + def __new__( + cls, + *args: typing.Union[dict, frozendict, str, date, datetime, int, float, decimal.Decimal, None, list, tuple, bytes], + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'CompositionAtRootSchema': + return super().__new__( + cls, + *args, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + +class CompositionInPropertySchema( + DictSchema +): + + + class someProp( + ComposedSchema + ): + + @classmethod + @property + def _composed_schemas(cls): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + + + class allOf_0( + _SchemaValidator( + min_length=1, + ), + StrSchema + ): + pass + return { + 'allOf': [ + allOf_0, + ], + 'oneOf': [ + ], + 'anyOf': [ + ], + } + + def __new__( + cls, + *args: typing.Union[dict, frozendict, str, date, datetime, int, float, decimal.Decimal, None, list, tuple, bytes], + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'someProp': + return super().__new__( + cls, + *args, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + + def __new__( + cls, + *args: typing.Union[dict, frozendict, ], + someProp: typing.Union[someProp, Unset] = unset, + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'CompositionInPropertySchema': + return super().__new__( + cls, + *args, + someProp=someProp, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) +RequestRequiredQueryParams = typing.TypedDict( + 'RequestRequiredQueryParams', + { + } +) +RequestOptionalQueryParams = typing.TypedDict( + 'RequestOptionalQueryParams', + { + 'compositionAtRoot': CompositionAtRootSchema, + 'compositionInProperty': CompositionInPropertySchema, + }, + total=False +) + + +class RequestQueryParams(RequestRequiredQueryParams, RequestOptionalQueryParams): + pass + + +request_query_composition_at_root = api_client.QueryParameter( + name="compositionAtRoot", + style=api_client.ParameterStyle.FORM, + schema=CompositionAtRootSchema, + explode=True, +) +request_query_composition_in_property = api_client.QueryParameter( + name="compositionInProperty", + style=api_client.ParameterStyle.FORM, + schema=CompositionInPropertySchema, + explode=True, +) +# body param + + +class SchemaForRequestBodyApplicationJson( + ComposedSchema +): + + @classmethod + @property + def _composed_schemas(cls): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + + + class allOf_0( + _SchemaValidator( + min_length=1, + ), + StrSchema + ): + pass + return { + 'allOf': [ + allOf_0, + ], + 'oneOf': [ + ], + 'anyOf': [ + ], + } + + def __new__( + cls, + *args: typing.Union[dict, frozendict, str, date, datetime, int, float, decimal.Decimal, None, list, tuple, bytes], + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'SchemaForRequestBodyApplicationJson': + return super().__new__( + cls, + *args, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + +class SchemaForRequestBodyMultipartFormData( + DictSchema +): + + + class someProp( + ComposedSchema + ): + + @classmethod + @property + def _composed_schemas(cls): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + + + class allOf_0( + _SchemaValidator( + min_length=1, + ), + StrSchema + ): + pass + return { + 'allOf': [ + allOf_0, + ], + 'oneOf': [ + ], + 'anyOf': [ + ], + } + + def __new__( + cls, + *args: typing.Union[dict, frozendict, str, date, datetime, int, float, decimal.Decimal, None, list, tuple, bytes], + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'someProp': + return super().__new__( + cls, + *args, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + + def __new__( + cls, + *args: typing.Union[dict, frozendict, ], + someProp: typing.Union[someProp, Unset] = unset, + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'SchemaForRequestBodyMultipartFormData': + return super().__new__( + cls, + *args, + someProp=someProp, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + +request_body_any_type = api_client.RequestBody( + content={ + 'application/json': api_client.MediaType( + schema=SchemaForRequestBodyApplicationJson), + 'multipart/form-data': api_client.MediaType( + schema=SchemaForRequestBodyMultipartFormData), + }, +) +_path = '/fake/inlineComposition/' +_method = 'POST' + + +class SchemaFor200ResponseBodyApplicationJson( + ComposedSchema +): + + @classmethod + @property + def _composed_schemas(cls): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + + + class allOf_0( + _SchemaValidator( + min_length=1, + ), + StrSchema + ): + pass + return { + 'allOf': [ + allOf_0, + ], + 'oneOf': [ + ], + 'anyOf': [ + ], + } + + def __new__( + cls, + *args: typing.Union[dict, frozendict, str, date, datetime, int, float, decimal.Decimal, None, list, tuple, bytes], + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'SchemaFor200ResponseBodyApplicationJson': + return super().__new__( + cls, + *args, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + +class SchemaFor200ResponseBodyMultipartFormData( + DictSchema +): + + + class someProp( + ComposedSchema + ): + + @classmethod + @property + def _composed_schemas(cls): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + + + class allOf_0( + _SchemaValidator( + min_length=1, + ), + StrSchema + ): + pass + return { + 'allOf': [ + allOf_0, + ], + 'oneOf': [ + ], + 'anyOf': [ + ], + } + + def __new__( + cls, + *args: typing.Union[dict, frozendict, str, date, datetime, int, float, decimal.Decimal, None, list, tuple, bytes], + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'someProp': + return super().__new__( + cls, + *args, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + + def __new__( + cls, + *args: typing.Union[dict, frozendict, ], + someProp: typing.Union[someProp, Unset] = unset, + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'SchemaFor200ResponseBodyMultipartFormData': + return super().__new__( + cls, + *args, + someProp=someProp, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + +@dataclass +class ApiResponseFor200(api_client.ApiResponse): + response: urllib3.HTTPResponse + body: typing.Union[ + SchemaFor200ResponseBodyApplicationJson, + SchemaFor200ResponseBodyMultipartFormData, + ] + headers: Unset = unset + + +_response_for_200 = api_client.OpenApiResponse( + response_cls=ApiResponseFor200, + content={ + 'application/json': api_client.MediaType( + schema=SchemaFor200ResponseBodyApplicationJson), + 'multipart/form-data': api_client.MediaType( + schema=SchemaFor200ResponseBodyMultipartFormData), + }, +) +_status_code_to_response = { + '200': _response_for_200, +} +_all_accept_content_types = ( + 'application/json', + 'multipart/form-data', +) + + +class InlineComposition(api_client.Api): + + def inline_composition( + self: api_client.Api, + body: typing.Union[SchemaForRequestBodyApplicationJson, SchemaForRequestBodyMultipartFormData, Unset] = unset, + query_params: RequestQueryParams = frozendict(), + content_type: str = 'application/json', + 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 + ]: + """ + testing composed schemas at inline locations + :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 + """ + self._verify_typed_dict_inputs(RequestQueryParams, query_params) + + _query_params = [] + for parameter in ( + request_query_composition_at_root, + request_query_composition_in_property, + ): + parameter_data = query_params.get(parameter.name, unset) + if parameter_data is unset: + continue + serialized_data = parameter.serialize(parameter_data) + _query_params.extend(serialized_data) + + _headers = HTTPHeaderDict() + # TODO add cookie handling + if accept_content_types: + for accept_content_type in accept_content_types: + _headers.add('Accept', accept_content_type) + + _fields = None + _body = None + if body is not unset: + serialized_data = request_body_any_type.serialize(body, content_type) + _headers.add('Content-Type', content_type) + if 'fields' in serialized_data: + _fields = serialized_data['fields'] + elif 'body' in serialized_data: + _body = serialized_data['body'] + response = self.api_client.call_api( + resource_path=_path, + method=_method, + query_params=tuple(_query_params), + headers=_headers, + fields=_fields, + body=_body, + 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 diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/api_client.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/api_client.py index adad78a8e84..3aa0d288d58 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/api_client.py @@ -11,6 +11,7 @@ from dataclasses import dataclass from decimal import Decimal import enum +import email import json import os import io @@ -781,6 +782,20 @@ class OpenApiResponse: else: return response.data + @staticmethod + def __deserialize_multipart_form_data( + response: urllib3.HTTPResponse + ) -> typing.Dict[str, typing.Any]: + msg = email.message_from_bytes(response.data) + return { + part.get_param("name", header="Content-Disposition"): part.get_payload( + decode=True + ).decode(part.get_content_charset()) + if part.get_content_charset() + else part.get_payload() + for part in msg.get_payload() + } + def deserialize(self, response: urllib3.HTTPResponse, configuration: Configuration) -> ApiResponse: content_type = response.getheader('content-type') deserialized_body = unset @@ -790,6 +805,9 @@ class OpenApiResponse: body_data = self.__deserialize_json(response) elif content_type == 'application/octet-stream': body_data = self.__deserialize_application_octet_stream(response) + elif content_type.startswith('multipart/form-data'): + body_data = self.__deserialize_multipart_form_data(response) + content_type = 'multipart/form-data' else: raise NotImplementedError('Deserialization of {} has not yet been implemented'.format(content_type)) body_schema = self.content[content_type].schema diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/model/composition_in_property.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/composition_in_property.py new file mode 100644 index 00000000000..38f4fb948ac --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/composition_in_property.py @@ -0,0 +1,132 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + +import re # noqa: F401 +import sys # noqa: F401 +import typing # noqa: F401 + +from frozendict import frozendict # noqa: F401 + +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, + InstantiationMetadata, + Unset, + unset, + ComposedBase, + ListBase, + DictBase, + NoneBase, + StrBase, + IntBase, + NumberBase, + DateBase, + DateTimeBase, + BoolBase, + BinaryBase, + Schema, + _SchemaValidator, + _SchemaTypeChecker, + _SchemaEnumMaker +) + + +class CompositionInProperty( + DictSchema +): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + + class someProp( + ComposedSchema + ): + + @classmethod + @property + def _composed_schemas(cls): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + + + class allOf_0( + _SchemaValidator( + min_length=1, + ), + StrSchema + ): + pass + return { + 'allOf': [ + allOf_0, + ], + 'oneOf': [ + ], + 'anyOf': [ + ], + } + + def __new__( + cls, + *args: typing.Union[dict, frozendict, str, date, datetime, int, float, decimal.Decimal, None, list, tuple, bytes], + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'someProp': + return super().__new__( + cls, + *args, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + + def __new__( + cls, + *args: typing.Union[dict, frozendict, ], + someProp: typing.Union[someProp, Unset] = unset, + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'CompositionInProperty': + return super().__new__( + cls, + *args, + someProp=someProp, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_with_inline_composition_property.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_with_inline_composition_property.py new file mode 100644 index 00000000000..ca57f8edd0c --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_with_inline_composition_property.py @@ -0,0 +1,132 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + +import re # noqa: F401 +import sys # noqa: F401 +import typing # noqa: F401 + +from frozendict import frozendict # noqa: F401 + +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, + InstantiationMetadata, + Unset, + unset, + ComposedBase, + ListBase, + DictBase, + NoneBase, + StrBase, + IntBase, + NumberBase, + DateBase, + DateTimeBase, + BoolBase, + BinaryBase, + Schema, + _SchemaValidator, + _SchemaTypeChecker, + _SchemaEnumMaker +) + + +class ObjectWithInlineCompositionProperty( + DictSchema +): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + + class someProp( + ComposedSchema + ): + + @classmethod + @property + def _composed_schemas(cls): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + + + class allOf_0( + _SchemaValidator( + min_length=1, + ), + StrSchema + ): + pass + return { + 'allOf': [ + allOf_0, + ], + 'oneOf': [ + ], + 'anyOf': [ + ], + } + + def __new__( + cls, + *args: typing.Union[dict, frozendict, str, date, datetime, int, float, decimal.Decimal, None, list, tuple, bytes], + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'someProp': + return super().__new__( + cls, + *args, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) + + + def __new__( + cls, + *args: typing.Union[dict, frozendict, ], + someProp: typing.Union[someProp, Unset] = unset, + _instantiation_metadata: typing.Optional[InstantiationMetadata] = None, + **kwargs: typing.Type[Schema], + ) -> 'ObjectWithInlineCompositionProperty': + return super().__new__( + cls, + *args, + someProp=someProp, + _instantiation_metadata=_instantiation_metadata, + **kwargs, + ) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/__init__.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/__init__.py index 07e026d2c2f..fd3e6fa8d14 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/__init__.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/__init__.py @@ -49,6 +49,7 @@ from petstore_api.model.composed_number import ComposedNumber from petstore_api.model.composed_object import ComposedObject from petstore_api.model.composed_one_of_different_types import ComposedOneOfDifferentTypes from petstore_api.model.composed_string import ComposedString +from petstore_api.model.composition_in_property import CompositionInProperty from petstore_api.model.currency import Currency from petstore_api.model.danish_pig import DanishPig from petstore_api.model.date_time_test import DateTimeTest @@ -100,6 +101,7 @@ from petstore_api.model.object_interface import ObjectInterface from petstore_api.model.object_model_with_ref_props import ObjectModelWithRefProps from petstore_api.model.object_with_decimal_properties import ObjectWithDecimalProperties from petstore_api.model.object_with_difficultly_named_props import ObjectWithDifficultlyNamedProps +from petstore_api.model.object_with_inline_composition_property import ObjectWithInlineCompositionProperty from petstore_api.model.object_with_validations import ObjectWithValidations from petstore_api.model.order import Order from petstore_api.model.parent_pet import ParentPet diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_composition_in_property.py b/samples/openapi3/client/petstore/python-experimental/test/test_composition_in_property.py new file mode 100644 index 00000000000..adddcae8b75 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_composition_in_property.py @@ -0,0 +1,35 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + +import unittest + +import petstore_api +from petstore_api.model.composition_in_property import CompositionInProperty + + +class TestCompositionInProperty(unittest.TestCase): + """CompositionInProperty unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_CompositionInProperty(self): + """Test CompositionInProperty""" + # FIXME: construct object with mandatory attributes with example values + # model = CompositionInProperty() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_fake_api.py b/samples/openapi3/client/petstore/python-experimental/test/test_fake_api.py index abe26cc6c4d..ecf7f950429 100644 --- a/samples/openapi3/client/petstore/python-experimental/test/test_fake_api.py +++ b/samples/openapi3/client/petstore/python-experimental/test/test_fake_api.py @@ -116,6 +116,13 @@ class TestFakeApi(unittest.TestCase): """ pass + def test_inline_composition(self): + """Test case for inline_composition + + testing composed schemas at inline locations # noqa: E501 + """ + pass + def test_json_form_data(self): """Test case for json_form_data diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_object_with_inline_composition_property.py b/samples/openapi3/client/petstore/python-experimental/test/test_object_with_inline_composition_property.py new file mode 100644 index 00000000000..442195d94d7 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_object_with_inline_composition_property.py @@ -0,0 +1,35 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + +import unittest + +import petstore_api +from petstore_api.model.object_with_inline_composition_property import ObjectWithInlineCompositionProperty + + +class TestObjectWithInlineCompositionProperty(unittest.TestCase): + """ObjectWithInlineCompositionProperty unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_ObjectWithInlineCompositionProperty(self): + """Test ObjectWithInlineCompositionProperty""" + # FIXME: construct object with mandatory attributes with example values + # model = ObjectWithInlineCompositionProperty() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/tests_manual/test_fake_api.py b/samples/openapi3/client/petstore/python-experimental/tests_manual/test_fake_api.py index c053cee62fb..42c4714476c 100644 --- a/samples/openapi3/client/petstore/python-experimental/tests_manual/test_fake_api.py +++ b/samples/openapi3/client/petstore/python-experimental/tests_manual/test_fake_api.py @@ -8,7 +8,8 @@ The version of the OpenAPI document: 1.0.0 Generated by: https://openapi-generator.tech """ - +from email.mime import multipart +from email.mime import nonmultipart import io import sys import unittest @@ -20,11 +21,18 @@ import urllib3 from urllib3._collections import HTTPHeaderDict import petstore_api -from petstore_api import api_client, schemas +from petstore_api import api_client, schemas, exceptions from petstore_api.api.fake_api import FakeApi # noqa: E501 from petstore_api.rest import RESTClientObject +class MIMEFormdata(nonmultipart.MIMENonMultipart): + def __init__(self, keyname, *args, **kwargs): + super(MIMEFormdata, self).__init__(*args, **kwargs) + self.add_header( + "Content-Disposition", "form-data; name=\"%s\"" % keyname) + + class TestFakeApi(unittest.TestCase): """FakeApi unit test stubs""" json_content_type = 'application/json' @@ -67,6 +75,7 @@ class TestFakeApi(unittest.TestCase): fields: typing.Optional[tuple[api_client.RequestField, ...]] = None, accept_content_type: str = 'application/json', stream: bool = False, + query_params: typing.Optional[typing.Tuple[typing.Tuple[str, str], ...]] = None ): mock_request.assert_called_with( 'POST', @@ -79,7 +88,7 @@ class TestFakeApi(unittest.TestCase): } ), body=body, - query_params=None, + query_params=query_params, fields=fields, stream=stream, timeout=None, @@ -570,6 +579,106 @@ class TestFakeApi(unittest.TestCase): ) self.assertEqual(api_response.body, response_json) + @staticmethod + def __encode_multipart_formdata(fields: typing.Dict[str, typing.Any]) -> multipart.MIMEMultipart: + m = multipart.MIMEMultipart("form-data") + + for field, value in fields.items(): + data = MIMEFormdata(field, "text", "plain") + # data.set_payload(value, charset='us-ascii') + data.set_payload(value) + m.attach(data) + + return m + + @patch.object(RESTClientObject, 'request') + def test_inline_composition(self, mock_request): + """Test case for inline_composition + + testing composed schemas at inline locations # noqa: E501 + """ + single_char_str = 'a' + json_bytes = self.__json_bytes(single_char_str) + + # tx and rx json with composition at root level of schema for request + response body + content_type = 'application/json' + mock_request.return_value = self.__response( + json_bytes + ) + api_response = self.api.inline_composition( + body=single_char_str, + query_params={ + 'compositionAtRoot': single_char_str, + 'compositionInProperty': {'someProp': single_char_str} + }, + accept_content_types=(content_type,) + ) + self.__assert_request_called_with( + mock_request, + 'http://petstore.swagger.io:80/v2/fake/inlineComposition/', + accept_content_type=content_type, + content_type=content_type, + query_params=(('compositionAtRoot', 'a'), ('someProp', 'a')), + body=json_bytes + ) + self.assertEqual(api_response.body, single_char_str) + self.assertTrue(isinstance(api_response.body, schemas.StrSchema)) + + # tx and rx json with composition at property level of schema for request + response body + content_type = 'multipart/form-data' + multipart_response = self.__encode_multipart_formdata(fields={'someProp': single_char_str}) + mock_request.return_value = self.__response( + bytes(multipart_response), + content_type=multipart_response.get_content_type() + ) + api_response = self.api.inline_composition( + body={'someProp': single_char_str}, + query_params={ + 'compositionAtRoot': single_char_str, + 'compositionInProperty': {'someProp': single_char_str} + }, + content_type=content_type, + accept_content_types=(content_type,) + ) + self.__assert_request_called_with( + mock_request, + 'http://petstore.swagger.io:80/v2/fake/inlineComposition/', + accept_content_type=content_type, + content_type=content_type, + query_params=(('compositionAtRoot', 'a'), ('someProp', 'a')), + fields=( + api_client.RequestField( + name='someProp', + data=single_char_str, + headers={'Content-Type': 'text/plain'} + ), + ), + ) + self.assertEqual(api_response.body, {'someProp': single_char_str}) + self.assertTrue(isinstance(api_response.body.someProp, schemas.StrSchema)) + + # error thrown when a str is input which doesn't meet the composed schema length constraint + invalid_value = '' + variable_locations = 4 + for invalid_index in range(variable_locations): + values = [single_char_str]*variable_locations + values[invalid_index] = invalid_value + with self.assertRaises(exceptions.ApiValueError): + multipart_response = self.__encode_multipart_formdata(fields={'someProp': values[0]}) + mock_request.return_value = self.__response( + bytes(multipart_response), + content_type=multipart_response.get_content_type() + ) + self.api.inline_composition( + body={'someProp': values[1]}, + query_params={ + 'compositionAtRoot': values[2], + 'compositionInProperty': {'someProp': values[3]} + }, + content_type=content_type, + accept_content_types=(content_type,) + ) + if __name__ == '__main__': unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/tests_manual/test_object_with_inline_composition_property.py b/samples/openapi3/client/petstore/python-experimental/tests_manual/test_object_with_inline_composition_property.py new file mode 100644 index 00000000000..98b902a01e6 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/tests_manual/test_object_with_inline_composition_property.py @@ -0,0 +1,33 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + +import unittest + +from petstore_api import schemas, exceptions +from petstore_api.model.object_with_inline_composition_property import ObjectWithInlineCompositionProperty + + +class TestObjectWithInlineCompositionProperty(unittest.TestCase): + """ObjectWithInlineCompositionProperty unit test stubs""" + + def test_ObjectWithInlineCompositionProperty(self): + """Test ObjectWithInlineCompositionProperty""" + model = ObjectWithInlineCompositionProperty(someProp='a') + self.assertTrue(isinstance(model.someProp, ObjectWithInlineCompositionProperty.someProp)) + self.assertTrue(isinstance(model.someProp, schemas.StrSchema)) + + # error thrown on length < 1 + with self.assertRaises(exceptions.ApiValueError): + ObjectWithInlineCompositionProperty(someProp='') + + +if __name__ == '__main__': + unittest.main()