from collections import defaultdict from dataclasses import dataclass from anydi import singleton from fansifter_common.auth.account import Account 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.app_connections.services import AdsConnectionService from dmp.google.exceptions import ( GoogleAdAccountNotFoundError, GoogleUserAdAccountNotFoundError, ) from dmp.google.models import GoogleAdReportingConnection, GoogleUserConnection from dmp.google.repositories import ( GoogleAdAccountRepository, GoogleAdReportingConnectionRepository, GoogleUserAdAccountRepository, GoogleUserConnectionRepository, ) @dataclass class DeleteGoogleAdAccountsRequest(AuthRequest): ad_account_id: str vendor_id: int subaccount_id: int @property def account(self) -> Account: return Account(vendor_id=self.vendor_id, subaccount_id=self.subaccount_id) @singleton class DeleteGoogleAdAccountsHandler: permission = Permission("ad_account", "delete") def __init__( self, db: DefaultDB, auth_service: AuthService, ad_account_repository: GoogleAdAccountRepository, user_ad_account_repository: GoogleUserAdAccountRepository, user_connection_repository: GoogleUserConnectionRepository, ad_reporting_connection_repository: GoogleAdReportingConnectionRepository, ads_connection_service: AdsConnectionService, ) -> None: self.db = db self.auth_service = auth_service self.ad_account_repository = ad_account_repository self.user_ad_account_repository = user_ad_account_repository self.user_connection_repository = user_connection_repository self.ad_reporting_connection_repository = ad_reporting_connection_repository self.ads_connection_service = ads_connection_service @transactional def handle(self, request: DeleteGoogleAdAccountsRequest) -> None: # noqa: C901 self.auth_service.check_account_resource( request.identity_id, permission=self.permission, resource_id=request.ad_account_id, account=request.account, ) # Get Ad account ad_account = self.ad_account_repository.get(request.ad_account_id) if ad_account is None: raise GoogleAdAccountNotFoundError users_ad_accounts = ( self.user_ad_account_repository.find_by_ad_account_id_and_account( ad_account_id=request.ad_account_id, vendor_id=request.vendor_id, subaccount_id=request.subaccount_id, ) ) if not users_ad_accounts: raise GoogleUserAdAccountNotFoundError # Get identity ids identity_ids = [ users_ad_account.identity_id for users_ad_account in users_ad_accounts ] # Find Google User connections user_connections_by_identity_id = self._get_user_connections_by_identity_ids( identity_ids ) # Find Google Ad reporting connections ad_reporting_connections_by_identity_id = ( self._get_ad_reporting_connections_by_identity_ids(identity_ids) ) # Store deleted ad reporting connections deleted_ad_reporting_connections: list[GoogleAdReportingConnection] = [] for user_ad_account in users_ad_accounts: # Delete user ad account self.user_ad_account_repository.delete(user_ad_account) user_connections = user_connections_by_identity_id[ user_ad_account.identity_id ] for user_connection in user_connections: # Get user connection ad account connection_ad_account = user_connection.get_connection_ad_account( user_ad_account.ad_account_id ) if connection_ad_account is not None: # Remove connection ad account from user connection user_connection.remove_connection_ad_account(connection_ad_account) # Delete self user connection if no connection ad accounts left if not user_connection.connection_ad_accounts: self.user_connection_repository.delete(user_connection) # Get Ad reporting connection ad_reporting_connections = ad_reporting_connections_by_identity_id[ user_ad_account.identity_id ] for ad_reporting_connection in ad_reporting_connections: ad_reporting_connection.remove_ad_account(user_ad_account.ad_account) if not ad_reporting_connection.ad_accounts: self.ad_reporting_connection_repository.delete( ad_reporting_connection ) deleted_ad_reporting_connections.append(ad_reporting_connection) # Delete Fivetran ad accounts for ad_reporting_connection in deleted_ad_reporting_connections: self.ads_connection_service.delete_fivetran_ad_account( ad_reporting_connection, ad_account_external_id=ad_account.external_id ) return None def _get_user_connections_by_identity_ids( self, identity_ids: list[str] ) -> dict[str, list[GoogleUserConnection]]: result = defaultdict(list) connections = self.user_connection_repository.find_by_identity_ids(identity_ids) for connection in connections: result[connection.identity_id].append(connection) return result def _get_ad_reporting_connections_by_identity_ids( self, identity_ids: list[str] ) -> dict[str, list[GoogleAdReportingConnection]]: result = defaultdict(list) connections = self.ad_reporting_connection_repository.find_by_identity_ids( identity_ids ) for connection in connections: result[connection.identity_id].append(connection) return result