Skip to content

Commit ff0669c

Browse files
committed
Release 0.0.45
1 parent b40ca33 commit ff0669c

File tree

5 files changed

+189
-8
lines changed

5 files changed

+189
-8
lines changed

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
[tool.poetry]
33
name = "ravendev"
4-
version = "0.0.44"
4+
version = "0.0.45"
55
description = ""
66
authors = []
77
packages = [
@@ -11,9 +11,9 @@ packages = [
1111
[tool.poetry.dependencies]
1212
python = "^3.7"
1313
httpx = "0.23.3"
14-
backports-cached_property = "1.0.2"
15-
pydantic = "^1.9.2"
1614
types-backports = "0.1.3"
15+
pydantic = "^1.9.2"
16+
backports-cached_property = "1.0.2"
1717

1818
[tool.poetry.dev-dependencies]
1919
mypy = "0.971"

src/raven/api/client.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
from .core.jsonable_encoder import jsonable_encoder
1414
from .core.remove_none_from_headers import remove_none_from_headers
1515
from .errors.event_not_found_error import EventNotFoundError
16-
from .resources.device.client import DeviceClient
16+
from .resources.device.client import AsyncDeviceClient, DeviceClient
1717
from .resources.ids.types.app_id import AppId
18-
from .resources.user.client import UserClient
18+
from .resources.user.client import AsyncUserClient, UserClient
1919
from .types.batch_event import BatchEvent
2020
from .types.event_not_found_error_body import EventNotFoundErrorBody
2121
from .types.event_override import EventOverride
@@ -81,3 +81,65 @@ def device(self) -> DeviceClient:
8181
@cached_property
8282
def user(self) -> UserClient:
8383
return UserClient(environment=self._environment, auth_key=self.auth_key)
84+
85+
86+
class AsyncRavenApi:
87+
def __init__(self, *, environment: RavenApiEnvironment, auth_key: str):
88+
self._environment = environment
89+
self.auth_key = auth_key
90+
91+
async def send(
92+
self,
93+
app_id: AppId,
94+
*,
95+
event: str,
96+
data: typing.Dict[str, typing.Any],
97+
user: typing.Optional[User] = None,
98+
schedule_at: typing.Optional[int] = None,
99+
override: typing.Optional[EventOverride] = None,
100+
idempotency_key: typing.Optional[str] = None,
101+
) -> SendEventResponse:
102+
async with httpx.AsyncClient() as _client:
103+
_response = await _client.request(
104+
"POST",
105+
urllib.parse.urljoin(f"{self._environment}/", f"v1/apps/{app_id}/events/send"),
106+
json=jsonable_encoder(
107+
{"event": event, "data": data, "user": user, "scheduleAt": schedule_at, "override": override}
108+
),
109+
headers=remove_none_from_headers({"Idempotency-Key": idempotency_key, "Authorization": self.auth_key}),
110+
)
111+
if 200 <= _response.status_code < 300:
112+
return pydantic.parse_obj_as(SendEventResponse, _response.json()) # type: ignore
113+
if _response.status_code == 404:
114+
raise EventNotFoundError(pydantic.parse_obj_as(EventNotFoundErrorBody, _response.json())) # type: ignore
115+
try:
116+
_response_json = _response.json()
117+
except JSONDecodeError:
118+
raise ApiError(status_code=_response.status_code, body=_response.text)
119+
raise ApiError(status_code=_response.status_code, body=_response_json)
120+
121+
async def send_bulk(
122+
self, app_id: AppId, *, event: str, batch: typing.List[BatchEvent], idempotency_key: typing.Optional[str] = None
123+
) -> SendEventResponse:
124+
async with httpx.AsyncClient() as _client:
125+
_response = await _client.request(
126+
"POST",
127+
urllib.parse.urljoin(f"{self._environment}/", f"v1/apps/{app_id}/events/bulk_send"),
128+
json=jsonable_encoder({"event": event, "batch": batch}),
129+
headers=remove_none_from_headers({"Idempotency-Key": idempotency_key, "Authorization": self.auth_key}),
130+
)
131+
if 200 <= _response.status_code < 300:
132+
return pydantic.parse_obj_as(SendEventResponse, _response.json()) # type: ignore
133+
try:
134+
_response_json = _response.json()
135+
except JSONDecodeError:
136+
raise ApiError(status_code=_response.status_code, body=_response.text)
137+
raise ApiError(status_code=_response.status_code, body=_response_json)
138+
139+
@cached_property
140+
def device(self) -> AsyncDeviceClient:
141+
return AsyncDeviceClient(environment=self._environment, auth_key=self.auth_key)
142+
143+
@cached_property
144+
def user(self) -> AsyncUserClient:
145+
return AsyncUserClient(environment=self._environment, auth_key=self.auth_key)

src/raven/api/resources/device/client.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,71 @@ def get_device(self, app_id: AppId, user_id: UserId, device_id: DeviceId) -> Dev
7878
except JSONDecodeError:
7979
raise ApiError(status_code=_response.status_code, body=_response.text)
8080
raise ApiError(status_code=_response.status_code, body=_response_json)
81+
82+
83+
class AsyncDeviceClient:
84+
def __init__(self, *, environment: RavenApiEnvironment, auth_key: str):
85+
self._environment = environment
86+
self.auth_key = auth_key
87+
88+
async def add(self, app_id: AppId, user_id: UserId, *, request: Device) -> Device:
89+
async with httpx.AsyncClient() as _client:
90+
_response = await _client.request(
91+
"POST",
92+
urllib.parse.urljoin(f"{self._environment}/", f"v1/apps/{app_id}/users/{user_id}/devices"),
93+
json=jsonable_encoder(request),
94+
headers=remove_none_from_headers({"Authorization": self.auth_key}),
95+
)
96+
if 200 <= _response.status_code < 300:
97+
return pydantic.parse_obj_as(Device, _response.json()) # type: ignore
98+
try:
99+
_response_json = _response.json()
100+
except JSONDecodeError:
101+
raise ApiError(status_code=_response.status_code, body=_response.text)
102+
raise ApiError(status_code=_response.status_code, body=_response_json)
103+
104+
async def update(self, app_id: AppId, user_id: UserId, device_id: DeviceId, *, request: Device) -> Device:
105+
async with httpx.AsyncClient() as _client:
106+
_response = await _client.request(
107+
"PUT",
108+
urllib.parse.urljoin(f"{self._environment}/", f"v1/apps/{app_id}/users/{user_id}/devices/{device_id}"),
109+
json=jsonable_encoder(request),
110+
headers=remove_none_from_headers({"Authorization": self.auth_key}),
111+
)
112+
if 200 <= _response.status_code < 300:
113+
return pydantic.parse_obj_as(Device, _response.json()) # type: ignore
114+
try:
115+
_response_json = _response.json()
116+
except JSONDecodeError:
117+
raise ApiError(status_code=_response.status_code, body=_response.text)
118+
raise ApiError(status_code=_response.status_code, body=_response_json)
119+
120+
async def delete(self, app_id: AppId, user_id: UserId, device_id: DeviceId) -> None:
121+
async with httpx.AsyncClient() as _client:
122+
_response = await _client.request(
123+
"DELETE",
124+
urllib.parse.urljoin(f"{self._environment}/", f"v1/apps/{app_id}/users/{user_id}/devices/{device_id}"),
125+
headers=remove_none_from_headers({"Authorization": self.auth_key}),
126+
)
127+
if 200 <= _response.status_code < 300:
128+
return
129+
try:
130+
_response_json = _response.json()
131+
except JSONDecodeError:
132+
raise ApiError(status_code=_response.status_code, body=_response.text)
133+
raise ApiError(status_code=_response.status_code, body=_response_json)
134+
135+
async def get_device(self, app_id: AppId, user_id: UserId, device_id: DeviceId) -> Device:
136+
async with httpx.AsyncClient() as _client:
137+
_response = await _client.request(
138+
"GET",
139+
urllib.parse.urljoin(f"{self._environment}/", f"v1/apps/{app_id}/users/{user_id}/devices/{device_id}"),
140+
headers=remove_none_from_headers({"Authorization": self.auth_key}),
141+
)
142+
if 200 <= _response.status_code < 300:
143+
return pydantic.parse_obj_as(Device, _response.json()) # type: ignore
144+
try:
145+
_response_json = _response.json()
146+
except JSONDecodeError:
147+
raise ApiError(status_code=_response.status_code, body=_response.text)
148+
raise ApiError(status_code=_response.status_code, body=_response_json)

src/raven/api/resources/user/client.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,48 @@ def get(self, app_id: AppId, user_id: UserId) -> RavenUser:
5757
except JSONDecodeError:
5858
raise ApiError(status_code=_response.status_code, body=_response.text)
5959
raise ApiError(status_code=_response.status_code, body=_response_json)
60+
61+
62+
class AsyncUserClient:
63+
def __init__(self, *, environment: RavenApiEnvironment, auth_key: str):
64+
self._environment = environment
65+
self.auth_key = auth_key
66+
67+
async def create_or_update(
68+
self,
69+
app_id: AppId,
70+
*,
71+
user_id: UserId,
72+
mobile: typing.Optional[str] = None,
73+
email: typing.Optional[str] = None,
74+
whats_app: typing.Optional[str] = None,
75+
) -> RavenUser:
76+
async with httpx.AsyncClient() as _client:
77+
_response = await _client.request(
78+
"POST",
79+
urllib.parse.urljoin(f"{self._environment}/", f"v1/apps/{app_id}/users"),
80+
json=jsonable_encoder({"user_id": user_id, "mobile": mobile, "email": email, "whats_app": whats_app}),
81+
headers=remove_none_from_headers({"Authorization": self.auth_key}),
82+
)
83+
if 200 <= _response.status_code < 300:
84+
return pydantic.parse_obj_as(RavenUser, _response.json()) # type: ignore
85+
try:
86+
_response_json = _response.json()
87+
except JSONDecodeError:
88+
raise ApiError(status_code=_response.status_code, body=_response.text)
89+
raise ApiError(status_code=_response.status_code, body=_response_json)
90+
91+
async def get(self, app_id: AppId, user_id: UserId) -> RavenUser:
92+
async with httpx.AsyncClient() as _client:
93+
_response = await _client.request(
94+
"GET",
95+
urllib.parse.urljoin(f"{self._environment}/", f"v1/apps/{app_id}/users/{user_id}"),
96+
headers=remove_none_from_headers({"Authorization": self.auth_key}),
97+
)
98+
if 200 <= _response.status_code < 300:
99+
return pydantic.parse_obj_as(RavenUser, _response.json()) # type: ignore
100+
try:
101+
_response_json = _response.json()
102+
except JSONDecodeError:
103+
raise ApiError(status_code=_response.status_code, body=_response.text)
104+
raise ApiError(status_code=_response.status_code, body=_response_json)

src/raven/api/types/email_override.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import datetime as dt
44
import typing
55

6+
import pydantic
7+
68
from ..core.datetime_utils import serialize_datetime
79
from .attachment import Attachment
810
from .channel_override import ChannelOverride
@@ -11,17 +13,21 @@
1113

1214

1315
class EmailOverride(ChannelOverride):
14-
from: EmailRecipient
16+
from_: EmailRecipient = pydantic.Field(alias="from")
1517
cc: typing.List[EmailRecipient]
1618
bcc: typing.List[EmailRecipient]
1719
attachments: typing.List[Attachment]
1820
message: EmailMessage
21+
1922
def json(self, **kwargs: typing.Any) -> str:
20-
kwargs_with_defaults: typing.Any = { "by_alias": True, "exclude_unset": True, **kwargs }
23+
kwargs_with_defaults: typing.Any = {"by_alias": True, "exclude_unset": True, **kwargs}
2124
return super().json(**kwargs_with_defaults)
25+
2226
def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
23-
kwargs_with_defaults: typing.Any = { "by_alias": True, "exclude_unset": True, **kwargs }
27+
kwargs_with_defaults: typing.Any = {"by_alias": True, "exclude_unset": True, **kwargs}
2428
return super().dict(**kwargs_with_defaults)
29+
2530
class Config:
2631
frozen = True
32+
allow_population_by_field_name = True
2733
json_encoders = {dt.datetime: serialize_datetime}

0 commit comments

Comments
 (0)