Flesh out the email implementation
This commit is contained in:
parent
c1a61730d5
commit
cee708f4d8
4 changed files with 29 additions and 10 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
from src.utils.secret import SecretBox
|
||||||
from src.config.parse import assert_key_of_type
|
from src.config.parse import assert_key_of_type
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
@ -8,11 +9,11 @@ EmailAddress = str
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Email:
|
class Email:
|
||||||
bird_api_key: str
|
bird_api_key: SecretBox[str]
|
||||||
sender: EmailAddress
|
sender: EmailAddress
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, config: dict[str, Any]) -> 'Email':
|
def from_dict(cls, config: dict[str, Any]) -> 'Email':
|
||||||
assert_key_of_type(config, 'bird_api_key', str)
|
assert_key_of_type(config, 'bird_api_key', str)
|
||||||
assert_key_of_type(config, 'sender', str)
|
assert_key_of_type(config, 'sender', str)
|
||||||
return Email(bird_api_key=config['bird_api_key'], sender=config['sender'])
|
return Email(bird_api_key=SecretBox(config['bird_api_key']), sender=config['sender'])
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from dataclasses import dataclass, field
|
import requests
|
||||||
from email.utils import formatdate, make_msgid
|
from dataclasses import dataclass
|
||||||
import abc
|
import abc
|
||||||
|
|
||||||
from ..config.email import Email as EmailConfig, EmailAddress
|
from ..config.email import Email as EmailConfig, EmailAddress
|
||||||
|
|
@ -17,10 +17,6 @@ class EmailDTO:
|
||||||
text: str
|
text: str
|
||||||
# HTML alternate content. (Optional: Textual content is required)
|
# HTML alternate content. (Optional: Textual content is required)
|
||||||
html: str | None = None
|
html: str | None = None
|
||||||
# A unique ID for identifying this Email
|
|
||||||
message_id: str = field(default_factory=make_msgid)
|
|
||||||
# The date that the email was sent
|
|
||||||
date: str = field(default_factory=lambda: formatdate(localtime=True))
|
|
||||||
|
|
||||||
|
|
||||||
class EmailService:
|
class EmailService:
|
||||||
|
|
@ -30,12 +26,20 @@ class EmailService:
|
||||||
|
|
||||||
|
|
||||||
class BirdEmailServiceImpl(EmailService):
|
class BirdEmailServiceImpl(EmailService):
|
||||||
|
BIRD_API_ENDPOINT = 'https://eu1.platform.bird.com/v1/email/messages'
|
||||||
|
|
||||||
def __init__(self, config: EmailConfig):
|
def __init__(self, config: EmailConfig):
|
||||||
self._config = config
|
self._config = config
|
||||||
|
|
||||||
def send_email(self, email: EmailDTO):
|
def send_email(self, email: EmailDTO):
|
||||||
# TODO: Implement Bird Specific Email sending and Error handling
|
payload = {'from': email.sender, 'to': email.to, 'subject': email.subject, 'text': email.text}
|
||||||
pass
|
|
||||||
|
if email.html:
|
||||||
|
payload['html'] = email.html
|
||||||
|
|
||||||
|
headers = {'Authorization': f'Bearer {self._config.bird_api_key.expose_secret()}'}
|
||||||
|
response = requests.post(BirdEmailServiceImpl.BIRD_API_ENDPOINT, json=payload, headers=headers)
|
||||||
|
return response.ok
|
||||||
|
|
||||||
|
|
||||||
def get_service(config: EmailConfig) -> EmailService:
|
def get_service(config: EmailConfig) -> EmailService:
|
||||||
|
|
|
||||||
0
src/utils/__init__.py
Normal file
0
src/utils/__init__.py
Normal file
14
src/utils/secret.py
Normal file
14
src/utils/secret.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
class SecretBox[T]:
|
||||||
|
"""A helper abstraction for making sure secrets aren't leaked accidentally"""
|
||||||
|
|
||||||
|
def __init__(self, item: T):
|
||||||
|
self._item = item
|
||||||
|
|
||||||
|
def expose_secret(self) -> T:
|
||||||
|
return self._item
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '<SECRET>'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'SecretBox<{type(self._item)}>'
|
||||||
Loading…
Add table
Add a link
Reference in a new issue