Skip to content

Commit

Permalink
Merge pull request #72 from artsmolin/path-allof
Browse files Browse the repository at this point in the history
Add supporting of allOf in properties
  • Loading branch information
artsmolin authored Oct 13, 2023
2 parents 9c9c374 + 11a04ae commit 3f7d0c5
Show file tree
Hide file tree
Showing 13 changed files with 557 additions and 8 deletions.
2 changes: 1 addition & 1 deletion examples/petstore/client_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#
# Generator info:
# GitHub Page: https://github.com/artsmolin/pythogen
# Version: 0.2.30
# Version: 0.2.31
# ==============================================================================

# jinja2: lstrip_blocks: "True"
Expand Down
2 changes: 1 addition & 1 deletion examples/petstore/client_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#
# Generator info:
# GitHub Page: https://github.com/artsmolin/pythogen
# Version: 0.2.30
# Version: 0.2.31
# ==============================================================================

# jinja2: lstrip_blocks: "True"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pythogen"
version = "0.2.30"
version = "0.2.31"
description = "Generator of python HTTP-clients from OpenApi specification."
homepage = "https://github.com/artsmolin/pythogen"
repository = "https://github.com/artsmolin/pythogen"
Expand Down
10 changes: 10 additions & 0 deletions pythogen/parsers/parameters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import re
from typing import Any

from pythogen import console
from pythogen import exceptions
from pythogen import models
from pythogen.parsers.references import RefResolver
from pythogen.parsers.schemas import SchemaParser
Expand Down Expand Up @@ -39,6 +41,14 @@ def parse_item(self, id_: str, data: dict[str, Any]) -> models.ParameterObject:
match = re.search(r"(__safety_key__)\((?P<safety_key>.+)\)", description)
safety_key = match['safety_key'] if match else None

if len(schema.all_of) > 1:
console.print_error(
title="Failed to generate a client",
msg="\"allOf\" field in property can contains only one item.",
invalid_data=data,
)
raise exceptions.Exit()

return models.ParameterObject(
id=id_,
orig_key=data['name'],
Expand Down
3 changes: 3 additions & 0 deletions pythogen/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ def j2_typerepr(schema: models.SchemaObject, document: models.Document) -> str:
elif schema.discriminator:
representation = " | ".join((j2_typerepr(item, document) for item in schema.discriminator.mapping.values()))

if schema.all_of and len(schema.all_of) == 1:
representation = j2_typerepr(schema.all_of[0], document)

return representation


Expand Down
101 changes: 100 additions & 1 deletion tests/clients/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#
# Generator info:
# GitHub Page: https://github.com/artsmolin/pythogen
# Version: 0.2.30
# Version: 0.2.31
# ==============================================================================

# jinja2: lstrip_blocks: "True"
Expand Down Expand Up @@ -181,6 +181,14 @@ class EmptyBody(BaseModel):
text: str


class GetMessageHeaders(BaseModel):
model_config = ConfigDict(
populate_by_name=True, # Addressing by field name, even if there is an alias.
)

x_auth_token: str = Field(alias="X-Auth-Token")


class GetObjectNoRefSchemaPathParams(BaseModel):
model_config = ConfigDict(
populate_by_name=True, # Addressing by field name, even if there is an alias.
Expand Down Expand Up @@ -671,6 +679,19 @@ class GetObjectResp(BaseModel):
animal: AnimalObj | None = None


class GetMessageResp(BaseModel):
"""
GetMessageResp
"""

model_config = ConfigDict(
populate_by_name=True, # Addressing by field name, even if there is an alias.
)
title: str | None = None
text: str | None = None


class Dog(BaseModel):
"""
Dog
Expand Down Expand Up @@ -780,6 +801,83 @@ def __init__(
self.logs_integration = logs_integration
self.client_name = client_name

async def getMessage(
self,
auth: BasicAuth | None = None,
content: str | bytes | None = None,
headers: GetMessageHeaders | dict[str, Any] | None = None,
meta: PythogenMetaBox | None = None,
) -> GetMessageResp | None:
"""
GET /messages
Operation ID: getMessage
Summary: Get message
Description: None
"""

method = "get"

path = "/messages"

url = f"{self.base_url}{path}"

params = None

headers_ = self.headers.copy()

if isinstance(headers, GetMessageHeaders):
headers_ = headers.model_dump(by_alias=True, exclude_none=True)
elif isinstance(headers, dict):
headers_ = headers

if auth is None:
auth_ = DEFAULT_AUTH
elif isinstance(auth, httpx.Auth):
auth_ = auth
else:
auth_ = (auth.username, auth.password)

try:
response = await self.client.request(
method, url, headers=headers_, params=params, content=content, auth=auth_
)
except Exception as exc:
if self.metrics_integration:
if self.metrics_integration.shadow_path():
metrics_path = "/messages"
else:
metrics_path = path
self.metrics_integration.on_request_error(self.client_name, exc, method, metrics_path)

raise exc

if self.metrics_integration:
if self.metrics_integration.shadow_path():
metrics_path = "/messages"
else:
metrics_path = path
self.metrics_integration.on_request_success(self.client_name, response, method, metrics_path)

req = RequestBox(
client_name=self.client_name,
method=method,
url=url,
params=params,
headers=headers_,
content=content,
)

resp = ResponseBox(
status_code=response.status_code,
)

if meta:
meta.request = req
meta.response = resp

if response.status_code == 200:
return GetMessageResp.model_validate(response.json())

async def get_object_no_ref_schema(
self,
path_params: GetObjectNoRefSchemaPathParams | dict[str, Any],
Expand Down Expand Up @@ -2617,6 +2715,7 @@ def _parse_any_of(self, item: dict[str, Any], schema_classes: list[Any]) -> Any:
PatchObjectData.model_rebuild()
PostObjectData.model_rebuild()
GetObjectResp.model_rebuild()
GetMessageResp.model_rebuild()
Dog.model_rebuild()
DogWithKind.model_rebuild()
CatWithKind.model_rebuild()
Expand Down
101 changes: 100 additions & 1 deletion tests/clients/async_client_with_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#
# Generator info:
# GitHub Page: https://github.com/artsmolin/pythogen
# Version: 0.2.30
# Version: 0.2.31
# ==============================================================================

# jinja2: lstrip_blocks: "True"
Expand Down Expand Up @@ -181,6 +181,14 @@ class EmptyBody(BaseModel):
text: str


class GetMessageHeaders(BaseModel):
model_config = ConfigDict(
populate_by_name=True, # Addressing by field name, even if there is an alias.
)

x_auth_token: str = Field(alias="X-Auth-Token")


class GetObjectNoRefSchemaPathParams(BaseModel):
model_config = ConfigDict(
populate_by_name=True, # Addressing by field name, even if there is an alias.
Expand Down Expand Up @@ -671,6 +679,19 @@ class GetObjectResp(BaseModel):
animal: AnimalObj | None = None


class GetMessageResp(BaseModel):
"""
GetMessageResp
"""

model_config = ConfigDict(
populate_by_name=True, # Addressing by field name, even if there is an alias.
)
title: str | None = None
text: str | None = None


class Dog(BaseModel):
"""
Dog
Expand Down Expand Up @@ -783,6 +804,83 @@ def __init__(
if set(["X-API-KEY", "X-API-SECRET"]) != set(self.headers):
raise RequiredHeaders("Headers ['X-API-KEY', 'X-API-SECRET'] is required")

async def getMessage(
self,
auth: BasicAuth | None = None,
content: str | bytes | None = None,
headers: GetMessageHeaders | dict[str, Any] | None = None,
meta: PythogenMetaBox | None = None,
) -> GetMessageResp | None:
"""
GET /messages
Operation ID: getMessage
Summary: Get message
Description: None
"""

method = "get"

path = "/messages"

url = f"{self.base_url}{path}"

params = None

headers_ = self.headers.copy()

if isinstance(headers, GetMessageHeaders):
headers_ = headers.model_dump(by_alias=True, exclude_none=True)
elif isinstance(headers, dict):
headers_ = headers

if auth is None:
auth_ = DEFAULT_AUTH
elif isinstance(auth, httpx.Auth):
auth_ = auth
else:
auth_ = (auth.username, auth.password)

try:
response = await self.client.request(
method, url, headers=headers_, params=params, content=content, auth=auth_
)
except Exception as exc:
if self.metrics_integration:
if self.metrics_integration.shadow_path():
metrics_path = "/messages"
else:
metrics_path = path
self.metrics_integration.on_request_error(self.client_name, exc, method, metrics_path)

raise exc

if self.metrics_integration:
if self.metrics_integration.shadow_path():
metrics_path = "/messages"
else:
metrics_path = path
self.metrics_integration.on_request_success(self.client_name, response, method, metrics_path)

req = RequestBox(
client_name=self.client_name,
method=method,
url=url,
params=params,
headers=headers_,
content=content,
)

resp = ResponseBox(
status_code=response.status_code,
)

if meta:
meta.request = req
meta.response = resp

if response.status_code == 200:
return GetMessageResp.model_validate(response.json())

async def get_object_no_ref_schema(
self,
path_params: GetObjectNoRefSchemaPathParams | dict[str, Any],
Expand Down Expand Up @@ -2620,6 +2718,7 @@ def _parse_any_of(self, item: dict[str, Any], schema_classes: list[Any]) -> Any:
PatchObjectData.model_rebuild()
PostObjectData.model_rebuild()
GetObjectResp.model_rebuild()
GetMessageResp.model_rebuild()
Dog.model_rebuild()
DogWithKind.model_rebuild()
CatWithKind.model_rebuild()
Expand Down
Loading

0 comments on commit 3f7d0c5

Please sign in to comment.