Skip to content

fix: Invalid code generation with some oneOf and anyOf combinations #642

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .default import DefaultEndpoints
from .location import LocationEndpoints
from .parameters import ParametersEndpoints
from .responses import ResponsesEndpoints
from .tag1 import Tag1Endpoints
from .tests import TestsEndpoints
from .true_ import True_Endpoints
Expand All @@ -15,6 +16,10 @@ class MyTestApiClientApi:
def tests(cls) -> Type[TestsEndpoints]:
return TestsEndpoints

@classmethod
def responses(cls) -> Type[ResponsesEndpoints]:
return ResponsesEndpoints

@classmethod
def default(cls) -> Type[DefaultEndpoints]:
return DefaultEndpoints
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
""" Contains methods for accessing the API Endpoints """

import types

from . import post_responses_unions_simple_before_complex


class ResponsesEndpoints:
@classmethod
def post_responses_unions_simple_before_complex(cls) -> types.ModuleType:
"""
Regression test for #603
"""
return post_responses_unions_simple_before_complex
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
from typing import Any, Dict, Optional

import httpx

from ...client import Client
from ...models.post_responses_unions_simple_before_complex_response_200 import (
PostResponsesUnionsSimpleBeforeComplexResponse200,
)
from ...types import Response


def _get_kwargs(
*,
client: Client,
) -> Dict[str, Any]:
url = "{}/responses/unions/simple_before_complex".format(client.base_url)

headers: Dict[str, str] = client.get_headers()
cookies: Dict[str, Any] = client.get_cookies()

return {
"method": "post",
"url": url,
"headers": headers,
"cookies": cookies,
"timeout": client.get_timeout(),
}


def _parse_response(*, response: httpx.Response) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]:
if response.status_code == 200:
response_200 = PostResponsesUnionsSimpleBeforeComplexResponse200.from_dict(response.json())

return response_200
return None


def _build_response(*, response: httpx.Response) -> Response[PostResponsesUnionsSimpleBeforeComplexResponse200]:
return Response(
status_code=response.status_code,
content=response.content,
headers=response.headers,
parsed=_parse_response(response=response),
)


def sync_detailed(
*,
client: Client,
) -> Response[PostResponsesUnionsSimpleBeforeComplexResponse200]:
"""Regression test for #603

Returns:
Response[PostResponsesUnionsSimpleBeforeComplexResponse200]
"""

kwargs = _get_kwargs(
client=client,
)

response = httpx.request(
verify=client.verify_ssl,
**kwargs,
)

return _build_response(response=response)


def sync(
*,
client: Client,
) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]:
"""Regression test for #603

Returns:
Response[PostResponsesUnionsSimpleBeforeComplexResponse200]
"""

return sync_detailed(
client=client,
).parsed


async def asyncio_detailed(
*,
client: Client,
) -> Response[PostResponsesUnionsSimpleBeforeComplexResponse200]:
"""Regression test for #603

Returns:
Response[PostResponsesUnionsSimpleBeforeComplexResponse200]
"""

kwargs = _get_kwargs(
client=client,
)

async with httpx.AsyncClient(verify=client.verify_ssl) as _client:
response = await _client.request(**kwargs)

return _build_response(response=response)


async def asyncio(
*,
client: Client,
) -> Optional[PostResponsesUnionsSimpleBeforeComplexResponse200]:
"""Regression test for #603

Returns:
Response[PostResponsesUnionsSimpleBeforeComplexResponse200]
"""

return (
await asyncio_detailed(
client=client,
)
).parsed
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def _get_kwargs(

params["list_prop"] = json_list_prop

json_union_prop: Union[float, str]

json_union_prop = union_prop

params["union_prop"] = json_union_prop
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def _get_kwargs(

params["an_enum_value_with_only_null"] = json_an_enum_value_with_only_null

json_some_date: str

if isinstance(some_date, datetime.date):
json_some_date = some_date.isoformat()
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
from .model_with_union_property_inlined_fruit_type_0 import ModelWithUnionPropertyInlinedFruitType0
from .model_with_union_property_inlined_fruit_type_1 import ModelWithUnionPropertyInlinedFruitType1
from .none import None_
from .post_responses_unions_simple_before_complex_response_200 import PostResponsesUnionsSimpleBeforeComplexResponse200
from .post_responses_unions_simple_before_complex_response_200a_type_1 import (
PostResponsesUnionsSimpleBeforeComplexResponse200AType1,
)
from .test_inline_objects_json_body import TestInlineObjectsJsonBody
from .test_inline_objects_response_200 import TestInlineObjectsResponse200
from .validation_error import ValidationError
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def to_dict(self) -> Dict[str, Any]:

an_allof_enum_with_overridden_default = self.an_allof_enum_with_overridden_default.value

a_camel_date_time: str

if isinstance(self.a_camel_date_time, datetime.datetime):
a_camel_date_time = self.a_camel_date_time.isoformat()

Expand All @@ -79,6 +81,7 @@ def to_dict(self) -> Dict[str, Any]:

a_date = self.a_date.isoformat()
required_not_nullable = self.required_not_nullable
one_of_models: Union[Any, Dict[str, Any]]

if isinstance(self.one_of_models, FreeFormModel):
one_of_models = self.one_of_models.to_dict()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from typing import Any, Dict, List, Type, TypeVar, Union, cast

import attr

from ..models.post_responses_unions_simple_before_complex_response_200a_type_1 import (
PostResponsesUnionsSimpleBeforeComplexResponse200AType1,
)

T = TypeVar("T", bound="PostResponsesUnionsSimpleBeforeComplexResponse200")


@attr.s(auto_attribs=True)
class PostResponsesUnionsSimpleBeforeComplexResponse200:
"""
Attributes:
a (Union[PostResponsesUnionsSimpleBeforeComplexResponse200AType1, str]):
"""

a: Union[PostResponsesUnionsSimpleBeforeComplexResponse200AType1, str]
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)

def to_dict(self) -> Dict[str, Any]:
a: Union[Dict[str, Any], str]

if isinstance(self.a, PostResponsesUnionsSimpleBeforeComplexResponse200AType1):
a = self.a.to_dict()

else:
a = self.a

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"a": a,
}
)

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
d = src_dict.copy()

def _parse_a(data: object) -> Union[PostResponsesUnionsSimpleBeforeComplexResponse200AType1, str]:
try:
if not isinstance(data, dict):
raise TypeError()
a_type_1 = PostResponsesUnionsSimpleBeforeComplexResponse200AType1.from_dict(data)

return a_type_1
except: # noqa: E722
pass
return cast(Union[PostResponsesUnionsSimpleBeforeComplexResponse200AType1, str], data)

a = _parse_a(d.pop("a"))

post_responses_unions_simple_before_complex_response_200 = cls(
a=a,
)

post_responses_unions_simple_before_complex_response_200.additional_properties = d
return post_responses_unions_simple_before_complex_response_200

@property
def additional_keys(self) -> List[str]:
return list(self.additional_properties.keys())

def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]

def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value

def __delitem__(self, key: str) -> None:
del self.additional_properties[key]

def __contains__(self, key: str) -> bool:
return key in self.additional_properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import Any, Dict, List, Type, TypeVar

import attr

T = TypeVar("T", bound="PostResponsesUnionsSimpleBeforeComplexResponse200AType1")


@attr.s(auto_attribs=True)
class PostResponsesUnionsSimpleBeforeComplexResponse200AType1:
""" """

additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)

def to_dict(self) -> Dict[str, Any]:

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
d = src_dict.copy()
post_responses_unions_simple_before_complex_response_200a_type_1 = cls()

post_responses_unions_simple_before_complex_response_200a_type_1.additional_properties = d
return post_responses_unions_simple_before_complex_response_200a_type_1

@property
def additional_keys(self) -> List[str]:
return list(self.additional_properties.keys())

def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]

def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value

def __delitem__(self, key: str) -> None:
del self.additional_properties[key]

def __contains__(self, key: str) -> bool:
return key in self.additional_properties
27 changes: 27 additions & 0 deletions end_to_end_tests/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,33 @@
}
}
},
"/responses/unions/simple_before_complex": {
"post": {
"tags": ["responses"],
"description": "Regression test for #603",
"responses": {
"200": {
"description": "A union with simple types before complex ones.",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["a"],
"properties": {
"a": {
"oneOf": [
{"type": "string"},
{"type": "object"}
]
}
}
}
}
}
}
}
}
},
"/auth/token_with_cookie": {
"get": {
"tags": [
Expand Down
2 changes: 1 addition & 1 deletion openapi_python_client/templates/model.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ field_dict: Dict[str, Any] = {}
{% endif %}
{% if prop_template and prop_template.transform %}
for prop_name, prop in self.additional_properties.items():
{{ prop_template.transform(model.additional_properties, "prop", "field_dict[prop_name]", multipart=multipart) | indent(4) }}
{{ prop_template.transform(model.additional_properties, "prop", "field_dict[prop_name]", multipart=multipart, declare_type=false) | indent(4) }}
{% elif multipart %}
field_dict.update({
key: (None, str(value).encode(), "text/plain")
Expand Down
Loading