import importlib from collections.abc import AsyncIterator import pyxdi import sqlalchemy from audience_common.auth.jwt import ( JWTAuth, get_default_audience, get_default_issuer, get_default_jwks_url, ) from audience_common.auth.m2m import AsyncM2MTokenManager from audience_common.aws.secretsmanager import SecretsManager from audience_common.cache.backends import AsyncCache, AsyncLocMemCache, AsyncNullCache from audience_common.cache.backends.asyncio.redis import AsyncRedisCache from audience_common.enums import Environment from audience_common.owsclient import AsyncOwsClient from pyxdi import provider from campaigns.config import AppSettings, settings as _settings from campaigns.connectors.aws.kms import KMS, BaseKMS, DummyKMS from campaigns.connectors.aws.s3 import S3Client from campaigns.connectors.aws.sts import STSClient from campaigns.connectors.db import Database from campaigns.connectors.facebook.base import FacebookClient from campaigns.connectors.ows_account import OwsAccount from campaigns.connectors.ows_permissions import OwsPermissionsClient from campaigns.connectors.ows_product import OwsProduct class AppModule(pyxdi.Module): @provider(scope="singleton") def settings(self) -> AppSettings: return _settings @provider(scope="singleton") def secrets_manager(self, settings: AppSettings) -> SecretsManager: return SecretsManager(region_name=settings.aws_region_name) @provider(scope="singleton") def sts_client(self, settings: AppSettings) -> STSClient: return STSClient(region_name=settings.aws_region_name) @provider(scope="singleton") def s3_client(self, settings: AppSettings) -> S3Client: return S3Client(region_name=settings.aws_region_name) @provider(scope="singleton") def jwt_auth(self, settings: AppSettings) -> JWTAuth: return JWTAuth( jwks_url=get_default_jwks_url(settings.environment), audience=get_default_audience(settings.environment), issuer=get_default_issuer(settings.environment), leeway=settings.jwt_auth_leeway, ) @provider(scope="singleton") async def facebook_client( self, settings: AppSettings ) -> AsyncIterator[FacebookClient]: async with FacebookClient( client_id=settings.facebook_app_id, client_secret=settings.facebook_app_secret, app_system_user_token=settings.facebook_app_system_user_access_token, app_system_user_id=settings.facebook_app_system_user_id, admin_system_user_token=settings.facebook_admin_system_user_access_token, ad_account_id=settings.facebook_adaccount_id, business_manager_id=settings.facebook_business_id, ) as facebook_client: yield facebook_client @provider(scope="singleton") async def cache(self, settings: AppSettings) -> AsyncIterator[AsyncCache]: if settings.cache_backend in ["locmem", "memory"]: cache: AsyncCache = AsyncLocMemCache( default_timeout=settings.cache_default_timeout ) elif settings.cache_backend == "redis": cache = AsyncRedisCache( host=settings.redis_host, port=settings.redis_port, db=settings.cache_redis_db, ssl=settings.redis_ssl, default_timeout=settings.cache_default_timeout, key_prefix=settings.cache_key_prefix, ) else: cache = AsyncNullCache() await cache.start() yield cache await cache.close() @provider(scope="singleton") def m2m_token_manager( self, settings: AppSettings, secrets_manager: SecretsManager, cache: AsyncCache ) -> AsyncM2MTokenManager: return AsyncM2MTokenManager( secrets_manager=secrets_manager, secret_name_key=settings.m2m_token_secret_key_name, secret_expire_name_key=settings.m2m_token_secret_expiry_key_name, cache=cache, ) @provider(scope="singleton") async def ows_client( self, settings: AppSettings, m2m_token_manager: AsyncM2MTokenManager, ) -> AsyncIterator[AsyncOwsClient]: ows_client = AsyncOwsClient( environment=settings.environment, service_name=settings.service_name, m2m_token_manager=m2m_token_manager if settings.environment in [Environment.QA, Environment.PROD] else None, ) yield ows_client await ows_client.close() @provider(scope="singleton") def ows_product(self, ows_client: AsyncOwsClient) -> OwsProduct: return OwsProduct(ows_client=ows_client) @provider(scope="singleton") def ows_account(self, ows_client: AsyncOwsClient) -> OwsAccount: return OwsAccount(ows_client=ows_client) @provider(scope="singleton") def ows_permissions_client( self, ows_client: AsyncOwsClient ) -> OwsPermissionsClient: return OwsPermissionsClient(ows_client=ows_client) @provider(scope="singleton") def kms(self, settings: AppSettings) -> BaseKMS: if settings.kms_enabled: return KMS(region_name=settings.aws_region_name, key_id=settings.kms_key_id) return DummyKMS() @provider(scope="singleton") def scan_orm_models(self, settings: AppSettings) -> None: for model_path in settings.orm_models: importlib.import_module(model_path) @provider(scope="singleton") async def db(self, settings: AppSettings) -> AsyncIterator[Database]: db = Database( url=settings.postgres_url, engine_args={ "echo": settings.postgres_echo, "poolclass": sqlalchemy.pool.QueuePool, "pool_size": settings.postgres_pool_size, "max_overflow": settings.postgres_pool_max_overflow, "pool_recycle": settings.postgres_pool_recycle, "pool_pre_ping": settings.postgres_pool_pre_ping, "pool_reset_on_return": settings.postgres_pool_reset_on_return, "connect_args": settings.postgres_connect_args, }, template_searchpath=settings.jinjasql_template_searchpath, ) yield db await db.close()