feat: wire SendGridEmailBackend with from_email/reply_to settings

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 12:12:05 -07:00
co-authored by Claude Sonnet 4.6
parent 63ad306a61
commit 5a41402644
3 changed files with 63 additions and 8 deletions
+21 -8
View File
@@ -8,12 +8,14 @@ intentionally left to be wired in R3-C.
from __future__ import annotations
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional, Protocol
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail, ReplyTo
from app.config import settings
@@ -66,11 +68,10 @@ class ConsoleEmailBackend:
class SendGridEmailBackend:
"""Stub. Wire in R3-C (real SendGrid client + sandbox mode + retries).
Deliberately raises so a misconfigured prod env fails loudly instead of
silently dropping mail.
"""
def __init__(self) -> None:
self._client = SendGridAPIClient(settings.SENDGRID_API_KEY)
self._from_email = settings.SENDGRID_FROM_EMAIL
self._reply_to = settings.SENDGRID_REPLY_TO
def send(
self,
@@ -78,8 +79,20 @@ class SendGridEmailBackend:
subject: str,
html: str,
text: Optional[str] = None,
) -> None: # pragma: no cover - stub
raise NotImplementedError("Wire SendGrid in R3-C")
) -> None:
message = Mail(
from_email=self._from_email,
to_emails=to,
subject=subject,
html_content=html,
plain_text_content=text,
)
message.reply_to = ReplyTo(self._reply_to)
response = self._client.send(message)
if response.status_code >= 400:
raise RuntimeError(
f"SendGrid error {response.status_code}: {response.body}"
)
def get_email_backend() -> EmailBackend: