From 4c512e1fdca7943592e0e0576ea6ca9e4111780e Mon Sep 17 00:00:00 2001 From: Michael Helmling Date: Fri, 26 Aug 2022 14:17:35 +0200 Subject: [PATCH 1/5] feat: support inline form_body, not only refs Additionally, refactor the form_body handling to be analog to multipart_body and json_body. --- openapi_python_client/parser/openapi.py | 51 ++++++++++---- .../parser/properties/model_property.py | 1 + .../templates/endpoint_macros.py.jinja | 6 +- .../templates/endpoint_module.py.jinja | 2 +- tests/test_parser/test_openapi.py | 67 ++++++++++++------- 5 files changed, 85 insertions(+), 42 deletions(-) diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index f945e844b..067a0475f 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -118,20 +118,33 @@ class Endpoint: header_parameters: Dict[str, Property] = field(default_factory=dict) cookie_parameters: Dict[str, Property] = field(default_factory=dict) responses: List[Response] = field(default_factory=list) - form_body_class: Optional[Class] = None + form_body: Optional[Property] = None json_body: Optional[Property] = None multipart_body: Optional[Property] = None errors: List[ParseError] = field(default_factory=list) used_python_identifiers: Set[PythonIdentifier] = field(default_factory=set) @staticmethod - def parse_request_form_body(*, body: oai.RequestBody, config: Config) -> Optional[Class]: - """Return form_body_reference""" + def parse_request_form_body( + *, body: oai.RequestBody, schemas: Schemas, parent_name: str, config: Config + ) -> Tuple[Union[Property, PropertyError, None], Schemas]: + """Return form_body and updated schemas""" body_content = body.content form_body = body_content.get("application/x-www-form-urlencoded") - if form_body is not None and isinstance(form_body.media_type_schema, oai.Reference): - return Class.from_string(string=form_body.media_type_schema.ref, config=config) - return None + if form_body is not None and form_body.media_type_schema is not None: + prop, schemas = property_from_data( + name="data", + required=True, + data=form_body.media_type_schema, + schemas=schemas, + parent_name=parent_name, + config=config, + ) + if isinstance(prop, ModelProperty): + prop = attr.evolve(prop, is_form_body=True) + schemas = attr.evolve(schemas, classes_by_name={**schemas.classes_by_name, prop.class_info.name: prop}) + return prop, schemas + return None, schemas @staticmethod def parse_multipart_body( @@ -186,7 +199,20 @@ def _add_body( if data.requestBody is None or isinstance(data.requestBody, oai.Reference): return endpoint, schemas - endpoint.form_body_class = Endpoint.parse_request_form_body(body=data.requestBody, config=config) + form_body, schemas = Endpoint.parse_request_form_body( + body=data.requestBody, schemas=schemas, parent_name=endpoint.name, config=config + ) + + if isinstance(form_body, ParseError): + return ( + ParseError( + header=f"Cannot parse form body of endpoint {endpoint.name}", + detail=form_body.detail, + data=form_body.data, + ), + schemas, + ) + json_body, schemas = Endpoint.parse_request_json_body( body=data.requestBody, schemas=schemas, parent_name=endpoint.name, config=config ) @@ -213,8 +239,9 @@ def _add_body( schemas, ) - if endpoint.form_body_class: - endpoint.relative_imports.add(import_string_from_class(endpoint.form_body_class, prefix="...models")) + if form_body is not None: + endpoint.form_body = form_body + endpoint.relative_imports.update(endpoint.form_body.get_imports(prefix="...")) if multipart_body is not None: endpoint.multipart_body = multipart_body endpoint.relative_imports.update(endpoint.multipart_body.get_imports(prefix="...")) @@ -285,9 +312,9 @@ def add_parameters( config: User-provided config for overrides within parameters. Returns: - `(result, schemas)` where `result` is either an updated Endpoint containing the parameters or a ParseError - describing what went wrong. `schemas` is an updated version of the `schemas` input, adding any new enums - or classes. + `(result, schemas, parameters)` where `result` is either an updated Endpoint containing the parameters or a + ParseError describing what went wrong. `schemas` is an updated version of the `schemas` input, adding any + new enums or classes. `parameters` is an updated version of the `parameters` input, adding new parameters. See Also: - https://swagger.io/docs/specification/describing-parameters/ diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 6e68a8f8e..3b256e2e6 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -27,6 +27,7 @@ class ModelProperty(Property): template: ClassVar[str] = "model_property.py.jinja" json_is_dict: ClassVar[bool] = True is_multipart_body: bool = False + is_form_body: bool = False def get_base_type_string(self) -> str: return self.class_info.name diff --git a/openapi_python_client/templates/endpoint_macros.py.jinja b/openapi_python_client/templates/endpoint_macros.py.jinja index d90cd6242..6eb4be11c 100644 --- a/openapi_python_client/templates/endpoint_macros.py.jinja +++ b/openapi_python_client/templates/endpoint_macros.py.jinja @@ -90,8 +90,8 @@ client: AuthenticatedClient, client: Client, {% endif %} {# Form data if any #} -{% if endpoint.form_body_class %} -form_data: {{ endpoint.form_body_class.name }}, +{% if endpoint.form_body %} +form_data: {{ endpoint.form_body.get_type_string() }}, {% endif %} {# Multipart data if any #} {% if endpoint.multipart_body %} @@ -120,7 +120,7 @@ json_body: {{ endpoint.json_body.get_type_string() }}, {{ parameter.python_name }}={{ parameter.python_name }}, {% endfor %} client=client, -{% if endpoint.form_body_class %} +{% if endpoint.form_body %} form_data=form_data, {% endif %} {% if endpoint.multipart_body %} diff --git a/openapi_python_client/templates/endpoint_module.py.jinja b/openapi_python_client/templates/endpoint_module.py.jinja index e87738fa9..ab801e2bd 100644 --- a/openapi_python_client/templates/endpoint_module.py.jinja +++ b/openapi_python_client/templates/endpoint_module.py.jinja @@ -44,7 +44,7 @@ def _get_kwargs( "headers": headers, "cookies": cookies, "timeout": client.get_timeout(), - {% if endpoint.form_body_class %} + {% if endpoint.form_body %} "data": form_data.to_dict(), {% elif endpoint.multipart_body %} "files": {{ "multipart_" + endpoint.multipart_body.python_name }}, diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index cf2c77215..a124c7802 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -134,34 +134,42 @@ def make_endpoint(self): relative_imports={"import_3"}, ) - def test_parse_request_form_body(self, mocker): - ref = mocker.MagicMock() + def test_parse_request_form_body(self, mocker, model_property_factory): + from openapi_python_client.parser.properties import Class + + schema = oai.Reference.construct(ref=mocker.MagicMock()) body = oai.RequestBody.construct( - content={ - "application/x-www-form-urlencoded": oai.MediaType.construct( - media_type_schema=oai.Reference.construct(ref=ref) - ) - } + content={"application/x-www-form-urlencoded": oai.MediaType.construct(media_type_schema=schema)} + ) + class_info = Class(name="class_name", module_name="module_name") + prop_before = model_property_factory(class_info=class_info, is_form_body=False) + schemas_before = Schemas() + property_from_data = mocker.patch( + f"{MODULE_NAME}.property_from_data", return_value=(prop_before, schemas_before) ) - from_string = mocker.patch(f"{MODULE_NAME}.Class.from_string") config = mocker.MagicMock() from openapi_python_client.parser.openapi import Endpoint - result = Endpoint.parse_request_form_body(body=body, config=config) + result = Endpoint.parse_request_form_body(body=body, schemas=schemas_before, parent_name="name", config=config) - from_string.assert_called_once_with(string=ref, config=config) - assert result == from_string.return_value + property_from_data.assert_called_once_with( + name="data", required=True, data=schema, schemas=schemas_before, parent_name="name", config=config + ) + prop_after = model_property_factory(class_info=class_info, is_form_body=True) + schemas_after = Schemas(classes_by_name={class_info.name: prop_after}) + assert result == (prop_after, schemas_after) def test_parse_request_form_body_no_data(self): body = oai.RequestBody.construct(content={}) config = MagicMock() + schemas = MagicMock() from openapi_python_client.parser.openapi import Endpoint - result = Endpoint.parse_request_form_body(body=body, config=config) + result = Endpoint.parse_request_form_body(body=body, schemas=schemas, parent_name="name", config=config) - assert result is None + assert result == (None, schemas) def test_parse_multipart_body(self, mocker, model_property_factory): from openapi_python_client.parser.openapi import Endpoint, Schemas @@ -279,13 +287,13 @@ def test_add_body_no_data(self, mocker): def test_add_body_bad_json_data(self, mocker): from openapi_python_client.parser.openapi import Endpoint, Schemas - mocker.patch.object(Endpoint, "parse_request_form_body") + schemas = Schemas() + mocker.patch.object(Endpoint, "parse_request_form_body", return_value=(None, schemas)) parse_error = ParseError(data=mocker.MagicMock(), detail=mocker.MagicMock()) other_schemas = mocker.MagicMock() mocker.patch.object(Endpoint, "parse_request_json_body", return_value=(parse_error, other_schemas)) endpoint = self.make_endpoint() request_body = mocker.MagicMock() - schemas = Schemas() result = Endpoint._add_body( endpoint=endpoint, @@ -306,14 +314,14 @@ def test_add_body_bad_json_data(self, mocker): def test_add_body_bad_multipart_data(self, mocker): from openapi_python_client.parser.openapi import Endpoint, Schemas - mocker.patch.object(Endpoint, "parse_request_form_body") + schemas = Schemas() + mocker.patch.object(Endpoint, "parse_request_form_body", return_value=(None, schemas)) mocker.patch.object(Endpoint, "parse_request_json_body", return_value=(mocker.MagicMock(), mocker.MagicMock())) parse_error = ParseError(data=mocker.MagicMock(), detail=mocker.MagicMock()) other_schemas = mocker.MagicMock() mocker.patch.object(Endpoint, "parse_multipart_body", return_value=(parse_error, other_schemas)) endpoint = self.make_endpoint() request_body = mocker.MagicMock() - schemas = Schemas() result = Endpoint._add_body( endpoint=endpoint, @@ -332,13 +340,19 @@ def test_add_body_bad_multipart_data(self, mocker): ) def test_add_body_happy(self, mocker): - from openapi_python_client.parser.openapi import Class, Endpoint + from openapi_python_client.parser.openapi import Endpoint from openapi_python_client.parser.properties import Property request_body = mocker.MagicMock() config = mocker.MagicMock() - form_body_class = Class(name="A", module_name="a") - parse_request_form_body = mocker.patch.object(Endpoint, "parse_request_form_body", return_value=form_body_class) + + form_body = mocker.MagicMock(autospec=Property) + form_body_imports = mocker.MagicMock() + form_body.get_imports.return_value = {form_body_imports} + form_schemas = mocker.MagicMock() + parse_request_form_body = mocker.patch.object( + Endpoint, "parse_request_form_body", return_value=(form_body, form_schemas) + ) multipart_body = mocker.MagicMock(autospec=Property) multipart_body_imports = mocker.MagicMock() @@ -355,7 +369,6 @@ def test_add_body_happy(self, mocker): parse_request_json_body = mocker.patch.object( Endpoint, "parse_request_json_body", return_value=(json_body, json_schemas) ) - import_string_from_class = mocker.patch(f"{MODULE_NAME}.import_string_from_class", return_value="import_1") endpoint = self.make_endpoint() initial_schemas = mocker.MagicMock() @@ -368,19 +381,21 @@ def test_add_body_happy(self, mocker): ) assert response_schemas == multipart_schemas - parse_request_form_body.assert_called_once_with(body=request_body, config=config) - parse_request_json_body.assert_called_once_with( + parse_request_form_body.assert_called_once_with( body=request_body, schemas=initial_schemas, parent_name="name", config=config ) + parse_request_json_body.assert_called_once_with( + body=request_body, schemas=form_schemas, parent_name="name", config=config + ) parse_multipart_body.assert_called_once_with( body=request_body, schemas=json_schemas, parent_name="name", config=config ) - import_string_from_class.assert_called_once_with(form_body_class, prefix="...models") + form_body.get_imports.assert_called_once_with(prefix="...") json_body.get_imports.assert_called_once_with(prefix="...") multipart_body.get_imports.assert_called_once_with(prefix="...") - assert endpoint.relative_imports == {"import_1", "import_3", json_body_imports, multipart_body_imports} + assert endpoint.relative_imports == {"import_3", form_body_imports, json_body_imports, multipart_body_imports} assert endpoint.json_body == json_body - assert endpoint.form_body_class == form_body_class + assert endpoint.form_body == form_body assert endpoint.multipart_body == multipart_body def test__add_responses_status_code_error(self, mocker): From d390a2fc2f5de29da6925c9c7eb9e0e52786b3bc Mon Sep 17 00:00:00 2001 From: Michael Helmling Date: Fri, 26 Aug 2022 14:25:20 +0200 Subject: [PATCH 2/5] e2e test: add endpoint with inlined form_body schema --- .../my_test_api_client/api/tests/__init__.py | 8 ++ .../api/tests/post_form_data.py | 4 +- .../api/tests/post_form_data_inline.py | 86 +++++++++++++++++++ .../my_test_api_client/models/__init__.py | 1 + .../models/post_form_data_inline_data.py | 67 +++++++++++++++ end_to_end_tests/openapi.json | 43 +++++++++- 6 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/post_form_data_inline_data.py diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py index b0615a2d2..8924dfa7a 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py @@ -14,6 +14,7 @@ no_response_tests_no_response_get, octet_stream_tests_octet_stream_get, post_form_data, + post_form_data_inline, post_tests_json_body_string, test_inline_objects, token_with_cookie_auth_token_with_cookie_get, @@ -66,6 +67,13 @@ def post_form_data(cls) -> types.ModuleType: """ return post_form_data + @classmethod + def post_form_data_inline(cls) -> types.ModuleType: + """ + Post form data (inline schema) + """ + return post_form_data_inline + @classmethod def upload_file_tests_upload_post(cls) -> types.ModuleType: """ diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py index 7ccca1a85..c1e0021e8 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py @@ -41,7 +41,7 @@ def sync_detailed( client: Client, form_data: AFormData, ) -> Response[Any]: - """Post from data + """Post form data Post form data @@ -67,7 +67,7 @@ async def asyncio_detailed( client: Client, form_data: AFormData, ) -> Response[Any]: - """Post from data + """Post form data Post form data diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py new file mode 100644 index 000000000..f2412ee27 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data_inline.py @@ -0,0 +1,86 @@ +from typing import Any, Dict + +import httpx + +from ...client import Client +from ...models.post_form_data_inline_data import PostFormDataInlineData +from ...types import Response + + +def _get_kwargs( + *, + client: Client, + form_data: PostFormDataInlineData, +) -> Dict[str, Any]: + url = "{}/tests/post_form_data_inline".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(), + "data": form_data.to_dict(), + } + + +def _build_response(*, response: httpx.Response) -> Response[Any]: + return Response( + status_code=response.status_code, + content=response.content, + headers=response.headers, + parsed=None, + ) + + +def sync_detailed( + *, + client: Client, + form_data: PostFormDataInlineData, +) -> Response[Any]: + """Post form data (inline schema) + + Post form data (inline schema) + + Returns: + Response[Any] + """ + + kwargs = _get_kwargs( + client=client, + form_data=form_data, + ) + + response = httpx.request( + verify=client.verify_ssl, + **kwargs, + ) + + return _build_response(response=response) + + +async def asyncio_detailed( + *, + client: Client, + form_data: PostFormDataInlineData, +) -> Response[Any]: + """Post form data (inline schema) + + Post form data (inline schema) + + Returns: + Response[Any] + """ + + kwargs = _get_kwargs( + client=client, + form_data=form_data, + ) + + async with httpx.AsyncClient(verify=client.verify_ssl) as _client: + response = await _client.request(**kwargs) + + return _build_response(response=response) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py index f34a171f6..b6f6d2a83 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py @@ -40,6 +40,7 @@ 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_form_data_inline_data import PostFormDataInlineData from .post_responses_unions_simple_before_complex_response_200 import PostResponsesUnionsSimpleBeforeComplexResponse200 from .post_responses_unions_simple_before_complex_response_200a_type_1 import ( PostResponsesUnionsSimpleBeforeComplexResponse200AType1, diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/post_form_data_inline_data.py b/end_to_end_tests/golden-record/my_test_api_client/models/post_form_data_inline_data.py new file mode 100644 index 000000000..baac858b3 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/post_form_data_inline_data.py @@ -0,0 +1,67 @@ +from typing import Any, Dict, List, Type, TypeVar, Union + +import attr + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="PostFormDataInlineData") + + +@attr.s(auto_attribs=True) +class PostFormDataInlineData: + """ + Attributes: + a_required_field (str): + an_optional_field (Union[Unset, str]): + """ + + a_required_field: str + an_optional_field: Union[Unset, str] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_required_field = self.a_required_field + an_optional_field = self.an_optional_field + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "a_required_field": a_required_field, + } + ) + if an_optional_field is not UNSET: + field_dict["an_optional_field"] = an_optional_field + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + d = src_dict.copy() + a_required_field = d.pop("a_required_field") + + an_optional_field = d.pop("an_optional_field", UNSET) + + post_form_data_inline_data = cls( + a_required_field=a_required_field, + an_optional_field=an_optional_field, + ) + + post_form_data_inline_data.additional_properties = d + return post_form_data_inline_data + + @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 diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index efae13e86..0a6e151e2 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -217,7 +217,7 @@ "tags": [ "tests" ], - "summary": "Post from data", + "summary": "Post form data", "description": "Post form data", "operationId": "post_form_data", "requestBody": { @@ -242,6 +242,47 @@ } } }, + "/tests/post_form_data_inline": { + "post": { + "tags": [ + "tests" + ], + "summary": "Post form data (inline schema)", + "description": "Post form data (inline schema)", + "operationId": "post_form_data_inline", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "an_optional_field": { + "type": "string" + }, + "a_required_field": { + "type": "string" + } + }, + "required": [ + "a_required_field" + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, "/tests/upload": { "post": { "tags": [ From 9ef796270342e6f1dbce00793f94cc1edb0c5c87 Mon Sep 17 00:00:00 2001 From: Dylan Anthony <43723790+dbanty@users.noreply.github.com> Date: Sat, 27 Aug 2022 14:06:11 -0600 Subject: [PATCH 3/5] chore: Remove residual code --- openapi_python_client/parser/properties/model_property.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 3b256e2e6..6e68a8f8e 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -27,7 +27,6 @@ class ModelProperty(Property): template: ClassVar[str] = "model_property.py.jinja" json_is_dict: ClassVar[bool] = True is_multipart_body: bool = False - is_form_body: bool = False def get_base_type_string(self) -> str: return self.class_info.name From 9053cbc7a68f8c3f9cbccaa7219b1092e70fa707 Mon Sep 17 00:00:00 2001 From: Dylan Anthony Date: Sat, 27 Aug 2022 14:14:49 -0600 Subject: [PATCH 4/5] test: Fix tests I broke --- openapi_python_client/parser/openapi.py | 1 - tests/test_parser/test_openapi.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index 067a0475f..387076a71 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -141,7 +141,6 @@ def parse_request_form_body( config=config, ) if isinstance(prop, ModelProperty): - prop = attr.evolve(prop, is_form_body=True) schemas = attr.evolve(schemas, classes_by_name={**schemas.classes_by_name, prop.class_info.name: prop}) return prop, schemas return None, schemas diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index a124c7802..f09a690ae 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -142,7 +142,7 @@ def test_parse_request_form_body(self, mocker, model_property_factory): content={"application/x-www-form-urlencoded": oai.MediaType.construct(media_type_schema=schema)} ) class_info = Class(name="class_name", module_name="module_name") - prop_before = model_property_factory(class_info=class_info, is_form_body=False) + prop_before = model_property_factory(class_info=class_info) schemas_before = Schemas() property_from_data = mocker.patch( f"{MODULE_NAME}.property_from_data", return_value=(prop_before, schemas_before) @@ -156,7 +156,7 @@ def test_parse_request_form_body(self, mocker, model_property_factory): property_from_data.assert_called_once_with( name="data", required=True, data=schema, schemas=schemas_before, parent_name="name", config=config ) - prop_after = model_property_factory(class_info=class_info, is_form_body=True) + prop_after = model_property_factory(class_info=class_info) schemas_after = Schemas(classes_by_name={class_info.name: prop_after}) assert result == (prop_after, schemas_after) From 1f5d597a4067c75c64ff9a972c94baf750437004 Mon Sep 17 00:00:00 2001 From: Dylan Anthony Date: Sat, 27 Aug 2022 14:37:07 -0600 Subject: [PATCH 5/5] test: Add coverage for missing branch --- tests/test_parser/test_openapi.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index f09a690ae..0c1cd509f 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -311,6 +311,35 @@ def test_add_body_bad_json_data(self, mocker): other_schemas, ) + def test_add_body_bad_form_data(self, enum_property_factory): + from openapi_python_client.parser.openapi import Endpoint, Schemas + + schemas = Schemas( + errors=[ParseError(detail="existing error")], + ) + endpoint = self.make_endpoint() + bad_schema = oai.Schema.construct(type=oai.DataType.ARRAY) + + result = Endpoint._add_body( + endpoint=endpoint, + data=oai.Operation.construct( + requestBody=oai.RequestBody.construct( + content={"application/x-www-form-urlencoded": oai.MediaType.construct(media_type_schema=bad_schema)} + ) + ), + schemas=schemas, + config=Config(), + ) + + assert result == ( + ParseError( + detail="type array must have items defined", + header="Cannot parse form body of endpoint name", + data=bad_schema, + ), + schemas, + ) + def test_add_body_bad_multipart_data(self, mocker): from openapi_python_client.parser.openapi import Endpoint, Schemas