Skip to content

Commit 722a718

Browse files
Merge pull request #12 from railsware/support-sandbox-env
Support sandbox and bulk modes
2 parents 0bd578d + a138158 commit 722a718

File tree

4 files changed

+98
-4
lines changed

4 files changed

+98
-4
lines changed

mailtrap/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .client import MailtrapClient
22
from .exceptions import APIError
33
from .exceptions import AuthorizationError
4+
from .exceptions import ClientConfigurationError
45
from .exceptions import MailtrapError
56
from .mail import Address
67
from .mail import Attachment

mailtrap/client.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,43 @@
11
from typing import NoReturn
2+
from typing import Optional
23
from typing import Union
34

45
import requests
56

67
from mailtrap.exceptions import APIError
78
from mailtrap.exceptions import AuthorizationError
9+
from mailtrap.exceptions import ClientConfigurationError
810
from mailtrap.mail.base import BaseMail
911

1012

1113
class MailtrapClient:
1214
DEFAULT_HOST = "send.api.mailtrap.io"
1315
DEFAULT_PORT = 443
16+
BULK_HOST = "bulk.api.mailtrap.io"
17+
SANDBOX_HOST = "sandbox.api.mailtrap.io"
1418

1519
def __init__(
1620
self,
1721
token: str,
18-
api_host: str = DEFAULT_HOST,
22+
api_host: Optional[str] = None,
1923
api_port: int = DEFAULT_PORT,
24+
bulk: bool = False,
25+
sandbox: bool = False,
26+
inbox_id: Optional[str] = None,
2027
) -> None:
2128
self.token = token
2229
self.api_host = api_host
2330
self.api_port = api_port
31+
self.bulk = bulk
32+
self.sandbox = sandbox
33+
self.inbox_id = inbox_id
34+
35+
self._validate_itself()
2436

2537
def send(self, mail: BaseMail) -> dict[str, Union[bool, list[str]]]:
26-
url = f"{self.base_url}/api/send"
27-
response = requests.post(url, headers=self.headers, json=mail.api_data)
38+
response = requests.post(
39+
self.api_send_url, headers=self.headers, json=mail.api_data
40+
)
2841

2942
if response.ok:
3043
data: dict[str, Union[bool, list[str]]] = response.json()
@@ -34,7 +47,15 @@ def send(self, mail: BaseMail) -> dict[str, Union[bool, list[str]]]:
3447

3548
@property
3649
def base_url(self) -> str:
37-
return f"https://{self.api_host.rstrip('/')}:{self.api_port}"
50+
return f"https://{self._host.rstrip('/')}:{self.api_port}"
51+
52+
@property
53+
def api_send_url(self) -> str:
54+
url = f"{self.base_url}/api/send"
55+
if self.sandbox and self.inbox_id:
56+
return f"{url}/{self.inbox_id}"
57+
58+
return url
3859

3960
@property
4061
def headers(self) -> dict[str, str]:
@@ -46,6 +67,16 @@ def headers(self) -> dict[str, str]:
4667
),
4768
}
4869

70+
@property
71+
def _host(self) -> str:
72+
if self.api_host:
73+
return self.api_host
74+
if self.sandbox:
75+
return self.SANDBOX_HOST
76+
if self.bulk:
77+
return self.BULK_HOST
78+
return self.DEFAULT_HOST
79+
4980
@staticmethod
5081
def _handle_failed_response(response: requests.Response) -> NoReturn:
5182
status_code = response.status_code
@@ -55,3 +86,15 @@ def _handle_failed_response(response: requests.Response) -> NoReturn:
5586
raise AuthorizationError(data["errors"])
5687

5788
raise APIError(status_code, data["errors"])
89+
90+
def _validate_itself(self) -> None:
91+
if self.sandbox and not self.inbox_id:
92+
raise ClientConfigurationError("`inbox_id` is required for sandbox mode")
93+
94+
if not self.sandbox and self.inbox_id:
95+
raise ClientConfigurationError(
96+
"`inbox_id` is not allowed in non-sandbox mode"
97+
)
98+
99+
if self.bulk and self.sandbox:
100+
raise ClientConfigurationError("bulk mode is not allowed in sandbox mode")

mailtrap/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ class MailtrapError(Exception):
22
pass
33

44

5+
class ClientConfigurationError(MailtrapError):
6+
def __init__(self, message: str) -> None:
7+
super().__init__(message)
8+
9+
510
class APIError(MailtrapError):
611
def __init__(self, status: int, errors: list[str]) -> None:
712
self.status = status

tests/unit/test_client.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,56 @@ def get_client(**kwargs: Any) -> mt.MailtrapClient:
3030
props = {"token": "fake_token", **kwargs}
3131
return mt.MailtrapClient(**props)
3232

33+
@pytest.mark.parametrize(
34+
"arguments",
35+
[
36+
{"sandbox": True},
37+
{"inbox_id": "12345"},
38+
{"bulk": True, "sandbox": True, "inbox_id": "12345"},
39+
],
40+
)
41+
def test_client_validation(self, arguments: dict[str, Any]) -> None:
42+
with pytest.raises(mt.ClientConfigurationError):
43+
self.get_client(**arguments)
44+
3345
def test_base_url_should_truncate_slash_from_host(self) -> None:
3446
client = self.get_client(api_host="example.send.com/", api_port=543)
3547

3648
assert client.base_url == "https://example.send.com:543"
3749

50+
@pytest.mark.parametrize(
51+
"arguments, expected_url",
52+
[
53+
({}, "https://send.api.mailtrap.io:443/api/send"),
54+
(
55+
{"api_host": "example.send.com", "api_port": 543},
56+
"https://example.send.com:543/api/send",
57+
),
58+
(
59+
{"api_host": "example.send.com", "sandbox": True, "inbox_id": "12345"},
60+
"https://example.send.com:443/api/send/12345",
61+
),
62+
(
63+
{"api_host": "example.send.com", "bulk": True},
64+
"https://example.send.com:443/api/send",
65+
),
66+
(
67+
{"sandbox": True, "inbox_id": "12345"},
68+
"https://sandbox.api.mailtrap.io:443/api/send/12345",
69+
),
70+
(
71+
{"bulk": True},
72+
"https://bulk.api.mailtrap.io:443/api/send",
73+
),
74+
],
75+
)
76+
def test_api_send_url_should_return_default_sending_url(
77+
self, arguments: dict[str, Any], expected_url: str
78+
) -> None:
79+
client = self.get_client(**arguments)
80+
81+
assert client.api_send_url == expected_url
82+
3883
def test_headers_should_return_appropriate_dict(self) -> None:
3984
client = self.get_client()
4085

0 commit comments

Comments
 (0)