Skip to content

Commit 452740d

Browse files
committed
Add ability to throw exception on failed responses
1 parent 0b84ee9 commit 452740d

File tree

8 files changed

+639
-590
lines changed

8 files changed

+639
-590
lines changed

openapi_python_client/parser/properties/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from itertools import chain
1515
from typing import Any, ClassVar, Dict, Generic, Iterable, List, Optional, Set, Tuple, TypeVar, Union
16+
from enum import Enum
1617

1718
import attr
1819

@@ -35,6 +36,12 @@
3536
)
3637

3738

39+
class ResponseType(Enum):
40+
AUTO = 'auto' # Automatically decide whether a response is a failed or success response - every response within the rance [200, 300) will be considered success, others as failed
41+
FAILED = 'failed' # Explicitly specified failed response
42+
SUCCESS = 'success' # Explicitly specified success response
43+
44+
3845
@attr.s(auto_attribs=True, frozen=True)
3946
class AnyProperty(Property):
4047
"""A property that can be any type (used for empty schemas)"""

openapi_python_client/parser/responses.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .. import schema as oai
1010
from ..utils import PythonIdentifier
1111
from .errors import ParseError, PropertyError
12-
from .properties import AnyProperty, Property, Schemas, property_from_data
12+
from .properties import ResponseType, AnyProperty, Property, Schemas, property_from_data
1313

1414

1515
@attr.s(auto_attribs=True, frozen=True)
@@ -19,6 +19,7 @@ class Response:
1919
status_code: HTTPStatus
2020
prop: Property
2121
source: str
22+
failed_response: bool
2223
data: object
2324

2425

@@ -50,6 +51,7 @@ def empty_response(
5051
description=description,
5152
example=None,
5253
),
54+
failed_response=status_code < HTTPStatus.OK or status_code >= HTTPStatus.MULTIPLE_CHOICES,
5355
source="None",
5456
)
5557

@@ -96,6 +98,15 @@ def response_from_data(
9698
schemas,
9799
)
98100

101+
response_type_val = schema_data['x-response-type'] if 'x-response-type' in schema_data else ResponseType.AUTO.value
102+
if response_type_val not in ResponseType._value2member_map_:
103+
return ParseError(data=data, detail=f"Unsupported x-response-type: {response_type}"), schemas
104+
response_type = ResponseType(response_type_val)
105+
if response_type == ResponseType.AUTO:
106+
failed_response = status_code < HTTPStatus.OK or status_code >= HTTPStatus.MULTIPLE_CHOICES
107+
else:
108+
failed_response = response_type == ResponseType.FAILED
109+
99110
prop, schemas = property_from_data(
100111
name=response_name,
101112
required=True,
@@ -108,4 +119,4 @@ def response_from_data(
108119
if isinstance(prop, PropertyError):
109120
return prop, schemas
110121

111-
return Response(status_code=status_code, prop=prop, source=source, data=data), schemas
122+
return Response(status_code=status_code, prop=prop, source=source, failed_response=failed_response, data=data), schemas

openapi_python_client/templates/client.py.jinja

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class Client:
1616
but can be set to False for testing purposes.
1717
raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
1818
status code that was not documented in the source OpenAPI document.
19+
raise_on_failed_status: Whether or not to raise an errors.FailedStatus if the API returns a
20+
status code that match a failed response (marked using x-failed-response, by default != 200).
1921
"""
2022

2123
base_url: str
@@ -24,6 +26,7 @@ class Client:
2426
timeout: float = attr.ib(5.0, kw_only=True)
2527
verify_ssl: Union[str, bool, ssl.SSLContext] = attr.ib(True, kw_only=True)
2628
raise_on_unexpected_status: bool = attr.ib(False, kw_only=True)
29+
raise_on_failed_status: bool = attr.ib(False, kw_only=True)
2730

2831
def get_headers(self) -> Dict[str, str]:
2932
""" Get headers to be used in all endpoints """

openapi_python_client/templates/endpoint_macros.py.jinja

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ Args:
161161
{% endif %}
162162
Raises:
163163
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
164+
errors.FailedStatus: If the server returns a failed status code and Client.raise_on_failed_status is True.
164165
httpx.TimeoutException: If the request takes longer than Client.timeout.
165166

166167
Returns:

openapi_python_client/templates/endpoint_module.py.jinja

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,16 @@ def _parse_response(*, client: Client, response: httpx.Response) -> Optional[{{
6868
{% else %}
6969
{{ response.prop.python_name }} = cast({{ response.prop.get_type_string() }}, {{ response.source }})
7070
{% endif %}
71+
{% if response.failed_response %}
72+
if client.raise_on_failed_status:
73+
raise errors.FailedStatus(f"Failed status code: {response.status_code}", {{ response.prop.python_name }})
74+
{% endif %}
7175
return {{ response.prop.python_name }}
7276
{% else %}
77+
{% if response.failed_response %}
78+
if client.raise_on_failed_status:
79+
raise errors.FailedStatus(f"Failed status code: {response.status_code}")
80+
{% endif %}
7381
return None
7482
{% endif %}
7583
{% endfor %}

openapi_python_client/templates/errors.py.jinja

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,10 @@ class UnexpectedStatus(Exception):
44
""" Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True """
55
...
66

7-
__all__ = ["UnexpectedStatus"]
7+
8+
class FailedStatus(Exception):
9+
""" Raised by api functions when the response status is a failed status and Client.raise_on_failed_status is True """
10+
error: object = None
11+
12+
13+
__all__ = ["UnexpectedStatus", "FailedStatus"]

0 commit comments

Comments
 (0)