from dataclasses import dataclass from typing import ClassVar from fansifter_common.adapters.ows_account import OwsAccountClient from fansifter_common.adapters.sendgrid import SendGridClient from fansifter_common.adapters.stripo import StripoClient from fansifter_common.email import EmailType from fansifter_common.legal_info.services import LegalInfoService from fansifter_common.translation.services import TranslationService from email_campaigns.adapters.db import Repository from email_campaigns.emails import tokens from email_campaigns.emails.cache import EmailPublicPreviewCache from email_campaigns.emails.enums import TokenDecryptionStatus from email_campaigns.emails.exceptions import ( EmailContentMissingError, EmailNotFoundError, EmailPublicPreviewTokenExpiredError, ) from email_campaigns.emails.models import BaseEmail from email_campaigns.emails.substitutions import apply_preview_substitutions @dataclass class GetEmailPublicPreviewRequest: email_id: str token: str | None = None @dataclass class GetEmailPublicPreviewResponse: html: str class GetEmailPublicPreviewHandler[T: BaseEmail]: """Base handler for getting public preview of emails (campaigns or automated).""" email_type: ClassVar[EmailType] def __init__( self, stripo_client: StripoClient, preview_cache: EmailPublicPreviewCache, sendgrid_client: SendGridClient, legal_info_service: LegalInfoService, translation_service: TranslationService, ows_account_client: OwsAccountClient, email_repository: Repository[T], ) -> None: self.stripo_client = stripo_client self.preview_cache = preview_cache self.sendgrid_client = sendgrid_client self.legal_info_service = legal_info_service self.translation_service = translation_service self.ows_account_client = ows_account_client self.email_repository = email_repository def handle( self, request: GetEmailPublicPreviewRequest ) -> GetEmailPublicPreviewResponse: """Generate and return the public preview HTML for an email.""" email_id = request.email_id if not request.token: raise EmailNotFoundError decryption_result = tokens.decrypt_token(request.token) if decryption_result.status == TokenDecryptionStatus.EXPIRED: raise EmailPublicPreviewTokenExpiredError if ( decryption_result.status == TokenDecryptionStatus.INVALID or not decryption_result.email_id or decryption_result.email_id != email_id ): raise EmailNotFoundError cached_html = self.preview_cache.get(email_id, self.email_type) if cached_html: return GetEmailPublicPreviewResponse(html=cached_html) email = self.email_repository.get(email_id) if email is None: raise EmailNotFoundError if not email.html_content or not email.css_content: raise EmailContentMissingError compressed_html = self.stripo_client.compress( email.html_content, email.css_content, ) final_html = apply_preview_substitutions( compressed_html, email=email, user_email="user@example.com", sendgrid_client=self.sendgrid_client, legal_info_service=self.legal_info_service, translation_service=self.translation_service, ows_account_client=self.ows_account_client, ) self.preview_cache.set(email_id, self.email_type, final_html) return GetEmailPublicPreviewResponse(html=final_html)