Skip to content

Commit 3d4ee95

Browse files
author
Cyrus Nouroozi
committed
Use HTTPX
1 parent bc88e91 commit 3d4ee95

File tree

11 files changed

+452
-1037
lines changed

11 files changed

+452
-1037
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ from clerk import Client
1717

1818

1919
async def main():
20-
async with Client("my-token") as client:
21-
users = await client.users.list()
22-
for user in users:
23-
print(f"Got user {user.id} -> {user.first_name} {user.last_name}")
20+
client = Client("my-token"):
21+
users = await client.users.list()
22+
for user in users:
23+
print(f"Got user {user.id} -> {user.first_name} {user.last_name}")
2424

2525

2626
asyncio.run(main())

clerk/client.py

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from contextlib import asynccontextmanager
2-
from typing import Any, AsyncIterator, Mapping
1+
from typing import Any, Mapping
32
import http
4-
import aiohttp
3+
54
from pydantic import BaseModel
5+
import httpx
66

77
from clerk.errors import ClerkAPIException
88

@@ -15,18 +15,12 @@ class Client:
1515
def __init__(
1616
self, token: str, base_url: str = "https://api.clerk.dev/v1/", timeout_seconds: float = 30.0
1717
) -> None:
18-
self._session = aiohttp.ClientSession(
18+
self._session = httpx.AsyncClient(
1919
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
20-
timeout=aiohttp.ClientTimeout(total=timeout_seconds),
20+
timeout=httpx.Timeout(timeout_seconds),
2121
)
2222
self._base_url = base_url
2323

24-
async def __aenter__(self):
25-
return self
26-
27-
async def __aexit__(self, exc_type, exc_val, exc_tb):
28-
await self._session.close()
29-
3024
@property
3125
def verification(self):
3226
from clerk.verification import VerificationService
@@ -57,46 +51,39 @@ def organizations(self):
5751

5852
return OrganizationsService(self)
5953

60-
@asynccontextmanager
61-
async def get(
62-
self, endpoint: str, params: Mapping[str, str] | None = None
63-
) -> AsyncIterator[aiohttp.ClientResponse]:
64-
async with self._session.get(self._make_url(endpoint), params=params) as r:
65-
await self._check_response_err(r)
66-
yield r
54+
async def get(self, endpoint: str, params: Mapping[str, str] | None = None) -> httpx.Response:
55+
r = await self._session.get(self._make_url(endpoint), params=params)
56+
await self._check_response_err(r)
57+
return r
6758

68-
@asynccontextmanager
6959
async def post(
7060
self, endpoint: str, request: BaseModel | None = None, json: Any = None
71-
) -> AsyncIterator[aiohttp.ClientResponse]:
72-
async with self._session.post(
61+
) -> httpx.Response:
62+
r = await self._session.post(
7363
self._make_url(endpoint),
74-
data=request and request.model_dump_json(),
64+
data=request.model_dump_json() if request else None,
7565
json=json,
76-
) as r:
77-
await self._check_response_err(r)
78-
yield r
66+
)
67+
await self._check_response_err(r)
68+
return r
7969

80-
@asynccontextmanager
81-
async def delete(self, endpoint: str) -> AsyncIterator[aiohttp.ClientResponse]:
82-
async with self._session.delete(self._make_url(endpoint)) as r:
83-
await self._check_response_err(r)
84-
yield r
70+
async def delete(self, endpoint: str) -> httpx.Response:
71+
r = await self._session.delete(self._make_url(endpoint))
72+
await self._check_response_err(r)
73+
return r
8574

86-
@asynccontextmanager
8775
async def patch(
8876
self, endpoint: str, request: BaseModel | None = None, json: Any = None
89-
) -> AsyncIterator[aiohttp.ClientResponse]:
90-
async with self._session.patch(
77+
) -> httpx.Response:
78+
r = await self._session.patch(
9179
self._make_url(endpoint), data=request and request.model_dump_json(), json=json
92-
) as r:
93-
await self._check_response_err(r)
94-
yield r
95-
96-
async def _check_response_err(self, r: aiohttp.ClientResponse):
97-
if http.HTTPStatus.OK <= r.status < http.HTTPStatus.BAD_REQUEST:
98-
return # no error
99-
raise await ClerkAPIException.from_response(r)
80+
)
81+
await self._check_response_err(r)
82+
return r
83+
84+
async def _check_response_err(self, r: httpx.Response):
85+
if not http.HTTPStatus.OK <= r.status_code < http.HTTPStatus.BAD_REQUEST:
86+
raise await ClerkAPIException.from_response(r)
10087

10188
def _make_url(self, endpoint: str) -> str:
10289
return f"{self._base_url.rstrip('/')}/{endpoint.strip('/')}/"

clerk/clients.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ class ClientsService(Service):
1010

1111
async def list(self) -> List[types.Client]:
1212
"""Retrieve a list of all clients"""
13-
async with self._client.get(self.endpoint) as r:
14-
return [types.Client.model_validate(s) for s in await r.json()]
13+
r = await self._client.get(self.endpoint)
14+
return [types.Client.model_validate(s) for s in r.json()["data"]]
1515

1616
async def get(self, client_id: str) -> types.Client:
1717
"""Retrieve a client by its id"""
18-
async with self._client.get(f"{self.endpoint}/{client_id}") as r:
19-
return types.Client.model_validate(await r.json())
18+
r = await self._client.get(f"{self.endpoint}/{client_id}")
19+
return types.Client.model_validate_json(r.content)
2020

2121
async def verify(self, token: str) -> types.Client:
2222
"""Verify a token and return its associated client, if valid"""
2323
request = types.VerifyRequest(token=token)
2424

25-
async with self._client.post(self.verify_endpoint, request=request) as r:
26-
return types.Client.model_validate(await r.json())
25+
r = await self._client.post(self.verify_endpoint, request=request)
26+
return types.Client.model_validate_json(r.content)

clerk/errors.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1-
import aiohttp
1+
import httpx
22

33
from clerk import types
44

55
__all__ = ["ClerkAPIException", "NoActiveSessionException"]
66

77

88
class ClerkAPIException(Exception):
9-
def __init__(self, status: int, method: str, url: str, *api_errors: types.Error) -> None:
10-
self.status = status
11-
self.method = method
9+
def __init__(self, status: int, data: str, url: str, *api_errors: types.Error) -> None:
10+
self.status_code = status
11+
self.data = data
1212
self.url = url
1313
self.api_errors = api_errors
14-
super().__init__(f"{self.method} {self.url}: {self.status} {self.api_errors!r}")
14+
super().__init__(f"{self.url}: {self.status_code} {self.api_errors!r}")
1515

1616
@classmethod
17-
async def from_response(cls, resp: aiohttp.ClientResponse) -> "ClerkAPIException":
17+
async def from_response(cls, resp: httpx.Response) -> "ClerkAPIException":
1818
try:
19-
data = await resp.json()
19+
data = resp.json()
2020
except: # noqa
2121
api_errors = []
2222
else:
2323
errors = data.get("errors", [])
2424
api_errors = [types.Error.model_validate(e) for e in errors]
2525

26-
return ClerkAPIException(resp.status, resp.method, str(resp.url), *api_errors)
26+
return ClerkAPIException(resp.status_code, resp.content, str(resp.url), *api_errors)
2727

2828

2929
class NoActiveSessionException(Exception):

clerk/organizations.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,25 @@ class OrganizationsService(Service):
99

1010
async def list(self) -> List[types.Organization]:
1111
"""Retrieve a list of all organizations"""
12-
async with self._client.get(self.endpoint) as r:
13-
s = await r.json()
14-
return [types.Organization.model_validate(s) for s in s.get("data")]
12+
r = await self._client.get(self.endpoint)
13+
return [types.Organization.model_validate(s) for s in r.json()["data"]]
1514

1615
async def get(self, organization_id: str) -> types.Organization:
1716
"""Retrieve an organization by their id"""
18-
async with self._client.get(f"{self.endpoint}/{organization_id}") as r:
19-
return types.Organization.model_validate(await r.json())
17+
r = await self._client.get(f"{self.endpoint}/{organization_id}")
18+
return types.Organization.model_validate_json(r.content)
2019

2120
async def delete(self, organization_id: str) -> types.DeleteOrganizationResponse:
2221
"""Delete an organization by their id"""
23-
async with self._client.delete(f"{self.endpoint}/{organization_id}") as r:
24-
return types.DeleteOrganizationResponse.model_validate(await r.json())
22+
r = await self._client.delete(f"{self.endpoint}/{organization_id}")
23+
return types.DeleteOrganizationResponse.model_validate_json(r.content)
2524

2625
async def update(
2726
self, organization_id: str, request: types.UpdateOrganizationRequest
2827
) -> types.Organization:
2928
"""Update an organization by their id"""
30-
async with self._client.patch(
31-
f"{self.endpoint}/{organization_id}", json=request.model_dump_json(exclude_unset=True)
32-
) as r:
33-
return types.Organization.model_validate(await r.json())
29+
r = await self._client.patch(
30+
f"{self.endpoint}/{organization_id}",
31+
json=request.model_dump_json(exclude_unset=True),
32+
)
33+
return types.Organization.model_validate_json(r.content)

clerk/sessions.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,22 @@ class SessionsService(Service):
99

1010
async def list(self) -> List[types.Session]:
1111
"""Retrieve a list of all sessions"""
12-
async with self._client.get(self.endpoint) as r:
13-
return [types.Session.model_validate(s) for s in await r.json()]
12+
r = await self._client.get(self.endpoint)
13+
return [types.Session.model_validate(s) for s in r.json()["data"]]
1414

1515
async def get(self, session_id: str) -> types.Session:
1616
"""Retrieve a session by its id"""
17-
async with self._client.get(f"{self.endpoint}/{session_id}") as r:
18-
return types.Session.model_validate(await r.json())
17+
r = await self._client.get(f"{self.endpoint}/{session_id}")
18+
return types.Session.model_validate_json(r.content)
1919

2020
async def revoke(self, session_id: str) -> types.Session:
2121
"""Revoke a session by its id"""
22-
async with self._client.post(f"{self.endpoint}/{session_id}/revoke") as r:
23-
return types.Session.model_validate(await r.json())
22+
r = await self._client.post(f"{self.endpoint}/{session_id}/revoke")
23+
return types.Session.model_validate_json(r.content)
2424

2525
async def verify(self, session_id: str, token: str) -> types.Session:
2626
"""Verify a session by its id and a given token"""
2727
request = types.VerifyRequest(token=token)
2828

29-
async with self._client.post(f"{self.endpoint}/{session_id}/verify", request=request) as r:
30-
return types.Session.model_validate(await r.json())
29+
r = await self._client.post(f"{self.endpoint}/{session_id}/verify", request=request)
30+
return types.Session.model_validate_json(r.content)

clerk/users.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,23 @@ class UsersService(Service):
99

1010
async def list(self) -> List[types.User]:
1111
"""Retrieve a list of all users"""
12-
async with self._client.get(self.endpoint) as r:
13-
return [types.User.model_validate(s) for s in await r.json()]
12+
r = await self._client.get(self.endpoint)
13+
return [types.User.model_validate(s) for s in r.json()["data"]]
1414

1515
async def get(self, user_id: str) -> types.User:
1616
"""Retrieve a user by their id"""
17-
async with self._client.get(f"{self.endpoint}/{user_id}") as r:
18-
return types.User.model_validate(await r.json())
17+
r = await self._client.get(f"{self.endpoint}/{user_id}")
18+
return types.User.model_validate_json(r.content)
1919

2020
async def delete(self, user_id: str) -> types.DeleteUserResponse:
2121
"""Delete a user by their id"""
22-
async with self._client.delete(f"{self.endpoint}/{user_id}") as r:
23-
return types.DeleteUserResponse.model_validate(await r.json())
22+
r = await self._client.delete(f"{self.endpoint}/{user_id}")
23+
return types.DeleteUserResponse.model_validate_json(r.content)
2424

2525
async def update(self, user_id: str, request: types.UpdateUserRequest) -> types.User:
2626
"""Update a user by their id"""
27-
async with self._client.patch(
28-
f"{self.endpoint}/{user_id}", json=request.model_dump_json(exclude_unset=True)
29-
) as r:
30-
return types.User.model_validate(await r.json())
27+
r = await self._client.patch(
28+
f"{self.endpoint}/{user_id}",
29+
json=request.model_dump_json(exclude_unset=True),
30+
)
31+
return types.User.model_validate_json(r.content)

0 commit comments

Comments
 (0)