feat: add reusable workflow definition graphs
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from govoplan_core.auth import ApiPrincipal, get_api_principal, has_scope
|
||||
from govoplan_workflow.backend.manifest import (
|
||||
ADMIN_SCOPE,
|
||||
DEFINITION_READ_SCOPE,
|
||||
DEFINITION_WRITE_SCOPE,
|
||||
)
|
||||
from govoplan_workflow.backend.node_library import WORKFLOW_GRAPH_LIBRARY
|
||||
from govoplan_workflow.backend.schemas import (
|
||||
WorkflowConfigFieldResponse,
|
||||
WorkflowDiagnosticResponse,
|
||||
WorkflowGraphValidationRequest,
|
||||
WorkflowGraphValidationResponse,
|
||||
WorkflowNodeLibraryResponse,
|
||||
WorkflowNodeTypeResponse,
|
||||
WorkflowPortResponse,
|
||||
)
|
||||
from govoplan_workflow.backend.validation import validate_workflow_graph
|
||||
|
||||
|
||||
router = APIRouter(prefix="/workflow", tags=["workflow"])
|
||||
|
||||
|
||||
def _require_any_scope(principal: ApiPrincipal, *scopes: str) -> None:
|
||||
if any(has_scope(principal, scope) for scope in scopes):
|
||||
return
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"Missing one of the required scopes: {', '.join(scopes)}",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/node-types", response_model=WorkflowNodeLibraryResponse)
|
||||
def api_node_types(
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> WorkflowNodeLibraryResponse:
|
||||
_require_any_scope(
|
||||
principal,
|
||||
DEFINITION_READ_SCOPE,
|
||||
DEFINITION_WRITE_SCOPE,
|
||||
ADMIN_SCOPE,
|
||||
)
|
||||
return WorkflowNodeLibraryResponse(
|
||||
id=WORKFLOW_GRAPH_LIBRARY.id,
|
||||
version=WORKFLOW_GRAPH_LIBRARY.version,
|
||||
allows_cycles=WORKFLOW_GRAPH_LIBRARY.constraints.allow_cycles,
|
||||
nodes=[
|
||||
WorkflowNodeTypeResponse(
|
||||
type=definition.type,
|
||||
category=definition.category,
|
||||
category_label=WORKFLOW_GRAPH_LIBRARY.category_labels[definition.category],
|
||||
label=definition.label,
|
||||
description=definition.description,
|
||||
icon=definition.icon,
|
||||
input_ports=[
|
||||
WorkflowPortResponse(
|
||||
id=port.id,
|
||||
label=port.label,
|
||||
required=port.required,
|
||||
multiple=port.multiple,
|
||||
minimum_connections=port.minimum_connections,
|
||||
)
|
||||
for port in definition.input_ports
|
||||
],
|
||||
output_ports=[
|
||||
WorkflowPortResponse(
|
||||
id=port.id,
|
||||
label=port.label,
|
||||
required=port.required,
|
||||
multiple=port.multiple,
|
||||
minimum_connections=port.minimum_connections,
|
||||
)
|
||||
for port in definition.output_ports
|
||||
],
|
||||
config_fields=[
|
||||
WorkflowConfigFieldResponse(
|
||||
id=field.id,
|
||||
label=field.label,
|
||||
kind=field.kind,
|
||||
required=field.required,
|
||||
description=field.description,
|
||||
options=list(field.options),
|
||||
)
|
||||
for field in definition.config_fields
|
||||
],
|
||||
default_config=dict(definition.default_config),
|
||||
)
|
||||
for definition in WORKFLOW_GRAPH_LIBRARY.node_types
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/definitions/validate",
|
||||
response_model=WorkflowGraphValidationResponse,
|
||||
)
|
||||
def api_validate_definition(
|
||||
payload: WorkflowGraphValidationRequest,
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> WorkflowGraphValidationResponse:
|
||||
_require_any_scope(
|
||||
principal,
|
||||
DEFINITION_READ_SCOPE,
|
||||
DEFINITION_WRITE_SCOPE,
|
||||
ADMIN_SCOPE,
|
||||
)
|
||||
diagnostics = validate_workflow_graph(payload.graph)
|
||||
return WorkflowGraphValidationResponse(
|
||||
valid=not any(item.severity == "error" for item in diagnostics),
|
||||
diagnostics=[
|
||||
WorkflowDiagnosticResponse(
|
||||
severity=item.severity,
|
||||
code=item.code,
|
||||
message=item.message,
|
||||
node_id=item.node_id,
|
||||
field=item.field,
|
||||
)
|
||||
for item in diagnostics
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["router"]
|
||||
Reference in New Issue
Block a user