import json import logging import uuid from datetime import date from typing import Any from pydantic import TypeAdapter import app from app.config import Settings from app.connectors.aws.s3 import S3Client from app.exceptions import RequestValidationError from app.handlers import SendgridWebhooksReplyHandler, SendgridWebhooksUnsubHandler from app.models import ReplyRequest, UnsubRequest from app.modules import container app.setup() logger = logging.getLogger(__name__) RequestValidator: TypeAdapter[UnsubRequest | ReplyRequest] = TypeAdapter( UnsubRequest | ReplyRequest ) @container.inject def handle( event_dict: dict[str, Any], _context: Any, ) -> dict[str, int]: try: settings = container.resolve(Settings) request = RequestValidator.validate_python( event_dict, context={"secret_key": settings.reply_to_secret_key} ) except RequestValidationError: logger.error("Request validation error") return {"statusCode": 200} except Exception: logger.exception("Request validation exception") # Temporarily store the failed event in S3 for debugging purposes. # This will be removed once we are confident that the validation is working correctly. settings = container.resolve(Settings) s3_client = container.resolve(S3Client) s3_client.put_object( bucket=settings.failed_events_s3_bucket, key=f"request/{date.today().isoformat()}/{uuid.uuid4()}.json", body=json.dumps(event_dict), ) return {"statusCode": 400} if isinstance(request, UnsubRequest): unsub_handler = container.resolve(SendgridWebhooksUnsubHandler) return unsub_handler.handle(request, event_dict) elif isinstance(request, ReplyRequest): # pyright: ignore[reportUnnecessaryIsInstance] reply_handler = container.resolve(SendgridWebhooksReplyHandler) return reply_handler.handle(request, event_dict) else: return {"statusCode": 404}