"""SendGrid email provider implementation.""" import logging import requests from flask import current_app from gatehouse_app.services.email_provider import EmailMessage, EmailProvider logger = logging.getLogger(__name__) class SendGridEmailProvider(EmailProvider): """SendGrid API-based email provider implementation.""" # Configuration keys SENDGRID_API_KEY = "SENDGRID_API_KEY" SENDGRID_FROM_EMAIL = "SENDGRID_FROM_EMAIL" FROM_ADDRESS = "FROM_ADDRESS" API_URL = "https://api.sendgrid.com/v3/mail/send" def send(self, message: EmailMessage) -> bool: """Send an email via SendGrid API. Args: message: EmailMessage instance containing email details Returns: bool: True if email was sent successfully, False otherwise """ api_key = current_app.config.get(self.SENDGRID_API_KEY) default_from = current_app.config.get(self.SENDGRID_FROM_EMAIL) fallback_from = current_app.config.get(self.FROM_ADDRESS) if not api_key: logger.error( f"[SENDGRID] Cannot send — missing SENDGRID_API_KEY config. " f"Would have sent to: {message.to} | Subject: {message.subject}" ) return False from_address = message.from_address or default_from or fallback_from if not from_address: logger.error( f"[SENDGRID] Cannot send — missing from address (SENDGRID_FROM_EMAIL or FROM_ADDRESS). " f"Would have sent to: {message.to} | Subject: {message.subject}" ) return False payload = { "personalizations": [ { "to": [{"email": message.to}] } ], "from": {"email": from_address}, "subject": message.subject, "content": [ { "type": "text/plain", "value": message.body } ] } if message.html_body: payload["content"].append({ "type": "text/html", "value": message.html_body }) try: response = requests.post( self.API_URL, json=payload, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } ) if response.status_code == 202: logger.info(f"[SENDGRID] Sent to {message.to} | Subject: {message.subject}") return True else: logger.error( f"[SENDGRID] Failed to send to {message.to}: " f"status={response.status_code} body={response.text}" ) return False except Exception as e: logger.error(f"[SENDGRID] Exception while sending to {message.to}: {e}") return False