Skip to content
This repository was archived by the owner on Mar 25, 2022. It is now read-only.

Develop #22

Merged
merged 5 commits into from
Sep 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 37 additions & 13 deletions src/apywrapper/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,62 @@
_api.py
"""

from typing import Any, Callable
from typing import Any, Callable, Optional

from httpx._types import HeaderTypes

from ._abc import Api
from ._request import HttpClient, make_request, make_request_function
from ._types import ApiFunc, Entity, ReturnEntity
from ._types import ApiFunc, Entity, HookFunc, ReturnEntity, SerializeFunc


class Apy(Api):
"""
Apy class
"""

def __init__(self, host: str, headers: HeaderTypes) -> None:
def __init__(
self,
host: str,
headers: HeaderTypes,
hook_func: Optional[HookFunc] = None,
serialize_func: Optional[SerializeFunc] = None,
) -> None:
self.hook_func = hook_func if hook_func else None
self.serialize_func = serialize_func if serialize_func else None
self.http_client = HttpClient(base_url=host, headers=headers)

def get(self, path: str) -> Callable[[ApiFunc], ReturnEntity]:
return make_request(path, self.http_client.get_request)
return make_request(
path, self.http_client.get_request, self.hook_func, self.serialize_func
)

def post(self, path: str) -> Callable[[ApiFunc], ReturnEntity]:
return make_request(path, self.http_client.post_request)
return make_request(
path, self.http_client.post_request, self.hook_func, self.serialize_func
)

def put(self, path: str) -> Callable[[ApiFunc], ReturnEntity]:
return make_request(path, self.http_client.put_request)
return make_request(
path, self.http_client.put_request, self.hook_func, self.serialize_func
)

def delete(self, path: str) -> Callable[[ApiFunc], ReturnEntity]:
return make_request(path, self.http_client.delete_request)
return make_request(
path, self.http_client.delete_request, self.hook_func, self.serialize_func
)

def patch(self, path: str) -> Callable[[ApiFunc], ReturnEntity]:
return make_request(path, self.http_client.patch_request)
return make_request(
path, self.http_client.patch_request, self.hook_func, self.serialize_func
)


def get(path: str) -> Callable[[ApiFunc], ReturnEntity]:
def _get(func: ApiFunc) -> ReturnEntity:
def wrapper(self: Apy, *args: Any, **kwargs: Any) -> Entity:
return make_request_function(func, self, *args, **kwargs)(
path, self.http_client.get_request
path, self.http_client.get_request, self.hook_func, self.serialize_func
)

return wrapper
Expand All @@ -51,7 +69,7 @@ def post(path: str) -> Callable[[ApiFunc], ReturnEntity]:
def _post(func: ApiFunc) -> ReturnEntity:
def wrapper(self: Apy, *args: Any, **kwargs: Any) -> Entity:
return make_request_function(func, self, *args, **kwargs)(
path, self.http_client.post_request
path, self.http_client.post_request, self.hook_func, self.serialize_func
)

return wrapper
Expand All @@ -63,7 +81,7 @@ def put(path: str) -> Callable[[ApiFunc], ReturnEntity]:
def _put(func: ApiFunc) -> ReturnEntity:
def wrapper(self: Apy, *args: Any, **kwargs: Any) -> Entity:
return make_request_function(func, self, *args, **kwargs)(
path, self.http_client.put_request
path, self.http_client.put_request, self.hook_func, self.serialize_func
)

return wrapper
Expand All @@ -75,7 +93,10 @@ def delete(path: str) -> Callable[[ApiFunc], ReturnEntity]:
def _delete(func: ApiFunc) -> ReturnEntity:
def wrapper(self: Apy, *args: Any, **kwargs: Any) -> Entity:
return make_request_function(func, self, *args, **kwargs)(
path, self.http_client.delete_request
path,
self.http_client.delete_request,
self.hook_func,
self.serialize_func,
)

return wrapper
Expand All @@ -87,7 +108,10 @@ def patch(path: str) -> Callable[[ApiFunc], ReturnEntity]:
def _patch(func: ApiFunc) -> ReturnEntity:
def wrapper(self: Apy, *args: Any, **kwargs: Any) -> Entity:
return make_request_function(func, self, *args, **kwargs)(
path, self.http_client.patch_request
path,
self.http_client.patch_request,
self.hook_func,
self.serialize_func,
)

return wrapper
Expand Down
47 changes: 34 additions & 13 deletions src/apywrapper/_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,44 @@
"""


from typing import Any, Callable, Dict, List, Optional, Type, Union
from typing import Any, Callable, Dict, List, Optional, Type, Union, cast

from dacite import from_dict
from httpx import Client, Response

from ._path import Path
from ._types import ApiFunc, Entity, EntityType, RequestFunc, ReturnEntity
from ._types import (
ApiFunc,
Entity,
EntityType,
HookFunc,
RequestFunc,
ReturnEntity,
SerializeFunc,
)
from ._utils import get_returntype_from_annotation


def serialize(
entity: Type[EntityType], json: Union[Dict, List]
) -> Union[List[EntityType], EntityType]:
if isinstance(json, list):
return [from_dict(data_class=entity, data=d) for d in json]
return [cast(EntityType, from_dict(data_class=entity, data=d)) for d in json]

return from_dict(data_class=entity, data=json)
return cast(EntityType, from_dict(data_class=entity, data=json))


def make_request_function(
func: ApiFunc, *args: Any, **kwargs: Any
) -> Callable[[str, RequestFunc], Entity]:
def wrapper(path_str: str, request_func: RequestFunc) -> Entity:
) -> Callable[
[str, RequestFunc, Optional[HookFunc], Optional[SerializeFunc]], Union[Entity, Any]
]:
def wrapper(
path_str: str,
request_func: RequestFunc,
hook_func: Optional[HookFunc] = None,
serialize_func: Optional[SerializeFunc] = None,
) -> Union[Entity, Any]:
entity = get_returntype_from_annotation(func)
params = func(*args, **kwargs)
path = Path(path_str, params)
Expand All @@ -34,17 +49,28 @@ def wrapper(path_str: str, request_func: RequestFunc) -> Entity:
entity is None or response.status_code == 204
): # entity is None or response body is None
return None
if hook_func:
return hook_func(entity, response)
elif serialize_func:
response.raise_for_status()
return serialize(entity, response.json())
response.raise_for_status()
return serialize(entity, response.json())

return wrapper


def make_request(
path_str: str, request_func: RequestFunc
path_str: str,
request_func: RequestFunc,
hook_func: Optional[HookFunc] = None,
serialize_func: Optional[SerializeFunc] = None,
) -> Callable[[ApiFunc], ReturnEntity]:
def decorator(func: ApiFunc) -> ReturnEntity:
def wrapper(*args: Any, **kwargs: Any) -> Entity:
return make_request_function(func, *args, **kwargs)(path_str, request_func)
return make_request_function(func, *args, **kwargs)(
path_str, request_func, hook_func, serialize_func
)

return wrapper

Expand All @@ -66,29 +92,24 @@ class HttpClient(Client):
def get_request(self, path: Path, params: Optional[Dict] = None) -> Response:
real_params = pick_params(path, params)
res = self.get(path, params=real_params)
res.raise_for_status()
return res

def post_request(self, path: Path, params: Optional[Dict] = None) -> Response:
params = pick_params(path, params)
res = self.post(path, json=params)
res.raise_for_status()
return res

def put_request(self, path: Path, params: Optional[Dict] = None) -> Response:
params = pick_params(path, params)
res = self.put(path, json=params)
res.raise_for_status()
return res

def delete_request(self, path: Path, params: Optional[Dict] = None) -> Response:
params = pick_params(path, params)
res = self.delete(path, params=params)
res.raise_for_status()
return res

def patch_request(self, path: Path, params: Optional[Dict] = None) -> Response:
params = pick_params(path, params)
res = self.patch(path, json=params)
res.raise_for_status()
return res
23 changes: 19 additions & 4 deletions src/apywrapper/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,36 @@
"""


from typing import Callable, Dict, List, Optional, Protocol, Union
from typing import Any, Callable, Dict, List, Optional, Protocol, Type, Union

from httpx import Response


class EntityType(Protocol):
class DataclassEntityType(Protocol):
# pylint: disable=too-few-public-methods
"""
Entity Object Type
Entity Object Type on dataclass
"""

__dataclass_fields__: Dict


class PydanticEntityType(Protocol):
"""
Entity Object Type on Pydantic
"""

__fields__: Dict


EntityType = Union[DataclassEntityType, PydanticEntityType]


Entity = Optional[Union[List[EntityType], EntityType]]
ReturnEntity = Callable[..., Entity]
ReturnEntity = Callable[..., Union[Entity, Any]]
RequestFunc = Callable[..., Response]
ApiFunc = Callable[..., Dict]
SerializeFunc = Callable[
[Type[EntityType], Union[Dict, List]], Union[List[EntityType], EntityType],
]
HookFunc = Callable[[Type[EntityType], Response], Any]