"""History response mappers. Convert domain models (from history_service) to response models (for HTTP API). This is the mapping layer at the router boundary, ensuring the service layer remains independent of HTTP response shapes. """ from __future__ import annotations from app.models.history import ( HistoryBanItem, HistoryListResponse, IpDetailResponse, IpTimelineEvent, ) from app.models.history_domain import ( DomainHistoryBanItem, DomainHistoryList, DomainIpDetail, DomainIpTimelineEvent, ) from app.utils.pagination import create_pagination_metadata def map_domain_history_ban_item_to_response( domain: DomainHistoryBanItem, ) -> HistoryBanItem: """Convert domain history ban item to response model.""" return HistoryBanItem( ip=domain.ip, jail=domain.jail, banned_at=domain.banned_at, ban_count=domain.ban_count, failures=domain.failures, matches=domain.matches or [], country_code=domain.country_code, country_name=domain.country_name, asn=domain.asn, org=domain.org, ) def map_domain_history_list_to_response(domain: DomainHistoryList) -> HistoryListResponse: """Convert domain history list to response model.""" return HistoryListResponse( items=[map_domain_history_ban_item_to_response(i) for i in domain.items], pagination=create_pagination_metadata( domain.total, domain.page, domain.page_size ), ) def map_domain_ip_timeline_event_to_response( domain: DomainIpTimelineEvent, ) -> IpTimelineEvent: """Convert domain IP timeline event to response model.""" return IpTimelineEvent( jail=domain.jail, banned_at=domain.banned_at, ban_count=domain.ban_count, failures=domain.failures, matches=domain.matches or [], ) def map_domain_ip_detail_to_response(domain: DomainIpDetail) -> IpDetailResponse: """Convert domain IP detail to response model.""" return IpDetailResponse( ip=domain.ip, total_bans=domain.total_bans, total_failures=domain.total_failures, last_ban_at=domain.last_ban_at, country_code=domain.country_code, country_name=domain.country_name, asn=domain.asn, org=domain.org, timeline=[ map_domain_ip_timeline_event_to_response(t) for t in (domain.timeline or []) ], )