from collections.abc import Iterator import boto3 import httpx from anydi import Container, Module, Provider, provider from fansifter_common import context from fansifter_common.adapters.aws.secretsmanager import SecretsManager from fansifter_common.m2m_token import M2MTokenManager from fansifter_common.utils.functional import lazy_proxy from owsclient import OwsClient from app.adapters.aws.kms import KMSClient from app.adapters.aws.s3 import S3Client from app.adapters.facebook import FacebookClient from app.adapters.google import GoogleClient from app.adapters.ows_dmp import OwsDmpClient from app.adapters.tiktok import TikTokClient from app.config import Settings, settings from app.handler import ShareAudienceHandler from app.strategies import ( GoogleAudienceShareStrategy, MetaAudienceShareStrategy, TikTokAudienceShareStrategy, ) class AppModule(Module): @provider(scope="singleton") def aws_session(self, settings: Settings) -> boto3.Session: return boto3.Session(region_name=settings.aws_region_name) @provider(scope="singleton") def secrets_manager( self, session: boto3.Session, settings: Settings ) -> SecretsManager: return SecretsManager(session=session, region_name=settings.aws_region_name) @provider(scope="singleton") def s3_client(self, session: boto3.Session, settings: Settings) -> S3Client: return S3Client(session=session, region_name=settings.aws_region_name) @provider(scope="singleton") def kms_client(self, settings: Settings, session: boto3.Session) -> KMSClient: return KMSClient( region_name=settings.aws_region_name, key_id=settings.kms_key_id, session=session, ) @provider(scope="singleton") def m2m_token_manager( self, settings: Settings, secrets_manager: SecretsManager ) -> M2MTokenManager: return M2MTokenManager( secrets_manager=secrets_manager, secret_name_key=settings.m2m_token_secret_key_name, secret_expire_name_key=settings.m2m_token_secret_expiry_key_name, ) @provider(scope="singleton") def ows_client( self, settings: Settings, m2m_token_manager: M2MTokenManager ) -> OwsClient: return OwsClient( environment=settings.environment, service_name=settings.service_name, m2m_token_manager=m2m_token_manager, correlation_id_getter=context.get_correlation_id, ) @provider(scope="singleton") def ows_dmp_client(self, ows_client: OwsClient) -> OwsDmpClient: return OwsDmpClient(ows_client=ows_client) @provider(scope="singleton") def facebook_client(self) -> Iterator[FacebookClient]: with FacebookClient( client_options={ "transport": httpx.HTTPTransport(retries=3), "timeout": 30, } ) as client: yield client @provider(scope="singleton") def tiktok_client(self) -> Iterator[TikTokClient]: with TikTokClient() as client: yield client @provider(scope="singleton") def google_client(self, settings: Settings) -> Iterator[GoogleClient]: with GoogleClient( developer_token=settings.google_developer_token, client_options={ "transport": httpx.HTTPTransport(retries=3), "timeout": 30, }, ) as client: yield client # Strategies @provider(scope="singleton") def meta_strategy( self, ows_dmp_client: OwsDmpClient, kms_client: KMSClient, facebook_client: FacebookClient, ) -> MetaAudienceShareStrategy: return MetaAudienceShareStrategy( ows_dmp_client=ows_dmp_client, kms_client=kms_client, facebook_client=facebook_client, ) @provider(scope="singleton") def tiktok_strategy( self, ows_dmp_client: OwsDmpClient, kms_client: KMSClient, tiktok_client: TikTokClient, ) -> TikTokAudienceShareStrategy: return TikTokAudienceShareStrategy( ows_dmp_client=ows_dmp_client, kms_client=kms_client, tiktok_client=tiktok_client, ) @provider(scope="singleton") def google_strategy( self, ows_dmp_client: OwsDmpClient, kms_client: KMSClient, google_client: GoogleClient, ) -> GoogleAudienceShareStrategy: return GoogleAudienceShareStrategy( ows_dmp_client=ows_dmp_client, kms_client=kms_client, google_client=google_client, ) # Handler @provider(scope="singleton") def handler( self, s3_client: S3Client, ows_dmp_client: OwsDmpClient, meta_strategy: MetaAudienceShareStrategy, tiktok_strategy: TikTokAudienceShareStrategy, google_strategy: GoogleAudienceShareStrategy, ) -> ShareAudienceHandler: return ShareAudienceHandler( s3_client=s3_client, ows_dmp_client=ows_dmp_client, strategies=[ meta_strategy, tiktok_strategy, google_strategy, ], ) def setup_container() -> Container: # Create DI container return Container( providers=[ Provider(call=lambda: settings, interface=Settings, scope="singleton") ], modules=[AppModule], default_scope="singleton", testing=settings.environment == "test", ) # Lazy proxy to setup the container only when it's needed container = lazy_proxy(setup_container)