Skip to content

Commit fb18eed

Browse files
committed
[webhooks] initial changes, WIP
1 parent cc7dc2e commit fb18eed

File tree

7 files changed

+154
-3
lines changed

7 files changed

+154
-3
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ VERSION=$(shell grep '__version__' $(PACKAGE_NAME)/__init__.py | cut -d '"' -f 2
66

77
# Install pipenv and project dependencies
88
install:
9-
pipenv install --dev
9+
pipenv install --dev --categories encryption
1010

1111
# Run tests with pytest or unittest
1212
test:

android_sms_gateway/client.py

+20
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,26 @@ def get_state(self, _id: str) -> domain.MessageState:
114114
)
115115
)
116116

117+
def get_webhooks(self) -> t.List[domain.Webhook]:
118+
return [
119+
domain.Webhook.from_dict(webhook)
120+
for webhook in self.http.get(
121+
f"{self.base_url}/webhooks", headers=self.headers
122+
)
123+
]
124+
125+
def create_webhook(self, webhook: domain.Webhook) -> domain.Webhook:
126+
return domain.Webhook.from_dict(
127+
self.http.post(
128+
f"{self.base_url}/webhooks",
129+
payload=webhook.asdict(),
130+
headers=self.headers,
131+
)
132+
)
133+
134+
def delete_webhook(self, _id: str) -> None:
135+
self.http.delete(f"{self.base_url}/webhooks/{_id}", headers=self.headers)
136+
117137

118138
class AsyncAPIClient(BaseClient):
119139
def __init__(

android_sms_gateway/domain.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dataclasses
22
import typing as t
33

4-
from .enums import ProcessState
4+
from .enums import ProcessState, WebhookEvent
55

66

77
def snake_to_camel(snake_str):
@@ -63,3 +63,43 @@ def from_dict(cls, payload: t.Dict[str, t.Any]) -> "MessageState":
6363
is_hashed=payload.get("isHashed", False),
6464
is_encrypted=payload.get("isEncrypted", False),
6565
)
66+
67+
68+
@dataclasses.dataclass(frozen=True)
69+
class Webhook:
70+
"""A webhook configuration."""
71+
72+
id: t.Optional[str]
73+
"""The unique identifier of the webhook."""
74+
url: str
75+
"""The URL the webhook will be sent to."""
76+
event: WebhookEvent
77+
"""The type of event the webhook is triggered for."""
78+
79+
@classmethod
80+
def from_dict(cls, payload: t.Dict[str, t.Any]) -> "Webhook":
81+
"""Creates a Webhook instance from a dictionary.
82+
83+
Args:
84+
payload: A dictionary containing the webhook's data.
85+
86+
Returns:
87+
A Webhook instance.
88+
"""
89+
return cls(
90+
id=payload["id"],
91+
url=payload["url"],
92+
event=WebhookEvent(payload["event"]),
93+
)
94+
95+
def asdict(self) -> t.Dict[str, t.Any]:
96+
"""Returns a dictionary representation of the webhook.
97+
98+
Returns:
99+
A dictionary containing the webhook's data.
100+
"""
101+
return {
102+
"id": self.id,
103+
"url": self.url,
104+
"event": self.event.value,
105+
}

android_sms_gateway/enums.py

+21
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,24 @@ class ProcessState(enum.Enum):
77
Sent = "Sent"
88
Delivered = "Delivered"
99
Failed = "Failed"
10+
11+
12+
class WebhookEvent(enum.Enum):
13+
"""
14+
Webhook events that can be sent by the server.
15+
"""
16+
17+
SMS_RECEIVED = "sms:received"
18+
"""Triggered when an SMS is received."""
19+
20+
SMS_SENT = "sms:sent"
21+
"""Triggered when an SMS is sent."""
22+
23+
SMS_DELIVERED = "sms:delivered"
24+
"""Triggered when an SMS is delivered."""
25+
26+
SMS_FAILED = "sms:failed"
27+
"""Triggered when an SMS processing fails."""
28+
29+
SYSTEM_PING = "system:ping"
30+
"""Triggered when the device pings the server."""

android_sms_gateway/http.py

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ def post(
1313
self, url: str, payload: dict, *, headers: t.Optional[t.Dict[str, str]] = None
1414
) -> dict: ...
1515

16+
@abc.abstractmethod
17+
def delete(self, url: str, *, headers: t.Optional[t.Dict[str, str]] = None) -> None:
18+
"""
19+
Sends a DELETE request to the specified URL.
20+
21+
Args:
22+
url: The URL to send the DELETE request to.
23+
headers: Optional dictionary of HTTP headers to send with the request.
24+
25+
Returns:
26+
None
27+
"""
28+
1629
def __enter__(self):
1730
pass
1831

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ dev = ["setuptools", "pytest", "black", "flake8", "wheel"]
4242
requests = ["requests"]
4343
httpx = ["httpx"]
4444
aiohttp = ["aiohttp"]
45+
encryption = ["pycryptodome"]

tests/test_domain.py

+57-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
from android_sms_gateway.enums import WebhookEvent
12
import pytest
23

3-
from android_sms_gateway.domain import MessageState, RecipientState
4+
from android_sms_gateway.domain import MessageState, RecipientState, Webhook
45

56

67
# Test for successful instantiation from a dictionary
@@ -79,3 +80,58 @@ def test_message_state_from_dict_incorrect_types():
7980
Exception
8081
): # Replace Exception with the specific exception you expect
8182
MessageState.from_dict(incorrect_payload)
83+
84+
85+
def test_webhook_from_dict():
86+
"""
87+
Tests that a Webhook instance can be successfully instantiated from a dictionary
88+
representation of a webhook.
89+
"""
90+
payload = {
91+
"id": "webhook_123",
92+
"url": "https://example.com/webhook",
93+
"event": "sms:received",
94+
}
95+
96+
webhook = Webhook.from_dict(payload)
97+
98+
assert webhook.id == payload["id"]
99+
assert webhook.url == payload["url"]
100+
assert webhook.event == WebhookEvent(payload["event"])
101+
102+
103+
def test_webhook_asdict():
104+
"""
105+
Tests that a Webhook instance can be successfully converted to a dictionary
106+
representation and that the fields match the expected values.
107+
108+
This test ensures that the asdict method of the Webhook class returns a dictionary
109+
with the correct keys and values.
110+
"""
111+
webhook = Webhook(
112+
id="webhook_123",
113+
url="https://example.com/webhook",
114+
event=WebhookEvent.SMS_RECEIVED,
115+
)
116+
117+
expected_dict = {
118+
"id": "webhook_123",
119+
"url": "https://example.com/webhook",
120+
"event": "sms:received",
121+
}
122+
123+
assert webhook.asdict() == expected_dict
124+
125+
webhook = Webhook(
126+
id=None,
127+
url="https://example.com/webhook",
128+
event=WebhookEvent.SMS_RECEIVED,
129+
)
130+
131+
expected_dict = {
132+
"id": None,
133+
"url": "https://example.com/webhook",
134+
"event": "sms:received",
135+
}
136+
137+
assert webhook.asdict() == expected_dict

0 commit comments

Comments
 (0)