"""Repository connection orchestration.""" import uuid from pydantic import BaseModel from sqlalchemy import select 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() status = self.provider.validate_connection(git_url, str(credential_id)) 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.refresh(row) 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, )