Skip to content

Commit 57f8686

Browse files
Ihor BilousIhor Bilous
authored andcommitted
Fix issue #45: Add ContactEventsApi, related models, tests and examples
1 parent 1a8fb79 commit 57f8686

File tree

7 files changed

+264
-0
lines changed

7 files changed

+264
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import mailtrap as mt
2+
from mailtrap.models.contacts import ContactEvent
3+
4+
API_TOKEN = "YOUR_API_TOKEN"
5+
ACCOUNT_ID = "YOUR_ACCOUNT_ID"
6+
7+
client = mt.MailtrapClient(token=API_TOKEN, account_id=ACCOUNT_ID)
8+
contact_events_api = client.contacts_api.contact_events
9+
10+
11+
def create_contact_event(
12+
contact_identifier: str,
13+
contact_event_params: mt.ContactEventParams,
14+
) -> ContactEvent:
15+
return contact_events_api.create(
16+
contact_identifier=contact_identifier,
17+
contact_event_params=contact_event_params,
18+
)
19+
20+
21+
if __name__ == "__main__":
22+
contact_event = create_contact_event(
23+
contact_identifier="01988623-832f-79df-8aae-a480f8ff7249",
24+
contact_event_params=mt.ContactEventParams(
25+
name="UserLogin",
26+
params={
27+
"user_id": 101,
28+
"user_name": "John Smith",
29+
"is_active": True,
30+
"last_seen": None,
31+
},
32+
),
33+
)
34+
print(contact_event)

mailtrap/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .exceptions import ClientConfigurationError
77
from .exceptions import MailtrapError
88
from .models.accounts import AccountAccessFilterParams
9+
from .models.contacts import ContactEventParams
910
from .models.contacts import ContactExportFilter
1011
from .models.contacts import ContactListParams
1112
from .models.contacts import CreateContactExportParams

mailtrap/api/contacts.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from mailtrap.api.resources.contact_events import ContactEventsApi
12
from mailtrap.api.resources.contact_exports import ContactExportsApi
23
from mailtrap.api.resources.contact_fields import ContactFieldsApi
34
from mailtrap.api.resources.contact_imports import ContactImportsApi
@@ -11,6 +12,10 @@ def __init__(self, client: HttpClient, account_id: str) -> None:
1112
self._account_id = account_id
1213
self._client = client
1314

15+
@property
16+
def contact_events(self) -> ContactEventsApi:
17+
return ContactEventsApi(account_id=self._account_id, client=self._client)
18+
1419
@property
1520
def contact_exports(self) -> ContactExportsApi:
1621
return ContactExportsApi(account_id=self._account_id, client=self._client)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from typing import Optional
2+
3+
from mailtrap.http import HttpClient
4+
from mailtrap.models.contacts import ContactEvent
5+
from mailtrap.models.contacts import ContactEventParams
6+
7+
8+
class ContactEventsApi:
9+
def __init__(self, client: HttpClient, account_id: str) -> None:
10+
self._account_id = account_id
11+
self._client = client
12+
13+
def create(
14+
self,
15+
contact_identifier: str,
16+
contact_event_params: ContactEventParams,
17+
) -> ContactEvent:
18+
"""Create a new Contact Event"""
19+
response = self._client.post(
20+
self._api_path(contact_identifier),
21+
json=contact_event_params.api_data,
22+
)
23+
return ContactEvent(**response)
24+
25+
def _api_path(self, contact_identifier: Optional[str] = None) -> str:
26+
return f"/api/accounts/{self._account_id}/contacts/{contact_identifier}/events"

mailtrap/models/contacts.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from datetime import datetime
22
from enum import Enum
3+
from typing import Any
34
from typing import Optional
45
from typing import Union
56

@@ -143,3 +144,17 @@ class ContactExportDetail:
143144
created_at: datetime
144145
updated_at: datetime
145146
url: Optional[str] = None
147+
148+
149+
@dataclass
150+
class ContactEventParams(RequestParams):
151+
name: str
152+
params: Optional[dict[str, Any]] = None
153+
154+
155+
@dataclass
156+
class ContactEvent:
157+
contact_id: str
158+
contact_email: str
159+
name: str
160+
params: dict[str, Any]
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
from typing import Any
2+
3+
import pytest
4+
import responses
5+
6+
from mailtrap.api.resources.contact_events import ContactEventsApi
7+
from mailtrap.config import GENERAL_HOST
8+
from mailtrap.exceptions import APIError
9+
from mailtrap.http import HttpClient
10+
from mailtrap.models.contacts import ContactEvent
11+
from mailtrap.models.contacts import ContactEventParams
12+
from tests import conftest
13+
14+
ACCOUNT_ID = "321"
15+
EXPORT_ID = 1
16+
CONTACT_IDENTIFIER = "d82d0d9e-bbb7-4656-a591-e64682bffae7"
17+
BASE_CONTACT_EVENTS_URL = (
18+
f"https://{GENERAL_HOST}/api/accounts/{ACCOUNT_ID}"
19+
f"/contacts/{CONTACT_IDENTIFIER}/events"
20+
)
21+
22+
23+
@pytest.fixture
24+
def client() -> ContactEventsApi:
25+
return ContactEventsApi(account_id=ACCOUNT_ID, client=HttpClient(GENERAL_HOST))
26+
27+
28+
@pytest.fixture
29+
def sample_contact_event_dict() -> dict[str, Any]:
30+
return {
31+
"contact_id": CONTACT_IDENTIFIER,
32+
"contact_email": "[email protected]",
33+
"name": "UserLogin",
34+
"params": {
35+
"user_id": 101,
36+
"user_name": "John Smith",
37+
"is_active": True,
38+
"last_seen": None,
39+
},
40+
}
41+
42+
43+
@pytest.fixture
44+
def sample_create_contact_event_params() -> ContactEventParams:
45+
return ContactEventParams(
46+
name="UserLogin",
47+
params={
48+
"user_id": 101,
49+
"user_name": "John Smith",
50+
"is_active": True,
51+
"last_seen": None,
52+
},
53+
)
54+
55+
56+
class TestContactEventsApi:
57+
58+
@pytest.mark.parametrize(
59+
"status_code,response_json,expected_error_message",
60+
[
61+
(
62+
conftest.UNAUTHORIZED_STATUS_CODE,
63+
conftest.UNAUTHORIZED_RESPONSE,
64+
conftest.UNAUTHORIZED_ERROR_MESSAGE,
65+
),
66+
(
67+
conftest.FORBIDDEN_STATUS_CODE,
68+
conftest.FORBIDDEN_RESPONSE,
69+
conftest.FORBIDDEN_ERROR_MESSAGE,
70+
),
71+
(
72+
conftest.NOT_FOUND_STATUS_CODE,
73+
conftest.NOT_FOUND_RESPONSE,
74+
conftest.NOT_FOUND_ERROR_MESSAGE,
75+
),
76+
(
77+
conftest.RATE_LIMIT_ERROR_STATUS_CODE,
78+
conftest.RATE_LIMIT_ERROR_RESPONSE,
79+
conftest.RATE_LIMIT_ERROR_MESSAGE,
80+
),
81+
(
82+
conftest.INTERNAL_SERVER_ERROR_STATUS_CODE,
83+
conftest.INTERNAL_SERVER_ERROR_RESPONSE,
84+
conftest.INTERNAL_SERVER_ERROR_MESSAGE,
85+
),
86+
(
87+
422,
88+
{
89+
"errors": {
90+
"name": [["must be a string", "is too long"]],
91+
"params": [
92+
[
93+
"must be a hash",
94+
"key 'foo' is too long",
95+
"value for 'bar' is too long",
96+
]
97+
],
98+
}
99+
},
100+
(
101+
"name: ['must be a string', 'is too long']; "
102+
"params: ['must be a hash', \"key 'foo' is too long\", "
103+
"\"value for 'bar' is too long\"]"
104+
),
105+
),
106+
],
107+
)
108+
@responses.activate
109+
def test_create_should_raise_api_errors(
110+
self,
111+
client: ContactEventsApi,
112+
status_code: int,
113+
response_json: dict,
114+
expected_error_message: str,
115+
sample_create_contact_event_params: ContactEventParams,
116+
) -> None:
117+
responses.post(
118+
BASE_CONTACT_EVENTS_URL,
119+
status=status_code,
120+
json=response_json,
121+
)
122+
123+
with pytest.raises(APIError) as exc_info:
124+
client.create(CONTACT_IDENTIFIER, sample_create_contact_event_params)
125+
126+
print(str(exc_info.value))
127+
128+
assert expected_error_message in str(exc_info.value)
129+
130+
@responses.activate
131+
def test_create_should_return_contact_event(
132+
self,
133+
client: ContactEventsApi,
134+
sample_contact_event_dict: dict,
135+
sample_create_contact_event_params: ContactEventParams,
136+
) -> None:
137+
responses.post(
138+
BASE_CONTACT_EVENTS_URL,
139+
json=sample_contact_event_dict,
140+
status=201,
141+
)
142+
143+
result = client.create(CONTACT_IDENTIFIER, sample_create_contact_event_params)
144+
145+
assert isinstance(result, ContactEvent)
146+
assert result.contact_id == CONTACT_IDENTIFIER
147+
assert result.contact_email == "[email protected]"
148+
assert result.name == "UserLogin"
149+
request = responses.calls[0].request
150+
assert request.url == BASE_CONTACT_EVENTS_URL

tests/unit/models/test_contacts.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22

3+
from mailtrap.models.contacts import ContactEventParams
34
from mailtrap.models.contacts import ContactExportFilter
45
from mailtrap.models.contacts import ContactListParams
56
from mailtrap.models.contacts import CreateContactExportParams
@@ -198,3 +199,35 @@ def test_create_contact_export_params_with_empty_filters_should_work(self) -> No
198199
api_data = params.api_data
199200

200201
assert api_data == {"filters": []}
202+
203+
204+
class TestContactEventParams:
205+
def test_contact_event_params_api_data_should_return_correct_dict(
206+
self,
207+
) -> None:
208+
params = ContactEventParams(
209+
name="UserLogin",
210+
params={
211+
"user_id": 101,
212+
"user_name": "John Smith",
213+
"is_active": True,
214+
"last_seen": None,
215+
},
216+
)
217+
218+
api_data = params.api_data
219+
assert api_data == {
220+
"name": "UserLogin",
221+
"params": {
222+
"user_id": 101,
223+
"user_name": "John Smith",
224+
"is_active": True,
225+
"last_seen": None,
226+
},
227+
}
228+
229+
def test_contact_event_params_with_empty_params_should_work(self) -> None:
230+
"""Test that empty filters list works correctly."""
231+
params = ContactEventParams(name="UserLogin")
232+
api_data = params.api_data
233+
assert api_data == {"name": "UserLogin"}

0 commit comments

Comments
 (0)