Skip to content

Commit 2a89512

Browse files
authored
Add email channel (#66)
* Added validate email addresses endpoint * Added get all domains for account endpoint * Added get domain details endpoint * Added add new domain endpoint * Added add delete existing domain endpoint * Added add verify domain endpoint * Added added integration tests for verify and delete domain * Added update tracking events method * Added update tracking events tests * Changed README.md and updated version number * Changed conftest.py comments
1 parent f444c6d commit 2a89512

36 files changed

+807
-29
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Python client for Infobip's API channels.
1414
## 📡 Supported channels
1515
- [SMS Reference](https://www.infobip.com/docs/api#channels/sms)
1616
- [Whatsapp Reference](https://www.infobip.com/docs/api#channels/whatsapp)
17+
- [Email Reference](https://www.infobip.com/docs/api#channels/email)
1718
- [WebRTC Reference](https://www.infobip.com/docs/api#channels/webrtc/)
1819
- [MMS Reference](https://www.infobip.com/docs/api#channels/mms)
1920
- [RCS Reference](https://www.infobip.com/docs/api#channels/rcs)

infobip_channels/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
from .email.channel import EmailChannel
12
from .mms.channel import MMSChannel
23
from .rcs.channel import RCSChannel
34
from .sms.channel import SMSChannel
45
from .web_rtc.channel import WebRtcChannel
56
from .whatsapp.channel import WhatsAppChannel
67

7-
__all__ = ["WhatsAppChannel", "WebRtcChannel", "MMSChannel", "RCSChannel", "SMSChannel"]
8+
__all__ = [
9+
"WhatsAppChannel",
10+
"WebRtcChannel",
11+
"MMSChannel",
12+
"RCSChannel",
13+
"SMSChannel",
14+
"EmailChannel",
15+
]

infobip_channels/core/channel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
Authentication,
1010
GetHeaders,
1111
MessageBodyBase,
12+
PathParameter,
1213
PostHeaders,
1314
QueryParameter,
1415
ResponseBase,
1516
)
16-
from infobip_channels.web_rtc.models.path_parameters.core import PathParameter
1717

1818

1919
class Channel(ABC):
@@ -137,7 +137,7 @@ def convert_model_to_dict(
137137
@staticmethod
138138
def validate_path_parameter(
139139
parameter: Union[PathParameter, Dict], parameter_type: Type[PathParameter]
140-
) -> PathParameter:
140+
) -> Union[PathParameter]:
141141
"""
142142
Validate path parameter by trying to instantiate the provided class and
143143
extract valid path parameter.

infobip_channels/core/http_client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def __init__(
3737
)
3838

3939
def post(
40-
self, endpoint: str, body: Union[Dict, bytes], headers: RequestHeaders = None
40+
self,
41+
endpoint: str,
42+
body: Union[Dict, bytes] = None,
43+
headers: RequestHeaders = None,
4144
) -> requests.Response:
4245
"""Send an HTTP post request to base_url + endpoint.
4346

infobip_channels/core/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class DeleteHeaders(RequestHeaders):
186186

187187

188188
class PathParameter(CamelCaseModel):
189-
sender: str
189+
pass
190190

191191

192192
class QueryParameter(CamelCaseModel):

infobip_channels/email/channel.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,38 @@
55

66
from infobip_channels.core.channel import Channel
77
from infobip_channels.core.models import PostHeaders, ResponseBase
8+
from infobip_channels.email.models.body.add_new_domain import AddNewDomainMessageBody
89
from infobip_channels.email.models.body.reschedule_messages import (
910
RescheduleMessagesMessageBody,
1011
)
1112
from infobip_channels.email.models.body.send_email import EmailMessageBody
1213
from infobip_channels.email.models.body.update_scheduled_status import (
1314
UpdateScheduledStatusMessageBody,
1415
)
16+
from infobip_channels.email.models.body.update_tracking_events import (
17+
UpdateTrackingEventsMessageBody,
18+
)
19+
from infobip_channels.email.models.body.validate_email_adresses import (
20+
ValidateEmailAddressesMessageBody,
21+
)
22+
from infobip_channels.email.models.path_paramaters.delete_existing_domain import (
23+
DeleteExistingDomainPathParameter,
24+
)
25+
from infobip_channels.email.models.path_paramaters.get_domain_details import (
26+
GetDomainDetailsPathParameter,
27+
)
28+
from infobip_channels.email.models.path_paramaters.update_tracking_events import (
29+
UpdateTrackingEventsPathParameter,
30+
)
31+
from infobip_channels.email.models.path_paramaters.verify_domain import (
32+
VerifyDomainPathParameter,
33+
)
1534
from infobip_channels.email.models.query_parameters.delivery_reports import (
1635
DeliveryReportsQueryParameters,
1736
)
37+
from infobip_channels.email.models.query_parameters.get_all_domains import (
38+
GetAllDomainsForAccountQueryParameters,
39+
)
1840
from infobip_channels.email.models.query_parameters.get_logs import (
1941
GetLogsQueryParameters,
2042
)
@@ -30,10 +52,17 @@
3052
from infobip_channels.email.models.query_parameters.update_scheduled_status import (
3153
UpdateScheduledStatusQueryParameters,
3254
)
55+
from infobip_channels.email.models.response.add_new_domain import AddNewDomainResponse
3356
from infobip_channels.email.models.response.core import EmailResponseError
3457
from infobip_channels.email.models.response.delivery_reports import (
3558
DeliveryReportsResponse,
3659
)
60+
from infobip_channels.email.models.response.get_all_domains import (
61+
GetAllDomainsForAccountResponse,
62+
)
63+
from infobip_channels.email.models.response.get_domain_details import (
64+
GetDomainDetailsResponse,
65+
)
3766
from infobip_channels.email.models.response.get_logs import GetLogsResponse
3867
from infobip_channels.email.models.response.get_sent_bulk_status import (
3968
GetSentEmailBulksStatusResponse,
@@ -48,6 +77,12 @@
4877
from infobip_channels.email.models.response.update_scheduled_status import (
4978
UpdateScheduledStatusResponse,
5079
)
80+
from infobip_channels.email.models.response.update_tracking_events import (
81+
UpdateTrackingEventsResponse,
82+
)
83+
from infobip_channels.email.models.response.validate_email_adresses import (
84+
ValidateEmailAddressesResponse,
85+
)
5186

5287

5388
class EmailChannel(Channel):
@@ -230,3 +265,149 @@ def update_scheduled_email_messages(
230265
params=query_parameters.dict(by_alias=True),
231266
)
232267
return self._construct_response(response, UpdateScheduledStatusResponse)
268+
269+
def validate_email_addresses(
270+
self, message: Union[ValidateEmailAddressesMessageBody, Dict]
271+
) -> Union[ResponseBase, requests.Response, Any]:
272+
"""
273+
Run validation to identify poor quality emails to clean up your recipient list.
274+
275+
:param message: Body of the message to send
276+
:return: Received response
277+
"""
278+
message = self.validate_message_body(message, ValidateEmailAddressesMessageBody)
279+
280+
response = self._client.post(
281+
self.EMAIL_URL_TEMPLATE_V2 + "validation",
282+
message.dict(by_alias=True),
283+
)
284+
return self._construct_response(response, ValidateEmailAddressesResponse)
285+
286+
def get_all_domains_for_account(
287+
self,
288+
query_parameters: Union[GetAllDomainsForAccountQueryParameters, Dict] = None,
289+
) -> Union[ResponseBase, requests.Response, Any]:
290+
"""
291+
This API is to get all domain associated with the account. It also provides
292+
details of the retrieved domain like the DNS records, Tracking details,
293+
Active/Blocked status,etc.
294+
295+
:param query_parameters: Query parameters to send with the request
296+
:return: Received response
297+
"""
298+
299+
query_parameters = self.validate_query_parameter(
300+
query_parameters or {}, GetAllDomainsForAccountQueryParameters
301+
)
302+
303+
response = self._client.get(
304+
self.EMAIL_URL_TEMPLATE_V1 + "domains",
305+
params=query_parameters.dict(by_alias=True),
306+
)
307+
return self._construct_response(response, GetAllDomainsForAccountResponse)
308+
309+
def get_domain_details(
310+
self,
311+
parameter: Union[GetDomainDetailsPathParameter, Dict],
312+
) -> Union[ResponseBase, requests.Response, Any]:
313+
"""
314+
This API provides with the details of the domain like the DNS records,
315+
Tracking details, Active/Blocked status,etc.
316+
317+
:param parameter: Domain for which the details need to be viewed.
318+
:return: Received response
319+
"""
320+
path_parameter = self.validate_path_parameter(
321+
parameter, GetDomainDetailsPathParameter
322+
)
323+
324+
response = self._client.get(
325+
self.EMAIL_URL_TEMPLATE_V1 + "domains/" + path_parameter.domain_name,
326+
)
327+
return self._construct_response(response, GetDomainDetailsResponse)
328+
329+
def add_new_domain(
330+
self, message: Union[AddNewDomainMessageBody, Dict]
331+
) -> Union[ResponseBase, requests.Response, Any]:
332+
"""
333+
This method allows you to add new domains with a limit to create a maximum of
334+
10 domains in a day.
335+
336+
:param message: Body of the message to send
337+
:return: Received response
338+
"""
339+
message = self.validate_message_body(message, AddNewDomainMessageBody)
340+
341+
response = self._client.post(
342+
self.EMAIL_URL_TEMPLATE_V1 + "domains",
343+
message.dict(by_alias=True),
344+
)
345+
return self._construct_response(response, AddNewDomainResponse)
346+
347+
def delete_existing_domain(
348+
self, parameter: Union[DeleteExistingDomainPathParameter, Dict]
349+
) -> Union[ResponseBase, requests.Response, Any]:
350+
"""
351+
This method allows you to delete an existing domain.
352+
353+
:param parameter: Domain name which needs to be deleted.
354+
:return: Received response
355+
"""
356+
path_parameter = self.validate_path_parameter(
357+
parameter, DeleteExistingDomainPathParameter
358+
)
359+
360+
response = self._client.delete(
361+
self.EMAIL_URL_TEMPLATE_V1 + "domains/" + path_parameter.domain_name,
362+
)
363+
return response
364+
365+
def verify_domain(
366+
self, parameter: Union[VerifyDomainPathParameter, Dict]
367+
) -> Union[ResponseBase, requests.Response, Any]:
368+
"""
369+
API request to verify records(TXT, MX, DKIM) associated with the provided
370+
domain.
371+
372+
:param parameter: Domain name which needs to be deleted.
373+
:return: Received response
374+
"""
375+
path_parameter = self.validate_path_parameter(
376+
parameter, VerifyDomainPathParameter
377+
)
378+
379+
response = self._client.post(
380+
self.EMAIL_URL_TEMPLATE_V1
381+
+ "domains/"
382+
+ path_parameter.domain_name
383+
+ "/verify"
384+
)
385+
return response
386+
387+
def update_tracking_events(
388+
self,
389+
parameter: Union[UpdateTrackingEventsPathParameter, Dict],
390+
message: Union[UpdateTrackingEventsMessageBody, Dict],
391+
) -> Union[ResponseBase, requests.Response, Any]:
392+
"""
393+
API to update tracking events for the provided domain. Tracking events can be
394+
updated only for CLICKS, OPENS and UNSUBSCRIBES.
395+
396+
:param parameter: Domain name which needs to be deleted.
397+
:param message: Body of the message to send
398+
:return: Received response
399+
"""
400+
path_parameter = self.validate_path_parameter(
401+
parameter, UpdateTrackingEventsPathParameter
402+
)
403+
404+
message = self.validate_message_body(message, UpdateTrackingEventsMessageBody)
405+
406+
response = self._client.put(
407+
self.EMAIL_URL_TEMPLATE_V1
408+
+ "domains/"
409+
+ path_parameter.domain_name
410+
+ "/tracking",
411+
message.dict(by_alias=True),
412+
)
413+
return self._construct_response(response, UpdateTrackingEventsResponse)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from infobip_channels.core.models import MessageBodyBase
2+
3+
4+
class AddNewDomainMessageBody(MessageBodyBase):
5+
domain_name: str
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from typing import Optional
2+
3+
from pydantic import StrictBool
4+
5+
from infobip_channels.core.models import MessageBodyBase
6+
7+
8+
class UpdateTrackingEventsMessageBody(MessageBodyBase):
9+
open: Optional[StrictBool] = None
10+
clicks: Optional[StrictBool] = None
11+
unsubscribe: Optional[StrictBool] = None
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from infobip_channels.core.models import MessageBodyBase
2+
3+
4+
class ValidateEmailAddressesMessageBody(MessageBodyBase):
5+
to: str

infobip_channels/email/models/path_paramaters/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)