Skip to content

Commit 3ecda49

Browse files
committed
Better error handling when fetching documents (#128)
1 parent 3cca302 commit 3ecda49

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Fixes
1010
- Relative paths are now allowed in securitySchemes/OAuthFlow/tokenUrl (#130).
1111
- Schema validation errors will no longer print a stack trace (#131).
12-
12+
- Invalid YAML/URL will no longer print stack trace (#128)
1313

1414
## 0.5.0 - 2020-08-05
1515
### Changes

openapi_python_client/__init__.py

+15-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pathlib import Path
88
from typing import Any, Dict, Optional, Sequence, Union
99

10+
import httpcore
1011
import httpx
1112
import yaml
1213
from jinja2 import Environment, PackageLoader
@@ -27,6 +28,8 @@
2728

2829
def _get_project_for_url_or_path(url: Optional[str], path: Optional[Path]) -> Union[_Project, GeneratorError]:
2930
data_dict = _get_document(url=url, path=path)
31+
if isinstance(data_dict, GeneratorError):
32+
return data_dict
3033
openapi = GeneratorData.from_dict(data_dict)
3134
if isinstance(openapi, GeneratorError):
3235
return openapi
@@ -70,18 +73,24 @@ def update_existing_client(*, url: Optional[str], path: Optional[Path]) -> Seque
7073
return project.update()
7174

7275

73-
def _get_document(*, url: Optional[str], path: Optional[Path]) -> Dict[str, Any]:
76+
def _get_document(*, url: Optional[str], path: Optional[Path]) -> Union[Dict[str, Any], GeneratorError]:
7477
yaml_bytes: bytes
7578
if url is not None and path is not None:
76-
raise ValueError("Provide URL or Path, not both.")
79+
return GeneratorError(header="Provide URL or Path, not both.")
7780
if url is not None:
78-
response = httpx.get(url)
79-
yaml_bytes = response.content
81+
try:
82+
response = httpx.get(url)
83+
yaml_bytes = response.content
84+
except (httpx.HTTPError, httpcore.NetworkError):
85+
return GeneratorError(header="Could not get OpenAPI document from provided URL")
8086
elif path is not None:
8187
yaml_bytes = path.read_bytes()
8288
else:
83-
raise ValueError("No URL or Path provided")
84-
return yaml.safe_load(yaml_bytes)
89+
return GeneratorError(header="No URL or Path provided")
90+
try:
91+
return yaml.safe_load(yaml_bytes)
92+
except yaml.YAMLError:
93+
return GeneratorError(header="Invalid YAML from provided source")
8594

8695

8796
class _Project:

tests/test___init__.py

+52-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import pathlib
22

3+
import httpcore
34
import jinja2
45
import pytest
6+
import yaml
57

68
from openapi_python_client import GeneratorError
79

@@ -44,6 +46,23 @@ def test__get_project_for_url_or_path_generator_error(mocker):
4446
assert project == error
4547

4648

49+
def test__get_project_for_url_or_path_document_error(mocker):
50+
error = GeneratorError()
51+
_get_document = mocker.patch("openapi_python_client._get_document", return_value=error)
52+
53+
from_dict = mocker.patch("openapi_python_client.parser.GeneratorData.from_dict")
54+
url = mocker.MagicMock()
55+
path = mocker.MagicMock()
56+
57+
from openapi_python_client import _get_project_for_url_or_path
58+
59+
project = _get_project_for_url_or_path(url=url, path=path)
60+
61+
_get_document.assert_called_once_with(url=url, path=path)
62+
from_dict.assert_not_called()
63+
assert project == error
64+
65+
4766
def test_create_new_client(mocker):
4867
project = mocker.MagicMock()
4968
_get_project_for_url_or_path = mocker.patch(
@@ -118,9 +137,9 @@ def test__get_document_no_url_or_path(self, mocker):
118137

119138
from openapi_python_client import _get_document
120139

121-
with pytest.raises(ValueError):
122-
_get_document(url=None, path=None)
140+
result = _get_document(url=None, path=None)
123141

142+
assert result == GeneratorError(header="No URL or Path provided")
124143
get.assert_not_called()
125144
Path.assert_not_called()
126145
loads.assert_not_called()
@@ -132,13 +151,28 @@ def test__get_document_url_and_path(self, mocker):
132151

133152
from openapi_python_client import _get_document
134153

135-
with pytest.raises(ValueError):
136-
_get_document(url=mocker.MagicMock(), path=mocker.MagicMock())
154+
result = _get_document(url=mocker.MagicMock(), path=mocker.MagicMock())
137155

156+
assert result == GeneratorError(header="Provide URL or Path, not both.")
138157
get.assert_not_called()
139158
Path.assert_not_called()
140159
loads.assert_not_called()
141160

161+
def test__get_document_bad_url(self, mocker):
162+
get = mocker.patch("httpx.get", side_effect=httpcore.NetworkError)
163+
Path = mocker.patch("openapi_python_client.Path")
164+
loads = mocker.patch("yaml.safe_load")
165+
166+
from openapi_python_client import _get_document
167+
168+
url = mocker.MagicMock()
169+
result = _get_document(url=url, path=None)
170+
171+
assert result == GeneratorError(header="Could not get OpenAPI document from provided URL")
172+
get.assert_called_once_with(url)
173+
Path.assert_not_called()
174+
loads.assert_not_called()
175+
142176
def test__get_document_url_no_path(self, mocker):
143177
get = mocker.patch("httpx.get")
144178
Path = mocker.patch("openapi_python_client.Path")
@@ -166,6 +200,20 @@ def test__get_document_path_no_url(self, mocker):
166200
path.read_bytes.assert_called_once()
167201
loads.assert_called_once_with(path.read_bytes())
168202

203+
def test__get_document_bad_yaml(self, mocker):
204+
get = mocker.patch("httpx.get")
205+
loads = mocker.patch("yaml.safe_load", side_effect=yaml.YAMLError)
206+
207+
from openapi_python_client import _get_document
208+
209+
path = mocker.MagicMock()
210+
result = _get_document(url=None, path=path)
211+
212+
get.assert_not_called()
213+
path.read_bytes.assert_called_once()
214+
loads.assert_called_once_with(path.read_bytes())
215+
assert result == GeneratorError(header="Invalid YAML from provided source")
216+
169217

170218
class TestProject:
171219
def test___init__(self, mocker):

0 commit comments

Comments
 (0)