Skip to content

Commit 6105da6

Browse files
committed
feat: add support for referenced request bodies
1 parent 14fb3df commit 6105da6

File tree

3 files changed

+58
-9
lines changed

3 files changed

+58
-9
lines changed

openapi_python_client/parser/openapi.py

+34-9
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
Property,
2222
Schemas,
2323
build_parameters,
24+
build_request_bodies,
2425
build_schemas,
2526
property_from_data,
2627
)
27-
from .properties.schemas import parameter_from_reference
28+
from .properties.schemas import RequestBodies, parameter_from_reference
2829
from .responses import Response, response_from_data
2930

3031
_PATH_PARAM_REGEX = re.compile("{([a-zA-Z_][a-zA-Z0-9_]*)}")
@@ -45,7 +46,12 @@ class EndpointCollection:
4546

4647
@staticmethod
4748
def from_data(
48-
*, data: Dict[str, oai.PathItem], schemas: Schemas, parameters: Parameters, config: Config
49+
*,
50+
data: Dict[str, oai.PathItem],
51+
schemas: Schemas,
52+
parameters: Parameters,
53+
request_bodies: RequestBodies,
54+
config: Config,
4955
) -> Tuple[Dict[utils.PythonIdentifier, "EndpointCollection"], Schemas, Parameters]:
5056
"""Parse the openapi paths data to get EndpointCollections by tag"""
5157
endpoints_by_tag: Dict[utils.PythonIdentifier, EndpointCollection] = {}
@@ -66,6 +72,7 @@ def from_data(
6672
tag=tag,
6773
schemas=schemas,
6874
parameters=parameters,
75+
request_bodies=request_bodies,
6976
config=config,
7077
)
7178
# Add `PathItem` parameters
@@ -192,15 +199,27 @@ def _add_body(
192199
endpoint: "Endpoint",
193200
data: oai.Operation,
194201
schemas: Schemas,
202+
request_bodies: RequestBodies,
195203
config: Config,
196204
) -> Tuple[Union[ParseError, "Endpoint"], Schemas]:
197205
"""Adds form or JSON body to Endpoint if included in data"""
198206
endpoint = deepcopy(endpoint)
199-
if data.requestBody is None or isinstance(data.requestBody, oai.Reference):
207+
if data.requestBody is None:
200208
return endpoint, schemas
201-
209+
if isinstance(data.requestBody, oai.Reference):
210+
request_body = request_bodies.bodies_by_reference.get(data.requestBody.ref, None)
211+
if request_body is None:
212+
return (
213+
ParseError(
214+
header=f"Cannot resolve request body reference {data.requestBody.ref}",
215+
data=data.requestBody,
216+
),
217+
schemas,
218+
)
219+
else:
220+
request_body = data.requestBody
202221
form_body, schemas = Endpoint.parse_request_form_body(
203-
body=data.requestBody, schemas=schemas, parent_name=endpoint.name, config=config
222+
body=request_body, schemas=schemas, parent_name=endpoint.name, config=config
204223
)
205224

206225
if isinstance(form_body, ParseError):
@@ -214,7 +233,7 @@ def _add_body(
214233
)
215234

216235
json_body, schemas = Endpoint.parse_request_json_body(
217-
body=data.requestBody, schemas=schemas, parent_name=endpoint.name, config=config
236+
body=request_body, schemas=schemas, parent_name=endpoint.name, config=config
218237
)
219238
if isinstance(json_body, ParseError):
220239
return (
@@ -227,7 +246,7 @@ def _add_body(
227246
)
228247

229248
multipart_body, schemas = Endpoint.parse_multipart_body(
230-
body=data.requestBody, schemas=schemas, parent_name=endpoint.name, config=config
249+
body=request_body, schemas=schemas, parent_name=endpoint.name, config=config
231250
)
232251
if isinstance(multipart_body, ParseError):
233252
return (
@@ -467,6 +486,7 @@ def from_data(
467486
tag: str,
468487
schemas: Schemas,
469488
parameters: Parameters,
489+
request_bodies: RequestBodies,
470490
config: Config,
471491
) -> Tuple[Union["Endpoint", ParseError], Schemas, Parameters]:
472492
"""Construct an endpoint from the OpenAPI data"""
@@ -492,7 +512,9 @@ def from_data(
492512
if isinstance(result, ParseError):
493513
return result, schemas, parameters
494514
result, schemas = Endpoint._add_responses(endpoint=result, data=data.responses, schemas=schemas, config=config)
495-
result, schemas = Endpoint._add_body(endpoint=result, data=data, schemas=schemas, config=config)
515+
result, schemas = Endpoint._add_body(
516+
endpoint=result, data=data, schemas=schemas, request_bodies=request_bodies, config=config
517+
)
496518

497519
return result, schemas, parameters
498520

@@ -547,12 +569,15 @@ def from_dict(data: Dict[str, Any], *, config: Config) -> Union["GeneratorData",
547569
return GeneratorError(header="Failed to parse OpenAPI document", detail=detail)
548570
schemas = Schemas()
549571
parameters = Parameters()
572+
request_bodies = RequestBodies()
550573
if openapi.components and openapi.components.schemas:
551574
schemas = build_schemas(components=openapi.components.schemas, schemas=schemas, config=config)
552575
if openapi.components and openapi.components.parameters:
553576
parameters = build_parameters(components=openapi.components.parameters, parameters=parameters)
577+
if openapi.components and openapi.components.requestBodies:
578+
request_bodies = build_request_bodies(components=openapi.components.requestBodies)
554579
endpoint_collections_by_tag, schemas, parameters = EndpointCollection.from_data(
555-
data=openapi.paths, schemas=schemas, parameters=parameters, config=config
580+
data=openapi.paths, schemas=schemas, parameters=parameters, request_bodies=request_bodies, config=config
556581
)
557582

558583
enums = (prop for prop in schemas.classes_by_name.values() if isinstance(prop, EnumProperty))

openapi_python_client/parser/properties/__init__.py

+15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"Schemas",
99
"build_schemas",
1010
"build_parameters",
11+
"build_request_bodies",
1112
"property_from_data",
1213
]
1314

@@ -19,6 +20,7 @@
1920
from ... import Config
2021
from ... import schema as oai
2122
from ... import utils
23+
from ...schema import Reference, RequestBody
2224
from ..errors import ParameterError, ParseError, PropertyError, ValidationError
2325
from .converter import convert, convert_chain
2426
from .enum_property import EnumProperty
@@ -27,6 +29,7 @@
2729
from .schemas import (
2830
Class,
2931
Parameters,
32+
RequestBodies,
3033
Schemas,
3134
parse_reference_path,
3235
update_parameters_with_data,
@@ -776,3 +779,15 @@ def build_parameters(
776779

777780
parameters.errors.extend(errors)
778781
return parameters
782+
783+
784+
def build_request_bodies(*, components: Dict[str, Union[RequestBody, Reference]]):
785+
786+
direct_entries = {
787+
f"#/components/requestBodies/{name}": body for name, body in components.items() if isinstance(body, RequestBody)
788+
}
789+
result = RequestBodies(bodies_by_reference=direct_entries)
790+
ref_entries: Dict[str, Reference] = {name: body for name, body in components.items() if isinstance(body, Reference)}
791+
for name, ref in ref_entries.items():
792+
result.errors.append(ParseError(detail=f"Unimplemented support for reference request body: {name}"))
793+
return result

openapi_python_client/parser/properties/schemas.py

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from ... import Config
1818
from ... import schema as oai
19+
from ...schema import RequestBody
1920
from ...schema.openapi_schema_pydantic import Parameter
2021
from ...utils import ClassName, PythonIdentifier
2122
from ..errors import ParameterError, ParseError, PropertyError
@@ -126,6 +127,14 @@ class Parameters:
126127
errors: List[ParseError] = attr.ib(factory=list)
127128

128129

130+
@attr.s(auto_attribs=True, frozen=True)
131+
class RequestBodies:
132+
"""Structure for containing all defined, shareable, and reusable request bodies"""
133+
134+
bodies_by_reference: Dict[str, RequestBody] = attr.ib(factory=dict)
135+
errors: List[ParseError] = attr.ib(factory=list)
136+
137+
129138
def parameter_from_data(
130139
*,
131140
name: str,

0 commit comments

Comments
 (0)