Skip to content

Commit

Permalink
Merge pull request #10 from CrazyProger1/experiments
Browse files Browse the repository at this point in the history
V0.0.6
  • Loading branch information
CrazyProger1 authored Apr 29, 2024
2 parents b4df3f4 + 2963506 commit 6517869
Show file tree
Hide file tree
Showing 22 changed files with 662 additions and 249 deletions.
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ from resty.clients.httpx import RESTClient
async def main():
client = RESTClient(httpx.AsyncClient(base_url="https://localhost:8000"))

response = await UserManager.create(
client=client,
manager = UserManager(client=client)

response = await manager.create(
obj=UserCreateSchema(
username="admin",
email="[email protected]",
Expand All @@ -111,44 +112,44 @@ async def main():
)
print(response) # id=1 username='admin' email='[email protected]' age=19

response = await UserManager.read(
client=client,
response = await manager.read(
response_type=UserReadSchema,
)

for obj in response:
print(obj) # id=1 username='admin' email='[email protected]' age=19

response = await UserManager.read_one(
client=client,
response = await manager.read_one(
obj_or_pk=1,
response_type=UserReadSchema,
)

print(response) # id=1 username='admin' email='[email protected]' age=19

response = await UserManager.update(
client=client,
obj=UserUpdateSchema(id=1, username="admin123", ),
response = await manager.update(
obj=UserUpdateSchema(
id=1,
username="admin123",
),
response_type=UserReadSchema,
)

print(response) # id=1 username='admin123' email='[email protected]' age=19

await UserManager.delete(
client=client,
await manager.delete(
obj_or_pk=1,
expected_status=204,
)



if __name__ == "__main__":
asyncio.run(main())
```

## Status

``0.0.5`` - **RELEASED**
``0.0.6`` - **RELEASED**

## Licence

Expand Down
34 changes: 34 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,37 @@
- Improved test coverage to 100%
- Improved architecture
- Added examples

## v0.0.6

- Changed Manager API:

Now instantiating manager is required.

You can pass the REST Client into the constructor:

```python
manager = UserManager(client=client)

response = await manager.read(
response_type=UserReadSchema,
)
```

or specify the client explicitly during calling:

```python
manager = UserManager()

response = await manager.read(
response_type=UserReadSchema,
client=client,
)
```

- Added Django pagination middlewares:

- `LimitOffsetPaginationMiddleware`
- `PagePaginationMiddleware`


22 changes: 11 additions & 11 deletions examples/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ class UserManager(Manager):
async def main():
client = RESTClient(httpx.AsyncClient(base_url="https://localhost:8000"))

response = await UserManager.create(
client=client,
manager = UserManager(client=client)

response = await manager.create(
obj=UserCreateSchema(
username="admin",
email="[email protected]",
Expand All @@ -55,32 +56,31 @@ async def main():
)
print(response) # id=1 username='admin' email='[email protected]' age=19

response = await UserManager.read(
client=client,
response = await manager.read(
response_type=UserReadSchema,
)

for obj in response:
print(obj) # id=1 username='admin' email='[email protected]' age=19

response = await UserManager.read_one(
client=client,
response = await manager.read_one(
obj_or_pk=1,
response_type=UserReadSchema,
)

print(response) # id=1 username='admin' email='[email protected]' age=19

response = await UserManager.update(
client=client,
obj=UserUpdateSchema(id=1, username="admin123", ),
response = await manager.update(
obj=UserUpdateSchema(
id=1,
username="admin123",
),
response_type=UserReadSchema,
)

print(response) # id=1 username='admin123' email='[email protected]' age=19

await UserManager.delete(
client=client,
await manager.delete(
obj_or_pk=1,
expected_status=204,
)
Expand Down
File renamed without changes.
70 changes: 70 additions & 0 deletions examples/middlewares/django.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import asyncio

import httpx

from resty.enums import Endpoint, Field
from resty.types import Schema
from resty.managers import Manager
from resty.clients.httpx import RESTClient
from resty.ext.django.middlewares.pagination import (
LimitOffsetPaginationMiddleware,
PagePaginationMiddleware,
)


class UserCreateSchema(Schema):
username: str
email: str
password: str
age: int


class UserReadSchema(Schema):
id: int
username: str
email: str
age: int


class UserUpdateSchema(Schema):
username: str = None
email: str = None


class UserManager(Manager):
endpoints = {
Endpoint.CREATE: "users/",
Endpoint.READ: "users/",
Endpoint.READ_ONE: "users/{pk}",
Endpoint.UPDATE: "users/{pk}",
Endpoint.DELETE: "users/{pk}",
}
fields = {
Field.PRIMARY: "id",
}


async def main():
client = RESTClient(httpx.AsyncClient(base_url="https://localhost:8000"))

# Using LimitOffset pagination middleware
with client.middlewares.middleware(LimitOffsetPaginationMiddleware(limit=200)):
manager = UserManager(client=client)

paginated_response = await manager.read(
response_type=UserReadSchema,
offset=100,
)

# Using Page pagination middleware
with client.middlewares.middleware(PagePaginationMiddleware()):
manager = UserManager(client=client)

paginated_response = await manager.read(
response_type=UserReadSchema,
page=3,
)


if __name__ == "__main__":
asyncio.run(main())
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "resty-client"
version = "0.0.5"
version = "0.0.6"
description = "RestyClient is a simple, easy-to-use Python library for interacting with REST APIs using Pydantic's powerful data validation and deserialization tools."
authors = ["CrazyProger1 <[email protected]>"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion resty/__version__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__title__ = "Resty-Client"
__version__ = "0.0.5"
__version__ = "0.0.6"
__description__ = """RestyClient is a simple, easy-to-use Python library for interacting with REST APIs using Pydantic's
powerful data validation and deserialization tools."""
13 changes: 7 additions & 6 deletions resty/clients/httpx/clients.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from urllib.parse import urljoin

import httpx

Expand All @@ -20,10 +21,10 @@
class RESTClient(BaseRESTClient):

def __init__(
self,
httpx_client: httpx.AsyncClient = None,
check_status: bool = True,
middleware_manager: BaseMiddlewareManager = None,
self,
httpx_client: httpx.AsyncClient = None,
check_status: bool = True,
middleware_manager: BaseMiddlewareManager = None,
):
self.middlewares = middleware_manager or MiddlewareManager()
self._xclient = httpx_client or httpx.AsyncClient()
Expand All @@ -45,7 +46,7 @@ async def _make_xrequest(self, request: Request) -> httpx.Response:
timeout=request.timeout,
)
except httpx.ConnectError:
raise ConnectError(url=request.url)
raise ConnectError(url=urljoin(str(self._xclient.base_url), request.url))

@staticmethod
def _extract_json_data(xresponse: httpx.Response) -> dict | list:
Expand All @@ -57,7 +58,7 @@ def _extract_json_data(xresponse: httpx.Response) -> dict | list:
return data

async def _parse_xresponse(
self, request: Request, xresponse: httpx.Response
self, request: Request, xresponse: httpx.Response
) -> Response:
return Response(
request=request,
Expand Down
Empty file added resty/ext/__init__.py
Empty file.
Empty file added resty/ext/django/__init__.py
Empty file.
Empty file.
6 changes: 6 additions & 0 deletions resty/ext/django/middlewares/pagination/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .middlewares import LimitOffsetPaginationMiddleware, PagePaginationMiddleware

__all__ = [
"LimitOffsetPaginationMiddleware",
"PagePaginationMiddleware",
]
1 change: 1 addition & 0 deletions resty/ext/django/middlewares/pagination/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DEFAULT_LIMIT = 100
58 changes: 58 additions & 0 deletions resty/ext/django/middlewares/pagination/middlewares.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from abc import ABC, abstractmethod
from typing import Container

from resty.enums import Endpoint
from resty.types import Request, Response
from resty.middlewares import BaseRequestMiddleware, BaseResponseMiddleware

from .constants import DEFAULT_LIMIT


class PaginationMiddleware(BaseRequestMiddleware, BaseResponseMiddleware, ABC):
def __init__(self, endpoints: Container[Endpoint] = None):
self._endpoints = endpoints or {
Endpoint.READ,
}

@abstractmethod
async def paginate(self, request: Request, **kwargs): # pragma: nocover
...

async def unpaginate(self, response: Response, **kwargs):
response.json = response.json.get("results", response.json)

async def _handle_request(self, request: Request, **kwargs):
if request.endpoint in self._endpoints:
await self.paginate(request=request, **kwargs)

async def _handle_response(self, response: Response, **kwargs):
if response.request.endpoint in self._endpoints:
await self.unpaginate(response=response, **kwargs)

async def __call__(self, reqresp: Request | Response, **kwargs):
if isinstance(reqresp, Request):
return await self._handle_request(request=reqresp, **kwargs)
return await self._handle_response(response=reqresp, **kwargs)


class LimitOffsetPaginationMiddleware(PaginationMiddleware):
def __init__(self, limit: int = DEFAULT_LIMIT, **kwargs):
self._limit = limit
super().__init__(**kwargs)

async def paginate(self, request: Request, **kwargs):
request.params.update(
{
"limit": kwargs.pop("limit", self._limit),
"offset": kwargs.pop("offset", 0),
}
)


class PagePaginationMiddleware(PaginationMiddleware):
async def paginate(self, request: Request, **kwargs):
request.params.update(
{
"page": kwargs.pop("page", 1),
}
)
11 changes: 4 additions & 7 deletions resty/managers/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,19 @@ def _normalize_url(cls, url: str | None) -> str:
if not url:
return ""

if not url.endswith('/'):
return url + '/'
if not url.endswith("/"):
return url + "/"
return url

@classmethod
def build(
cls, endpoints: Endpoints, endpoint: Endpoint, base_url: str = None, **kwargs
cls, endpoints: Endpoints, endpoint: Endpoint, base_url: str = None, **kwargs
) -> str:

endpoint_url = cls._get_endpoint_url(endpoints=endpoints, endpoint=endpoint)

if endpoint_url:
url = urljoin(
cls._normalize_url(url=base_url),
endpoint_url
)
url = urljoin(cls._normalize_url(url=base_url), endpoint_url)
else:
url = base_url or ""

Expand Down
Loading

0 comments on commit 6517869

Please sign in to comment.