Skip to content

Commit 1f2d2af

Browse files
authored
Revert "SES integration (#23)" (#24)
This reverts commit 0289040.
1 parent 0289040 commit 1f2d2af

File tree

10 files changed

+35
-138
lines changed

10 files changed

+35
-138
lines changed

app/config.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,5 @@ class Config(BaseSettings):
4949
DESCRIPTION: str = SWAGGER_APP_DESCRIPTION
5050
PORT: int = cast(int, os.getenv("PORT", "8000"))
5151

52-
AWS_SECRET_KEY: str = cast(str, os.getenv("AWS_SECRET_KEY"))
53-
AWS_ACCESS_KEY: str = cast(str, os.getenv("AWS_ACCESS_KEY"))
54-
AWS_REGION: str = cast(str, os.getenv("AWS_REGION"))
55-
AWS_EMAIL: str = cast(str, os.getenv("AWS_EMAIL"))
5652

5753
config = Config()

app/routers/email_router.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@
22
Email router for creating and controlling the endpoint.
33
"""
44

5-
from typing import Dict, Union
6-
75
from fastapi import APIRouter, Depends, HTTPException, status, Request
86
from fastapi.templating import Jinja2Templates
9-
107
from app.services.email_service import EmailService
11-
from app.schemas.email import SendEmailRequestBody, SendEmailResponseBody, EmailProvider
8+
from app.schemas.email import SendEmailRequestBody, SendEmailResponseBody
129
from app.utils import (
1310
EmailSender,
14-
SESEmailSender,
1511
success_response,
1612
get_email_sender,
1713
require_authentication,
@@ -27,9 +23,7 @@
2723
async def send_email(
2824
request: Request,
2925
email_details: SendEmailRequestBody,
30-
email_sender: Dict[EmailProvider, Union[EmailSender, SESEmailSender]] = Depends(
31-
get_email_sender
32-
),
26+
email_sender: EmailSender = Depends(get_email_sender),
3327
):
3428
"""
3529
Sends an email to the recipient using provided details.

app/schemas/email.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,11 @@
33
"""
44

55
from typing import Optional, Dict, List
6-
from enum import Enum
76
from fastapi import HTTPException, status
87
from pydantic import BaseModel, EmailStr, field_validator, ValidationInfo
98
from app.config import config
109

1110

12-
class EmailProvider(str, Enum):
13-
GMAIL = "GMAIL"
14-
SES = "SES"
15-
16-
1711
class SendEmailRequestBody(BaseModel):
1812
"""
1913
Request body for sending emails.
@@ -34,7 +28,6 @@ class SendEmailRequestBody(BaseModel):
3428
cc: Optional[List[EmailStr]] = None
3529
bcc: Optional[List[EmailStr]] = None
3630
self: bool = False
37-
provider: EmailProvider = EmailProvider.SES
3831

3932
@field_validator("body")
4033
@classmethod

app/services/email_service.py

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,45 @@
22
Third party email service configuration for sending emails.
33
"""
44

5-
from typing import Dict, Union
6-
75
from smtplib import SMTPException, SMTPAuthenticationError, SMTPSenderRefused
8-
import botocore.exceptions
9-
10-
from app.utils import EmailSender, SESEmailSender
11-
from app.schemas.email import SendEmailRequestBody, EmailProvider
6+
from app.utils import EmailSender
7+
from app.schemas.email import SendEmailRequestBody
128

139

1410
class EmailService:
1511
"""
1612
Service layer for handling email sending logic.
1713
"""
1814

19-
def __init__(
20-
self, email_sender: Dict[EmailProvider, Union[EmailSender, SESEmailSender]]
21-
):
15+
def __init__(self, email_sender: EmailSender):
2216
self.email_sender = email_sender
2317

2418
def send_email(self, email_details: SendEmailRequestBody, body: str):
2519
"""
2620
Sends an email based on email details.
21+
2722
Args:
2823
email_details (SendEmailRequestBody): Details for the email.
2924
body (str): body of the email.
3025
3126
Returns:
32-
dict: A response dictionary containing:
33-
- "success" (bool): Indicates whether the email was sent successfully.
34-
- "error": Error message if the email sending fails.
27+
bool: True if no failure occured on sending email.
3528
"""
3629
try:
37-
result = self._send_email_by_provider(email_details, body)
30+
result = self.email_sender.send_email(
31+
to_email=email_details.recipient,
32+
subject=email_details.subject,
33+
body=body,
34+
cc=email_details.cc,
35+
bcc=email_details.bcc,
36+
is_html=True,
37+
)
3838
return result
39-
except Exception as error:
40-
self._handle_email_error(error)
41-
42-
def _send_email_by_provider(self, email_details: SendEmailRequestBody, body: str):
43-
provider = email_details.provider
44-
sender = self.email_sender.get(provider)
45-
result = sender.send_email(
46-
to_email=email_details.recipient,
47-
subject=email_details.subject,
48-
body=body,
49-
cc=email_details.cc,
50-
bcc=email_details.bcc,
51-
is_html=True,
52-
)
53-
return result
54-
55-
def _handle_email_error(self, error: Exception):
56-
if isinstance(
57-
error, (SMTPAuthenticationError, botocore.exceptions.ClientError)
58-
):
59-
raise PermissionError(f"Authentication failed: {str(error)}") from error
60-
elif isinstance(error, SMTPSenderRefused):
61-
raise ValueError(f"Email address refused: {str(error)}") from error
62-
else:
63-
raise ConnectionError(f"Email sending failed: {str(error)}") from error
39+
except SMTPAuthenticationError as exc:
40+
raise PermissionError(
41+
"Failed to authenticate with the SMTP server."
42+
) from exc
43+
except SMTPSenderRefused as exc:
44+
raise ValueError("Sender address refused by the SMTP server.") from exc
45+
except SMTPException as smtp_exc:
46+
raise ConnectionError(f"SMTP error occurred: {smtp_exc}") from smtp_exc

app/utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .email_sender import EmailSender, SESEmailSender
1+
from .email_sender import EmailSender
22
from .dependencies import require_authentication, get_email_sender
33
from .response import error_response, success_response
44
from .exceptions import custom_http_exception_handler, custom_general_exception_handler

app/utils/dependencies.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
"""
44

55
from functools import wraps
6-
from typing import Dict, Union
76
from fastapi import HTTPException, Request
87
from app.config import config
9-
from app.schemas.email import EmailProvider
10-
from .email_sender import EmailSender, SESEmailSender
8+
from .email_sender import EmailSender
119

1210

1311
def require_authentication():
@@ -29,23 +27,14 @@ async def wrapper(request: Request, *args, **kwargs):
2927
return decorator
3028

3129

32-
def get_email_sender() -> Dict[EmailProvider, Union[EmailSender, SESEmailSender]]:
30+
def get_email_sender() -> EmailSender:
3331
"""
34-
Creates and returns a dictionary of email sender objects.
32+
Creates and returns an EmailSender object.
3533
This function is used as a dependency injection in the controller.
3634
"""
37-
gmail_sender = EmailSender(
35+
return EmailSender(
3836
smtp_server=config.EMAIL_HOST,
3937
smtp_port=config.EMAIL_PORT,
4038
username=config.EMAIL_ADDRESS,
4139
password=config.EMAIL_PASSWORD,
4240
)
43-
44-
ses_sender = SESEmailSender(
45-
aws_access_key=config.AWS_ACCESS_KEY,
46-
aws_secret_key=config.AWS_SECRET_KEY,
47-
aws_region=config.AWS_REGION,
48-
aws_email=config.AWS_EMAIL,
49-
)
50-
51-
return {EmailProvider.GMAIL: gmail_sender, EmailProvider.SES: ses_sender}

app/utils/email_sender.py

Lines changed: 5 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"""
44

55
import smtplib
6-
import boto3
76
from email.message import EmailMessage
87
from typing import List
98

@@ -49,7 +48,7 @@ def send_email(
4948
cc: List[EmailStr],
5049
bcc: List[EmailStr],
5150
is_html: bool = False,
52-
) -> dict:
51+
) -> bool:
5352
"""
5453
Sends an email using the configured SMTP settings.
5554
@@ -59,10 +58,9 @@ def send_email(
5958
body (str): The body of the email, which can be in plain text or HTML.
6059
is_html (bool, optional): Specifies whether the email body is HTML content.
6160
Defaults to False.
61+
6262
Returns:
63-
dict: A response dictionary containing:
64-
- "success" (bool): Indicates whether the email was sent successfully.
65-
- "error": Error message if the email sending fails.
63+
dict: A response dictionary containing the success of the email send action.
6664
6765
Raises:
6866
Exception: If there is an error during the email-sending process,
@@ -84,56 +82,5 @@ def send_email(
8482
server.login(self.username, self.password)
8583
server.send_message(msg)
8684
return {"success": True}
87-
except Exception as error:
88-
raise ConnectionError(f"Email sending failed: {str(error)}") from error
89-
90-
91-
class SESEmailSender:
92-
"""
93-
A helper class for sending emails using AWS SES.
94-
95-
This class provides methods for sending emails through Amazon Simple Email Service (SES).
96-
"""
97-
98-
def __init__(
99-
self, aws_access_key: str, aws_secret_key: str, aws_region: str, aws_email: str
100-
):
101-
self.aws_access_key = aws_access_key
102-
self.aws_secret_key = aws_secret_key
103-
self.aws_region = aws_region
104-
self.aws_email = aws_email
105-
106-
def send_email(
107-
self,
108-
to_email: str,
109-
subject: str,
110-
body: str,
111-
cc: List[EmailStr],
112-
bcc: List[EmailStr],
113-
is_html: bool = False,
114-
) -> dict:
115-
try:
116-
ses_client = boto3.client(
117-
"ses",
118-
region_name=self.aws_region,
119-
aws_access_key_id=self.aws_access_key,
120-
aws_secret_access_key=self.aws_secret_key,
121-
)
122-
message = {"Subject": {"Data": subject}, "Body": {}}
123-
if is_html:
124-
message["Body"]["Html"] = {"Data": body}
125-
else:
126-
message["Body"]["Text"] = {"Data": body}
127-
destination = {
128-
"ToAddresses": [to_email] if to_email else [],
129-
}
130-
if cc:
131-
destination["CcAddresses"] = cc
132-
if bcc:
133-
destination["BccAddresses"] = bcc
134-
response = ses_client.send_email(
135-
Source=self.aws_email, Destination=destination, Message=message
136-
)
137-
return {"success": True, "message_id": response["MessageId"]}
138-
except Exception as error:
139-
raise ConnectionError(f"Email sending failed: {str(error)}") from error
85+
except Exception as e:
86+
raise e

env_example

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,4 @@ EMAIL_PASSWORD=somepassword
55
APP_SECRET=appsectsdjsnd
66
ENV=development
77
ORIGINS=https://abc.com,https://kbc.com
8-
PORT=5000
9-
AWS_SECRET_KEY=somesecretkey
10-
AWS_ACCESS_KEY=someaccesskey
11-
AWS_REGION=someregion
12-
AWS_EMAIL=awsverifiedemail
8+
PORT=5000

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,3 @@ typing_extensions==4.12.2
3535
uvicorn==0.32.0
3636
watchfiles==0.24.0
3737
websockets==13.1
38-
boto3>=1.37.27

run.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
if __name__ == "__main__":
1414
# uvicorn.run("app.main:app", port=config.PORT, reload=True)
1515
# --> run locally with auto reload on change
16-
uvicorn.run(app, port=config.PORT) # to run on vercel
16+
uvicorn.run(app, port=config.PORT) # to run on vercel

0 commit comments

Comments
 (0)