Skip to content

Commit

Permalink
Add option return the raw response when using the generated client
Browse files Browse the repository at this point in the history
  • Loading branch information
gacou54 committed Aug 16, 2023
1 parent 39ba2b4 commit fd73e23
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 210 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@
!tests/
!tests/**

!scripts/
!scripts/**

*.pyc
331 changes: 168 additions & 163 deletions poetry.lock

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "simple-openapi-client"
version = "0.3.1"
version = "0.4.0"
description = "OpenAPI Python client generator that follows the KISS principle."
authors = ["Gabriel Couture <[email protected]>"]
license = "BSD-3-Clause"
Expand All @@ -11,11 +11,15 @@ homepage = 'https://github.com/gacou54/openapi-client'
python = "^3.8"
httpx = "^0.24.1"
Jinja2 = "^3.1.2"
black = "^22.6.0"
black = "^23.7.0"

[tool.poetry.dev-dependencies]
pytest = "^7.1.2"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
pytest-asyncio = "^0.21.1"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
Expand Down
44 changes: 44 additions & 0 deletions scripts/make_orthanc_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import simple_openapi_client

ORTHANC_API_URL = 'https://api.orthanc-server.com/orthanc-openapi.json'


def generate_client(path: str, async_mode: bool = False):
config = simple_openapi_client.Config(client_name='AsyncOrthanc' if async_mode else 'Orthanc')

document = simple_openapi_client.parse_openapi(ORTHANC_API_URL)
document = _apply_corrections_to_documents(document)
client_str = simple_openapi_client.make_client(document, config, async_mode=async_mode)

with open(path, 'w') as file:
file.write(client_str)


def _apply_corrections_to_documents(document):
"""Correcting Orthanc OpenAPI specs"""
to_change = []

for route, path in document.paths.items():
if path.operations is not None:
for operation_name, operation in path.operations.items():
if operation.parameters is None:
continue

for param in operation.parameters:
if param.name == '...':
param.name = 'tags_path'

to_change.append({
'old_route': route,
'new_route': route + '/{tags_path}',
})

for change in to_change:
document.paths[change['new_route']] = document.paths.pop(change['old_route'])

return document


if __name__ == '__main__':
generate_client('./client.py', async_mode=False)
generate_client('./async_client.py', async_mode=True)
47 changes: 34 additions & 13 deletions simple_openapi_client/templates/async_client.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,25 @@ class {{ CLIENT }}(httpx.AsyncClient):
{%- endif -%}
"""

def __init__(self, url: str, username: Optional[str] = None, password: Optional[str] = None, headers: Optional[HeaderTypes] = None):
def __init__(self, url: str, username: Optional[str] = None, password: Optional[str] = None, headers: Optional[HeaderTypes] = None, return_raw_response: bool = False):
"""
Parameters
----------
url
server's URL
username
Orthanc's username
password
Orthanc's password
headers
Headers that will be share in all requests
return_raw_response
All Orthanc's methods will return que raw httpx.Response rather than the serialized
"""
super().__init__()
self.url = url
self.version = '{{ document.info.version }}'
self.return_raw_response = return_raw_response

if username and password:
self.setup_credentials(username, password)
Expand All @@ -60,7 +69,7 @@ class {{ CLIENT }}(httpx.AsyncClient):
route: str,
params: Optional[QueryParamTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None) -> Union[Dict, List, str, bytes, int]:
cookies: Optional[CookieTypes] = None) -> Union[Dict, List, str, bytes, int, httpx.Response]:
"""GET request with specified route

Parameters
Expand All @@ -75,11 +84,14 @@ class {{ CLIENT }}(httpx.AsyncClient):

Returns
-------
Union[Dict, List, str, bytes, int]
Serialized response of the HTTP GET request.
Union[Dict, List, str, bytes, int, httpx.Response]
Serialized response of the HTTP GET request or httpx.Response.
"""
response = await self.get(url=route, params=params, headers=headers, cookies=cookies)

if self.return_raw_response:
return response

if 200 <= response.status_code < 300:
if 'application/json' in response.headers['content-type']:
return response.json()
Expand All @@ -94,7 +106,7 @@ class {{ CLIENT }}(httpx.AsyncClient):
route: str,
params: Optional[QueryParamTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None) -> None:
cookies: Optional[CookieTypes] = None) -> Optional[httpx.Response]:
"""DELETE to specified route

Parameters
Expand All @@ -109,11 +121,14 @@ class {{ CLIENT }}(httpx.AsyncClient):

Returns
-------
None
If the HTTP DELETE request fails, HTTPError is raised.
Optional[httpx.Response]
If the HTTP DELETE request fails, HTTPError is raised, or return httpx.Response.
"""
response = await self.delete(route, params=params, headers=headers, cookies=cookies)

if self.return_raw_response:
return response

if 200 <= response.status_code < 300:
return

Expand All @@ -127,7 +142,7 @@ class {{ CLIENT }}(httpx.AsyncClient):
json: Optional[Any] = None,
params: Optional[QueryParamTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None) -> Union[Dict, List, str, bytes, int]:
cookies: Optional[CookieTypes] = None) -> Union[Dict, List, str, bytes, int, httpx.Response]:
"""POST to specified route

Parameters
Expand All @@ -145,11 +160,14 @@ class {{ CLIENT }}(httpx.AsyncClient):

Returns
-------
Union[Dict, List, str, bytes, int]
Serialized response of the HTTP POST request.
Union[Dict, List, str, bytes, int, httpx.Response]
Serialized response of the HTTP POST request or httpx.Response.
"""
response = await self.post(route, content=content, data=data, files=files, json=json, params=params, headers=headers, cookies=cookies)

if self.return_raw_response:
return response

if 200 <= response.status_code < 300:
if 'application/json' in response.headers['content-type']:
return response.json()
Expand All @@ -168,7 +186,7 @@ class {{ CLIENT }}(httpx.AsyncClient):
json: Optional[Any] = None,
params: Optional[QueryParamTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None) -> None:
cookies: Optional[CookieTypes] = None) -> Optional[httpx.Response]:
"""PUT to specified route

Parameters
Expand All @@ -186,11 +204,14 @@ class {{ CLIENT }}(httpx.AsyncClient):

Returns
-------
None
If the HTTP PUT request fails, HTTPError is raised.
Optional[httpx.Response]
If the HTTP PUT request fails, HTTPError is raised, or return httpx.Response.
"""
response = await self.put(route, content=content, data=data, files=files, json=json, params=params, headers=headers, cookies=cookies)

if self.return_raw_response:
return response

if 200 <= response.status_code < 300:
return

Expand Down
4 changes: 2 additions & 2 deletions simple_openapi_client/templates/async_method.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{% endif -%}
{%- if HEADERS -%}headers: HeaderTypes = None,
{% endif -%}
) -> {% if METHOD not in ['delete', 'put'] %}Union[Dict, List, str, bytes, int]{% else %}None{% endif %}:
) -> {% if METHOD not in ['delete', 'put'] %}Union[Dict, List, str, bytes, int, httpx.Response]{% else %}Optional[httpx.Response]{% endif %}:
"""(async) {{ OPERATION.summary }}

{{ OPERATION.description }}
Expand Down Expand Up @@ -83,7 +83,7 @@

Returns
-------
Union[Dict, List, str, bytes, int]
{% if METHOD not in ['delete', 'put'] %}Union[Dict, List, str, bytes, int, httpx.Response]{% else %}Optional[httpx.Response]{% endif %}
{%- for response in RESPONSES %}
{{ response.description }}
{%- endfor %}
Expand Down
47 changes: 34 additions & 13 deletions simple_openapi_client/templates/client.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,25 @@ class {{ CLIENT }}(httpx.Client):
{%- endif -%}
"""

def __init__(self, url: str, username: Optional[str] = None, password: Optional[str] = None, headers: Optional[HeaderTypes] = None):
def __init__(self, url: str, username: Optional[str] = None, password: Optional[str] = None, headers: Optional[HeaderTypes] = None, return_raw_response: bool = False):
"""
Parameters
----------
url
server's URL
username
Orthanc's username
password
Orthanc's password
headers
Headers that will be share in all requests
return_raw_response
All Orthanc's methods will return que raw httpx.Response rather than the serialized
"""
super().__init__()
self.url = url
self.version = '{{ document.info.version }}'
self.return_raw_response = return_raw_response

if username and password:
self.setup_credentials(username, password)
Expand All @@ -60,7 +69,7 @@ class {{ CLIENT }}(httpx.Client):
route: str,
params: Optional[QueryParamTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None) -> Union[Dict, List, str, bytes, int]:
cookies: Optional[CookieTypes] = None) -> Union[Dict, List, str, bytes, int, httpx.Response]:
"""GET request with specified route

Parameters
Expand All @@ -75,11 +84,14 @@ class {{ CLIENT }}(httpx.Client):

Returns
-------
Union[Dict, List, str, bytes, int]
Serialized response of the HTTP GET request.
Union[Dict, List, str, bytes, int, httpx.Response]
Serialized response of the HTTP GET request or httpx.Response.
"""
response = self.get(url=route, params=params, headers=headers, cookies=cookies)

if self.return_raw_response:
return response

if 200 <= response.status_code < 300:
if 'application/json' in response.headers['content-type']:
return response.json()
Expand All @@ -94,7 +106,7 @@ class {{ CLIENT }}(httpx.Client):
route: str,
params: Optional[QueryParamTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None) -> None:
cookies: Optional[CookieTypes] = None) -> Optional[httpx.Response]:
"""DELETE to specified route

Parameters
Expand All @@ -109,11 +121,14 @@ class {{ CLIENT }}(httpx.Client):

Returns
-------
None
If the HTTP DELETE request fails, HTTPError is raised.
Optional[httpx.Response]
If the HTTP DELETE request fails, HTTPError is raised, or return httpx.Response.
"""
response = self.delete(route, params=params, headers=headers, cookies=cookies)

if self.return_raw_response:
return response

if 200 <= response.status_code < 300:
return

Expand All @@ -127,7 +142,7 @@ class {{ CLIENT }}(httpx.Client):
json: Optional[Any] = None,
params: Optional[QueryParamTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None) -> Union[Dict, List, str, bytes, int]:
cookies: Optional[CookieTypes] = None) -> Union[Dict, List, str, bytes, int, httpx.Response]:
"""POST to specified route

Parameters
Expand All @@ -145,11 +160,14 @@ class {{ CLIENT }}(httpx.Client):

Returns
-------
Union[Dict, List, str, bytes, int]
Serialized response of the HTTP POST request.
Union[Dict, List, str, bytes, int, httpx.Response]
Serialized response of the HTTP POST request or httpx.Response.
"""
response = self.post(route, content=content, data=data, files=files, json=json, params=params, headers=headers, cookies=cookies)

if self.return_raw_response:
return response

if 200 <= response.status_code < 300:
if 'application/json' in response.headers['content-type']:
return response.json()
Expand All @@ -168,7 +186,7 @@ class {{ CLIENT }}(httpx.Client):
json: Optional[Any] = None,
params: Optional[QueryParamTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None) -> None:
cookies: Optional[CookieTypes] = None) -> Optional[httpx.Response]:
"""PUT to specified route

Parameters
Expand All @@ -186,11 +204,14 @@ class {{ CLIENT }}(httpx.Client):

Returns
-------
None
If the HTTP PUT request fails, HTTPError is raised.
Optional[httpx.Response]
If the HTTP PUT request fails, HTTPError is raised, or return httpx.Response.
"""
response = self.put(route, content=content, data=data, files=files, json=json, params=params, headers=headers, cookies=cookies)

if self.return_raw_response:
return response

if 200 <= response.status_code < 300:
return

Expand Down
4 changes: 2 additions & 2 deletions simple_openapi_client/templates/method.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{% endif -%}
{%- if HEADERS -%}headers: HeaderTypes = None,
{% endif -%}
) -> {% if METHOD not in ['delete', 'put'] %}Union[Dict, List, str, bytes, int]{% else %}None{% endif %}:
) -> {% if METHOD not in ['delete', 'put'] %}Union[Dict, List, str, bytes, int, httpx.Response]{% else %}Optional[httpx.Response]{% endif %}:
"""{{ OPERATION.summary }}

{{ OPERATION.description }}
Expand Down Expand Up @@ -83,7 +83,7 @@

Returns
-------
Union[Dict, List, str, bytes, int]
{% if METHOD not in ['delete', 'put'] %}Union[Dict, List, str, bytes, int, httpx.Response]{% else %}Optional[httpx.Response]{% endif %}
{%- for response in RESPONSES %}
{{ response.description }}
{%- endfor %}
Expand Down
Loading

0 comments on commit fd73e23

Please sign in to comment.