"""Repository connection orchestration.""" import uuid from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from app.git.credentials import CredentialStorage, GitCredential from app.git.provider import GitProvider from app.git.types import ConnectionStatus, ProviderKind from app.models.repository_connection import RepositoryConnection class RepositoryConnectionData(BaseModel): """Domain-level read model for a repository connection.""" id: uuid.UUID project_id: uuid.UUID repository_id: uuid.UUID | None provider_kind: ProviderKind credential_id: uuid.UUID | None connection_status: ConnectionStatus default_branch: str | None class ConnectionManager: """Orchestrates creating, validating, and retrieving repository connections.""" def __init__(self, provider: GitProvider, storage: CredentialStorage) -> None: self.provider = provider self.storage = storage async def connect( self, session: AsyncSession, project_id: uuid.UUID, git_url: str, credential: GitCredential, ) -> RepositoryConnectionData: """Store *credential*, create a connection row, and validate with the provider.""" credential_id = self.storage.create(credential) row = RepositoryConnection( project_id=project_id, provider_kind=str(self.provider.get_kind()), credential_id=credential_id, connection_status=str(ConnectionStatus.pending), ) session.add(row) await session.flush() try: status = self.provider.validate_connection( git_url, str(credential_id) ) except Exception: row.connection_status = str(ConnectionStatus.error) await session.flush() raise RuntimeError("Connection validation failed") if status == ConnectionStatus.connected: row.connection_status = str(ConnectionStatus.connected) else: row.connection_status = str(ConnectionStatus.error) await session.flush() raise RuntimeError("Connection validation failed") await session.flush() return _map_row(row) async def disconnect( self, session: AsyncSession, connection_id: uuid.UUID ) -> None: """Mark the connection as disconnected.""" row = await session.get(RepositoryConnection, connection_id) if row is None: return row.connection_status = str(ConnectionStatus.disconnected) await session.flush() async def get_connection( self, session: AsyncSession, connection_id: uuid.UUID ) -> RepositoryConnectionData | None: """Fetch a connection by ID and map it to the Pydantic read model.""" row = await session.get(RepositoryConnection, connection_id) if row is None: return None return _map_row(row) def _map_row(row: RepositoryConnection) -> RepositoryConnectionData: return RepositoryConnectionData( id=row.id, project_id=row.project_id, repository_id=row.repository_id, provider_kind=ProviderKind(row.provider_kind), credential_id=row.credential_id, connection_status=ConnectionStatus(row.connection_status), default_branch=row.default_branch, )