Skip to content

Commit 3cc506a

Browse files
committed
feat: first async call
1 parent c48bc8e commit 3cc506a

6 files changed

Lines changed: 94 additions & 19 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typeguard = ">=2.13.3"
4949
xdoctest = {extras = ["colors"], version = ">=0.15.10"}
5050
myst-parser = {version = ">=0.16.1"}
5151
respx = "^0.20.1"
52+
pytest-asyncio = "^0.20.3"
5253

5354
[tool.coverage.paths]
5455
source = ["src", "*/site-packages"]

src/checkedid/client.py

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,31 @@
99
from httpx import Request
1010
from httpx import Response
1111

12+
from . import endpoints
13+
from . import errors
1214
from . import models
13-
from .errors import CheckedIDAuthenticationError
14-
from .errors import CheckedIDError
15-
from .errors import CheckedIDNotFoundError
16-
from .errors import CheckedIDValidationError
1715

1816

1917
_T = TypeVar("_T")
2018

2119

2220
class Client:
23-
ERROR_RESPONSE_MAPPING: Dict[int, Type[CheckedIDError]] = {
24-
422: CheckedIDValidationError,
25-
403: CheckedIDAuthenticationError,
26-
404: CheckedIDNotFoundError,
21+
ERROR_RESPONSE_MAPPING: Dict[int, Type[errors.CheckedIDError]] = {
22+
422: errors.CheckedIDValidationError,
23+
403: errors.CheckedIDAuthenticationError,
24+
404: errors.CheckedIDNotFoundError,
2725
}
2826

2927
def __init__(self, customer_code: str, base_url: str = "https://api.checkedid.eu/"):
30-
self.httpx = httpx.Client(base_url=base_url, auth=self.authenticate_request)
28+
self.client: Optional[httpx.Client] = None
29+
self.base_url = base_url
30+
self.create_client(base_url)
3131
self.access_token: Optional[str] = None
3232
self.customer_code = customer_code
3333

34+
def create_client(self, base_url):
35+
self.httpx = httpx.Client(base_url=base_url, auth=self.authenticate_request)
36+
3437
def authenticate_request(self, request: Request) -> Request:
3538
if self.access_token:
3639
request.headers["Authorization"] = f"Bearer {self.access_token}"
@@ -112,7 +115,7 @@ def dossier_with_scope(
112115

113116
def handle_error_response(self, response: Response) -> None:
114117
if response.status_code == 400:
115-
raise CheckedIDValidationError(
118+
raise errors.CheckedIDValidationError(
116119
response.text, status_code=response.status_code
117120
)
118121

@@ -128,8 +131,37 @@ def handle_error_response(self, response: Response) -> None:
128131
status_code=response.status_code, json=json, message="Error from server"
129132
)
130133

131-
def map_exception(self, response: Response) -> Type[CheckedIDError]:
134+
def map_exception(self, response: Response) -> Type[errors.CheckedIDError]:
132135
exception_type = self.ERROR_RESPONSE_MAPPING.get(
133-
response.status_code, CheckedIDError
136+
response.status_code, errors.CheckedIDError
134137
)
135138
return exception_type
139+
140+
141+
class ClientAsync(Client):
142+
"""for asyncio"""
143+
144+
def create_client(self, base_url):
145+
self.client = httpx.AsyncClient(base_url=base_url)
146+
147+
async def dossier(self, dossier_number: str) -> endpoints.DossierEndpoint.response:
148+
response = await self.client.get(
149+
url=endpoints.DossierEndpoint.url(dossier_number=dossier_number)
150+
)
151+
152+
return self.process_response(response, endpoints.DossierEndpoint.response)
153+
154+
def close(self):
155+
self.client.aclose()
156+
157+
def open(self):
158+
self.create_client(self.base_url)
159+
160+
def __enter__(self):
161+
"""Open the httpx client"""
162+
self.open()
163+
return self
164+
165+
def __exit__(self, exc_type, exc_val, exc_tb):
166+
"""Close the httpx client"""
167+
self.close()

src/checkedid/endpoints.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Type
2+
3+
from pydantic import BaseModel
4+
5+
from checkedid import models
6+
7+
8+
class Endpoint:
9+
path: str
10+
request: Type[BaseModel]
11+
response = Type[BaseModel]
12+
13+
14+
class DossierEndpoint(Endpoint):
15+
path = "/report/{dossier_number}"
16+
request = None
17+
response = models.ReportResponse
18+
19+
@classmethod
20+
def url(cls, **kwargs):
21+
return cls.path.format(**kwargs)

tests/conftest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,17 @@ def auth_client(client: Client, access_token_mock) -> Client:
5050
assert response.access_token
5151

5252
return client
53+
54+
55+
@pytest.fixture
56+
def dossier_number():
57+
return "999999-8888800"
58+
59+
60+
@pytest.fixture()
61+
def dossier_response_200(respx_mock, dossier_number):
62+
respx_mock.get("").mock(
63+
return_value=Response(
64+
status_code=200, json={"DossierNumber": dossier_number, "ReportPDF": ""}
65+
)
66+
)

tests/test_async_dossiers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
3+
from checkedid.client import ClientAsync
4+
5+
6+
@pytest.mark.asyncio
7+
async def test_dossiers(customer_code, dossier_response_200):
8+
client = ClientAsync(customer_code)
9+
10+
with client as client:
11+
response = await client.dossier("999999-8888800")
12+
13+
assert response.DossierNumber == "999999-8888800"

tests/test_dossiers.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@
44
from checkedid import errors
55

66

7-
def test_dossier(auth_client, respx_mock):
8-
dossier_number = "999999-8888800"
9-
respx_mock.get("").mock(
10-
return_value=Response(
11-
status_code=200, json={"DossierNumber": dossier_number, "ReportPDF": ""}
12-
)
13-
)
7+
def test_dossier(auth_client, respx_mock, dossier_response_200, dossier_number):
148
response = auth_client.dossier(dossier_number)
159

1610
assert response.DossierNumber == dossier_number

0 commit comments

Comments
 (0)