from dataclasses import dataclass from anydi import singleton from fansifter_common.auth.requests import AuthRequest from fansifter_common.auth.services import AuthService from fansifter_common.auth.types import Permission from dmp.adapters.db import DefaultDB, transactional from dmp.adapters.fivetran import FivetranClient from dmp.adapters.fivetran.models import ConnectCard from dmp.config import Settings from dmp.tiktok.exceptions import TikTokUserConnectionNotFoundError from dmp.tiktok.models import TikTokAdReportingConnection from dmp.tiktok.repositories import TikTokAdReportingConnectionRepository from dmp.tiktok.services import TikTokUserConnectionService @dataclass class ConnectTikTokAdReportingRequest(AuthRequest): redirect_uri: str user_id: str @singleton class ConnectTikTokAdReportingHandler: permission = Permission("ad_connection", "connect") def __init__( self, db: DefaultDB, auth_service: AuthService, ad_reporting_connection_repository: TikTokAdReportingConnectionRepository, user_connection_service: TikTokUserConnectionService, fivetran_client: FivetranClient, settings: Settings, ) -> None: self.db = db self.auth_service = auth_service self.ad_reporting_connection_repository = ad_reporting_connection_repository self.user_connection_service = user_connection_service self.fivetran_client = fivetran_client self.settings = settings @transactional def handle( self, request: ConnectTikTokAdReportingRequest ) -> tuple[TikTokAdReportingConnection, ConnectCard]: self.auth_service.check_owned_resource( request.identity_id, permission=self.permission ) user_connection = self.user_connection_service.get_user_connection( request.identity_id, request.user_id, ) if user_connection: ad_account_external_ids = [ ad_account.external_id for ad_account in user_connection.ad_accounts ] else: raise TikTokUserConnectionNotFoundError ad_reporting_connection = self.ad_reporting_connection_repository.get_connection_by_identity_id_and_user_id( identity_id=request.identity_id, user_id=request.user_id ) if ad_reporting_connection: self.auth_service.check_owned_resource( request.identity_id, permission=self.permission, resource_id=ad_reporting_connection.id, ) ad_reporting_connection.initial_email_sent = False connect_card = self.fivetran_client.get_connect_card( connection_id=ad_reporting_connection.fivetran_connector_id, redirect_uri=request.redirect_uri, ) else: fivetran_schema = self._generate_fivetran_schema( request.identity_id, request.user_id, prefix=self.settings.fivetran_schema_prefix, ) connection = self.fivetran_client.create_tiktok_ads_connection( group_id=self.settings.fivetran_reporting_group_id, schema=fivetran_schema, redirect_uri=request.redirect_uri, accounts=ad_account_external_ids, ) ad_reporting_connection = TikTokAdReportingConnection( fivetran_connector_id=connection.id, fivetran_schema=fivetran_schema, identity_id=request.identity_id, user_id=request.user_id, ) connect_card = connection.connect_card self.ad_reporting_connection_repository.add(ad_reporting_connection) return ad_reporting_connection, connect_card @staticmethod def _generate_fivetran_schema( identity_id: str, user_id: str, *, prefix: str | None = None ) -> str: user_slug = identity_id.replace("-", "_").lower() fivetran_schema = f"tiktok_{user_slug}_{user_id}" if prefix: return f"{prefix.lower()}_{fivetran_schema}" return fivetran_schema