4214b48c37
Fusion-Task-Id: FN-011 Fusion-Task-Lineage: 4a9aca6f-9d91-43aa-8d2a-d59657c1541a
101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
"""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,
|
|
)
|