from collections.abc import Sequence from dataclasses import dataclass from typing import Any, ClassVar from anydi import singleton from fansifter_common.auth.account import Account from fansifter_common.auth.exceptions import PermissionDenied from fansifter_common.auth.services import AuthService from fansifter_common.auth.types import Permission from pydantic import TypeAdapter from dmp.adapters.db import DefaultDB from dmp.app_connections.enums import AppConnectionStatus from dmp.google.dtos import GoogleAdAccountOverview from dmp.google.enums import GoogleUserConnectionStatus from dmp.google.services import GoogleAdAccountService from dmp.google.types import GoogleAdAccountOrderBy GoogleAdAccountOverviewListValidator = TypeAdapter(list[GoogleAdAccountOverview]) @dataclass(frozen=True) class GetGoogleAdAccountsV2Request: identity_id: str vendor_id: int | None subaccount_id: int | None limit: int offset: int order_by: list[GoogleAdAccountOrderBy] DEFAULT_LIMIT: ClassVar[int] = 10 DEFAULT_OFFSET: ClassVar[int] = 0 DEFAULT_ORDER_BY: ClassVar[list[GoogleAdAccountOrderBy]] = [ "vendorId.asc.nullsFirst", "name.asc", ] @dataclass(frozen=True) class GetGoogleAdAccountsV2Response: total: int items: Sequence[GoogleAdAccountOverview] @singleton class GetGoogleAdAccountsV2Handler: permission = Permission("ad_account", "view") def __init__( self, db: DefaultDB, auth_service: AuthService, ad_account_service: GoogleAdAccountService, ) -> None: self.db = db self.auth_service = auth_service self.ad_account_service = ad_account_service def handle( self, request: GetGoogleAdAccountsV2Request ) -> GetGoogleAdAccountsV2Response: account_access = self.auth_service.authorize_for_permission( request.identity_id, permission=self.permission ) if request.vendor_id is not None and request.subaccount_id is not None: if not account_access.has_access( Account( vendor_id=request.vendor_id or 0, subaccount_id=request.subaccount_id or 0, ) ): raise PermissionDenied vendor_ids = account_access.filter_vendor_ids(request.vendor_id) subaccount_ids = account_access.filter_subaccount_ids(request.subaccount_id) return self._get_overview( identity_id=request.identity_id, vendor_ids=vendor_ids, subaccount_ids=subaccount_ids, limit=request.limit, offset=request.offset, order_by=request.order_by, ) def _get_overview( self, identity_id: str, vendor_ids: list[int], subaccount_ids: list[int], limit: int, offset: int, order_by: list[GoogleAdAccountOrderBy], ) -> GetGoogleAdAccountsV2Response: base_context: dict[str, Any] = { "vendor_ids": vendor_ids, "subaccount_ids": subaccount_ids, "identity_id": identity_id, } count_query = self.db.query_from_template( "google-ad-account/count-by-account.sql", context=base_context, ) total = int(self.db.session.execute(count_query).scalar_one()) if total == 0: return GetGoogleAdAccountsV2Response(total=0, items=[]) items_query = self.db.query_from_template( "google-ad-account/find-by-account.sql", context={ **base_context, "ordered_reporting_enum_type": AppConnectionStatus.ordered(), "ordered_sharing_enum_type": GoogleUserConnectionStatus.ordered(), "limit": limit, "offset": offset, "order_by": order_by, }, ) rows = self.db.session.execute(items_query).mappings() ad_accounts = GoogleAdAccountOverviewListValidator.validate_python(rows) items = list(self.ad_account_service.fill_ad_account_empty_data(ad_accounts)) return GetGoogleAdAccountsV2Response(total=total, items=items)