feat: persist and edit versioned workflow definitions

This commit is contained in:
2026-07-28 13:48:06 +02:00
parent 85eef00913
commit 6737b60c11
25 changed files with 3913 additions and 9 deletions
+158
View File
@@ -0,0 +1,158 @@
import { apiFetch, type ApiSettings } from "@govoplan/core-webui";
import type {
DefinitionGraph,
DefinitionGraphNode,
DefinitionGraphNodeType
} from "@govoplan/core-webui/definition-graph";
export type WorkflowStatus = "draft" | "active" | "archived";
export type WorkflowGraphNode = DefinitionGraphNode;
export type WorkflowGraph = DefinitionGraph & {
schema_version: 1;
nodes: WorkflowGraphNode[];
};
export type WorkflowDiagnostic = {
severity: "error" | "warning";
code: string;
message: string;
node_id?: string | null;
field?: string | null;
};
export type WorkflowNodeType = DefinitionGraphNodeType;
export type WorkflowRevision = {
id: string;
revision: number;
schema_version: number;
graph: WorkflowGraph;
content_hash: string;
library_id: string;
library_version: string;
created_by?: string | null;
created_at: string;
};
export type WorkflowDefinition = {
id: string;
tenant_id: string;
key: string;
name: string;
description?: string | null;
status: WorkflowStatus;
current_revision: number;
active_revision?: number | null;
metadata: Record<string, unknown>;
created_by?: string | null;
updated_by?: string | null;
created_at: string;
updated_at: string;
revision: WorkflowRevision;
};
export type WorkflowDefinitionPayload = {
name: string;
description?: string | null;
graph: WorkflowGraph;
metadata: Record<string, unknown>;
};
export async function listWorkflowNodeTypes(
settings: ApiSettings
): Promise<{ id: string; version: string; allows_cycles: boolean; nodes: WorkflowNodeType[] }> {
return apiFetch(settings, "/api/v1/workflow/node-types");
}
export async function listWorkflowDefinitions(
settings: ApiSettings
): Promise<WorkflowDefinition[]> {
const response = await apiFetch<{ definitions: WorkflowDefinition[] }>(
settings,
"/api/v1/workflow/definitions"
);
return response.definitions;
}
export function createWorkflowDefinition(
settings: ApiSettings,
payload: WorkflowDefinitionPayload
): Promise<WorkflowDefinition> {
return apiFetch(settings, "/api/v1/workflow/definitions", {
method: "POST",
body: JSON.stringify(payload)
});
}
export function updateWorkflowDefinition(
settings: ApiSettings,
definitionId: string,
payload: WorkflowDefinitionPayload & { expected_revision: number }
): Promise<WorkflowDefinition> {
return apiFetch(
settings,
`/api/v1/workflow/definitions/${encodeURIComponent(definitionId)}`,
{
method: "PUT",
body: JSON.stringify(payload)
}
);
}
export async function listWorkflowRevisions(
settings: ApiSettings,
definitionId: string
): Promise<WorkflowRevision[]> {
const response = await apiFetch<{ revisions: WorkflowRevision[] }>(
settings,
`/api/v1/workflow/definitions/${encodeURIComponent(definitionId)}/revisions`
);
return response.revisions;
}
export function activateWorkflowDefinition(
settings: ApiSettings,
definitionId: string,
revision?: number
): Promise<WorkflowDefinition> {
return apiFetch(
settings,
`/api/v1/workflow/definitions/${encodeURIComponent(definitionId)}/activate`,
{
method: "POST",
body: JSON.stringify({ revision })
}
);
}
export function archiveWorkflowDefinition(
settings: ApiSettings,
definitionId: string
): Promise<WorkflowDefinition> {
return apiFetch(
settings,
`/api/v1/workflow/definitions/${encodeURIComponent(definitionId)}/archive`,
{ method: "POST" }
);
}
export function deleteWorkflowDefinition(
settings: ApiSettings,
definitionId: string
): Promise<{ deleted: boolean; definition_id: string }> {
return apiFetch(
settings,
`/api/v1/workflow/definitions/${encodeURIComponent(definitionId)}`,
{ method: "DELETE" }
);
}
export function validateWorkflowDefinition(
settings: ApiSettings,
graph: WorkflowGraph
): Promise<{ valid: boolean; diagnostics: WorkflowDiagnostic[] }> {
return apiFetch(settings, "/api/v1/workflow/definitions/validate", {
method: "POST",
body: JSON.stringify({ graph })
});
}