"""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 ( CreateCommentUseCase, CreatePostUseCase, DeleteCommentUseCase, DeletePostUseCase, GetPostUseCase, ListCommentsUseCase, ListPostsUseCase, PublishPostUseCase, ToggleCommentLikeUseCase, TogglePostLikeUseCase, UpdatePostUseCase, ) from app.application.interfaces import TransactionManager from app.domain.repositories import CommentRepository, 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.comment import SQLAlchemyCommentRepository 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) @provide(scope=Scope.REQUEST) def get_comment_repository(self, session: AsyncSession) -> CommentRepository: """Provide CommentRepository implementation. Args: session: Database session from DI container. Returns: SQLAlchemyCommentRepository instance. """ return SQLAlchemyCommentRepository(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, ) @provide(scope=Scope.REQUEST) def get_create_comment_use_case( self, post_repo: PostRepository, comment_repo: CommentRepository, tx_manager: TransactionManager, ) -> CreateCommentUseCase: """Provide CreateCommentUseCase. Args: post_repo: Post repository dependency. comment_repo: Comment repository dependency. tx_manager: Transaction manager dependency. Returns: Configured CreateCommentUseCase instance. """ return CreateCommentUseCase( post_repo=post_repo, comment_repo=comment_repo, tx_manager=tx_manager, ) @provide(scope=Scope.REQUEST) def get_list_comments_use_case( self, comment_repo: CommentRepository, ) -> ListCommentsUseCase: """Provide ListCommentsUseCase. Args: comment_repo: Comment repository dependency. Returns: Configured ListCommentsUseCase instance. """ return ListCommentsUseCase( comment_repo=comment_repo, ) @provide(scope=Scope.REQUEST) def get_delete_comment_use_case( self, comment_repo: CommentRepository, tx_manager: TransactionManager, ) -> DeleteCommentUseCase: """Provide DeleteCommentUseCase. Args: comment_repo: Comment repository dependency. tx_manager: Transaction manager dependency. Returns: Configured DeleteCommentUseCase instance. """ return DeleteCommentUseCase( comment_repo=comment_repo, tx_manager=tx_manager, ) @provide(scope=Scope.REQUEST) def get_toggle_comment_like_use_case( self, comment_repo: CommentRepository, tx_manager: TransactionManager, ) -> ToggleCommentLikeUseCase: """Provide ToggleCommentLikeUseCase. Args: comment_repo: Comment repository dependency. tx_manager: Transaction manager dependency. Returns: Configured ToggleCommentLikeUseCase instance. """ return ToggleCommentLikeUseCase( comment_repo=comment_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)