Skip to content

Commit

Permalink
feat: allow skipping response validation
Browse files Browse the repository at this point in the history
  • Loading branch information
uniqueg committed Nov 11, 2021
1 parent b1ef580 commit 277a449
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 20 deletions.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,23 @@ from trs_cli import TRSClient
### Configure client class

It is possible to configure the `TRSClient` class with the `.config()` class
method. Currently there is only a single configuration parameter available,
the `debug` flag. Setting it to `True` (default: False) will have the exception
handler print tracebacks for every exception encountered.
method. The following configuration parameters are available:

| Parameter | Type | Default | Description |
| --- | --- | ---- | --- |
| `debug` | `bool` | `False` | If set, the exception handler prints tracebacks for every exception encountered. |
| `no_validate` | `bool` | `False` | If set, responses are not validated. In that case, unserialized `response` objects are returned. |

Example:

```py
from trs_cli import TRSClient

TRSClient.config(debug=True)
TRSClient.config(debug=True, no_validate=True)
```

> Note that as a _class method_, the `.config()` method will affect _all_
> client instances.
> client instances, including existing ones.
### Create client instance

Expand Down
9 changes: 9 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,15 @@ def test_connection_error(self, monkeypatch):
url=MOCK_API,
)

def test_no_validation(self, requests_mock):
TRSClient.config(no_validate=True)
requests_mock.get(self.endpoint, text=MOCK_ID)
response = self.cli._send_request_and_validate_response(
url=MOCK_API,
)
assert response.text == MOCK_ID
TRSClient.config(no_validate=False)

def test_get_str_validation(self, requests_mock):
"""Test for getter with string response."""
requests_mock.get(self.endpoint, json=MOCK_ID)
Expand Down
2 changes: 1 addition & 1 deletion trs_cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = '0.6.1'
__version__ = '0.7.0'

from trs_cli.client import TRSClient # noqa: F401
51 changes: 37 additions & 14 deletions trs_cli/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,26 @@ class TRSClient():
rf"(\/versions\/(?P<version_id>{_RE_TRS_ID}))?$"
)

# class configuration variables
no_validate: bool = False

@classmethod
def config(
cls,
debug: bool = False,
):
no_validate: bool = False,
) -> None:
"""Class configuration.
Args:
debug: Set to print error tracebacks.
no_validate: Set to skip validation of error responses.
"""
if debug:
sys.excepthook = partial(exception_handler, print_traceback=True)
else:
sys.excepthook = partial(exception_handler, print_traceback=False)
cls.no_validate = no_validate

def __init__(
self,
Expand Down Expand Up @@ -1681,7 +1692,8 @@ def _validate_content_type(
"""
if requested_type not in available_types:
raise ContentTypeUnavailable(
"Requested content type not provided by the service"
f"Requested content type '{requested_type}' not provided by "
f"the service; available types: {available_types}"
)

def _send_request_and_validate_response(
Expand All @@ -1693,7 +1705,11 @@ def _send_request_and_validate_response(
validation_class_error: ModelMetaclass = Error,
method: str = 'get',
payload: Optional[Dict] = None,
) -> Optional[Union[str, ModelMetaclass, List[ModelMetaclass]]]:
) -> Optional[Union[
str,
requests.models.Response,
ModelMetaclass, List[ModelMetaclass],
]]:
"""Send a HTTP equest, validate the response and handle potential
exceptions.
Expand All @@ -1708,17 +1724,10 @@ def _send_request_and_validate_response(
method: HTTP method to use for the request.
Returns:
Unmarshalled response.
Unmarshalled response (default) or unserialized response if
class configuration flag `TRSClient.no_validate` is set.
"""
# Process parameters
validation_type = "model"
if isinstance(validation_class_ok, tuple):
validation_class_ok = validation_class_ok[0]
validation_type = "list"
elif validation_class_ok is None:
validation_type = None
elif validation_class_ok is str:
validation_type = "str"
# Validate HTTP method
try:
request_func = eval('.'.join(['requests', method]))
except AttributeError as e:
Expand All @@ -1732,7 +1741,7 @@ def _send_request_and_validate_response(
if payload is not None:
kwargs['json'] = payload

# Send request and manage response
# Send request
try:
response = request_func(**kwargs)
except (
Expand All @@ -1743,6 +1752,20 @@ def _send_request_and_validate_response(
raise requests.exceptions.ConnectionError(
"Could not connect to API endpoint"
)

# skip validation
if TRSClient.no_validate:
return response

# set validation parameters
validation_type = "model"
if isinstance(validation_class_ok, tuple):
validation_class_ok = validation_class_ok[0]
validation_type = "list"
elif validation_class_ok is None:
validation_type = None
elif validation_class_ok is str:
validation_type = "str"
if response.status_code not in [200, 201]:
logger.warning(
f"Received error response: {response.status_code}"
Expand Down

0 comments on commit 277a449

Please sign in to comment.