"""Dishka providers for dependency injection. This module defines Dishka providers for all application dependencies. Providers configure how dependencies are created and scoped. """ from collections.abc import AsyncGenerator from dishka import Provider, Scope, provide from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession from app.application import ( CreatePostUseCase, DeletePostUseCase, GetPostUseCase, ListPostsUseCase, PublishPostUseCase, TogglePostLikeUseCase, UpdatePostUseCase, ) from app.application.interfaces import TransactionManager from app.domain.repositories import PostRepository from app.infrastructure.auth import KeycloakAuthClient, MockKeycloakClient from app.infrastructure.config.settings import settings from app.infrastructure.database.connection import AsyncSessionLocal, engine from app.infrastructure.repositories.post import SQLAlchemyPostRepository class DatabaseProvider(Provider): """Provider for database-related dependencies. Provides database engine and session scoped appropriately. Engine is application-scoped, sessions are request-scoped. Example: >>> provider = DatabaseProvider() """ @provide(scope=Scope.APP) def get_engine(self) -> AsyncEngine: """Provide SQLAlchemy engine. Returns: AsyncEngine instance for database operations. """ return engine @provide(scope=Scope.REQUEST) async def get_session(self) -> AsyncGenerator[AsyncSession]: """Provide database session per request. Yields: AsyncSession instance for the request lifetime. """ async with AsyncSessionLocal() as session: try: yield session finally: await session.close() class RepositoryProvider(Provider): """Provider for repository implementations. Provides concrete repository implementations for interfaces. All repositories are request-scoped. Example: >>> provider = RepositoryProvider() """ @provide(scope=Scope.REQUEST) def get_post_repository(self, session: AsyncSession) -> PostRepository: """Provide PostRepository implementation. Args: session: Database session from DI container. Returns: SQLAlchemyPostRepository instance. """ return SQLAlchemyPostRepository(session) class TransactionManagerProvider(Provider): """Provider for transaction manager. Provides transaction manager implementation for use cases. Scoped per request for transaction isolation. Example: >>> provider = TransactionManagerProvider() """ @provide(scope=Scope.REQUEST) def get_transaction_manager(self, session: AsyncSession) -> TransactionManager: """Provide TransactionManager implementation. Args: session: Database session from DI container. Returns: SessionTransactionManager instance. """ from app.infrastructure.di.transaction_manager import SessionTransactionManager return SessionTransactionManager(session) class UseCaseProvider(Provider): """Provider for use cases. Provides all application use cases with their dependencies. All use cases are request-scoped for transaction isolation. Example: >>> provider = UseCaseProvider() """ @provide(scope=Scope.REQUEST) def get_create_post_use_case( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> CreatePostUseCase: """Provide CreatePostUseCase. Args: post_repo: Post repository dependency. tx_manager: Transaction manager dependency. Returns: Configured CreatePostUseCase instance. """ return CreatePostUseCase( post_repo=post_repo, tx_manager=tx_manager, ) @provide(scope=Scope.REQUEST) def get_get_post_use_case( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> GetPostUseCase: """Provide GetPostUseCase. Args: post_repo: Post repository dependency. tx_manager: Transaction manager dependency. Returns: Configured GetPostUseCase instance. """ return GetPostUseCase( post_repo=post_repo, tx_manager=tx_manager, ) @provide(scope=Scope.REQUEST) def get_update_post_use_case( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> UpdatePostUseCase: """Provide UpdatePostUseCase. Args: post_repo: Post repository dependency. tx_manager: Transaction manager dependency. Returns: Configured UpdatePostUseCase instance. """ return UpdatePostUseCase( post_repo=post_repo, tx_manager=tx_manager, ) @provide(scope=Scope.REQUEST) def get_delete_post_use_case( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> DeletePostUseCase: """Provide DeletePostUseCase. Args: post_repo: Post repository dependency. tx_manager: Transaction manager dependency. Returns: Configured DeletePostUseCase instance. """ return DeletePostUseCase( post_repo=post_repo, tx_manager=tx_manager, ) @provide(scope=Scope.REQUEST) def get_list_posts_use_case( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> ListPostsUseCase: """Provide ListPostsUseCase. Args: post_repo: Post repository dependency. tx_manager: Transaction manager dependency. Returns: Configured ListPostsUseCase instance. """ return ListPostsUseCase( post_repo=post_repo, tx_manager=tx_manager, ) @provide(scope=Scope.REQUEST) def get_publish_post_use_case( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> PublishPostUseCase: """Provide PublishPostUseCase. Args: post_repo: Post repository dependency. tx_manager: Transaction manager dependency. Returns: Configured PublishPostUseCase instance. """ return PublishPostUseCase( post_repo=post_repo, tx_manager=tx_manager, ) @provide(scope=Scope.REQUEST) def get_toggle_like_use_case( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> TogglePostLikeUseCase: """Provide TogglePostLikeUseCase. Args: post_repo: Post repository dependency. tx_manager: Transaction manager dependency. Returns: Configured TogglePostLikeUseCase instance. """ return TogglePostLikeUseCase( post_repo=post_repo, tx_manager=tx_manager, ) class KeycloakProvider(Provider): """Provider for Keycloak authentication client. Provides Keycloak client as application-scoped singleton. In development mode uses MockKeycloakClient for local testing. Example: >>> provider = KeycloakProvider() """ @provide(scope=Scope.APP) def get_keycloak_client(self) -> KeycloakAuthClient: """Provide KeycloakAuthClient or MockKeycloakClient singleton. Returns MockKeycloakClient in dev mode for local testing without a real Keycloak server. Returns: KeycloakAuthClient instance. """ if settings.is_dev: return MockKeycloakClient() # type: ignore[return-value] return KeycloakAuthClient(settings)