122 lines
3.7 KiB
Python
122 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
import uuid
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from app.db import Database
|
|
from app.schemas import WorkflowCreate
|
|
from wov_sdk.models import WorkflowDefinition
|
|
|
|
router = APIRouter(prefix="/api/admin/workflows", tags=["workflows"])
|
|
|
|
|
|
def _get_db() -> Database:
|
|
from app.main import app
|
|
|
|
return app.state.db
|
|
|
|
|
|
def _slugify(value: str) -> str:
|
|
slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
|
|
return slug or uuid.uuid4().hex[:8]
|
|
|
|
|
|
def _validate_definition(raw: dict) -> WorkflowDefinition:
|
|
try:
|
|
definition = WorkflowDefinition.from_dict(raw)
|
|
definition.validate()
|
|
return definition
|
|
except (KeyError, TypeError, ValueError) as exc:
|
|
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("")
|
|
def list_workflows(db: Database = Depends(_get_db)) -> list[dict]:
|
|
return db.list_workflows()
|
|
|
|
|
|
@router.post("")
|
|
def create_workflow(
|
|
payload: WorkflowCreate,
|
|
db: Database = Depends(_get_db),
|
|
) -> dict:
|
|
definition = _validate_definition(payload.definition)
|
|
workflow_id = payload.id or _slugify(payload.name)
|
|
existing = db.get_workflow(workflow_id)
|
|
version = (existing or {}).get("latest_version", 0) + 1
|
|
db.upsert_workflow(
|
|
{
|
|
"id": workflow_id,
|
|
"name": payload.name,
|
|
"description": payload.description,
|
|
"published": 0,
|
|
"latest_version": version,
|
|
}
|
|
)
|
|
db.create_workflow_version(workflow_id, version, definition.to_dict())
|
|
return {
|
|
"id": workflow_id,
|
|
"name": payload.name,
|
|
"description": payload.description,
|
|
"published": False,
|
|
"latest_version": version,
|
|
}
|
|
|
|
|
|
@router.get("/{workflow_id}")
|
|
def get_workflow(workflow_id: str, db: Database = Depends(_get_db)) -> dict:
|
|
workflow = db.get_workflow(workflow_id)
|
|
if workflow is None:
|
|
raise HTTPException(status_code=404, detail="workflow not found")
|
|
latest = db.get_latest_workflow_version(workflow_id)
|
|
workflow["latest_version_data"] = latest
|
|
return workflow
|
|
|
|
|
|
@router.delete("/{workflow_id}")
|
|
def delete_workflow(workflow_id: str, db: Database = Depends(_get_db)) -> dict:
|
|
if db.get_workflow(workflow_id) is None:
|
|
raise HTTPException(status_code=404, detail="workflow not found")
|
|
db.delete_workflow(workflow_id)
|
|
return {"deleted": workflow_id}
|
|
|
|
|
|
@router.post("/{workflow_id}/validate")
|
|
def validate_workflow(
|
|
workflow_id: str,
|
|
definition: dict,
|
|
db: Database = Depends(_get_db),
|
|
) -> dict:
|
|
if db.get_workflow(workflow_id) is None:
|
|
raise HTTPException(status_code=404, detail="workflow not found")
|
|
parsed = _validate_definition(definition)
|
|
return {"valid": True, "node_ids": [node.id for node in parsed.nodes]}
|
|
|
|
|
|
@router.post("/{workflow_id}/publish")
|
|
def publish_workflow(workflow_id: str, db: Database = Depends(_get_db)) -> dict:
|
|
workflow = db.get_workflow(workflow_id)
|
|
if workflow is None:
|
|
raise HTTPException(status_code=404, detail="workflow not found")
|
|
if workflow["latest_version"] == 0:
|
|
raise HTTPException(status_code=422, detail="workflow has no version")
|
|
db.upsert_workflow(
|
|
{
|
|
"id": workflow_id,
|
|
"name": workflow["name"],
|
|
"description": workflow["description"],
|
|
"published": 1,
|
|
"latest_version": workflow["latest_version"],
|
|
}
|
|
)
|
|
return {"published": workflow_id}
|
|
|
|
|
|
@router.get("/{workflow_id}/versions")
|
|
def list_versions(workflow_id: str, db: Database = Depends(_get_db)) -> list[dict]:
|
|
if db.get_workflow(workflow_id) is None:
|
|
raise HTTPException(status_code=404, detail="workflow not found")
|
|
return db.list_workflow_versions(workflow_id)
|