2023-03-21 11:08:13 +00:00
|
|
|
from typing import Any, Iterable, Tuple, Type, Union
|
|
|
|
|
|
|
|
from boltons.urlutils import URL
|
2023-03-21 16:31:43 +00:00
|
|
|
from ereuse_devicehub.ereuse_utils.test import JSON
|
|
|
|
from ereuse_devicehub.ereuse_utils.test import Client as EreuseUtilsClient
|
|
|
|
from ereuse_devicehub.ereuse_utils.test import Res
|
2023-03-21 11:08:13 +00:00
|
|
|
from werkzeug.exceptions import HTTPException
|
|
|
|
|
|
|
|
from ereuse_devicehub.teal.marshmallow import ValidationError
|
|
|
|
|
|
|
|
Status = Union[int, Type[HTTPException], Type[ValidationError]]
|
|
|
|
Query = Iterable[Tuple[str, Any]]
|
|
|
|
|
|
|
|
|
|
|
|
class Client(EreuseUtilsClient):
|
|
|
|
"""A REST interface to a Teal app."""
|
|
|
|
|
|
|
|
def open(
|
|
|
|
self,
|
|
|
|
uri: str,
|
|
|
|
res: str = None,
|
|
|
|
status: Status = 200,
|
|
|
|
query: Query = tuple(),
|
|
|
|
accept=JSON,
|
|
|
|
content_type=JSON,
|
|
|
|
item=None,
|
|
|
|
headers: dict = None,
|
|
|
|
token: str = None,
|
|
|
|
**kw,
|
|
|
|
) -> Res:
|
|
|
|
headers = headers or {}
|
|
|
|
if res:
|
|
|
|
resource_url = self.application.resources[res].url_prefix + '/'
|
|
|
|
uri = URL(uri).navigate(resource_url).to_text()
|
|
|
|
if token:
|
|
|
|
headers['Authorization'] = 'Basic {}'.format(token)
|
|
|
|
res = super().open(
|
|
|
|
uri, status, query, accept, content_type, item, headers, **kw
|
|
|
|
)
|
|
|
|
# ereuse-utils checks for status code
|
|
|
|
# here we check for specific type
|
|
|
|
# (when response: {'type': 'foobar', 'code': 422})
|
|
|
|
_status = getattr(status, 'code', status)
|
|
|
|
if not isinstance(status, int) and res[1].status_code == _status:
|
|
|
|
assert (
|
|
|
|
status.__name__ == res[0]['type']
|
|
|
|
), 'Expected exception {0} but it was {1}'.format(
|
|
|
|
status.__name__, res[0]['type']
|
|
|
|
)
|
|
|
|
return res
|
|
|
|
|
|
|
|
def get(
|
|
|
|
self,
|
|
|
|
uri: str = '',
|
|
|
|
res: str = None,
|
|
|
|
query: Query = tuple(),
|
|
|
|
status: Status = 200,
|
|
|
|
item=None,
|
|
|
|
accept: str = JSON,
|
|
|
|
headers: dict = None,
|
|
|
|
token: str = None,
|
|
|
|
**kw,
|
|
|
|
) -> Res:
|
|
|
|
"""
|
|
|
|
Performs GET.
|
|
|
|
|
|
|
|
:param uri: The uri where to GET from. This is optional, as you
|
|
|
|
can build the URI too through ``res`` and ``item``.
|
|
|
|
:param res: The resource where to GET from, if any.
|
|
|
|
If this is set, the client will try to get the
|
|
|
|
url from the resource definition.
|
|
|
|
:param query: The query params in a dict. This method
|
|
|
|
automatically converts the dict to URL params,
|
|
|
|
and if the dict had nested dictionaries, those
|
|
|
|
are converted to JSON.
|
|
|
|
:param status: A status code or exception to assert.
|
|
|
|
:param item: The id of a resource to GET from, if any.
|
|
|
|
:param accept: The accept headers. By default
|
|
|
|
``application/json``.
|
|
|
|
:param headers: A dictionary of header name - header value.
|
|
|
|
:param token: A token to add to an ``Authentication`` header.
|
|
|
|
:return: A tuple containing 1. a dict (if content-type is JSON)
|
|
|
|
or a str with the data, and 2. the ``Response`` object.
|
|
|
|
"""
|
|
|
|
kw['res'] = res
|
|
|
|
kw['token'] = token
|
|
|
|
return super().get(uri, query, item, status, accept, headers, **kw)
|
|
|
|
|
|
|
|
def post(
|
|
|
|
self,
|
|
|
|
data: str or dict,
|
|
|
|
uri: str = '',
|
|
|
|
res: str = None,
|
|
|
|
query: Query = tuple(),
|
|
|
|
status: Status = 201,
|
|
|
|
content_type: str = JSON,
|
|
|
|
accept: str = JSON,
|
|
|
|
headers: dict = None,
|
|
|
|
token: str = None,
|
|
|
|
**kw,
|
|
|
|
) -> Res:
|
|
|
|
kw['res'] = res
|
|
|
|
kw['token'] = token
|
|
|
|
return super().post(
|
|
|
|
uri, data, query, status, content_type, accept, headers, **kw
|
|
|
|
)
|
|
|
|
|
|
|
|
def patch(
|
|
|
|
self,
|
|
|
|
data: str or dict,
|
|
|
|
uri: str = '',
|
|
|
|
res: str = None,
|
|
|
|
query: Query = tuple(),
|
|
|
|
item=None,
|
|
|
|
status: Status = 200,
|
|
|
|
content_type: str = JSON,
|
|
|
|
accept: str = JSON,
|
|
|
|
token: str = None,
|
|
|
|
headers: dict = None,
|
|
|
|
**kw,
|
|
|
|
) -> Res:
|
|
|
|
kw['res'] = res
|
|
|
|
kw['token'] = token
|
|
|
|
return super().patch(
|
|
|
|
uri, data, query, status, content_type, item, accept, headers, **kw
|
|
|
|
)
|
|
|
|
|
|
|
|
def put(
|
|
|
|
self,
|
|
|
|
data: str or dict,
|
|
|
|
uri: str = '',
|
|
|
|
res: str = None,
|
|
|
|
query: Query = tuple(),
|
|
|
|
item=None,
|
|
|
|
status: Status = 201,
|
|
|
|
content_type: str = JSON,
|
|
|
|
accept: str = JSON,
|
|
|
|
token: str = None,
|
|
|
|
headers: dict = None,
|
|
|
|
**kw,
|
|
|
|
) -> Res:
|
|
|
|
kw['res'] = res
|
|
|
|
kw['token'] = token
|
|
|
|
return super().put(
|
|
|
|
uri, data, query, status, content_type, item, accept, headers, **kw
|
|
|
|
)
|
|
|
|
|
|
|
|
def delete(
|
|
|
|
self,
|
|
|
|
uri: str = '',
|
|
|
|
res: str = None,
|
|
|
|
query: Query = tuple(),
|
|
|
|
status: Status = 204,
|
|
|
|
item=None,
|
|
|
|
accept: str = JSON,
|
|
|
|
headers: dict = None,
|
|
|
|
token: str = None,
|
|
|
|
**kw,
|
|
|
|
) -> Res:
|
|
|
|
kw['res'] = res
|
|
|
|
kw['token'] = token
|
|
|
|
return super().delete(uri, query, item, status, accept, headers, **kw)
|
|
|
|
|
|
|
|
def post_get(
|
|
|
|
self,
|
|
|
|
res: str,
|
|
|
|
data: str or dict,
|
|
|
|
query: Query = tuple(),
|
|
|
|
status: Status = 200,
|
|
|
|
content_type: str = JSON,
|
|
|
|
accept: str = JSON,
|
|
|
|
headers: dict = None,
|
|
|
|
key='id',
|
|
|
|
token: str = None,
|
|
|
|
**kw,
|
|
|
|
) -> Res:
|
|
|
|
"""Performs post and then gets the resource through its key."""
|
|
|
|
r, _ = self.post(
|
|
|
|
'', data, res, query, status, content_type, accept, token, headers, **kw
|
|
|
|
)
|
|
|
|
return self.get(res=res, item=r[key])
|