Skip to content

Commit 864021b

Browse files
committed
Raise an error in generated clients when receiving an unknown response code
1 parent e8f31dc commit 864021b

File tree

6 files changed

+30
-2
lines changed

6 files changed

+30
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## 0.1.1 - 2020-03-06
88
- Fix mypy issue in generated models `from_dict` with datetime or reference properties
9+
- Generated clients now raise an `ApiResponseError` if they receive a response that was not declared
910

1011
## 0.1.0 - 2020-02-28
1112
- Initial Release

openapi_python_client/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ def _build_api(self) -> None:
110110
api_dir.mkdir()
111111
api_init = api_dir / "__init__.py"
112112
api_init.write_text('""" Contains all methods for accessing the API """')
113+
114+
api_errors = api_dir / "errors.py"
115+
errors_template = self.env.get_template("errors.pyi")
116+
api_errors.write_text(errors_template.render())
117+
113118
endpoint_template = self.env.get_template("endpoint_module.pyi")
114119
for tag, collection in self.openapi.endpoint_collections_by_tag.items():
115120
module_path = api_dir / f"{tag}.py"

openapi_python_client/templates/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ Things to know:
3232
1. All path/query params, and bodies become method arguments.
3333
1. If your endpoint had any tags on it, the first tag will be used as a module name for the function (my_tag above)
3434
1. Any endpoint which did not have a tag will be in `{{ package_name }}.api.default`
35+
1. If the API returns a response code that was not declared in the OpenAPI document, a
36+
`{{ package_name }}.api.errors.ApiResponseError` wil be raised
37+
with the `response` attribute set to the `requests.Response` that was received.

openapi_python_client/templates/endpoint_module.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ from typing import Dict, List, Optional, Union
33

44
import requests
55

6+
from .errors import ApiResponseError
67
from ..client import AuthenticatedClient, Client
78

89
{% for relative in collection.relative_imports %}
@@ -69,4 +70,6 @@ def {{ endpoint.name }}(
6970
if response.status_code == {{ response.status_code }}:
7071
return {{ response.constructor() }}
7172
{% endfor %}
73+
else:
74+
raise ApiResponseError(response=response)
7275
{% endfor %}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from requests import Response
2+
3+
4+
class ApiResponseError(Exception):
5+
""" An exception raised when an unknown response occurs """
6+
7+
def __init__(self, *, response: Response):
8+
super().__init__()
9+
self.response: Response = response

tests/test___init__.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ def test__build_models(self, mocker):
220220

221221
def test__build_api(self, mocker):
222222
import pathlib
223+
from jinja2 import Template
223224
from openapi_python_client import _Project, OpenAPI
224225

225226
openapi = mocker.MagicMock(autospec=OpenAPI, title="My Test API")
@@ -232,10 +233,12 @@ def test__build_api(self, mocker):
232233
project.package_dir = mocker.MagicMock()
233234
client_path = mocker.MagicMock()
234235
api_init = mocker.MagicMock(autospec=pathlib.Path)
236+
api_errors = mocker.MagicMock(autospec=pathlib.Path)
235237
collection_1_path = mocker.MagicMock(autospec=pathlib.Path)
236238
collection_2_path = mocker.MagicMock(autospec=pathlib.Path)
237239
api_paths = {
238240
"__init__.py": api_init,
241+
"errors.py": api_errors,
239242
f"{tag_1}.py": collection_1_path,
240243
f"{tag_2}.py": collection_2_path,
241244
}
@@ -246,10 +249,12 @@ def test__build_api(self, mocker):
246249
"api": api_dir,
247250
}
248251
project.package_dir.__truediv__.side_effect = lambda x: package_paths[x]
249-
client_template = mocker.MagicMock()
250-
endpoint_template = mocker.MagicMock()
252+
client_template = mocker.MagicMock(autospec=Template)
253+
errors_template = mocker.MagicMock(autospec=Template)
254+
endpoint_template = mocker.MagicMock(autospec=Template)
251255
templates = {
252256
"client.pyi": client_template,
257+
"errors.pyi": errors_template,
253258
"endpoint_module.pyi": endpoint_template,
254259
}
255260
mocker.patch.object(project.env, "get_template", autospec=True, side_effect=lambda x: templates[x])
@@ -268,6 +273,8 @@ def test__build_api(self, mocker):
268273
api_dir.mkdir.assert_called_once()
269274
api_dir.__truediv__.assert_has_calls([mocker.call(key) for key in api_paths])
270275
api_init.write_text.assert_called_once_with('""" Contains all methods for accessing the API """')
276+
errors_template.render.assert_called_once()
277+
api_errors.write_text.assert_called_once_with(errors_template.render())
271278
endpoint_template.render.assert_has_calls(
272279
[mocker.call(collection=collection_1), mocker.call(collection=collection_2),]
273280
)

0 commit comments

Comments
 (0)