kotoba_server_workflow/
handlers.rs1use axum::{
4 extract::{Path as AxumPath, State},
5 response::Json,
6 http::StatusCode,
7};
8use serde::{Deserialize, Serialize};
9use std::sync::Arc;
10use chrono::{DateTime, Utc};
11
12#[cfg(feature = "workflow")]
13use crate::{WorkflowEngineInterface, WorkflowExecutionId, WorkflowIR, WorkflowExecution};
14
15#[cfg(feature = "workflow")]
17#[derive(Clone)]
18pub struct WorkflowState<E> {
19 pub engine: Arc<E>,
20}
21
22#[cfg(feature = "workflow")]
23impl<E> WorkflowState<E> {
24 pub fn new(engine: E) -> Self {
25 Self {
26 engine: Arc::new(engine),
27 }
28 }
29}
30
31#[derive(Debug, Serialize, Deserialize)]
33pub struct StartWorkflowResponse {
34 pub execution_id: String,
35}
36
37#[cfg(feature = "workflow")]
39#[derive(Clone)]
40pub struct WorkflowApiHandler<E> {
41 state: WorkflowState<E>,
42}
43
44#[cfg(feature = "workflow")]
45impl<E> WorkflowApiHandler<E>
46where
47 E: WorkflowEngineInterface + Send + Sync + 'static,
48{
49 pub fn new(engine: E) -> Self {
50 Self {
51 state: WorkflowState::new(engine),
52 }
53 }
54
55 pub async fn start_workflow(
56 State(state): State<WorkflowState<E>>,
57 Json(payload): Json<WorkflowIR>,
58 ) -> Result<Json<StartWorkflowResponse>, (StatusCode, String)> {
59 let execution_id = state.engine
60 .start_workflow(&payload, serde_json::Value::Null)
61 .await
62 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
63
64 Ok(Json(StartWorkflowResponse {
65 execution_id: execution_id.0,
66 }))
67 }
68
69 pub async fn get_workflow_status(
70 State(state): State<WorkflowState<E>>,
71 AxumPath(execution_id): AxumPath<String>,
72 ) -> Result<Json<WorkflowExecution>, (StatusCode, String)> {
73 let exec_id = WorkflowExecutionId(execution_id);
74 let execution = state.engine
75 .get_execution(&exec_id)
76 .await
77 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
78 .ok_or_else(|| (StatusCode::NOT_FOUND, "Workflow execution not found".to_string()))?;
79
80 Ok(Json(execution))
81 }
82
83 pub async fn list_workflows(
84 State(state): State<WorkflowState<E>>,
85 ) -> Result<Json<Vec<WorkflowExecution>>, (StatusCode, String)> {
86 let executions = state.engine
87 .list_executions()
88 .await
89 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
90
91 Ok(Json(executions))
92 }
93
94 pub async fn cancel_workflow(
95 State(state): State<WorkflowState<E>>,
96 AxumPath(execution_id): AxumPath<String>,
97 ) -> Result<StatusCode, (StatusCode, String)> {
98 let exec_id = WorkflowExecutionId(execution_id);
99 state.engine
100 .cancel_execution(&exec_id)
101 .await
102 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
103
104 Ok(StatusCode::NO_CONTENT)
105 }
106}
107
108#[derive(Clone)]
110pub struct WorkflowStatusHandler;
111
112impl WorkflowStatusHandler {
113 pub fn new() -> Self {
114 Self
115 }
116
117 pub async fn health() -> Result<Json<serde_json::Value>, (StatusCode, String)> {
118 Ok(Json(serde_json::json!({
119 "status": "workflow_integration_available",
120 "version": env!("CARGO_PKG_VERSION")
121 })))
122 }
123}
124
125impl Default for WorkflowStatusHandler {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131#[derive(Debug, Serialize, Deserialize)]
133pub struct WorkflowListResponse {
134 pub workflows: Vec<WorkflowSummary>,
135 pub total: usize,
136}
137
138#[derive(Debug, Serialize, Deserialize)]
139pub struct WorkflowSummary {
140 pub execution_id: String,
141 pub status: String,
142 pub created_at: DateTime<Utc>,
143 pub updated_at: DateTime<Utc>,
144}