← Files NGS Analysis WorkbenchARCHIVED FILE

mcp/ngs_workbench_daemon/persistence/workflows.py

6.46 KB · Sep 30, 2026 · 23:20 UTC

↓ Download file

"""Persist reusable workflows with the existing registry transaction."""

from __future__ import annotations

import json
from dataclasses import dataclass
from typing import Any

from sqlalchemy import select
from sqlalchemy.orm import Session

from .orm import WorkflowRow, WorkflowVersionRow


@dataclass(frozen=True)
class WorkflowEntryRecord:
    id: str
    name: str
    engine: str
    description: str | None
    metadata: dict[str, Any]
    owner: str
    current_version_id: str
    archived_at_ms: int | None
    created_at_ms: int
    updated_at_ms: int


@dataclass(frozen=True)
class WorkflowVersionRecord:
    id: str
    workflow_id: str
    source_kind: str
    local_root: str | None
    entrypoint: str | None
    source_sha256: str | None
    remote_workflow: str | None
    revision: str | None
    execution: dict[str, Any]
    created_at_ms: int


class WorkflowRepository:
    """Own only saved-workflow rows within the existing SQLAlchemy session."""

    def __init__(self, session: Session) -> None:
        self._session = session

    def get_entry(self, workflow_id: str) -> WorkflowEntryRecord | None:
        row = self._session.get(WorkflowRow, workflow_id)
        return self._entry_record(row) if row is not None else None

    def get_current_version(self, workflow_id: str) -> WorkflowVersionRecord | None:
        row = self._session.get(WorkflowRow, workflow_id)
        if row is None:
            return None
        version = self._session.get(WorkflowVersionRow, row.current_version_id)
        if version is None or version.workflow_id != workflow_id:
            raise ValueError("workflow current version is unavailable")
        return self._version_record(version)

    def list_entries(self) -> list[WorkflowEntryRecord]:
        rows = self._session.scalars(select(WorkflowRow).order_by(WorkflowRow.name)).all()
        return [self._entry_record(row) for row in rows]

    def list_versions(self, workflow_id: str) -> list[WorkflowVersionRecord]:
        rows = self._session.scalars(
            select(WorkflowVersionRow)
            .where(WorkflowVersionRow.workflow_id == workflow_id)
            .order_by(WorkflowVersionRow.created_at_ms, WorkflowVersionRow.id)
        ).all()
        return [self._version_record(row) for row in rows]

    def get_version(self, version_id: str) -> WorkflowVersionRecord | None:
        row = self._session.get(WorkflowVersionRow, version_id)
        return self._version_record(row) if row is not None else None

    def add_entry(self, entry: WorkflowEntryRecord) -> None:
        self._session.add(
            WorkflowRow(
                id=entry.id,
                name=entry.name,
                engine=entry.engine,
                description=entry.description,
                metadata_json=json.dumps(entry.metadata, sort_keys=True),
                owner=entry.owner,
                current_version_id=entry.current_version_id,
                archived_at_ms=entry.archived_at_ms,
                created_at_ms=entry.created_at_ms,
                updated_at_ms=entry.updated_at_ms,
            )
        )

    def add_version(self, version: WorkflowVersionRecord) -> None:
        self._session.add(
            WorkflowVersionRow(
                id=version.id,
                workflow_id=version.workflow_id,
                source_kind=version.source_kind,
                local_root=version.local_root,
                entrypoint=version.entrypoint,
                source_sha256=version.source_sha256,
                remote_workflow=version.remote_workflow,
                revision=version.revision,
                execution_json=json.dumps(version.execution, sort_keys=True),
                created_at_ms=version.created_at_ms,
            )
        )

    def reconcile_entry(
        self,
        workflow_id: str,
        *,
        name: str,
        description: str | None,
        metadata: dict[str, Any],
        current_version_id: str,
        archived_at_ms: int | None,
        updated_at_ms: int,
    ) -> None:
        row = self._session.get(WorkflowRow, workflow_id)
        if row is None:
            raise ValueError("workflow entry is unavailable")
        row.name = name
        row.description = description
        row.metadata_json = json.dumps(metadata, sort_keys=True)
        row.current_version_id = current_version_id
        row.archived_at_ms = archived_at_ms
        row.updated_at_ms = updated_at_ms

    def archive_entry(self, workflow_id: str, *, archived_at_ms: int) -> None:
        row = self._session.get(WorkflowRow, workflow_id)
        if row is None:
            raise ValueError("workflow entry is unavailable")
        row.archived_at_ms = archived_at_ms
        row.updated_at_ms = archived_at_ms

    def set_entry_state(
        self,
        workflow_id: str,
        *,
        current_version_id: str | None = None,
        archived_at_ms: int | None,
        updated_at_ms: int,
    ) -> None:
        row = self._session.get(WorkflowRow, workflow_id)
        if row is None:
            raise ValueError("workflow entry is unavailable")
        if current_version_id is not None:
            version = self._session.get(WorkflowVersionRow, current_version_id)
            if version is None or version.workflow_id != workflow_id:
                raise ValueError("workflow version does not belong to this entry")
            row.current_version_id = current_version_id
        row.archived_at_ms = archived_at_ms
        row.updated_at_ms = updated_at_ms

    @staticmethod
    def _entry_record(row: WorkflowRow) -> WorkflowEntryRecord:
        return WorkflowEntryRecord(
            id=row.id,
            name=row.name,
            engine=row.engine,
            description=row.description,
            metadata=json.loads(row.metadata_json),
            owner=row.owner,
            current_version_id=row.current_version_id,
            archived_at_ms=row.archived_at_ms,
            created_at_ms=row.created_at_ms,
            updated_at_ms=row.updated_at_ms,
        )

    @staticmethod
    def _version_record(row: WorkflowVersionRow) -> WorkflowVersionRecord:
        return WorkflowVersionRecord(
            id=row.id,
            workflow_id=row.workflow_id,
            source_kind=row.source_kind,
            local_root=row.local_root,
            entrypoint=row.entrypoint,
            source_sha256=row.source_sha256,
            remote_workflow=row.remote_workflow,
            revision=row.revision,
            execution=json.loads(row.execution_json),
            created_at_ms=row.created_at_ms,
        )

SHA-256: 7e2437bf16c110babc0ea0d380bd5a1b21f119e05d4fe8df31f222c2d6cd6905