feat(FN-011): complete Step 4 — repository connection model and connection manager
Fusion-Task-Id: FN-011 Fusion-Task-Lineage: 4a9aca6f-9d91-43aa-8d2a-d59657c1541a
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""Git provider abstraction, credentials, SSH keys, and operations."""
|
||||
|
||||
from app.git.connection import ConnectionManager, RepositoryConnectionData
|
||||
from app.git.credentials import AccessTokenCredential, CredentialStorage, GitCredential
|
||||
from app.git.provider import GitProvider
|
||||
from app.git.ssh_key import SshKeyLifecycle, SshKeyPair
|
||||
@@ -7,12 +8,14 @@ from app.git.types import ConnectionStatus, CredentialKind, ProviderKind, SshKey
|
||||
|
||||
__all__ = [
|
||||
"AccessTokenCredential",
|
||||
"ConnectionManager",
|
||||
"ConnectionStatus",
|
||||
"CredentialKind",
|
||||
"CredentialStorage",
|
||||
"GitCredential",
|
||||
"GitProvider",
|
||||
"ProviderKind",
|
||||
"RepositoryConnectionData",
|
||||
"SshKeyLifecycle",
|
||||
"SshKeyPair",
|
||||
"SshKeyStatus",
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
"""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,
|
||||
)
|
||||
Reference in New Issue
Block a user