"""This module defines Pydantic models for validating and serializing data related to fan-triggered sends.""" from datetime import date from typing import Optional from pydantic import BaseModel from pydantic import ConfigDict from pydantic import Field from pydantic import RootModel # Input models class Recipient(BaseModel): """Recipient of a triggered send.""" email_address: str profile_uuid: str source_event_id: str first_name: str last_name: Optional[str] = None postal_code: Optional[str] = None class TriggeredSend(BaseModel): """Triggered send request record model.""" business_unit_id: str triggered_send_definition_key: str recipients: list[Recipient] # Marketing cloud API payload models class SubscriberAttributes(BaseModel): """Subscriber attributes for the marketing cloud API.""" model_config = ConfigDict(serialize_by_alias=True) encrypted_id: str = Field(serialization_alias='Encrypted ID') first_name: str = Field(serialization_alias='First Name') last_name: Optional[str] = Field(default=None, serialization_alias='Last Name') postal_code: Optional[str] = Field(default=None, serialization_alias='Postal Code') start_date: date = Field(default_factory=date.today) class ContactAttributes(BaseModel): """Contact attributes for the marketing cloud API.""" model_config = ConfigDict(serialize_by_alias=True) subscriber_attributes: SubscriberAttributes = Field(serialization_alias='SubscriberAttributes') class APIRecipient(BaseModel): """API recipient for the marketing cloud API.""" model_config = ConfigDict(serialize_by_alias=True) address: str = Field(serialization_alias='Address') subscriber_key: str = Field(serialization_alias='SubscriberKey') contact_attributes: ContactAttributes = Field(serialization_alias='ContactAttributes') class APIRecipientWrapper(BaseModel): """API recipient model wrapper for the payload.""" model_config = ConfigDict(serialize_by_alias=True) to: APIRecipient = Field(serialization_alias='To') class TriggeredSendBatchPayload(RootModel[list[APIRecipientWrapper]]): """Triggered send batch API payload model.""" root: list[APIRecipientWrapper] @classmethod def from_input_model(cls, triggered_send: TriggeredSend) -> 'TriggeredSendBatchPayload': """Convert a TriggeredSend model into a TriggeredSendBatchPayload model. Args: triggered_send (TriggeredSend): The input model containing triggered send data. Returns: TriggeredSendBatchPayload: A payload model containing recipient data. """ recipients = [] for recipient in triggered_send.recipients: subscriber_attributes = SubscriberAttributes( encrypted_id=recipient.profile_uuid, first_name=recipient.first_name, last_name=recipient.last_name, postal_code=recipient.postal_code, ) contact_attributes = ContactAttributes(subscriber_attributes=subscriber_attributes) to_model = APIRecipient( address=recipient.email_address, subscriber_key=f'{recipient.source_event_id}_{triggered_send.triggered_send_definition_key}', contact_attributes=contact_attributes) final_recipient = APIRecipientWrapper(to=to_model) recipients.append(final_recipient) return cls(root=recipients) # Marketing cloud API response models class ResponseItem(BaseModel): """Represents an individual response item from the API.""" recipient_send_id: str = Field(alias='recipientSendId') has_errors: bool = Field(alias='hasErrors') messages: list[str] class APIResponse(BaseModel): """Marketing cloud API response model.""" request_id: str = Field(alias='requestId') batch_has_errors: bool = Field(alias='batchHasErrors') responses: list[ResponseItem] # Kafka messages class TriggeredSendFailedEvent(BaseModel): """Kafka event for a failed triggered send.""" triggered_send_info: TriggeredSend error: str api_response: Optional[APIResponse] = None class TriggeredSendSuccessfulEvent(BaseModel): """Kafka event for a successful triggered send.""" triggered_send_info: TriggeredSend