Skip to content

Commit

Permalink
Merge pull request #4 from CrazyProger1/dev
Browse files Browse the repository at this point in the history
0.0.2
  • Loading branch information
CrazyProger1 authored Apr 22, 2024
2 parents 9966332 + 4cd54b7 commit 692066b
Show file tree
Hide file tree
Showing 21 changed files with 208 additions and 181 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,7 @@ cython_debug/

# MyPy
.mypy_cache


# Test File
main.py
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.PHONY: test
test:
poetry run python -m pytest tests/

.PHONY: build
build:
poetry build


.PHONY: coverage
coverage:
poetry run coverage run -m pytest tests/


.PHONY: coverage-report
coverage-report: coverage;
poetry run coverage report -m


.PHONY: format
format:
poetry run python -m black tests
poetry run python -m black resty
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# RestyClient

<p align="center">
<img src="docs/resty-cat.png" alt="resty lib logo">
<img src="https://github.com/CrazyProger1/RestyClient/blob/master/docs/resty-cat.png" alt="resty lib logo">
</p>

<p align="center">
<a href="https://github.com/CrazyProger1/RestyClient/blob/master/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/CrazyProger1/RestyClient"></a>
<a href="https://github.com/CrazyProger1/RestyClient/releases/latest"><img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/CrazyProger1/RestyClient"></a>
<a href="https://pypi.org/project/resty-client/"><img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dm/resty-client"></a>
<a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Code Style"></a>
</p>

RestyClient is a simple, easy-to-use Python library for interacting with REST APIs using Pydantic's powerful data
Expand Down Expand Up @@ -118,7 +119,7 @@ async def main():

## Status

``0.0.1`` - RELEASED
``0.0.2`` - **RELEASED**

## Licence

Expand Down
6 changes: 6 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Rest-Client Changelog

## v0.0.2

- Serializer many schemas support

7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "resty-client"
version = "0.0.1"
version = "0.0.2"
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 All @@ -12,12 +12,12 @@ packages = [
[tool.poetry.dependencies]
python = "^3.12"
pydantic = "^2.5.3"
httpx = "^0.26.0"

[tool.poetry.group.dev.dependencies]
mypy = "^1.8.0"
ruff = "^0.1.14"
pytest = "^7.4.4"
httpx = "^0.26.0"
black = "^24.4.0"

[tool.ruff]
exclude = [
Expand Down Expand Up @@ -50,7 +50,6 @@ show_error_codes = true
strict = true



[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
6 changes: 2 additions & 4 deletions resty/clients/httpx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
try:
import httpx
except ImportError:
raise ImportError('Please install httpx to use httpx rest client')
raise ImportError("Please install httpx to use httpx rest client")

from .client import RESTClient

__all__ = [
'RESTClient'
]
__all__ = ["RESTClient"]
58 changes: 31 additions & 27 deletions resty/clients/httpx/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,25 @@

import httpx

from resty.constants import (
DEFAULT_CODES,
STATUS_ERRORS
)
from resty.constants import DEFAULT_CODES, STATUS_ERRORS
from resty.types import (
BaseRESTClient,
Request,
Response,
BaseMiddleware,
BaseMiddlewareManager
)
from resty.exceptions import (
HTTPError
BaseMiddlewareManager,
)
from resty.exceptions import HTTPError
from resty.middlewares import MiddlewareManager


class RESTClient(BaseRESTClient):

def __init__(self, xclient: httpx.AsyncClient, middleware_manager: BaseMiddlewareManager = None):
def __init__(
self,
xclient: httpx.AsyncClient,
middleware_manager: BaseMiddlewareManager = None,
):
self._xclient = xclient
self._middleware_manager = middleware_manager or MiddlewareManager(
default_middlewares=None,
Expand All @@ -38,61 +37,66 @@ def _parse_xresponse(xresponse: httpx.Response) -> dict | list | None:
return data

@staticmethod
def _check_status(status: int, expected_status: int | Container[int], request: Request, url: str):
def _check_status(
status: int, expected_status: int | Container[int], request: Request, url: str,
data: dict = None
):
if status != expected_status:
if isinstance(expected_status, Container) and status in expected_status:
pass
else:
exc: type[HTTPError] = STATUS_ERRORS.get(status, HTTPError)
raise exc(
request=request,
status=status,
url=url
)
raise exc(request=request, status=status, url=url, data=data)

def add_middleware(self, middleware: BaseMiddleware):
self._middleware_manager.add_middleware(middleware=middleware)

async def request(self, request: Request, **kwargs) -> Response:
if not isinstance(request, Request):
raise TypeError('request is not of type Request')
raise TypeError("request is not of type Request")

expected_status: int = kwargs.pop('expected_status', DEFAULT_CODES.get(request.method))
check_status: bool = kwargs.pop('check_status', True)
expected_status: int = kwargs.pop(
"expected_status", DEFAULT_CODES.get(request.method)
)
check_status: bool = kwargs.pop("check_status", True)

if not isinstance(expected_status, (int, Container[int])):
raise TypeError('expected status should be type of int or Container[int]')
if not isinstance(expected_status, (int, Container)):
raise TypeError("expected status should be type of int or Container[int]")

await self._middleware_manager.call_pre_middlewares(request=request, **kwargs)

xresponse = await self._xclient.request(
method=request.method.value,
url=request.url,
headers=request.headers,
json=request.json,
data=request.data,
params=request.params,
cookies=request.cookies,
follow_redirects=request.redirects,
timeout=request.timeout
timeout=request.timeout,
)

status = xresponse.status_code

data = self._parse_xresponse(xresponse=xresponse)

if check_status:
self._check_status(
status=status,
expected_status=expected_status,
request=request,
url=str(xresponse.url)
url=str(xresponse.url),
data=data,
)

response = Response(
request=request,
status=status,
data=self._parse_xresponse(
xresponse=xresponse
)
data=data,
)

await self._middleware_manager.call_post_middlewares(
response=response, **kwargs
)
await self._middleware_manager.call_post_middlewares(response=response, **kwargs)

return response
8 changes: 4 additions & 4 deletions resty/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
UnauthorizedError,
MethodNotAllowedError,
InternalServerError,
ForbiddenError
ForbiddenError,
)

DEFAULT_CODES = {
Method.GET: 200,
Method.POST: 201,
Method.POST: {201, 200},
Method.PUT: 200,
Method.PATCH: 200,
Method.DELETE: 204
Method.DELETE: {204, 200},
}

STATUS_ERRORS = {
Expand All @@ -22,5 +22,5 @@
403: ForbiddenError,
404: NotFoundError,
405: MethodNotAllowedError,
500: InternalServerError
500: InternalServerError,
}
24 changes: 12 additions & 12 deletions resty/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@


class Endpoint(str, Enum):
BASE = 'base'
CREATE = 'create'
READ = 'read'
READ_ONE = 'read_one'
UPDATE = 'update'
DELETE = 'delete'
BASE = "base"
CREATE = "create"
READ = "read"
READ_ONE = "read_one"
UPDATE = "update"
DELETE = "delete"


class Field(str, Enum):
PRIMARY = 'primary'
PRIMARY = "primary"


class Method(str, Enum):
GET = 'GET'
POST = 'POST'
PATCH = 'PATCH'
PUT = 'PUT'
DELETE = 'DELETE'
GET = "GET"
POST = "POST"
PATCH = "PATCH"
PUT = "PUT"
DELETE = "DELETE"
5 changes: 3 additions & 2 deletions resty/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@


class HTTPError(Exception):
def __init__(self, request: Request, status: int, url: str):
def __init__(self, request: Request, status: int, url: str, data: dict):
self.request = request
self.status = status
self.url = url
super().__init__(f'{request.method.value}: {url} -> {status}')
self.data = data
super().__init__(f"{request.method.value}: {url} -> {status}")


class BadRequestError(HTTPError):
Expand Down
7 changes: 2 additions & 5 deletions resty/ext/django/middlewares/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
from .pagination import (
DjangoLimitOffsetPaginationMiddleware,
DjangoPagePaginationMiddleware
DjangoPagePaginationMiddleware,
)

__all__ = [
'DjangoLimitOffsetPaginationMiddleware',
'DjangoPagePaginationMiddleware'
]
__all__ = ["DjangoLimitOffsetPaginationMiddleware", "DjangoPagePaginationMiddleware"]
46 changes: 23 additions & 23 deletions resty/ext/django/middlewares/pagination.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
from resty.middlewares import BasePaginationMiddleware
from resty.types import (
Response,
Request
)
from resty.enums import (
Method
)
from resty.types import Response, Request
from resty.enums import Method


class DjangoPagePaginationMiddleware(BasePaginationMiddleware):
async def handle_request(self, request: Request, **kwargs):
if request.method in {Method.GET, }:
page = kwargs.pop('page', 1)
if request.method in {
Method.GET,
}:
page = kwargs.pop("page", 1)

request.params.update({
'page': page,
})
request.params.update(
{
"page": page,
}
)

async def handle_response(self, response: Response, **kwargs):
if response.request.method in {Method.GET, }:
if response.request.method in {
Method.GET,
}:
data = response.data
results = data.get('results', data)
results = data.get("results", data)
response.data = results


Expand All @@ -29,12 +30,11 @@ def __init__(self, page_size: int = 100):
self._limit = page_size

async def handle_request(self, request: Request, **kwargs):
if request.method in {Method.GET, }:
page = kwargs.pop('page', 1) - 1
limit = kwargs.pop('limit', self._limit)
offset = kwargs.pop('offset', page * self._limit)

request.params.update({
'limit': limit,
'offset': offset
})
if request.method in {
Method.GET,
}:
page = kwargs.pop("page", 1) - 1
limit = kwargs.pop("limit", self._limit)
offset = kwargs.pop("offset", page * self._limit)

request.params.update({"limit": limit, "offset": offset})
2 changes: 1 addition & 1 deletion resty/managers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .manager import Manager

__all__ = ['Manager']
__all__ = ["Manager"]
Loading

0 comments on commit 692066b

Please sign in to comment.