Skip to content

Commit de20cff

Browse files
committed
Refactor model building, add e2e section for inline objects.
1 parent 725c056 commit de20cff

File tree

14 files changed

+265
-253
lines changed

14 files changed

+265
-253
lines changed

end_to_end_tests/golden-record/my_test_api_client/api/default/ping_ping_get.py

Lines changed: 0 additions & 90 deletions
This file was deleted.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from typing import Any, Dict
2+
3+
import httpx
4+
5+
from ...client import Client
6+
from ...types import Response
7+
8+
9+
def _get_kwargs(
10+
*,
11+
client: Client,
12+
json_body: Dict[Any, Any],
13+
) -> Dict[str, Any]:
14+
url = "{}/tests/inline_objects".format(client.base_url)
15+
16+
headers: Dict[str, Any] = client.get_headers()
17+
18+
json_json_body = json_body
19+
20+
return {
21+
"url": url,
22+
"headers": headers,
23+
"cookies": client.get_cookies(),
24+
"timeout": client.get_timeout(),
25+
"json": json_json_body,
26+
}
27+
28+
29+
def _build_response(*, response: httpx.Response) -> Response[None]:
30+
return Response(
31+
status_code=response.status_code,
32+
content=response.content,
33+
headers=response.headers,
34+
parsed=None,
35+
)
36+
37+
38+
def sync_detailed(
39+
*,
40+
client: Client,
41+
json_body: Dict[Any, Any],
42+
) -> Response[None]:
43+
kwargs = _get_kwargs(
44+
client=client,
45+
json_body=json_body,
46+
)
47+
48+
response = httpx.post(
49+
**kwargs,
50+
)
51+
52+
return _build_response(response=response)
53+
54+
55+
async def asyncio_detailed(
56+
*,
57+
client: Client,
58+
json_body: Dict[Any, Any],
59+
) -> Response[None]:
60+
kwargs = _get_kwargs(
61+
client=client,
62+
json_body=json_body,
63+
)
64+
65+
async with httpx.AsyncClient() as _client:
66+
response = await _client.post(**kwargs)
67+
68+
return _build_response(response=response)

end_to_end_tests/openapi.json

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,6 @@
66
"version": "0.1.0"
77
},
88
"paths": {
9-
"/ping": {
10-
"get": {
11-
"summary": "Ping",
12-
"description": "A quick check to see if the system is running ",
13-
"operationId": "ping_ping_get",
14-
"responses": {
15-
"200": {
16-
"description": "Successful Response",
17-
"content": {
18-
"application/json": {
19-
"schema": {
20-
"title": "Response Ping Ping Get",
21-
"type": "boolean"
22-
}
23-
}
24-
}
25-
}
26-
}
27-
}
28-
},
299
"/tests/": {
3010
"get": {
3111
"tags": ["tests"],
@@ -516,6 +496,46 @@
516496
}
517497
}
518498
}
499+
},
500+
"/tests/inline_objects": {
501+
"post": {
502+
"tags": ["tests"],
503+
"summary": "Test Inline Objects",
504+
"operationId": "test_inline_objects",
505+
"requestBody": {
506+
"description": "An inline body object",
507+
"required": true,
508+
"content": {
509+
"application/json": {
510+
"schema": {
511+
"type": "object",
512+
"properties": {
513+
"a_property": {
514+
"type": "string"
515+
}
516+
}
517+
}
518+
}
519+
}
520+
},
521+
"responses": {
522+
"200": {
523+
"description": "Inline object response",
524+
"content": {
525+
"application/json": {
526+
"schema": {
527+
"type": "object",
528+
"properties": {
529+
"a_property": {
530+
"type": "string"
531+
}
532+
}
533+
}
534+
}
535+
}
536+
}
537+
}
538+
}
519539
}
520540
},
521541
"components": {

openapi_python_client/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def _get_errors(self) -> Sequence[GeneratorError]:
9595
errors = []
9696
for collection in self.openapi.endpoint_collections_by_tag.values():
9797
errors.extend(collection.parse_errors)
98-
errors.extend(self.openapi.schemas.errors)
98+
errors.extend(self.openapi.errors)
9999
return errors
100100

101101
def _create_package(self) -> None:
@@ -148,7 +148,7 @@ def _build_models(self) -> None:
148148
imports = []
149149

150150
model_template = self.env.get_template("model.pyi")
151-
for model in self.openapi.schemas.models.values():
151+
for model in self.openapi.models.values():
152152
module_path = models_dir / f"{model.reference.module_name}.py"
153153
module_path.write_text(model_template.render(model=model))
154154
imports.append(import_string_from_reference(model.reference))

openapi_python_client/parser/model.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
""" A Model is used to generate classes from schemas. This module contains Model and helper functions for it """
2+
from dataclasses import dataclass
3+
from typing import List, Set, Union
4+
5+
from .. import schema as oai
6+
from .errors import ParseError
7+
from .properties import Property, property_from_data
8+
from .reference import Reference
9+
10+
11+
@dataclass
12+
class Model:
13+
"""
14+
A data model used by the API- usually a Schema with type "object".
15+
16+
These will all be converted to dataclasses in the client
17+
"""
18+
19+
reference: Reference
20+
required_properties: List[Property]
21+
optional_properties: List[Property]
22+
description: str
23+
relative_imports: Set[str]
24+
25+
26+
def model_from_data(*, data: oai.Schema, name: str) -> Union["Model", ParseError]:
27+
"""A single Model from its OAI data
28+
29+
Args:
30+
data: Data of a single Schema
31+
name: Name by which the schema is referenced, such as a model name.
32+
Used to infer the type name if a `title` property is not available.
33+
"""
34+
required_set = set(data.required or [])
35+
required_properties: List[Property] = []
36+
optional_properties: List[Property] = []
37+
relative_imports: Set[str] = set()
38+
39+
ref = Reference.from_ref(data.title or name)
40+
41+
for key, value in (data.properties or {}).items():
42+
required = key in required_set
43+
p = property_from_data(name=key, required=required, data=value)
44+
if isinstance(p, ParseError):
45+
return p
46+
if required:
47+
required_properties.append(p)
48+
else:
49+
optional_properties.append(p)
50+
relative_imports.update(p.get_imports(prefix=".."))
51+
52+
model = Model(
53+
reference=ref,
54+
required_properties=required_properties,
55+
optional_properties=optional_properties,
56+
relative_imports=relative_imports,
57+
description=data.description or "",
58+
)
59+
return model

0 commit comments

Comments
 (0)