-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #192 from grillazz/171-simple-and-fast-smtp-client
add endpoint to send email with smtp service
- Loading branch information
Showing
2 changed files
with
103 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,102 @@ | ||
import logging | ||
from typing import Annotated | ||
|
||
from fastapi import APIRouter, status, Request | ||
from fastapi import APIRouter, status, Request, Depends, Query | ||
from pydantic import EmailStr | ||
from starlette.concurrency import run_in_threadpool | ||
|
||
from app.services.smtp import SMTPEmailService | ||
|
||
from app.utils.logging import AppLogger | ||
|
||
logger = AppLogger().get_logger() | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get("/redis", status_code=status.HTTP_200_OK) | ||
async def redis_check(request: Request): | ||
_redis = await request.app.redis | ||
_info = None | ||
""" | ||
Endpoint to check Redis health and retrieve server information. | ||
This endpoint connects to the Redis client configured in the application | ||
and attempts to fetch server information using the `info()` method. | ||
If an error occurs during the Redis operation, it logs the error. | ||
Args: | ||
request (Request): The incoming HTTP request. | ||
Returns: | ||
dict or None: Returns Redis server information as a dictionary if successful, | ||
otherwise returns `None` in case of an error. | ||
""" | ||
redis_client = await request.app.redis | ||
redis_info = None | ||
try: | ||
_info = await _redis.info() | ||
redis_info = await redis_client.info() | ||
except Exception as e: | ||
logging.error(f"Redis error: {e}") | ||
return _info | ||
return redis_info | ||
|
||
|
||
@router.post("/email", status_code=status.HTTP_200_OK) | ||
async def smtp_check( | ||
request: Request, | ||
smtp: Annotated[SMTPEmailService, Depends()], | ||
sender: Annotated[EmailStr, Query(description="Email address of the sender")], | ||
recipients: Annotated[ | ||
list[EmailStr], Query(description="List of recipient email addresses") | ||
], | ||
subject: Annotated[str, Query(description="Subject line of the email")], | ||
body_text: Annotated[str, Query(description="Body text of the email")] = "", | ||
): | ||
""" | ||
Endpoint to send an email via an SMTP service. | ||
This endpoint facilitates sending an email using the configured SMTP service. It performs | ||
the operation in a separate thread using `run_in_threadpool`, which is suitable for blocking I/O | ||
operations, such as sending emails. By offloading the sending process to a thread pool, it prevents | ||
the asynchronous event loop from being blocked, ensuring that other tasks in the application | ||
remain responsive. | ||
Args: | ||
request (Request): The incoming HTTP request, providing context such as the base URL. | ||
smtp (SMTPEmailService): The SMTP email service dependency injected to send emails. | ||
sender (EmailStr): The sender's email address. | ||
recipients (list[EmailStr]): A list of recipient email addresses. | ||
subject (str): The subject line of the email. | ||
body_text (str, optional): The plain-text body of the email. Defaults to an empty string. | ||
Returns: | ||
dict: A JSON object indicating success with a message, e.g., {"message": "Email sent"}. | ||
Logs: | ||
Logs relevant email metadata: request base URL, sender, recipients, and subject. | ||
Why `run_in_threadpool`: | ||
Sending an email often involves interacting with external SMTP servers, which can be | ||
a slow, blocking operation. Using `run_in_threadpool` is beneficial because: | ||
1. Blocking I/O operations like SMTP requests do not interrupt the main event loop, | ||
preventing other tasks (e.g., handling HTTP requests) from slowing down. | ||
2. The email-sending logic is offloaded to a separate, managed thread pool, improving | ||
application performance and scalability. | ||
""" | ||
|
||
email_data = { | ||
"base_url": request.base_url, | ||
"sender": sender, | ||
"recipients": recipients, | ||
"subject": subject, | ||
} | ||
|
||
logger.info("Sending email with data: %s", email_data) | ||
|
||
await run_in_threadpool( | ||
smtp.send_email, | ||
sender=sender, | ||
recipients=recipients, | ||
subject=subject, | ||
body_text=body_text, | ||
body_html=None, | ||
) | ||
return {"message": "Email sent"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters