1use chrono::{DateTime, Utc};
11use hammerwork::queue::DatabaseQueue;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::sync::Arc;
15use uuid::Uuid;
16use warp::{Filter, Rejection, Reply};
17
18use super::ApiResponse;
19
20#[derive(Debug, Deserialize)]
22pub struct SpawnChildrenParams {
23 pub include_grandchildren: Option<bool>,
25 pub status_filter: Option<String>,
27 pub depth: Option<u32>,
29}
30
31#[derive(Debug, Deserialize)]
33pub struct SpawnTreeParams {
34 pub format: Option<String>,
36 pub max_depth: Option<u32>,
38 pub include_config: Option<bool>,
40}
41
42#[derive(Debug, Serialize)]
44pub struct SpawnOperationInfo {
45 pub operation_id: String,
46 pub parent_job_id: Uuid,
47 pub spawned_job_ids: Vec<Uuid>,
48 pub spawned_at: DateTime<Utc>,
49 pub spawn_count: u32,
50 pub success_count: u32,
51 pub failed_count: u32,
52 pub pending_count: u32,
53 pub config: Option<serde_json::Value>,
54}
55
56#[derive(Debug, Serialize)]
58pub struct SpawnTreeNode {
59 pub id: Uuid,
60 pub queue_name: String,
61 pub status: String,
62 pub priority: String,
63 pub created_at: DateTime<Utc>,
64 pub has_spawn_config: bool,
65 pub children: Vec<SpawnTreeNode>,
66 pub parent_id: Option<Uuid>,
67 pub depth: u32,
68 pub spawn_operation_id: Option<String>,
69}
70
71#[derive(Debug, Serialize)]
73pub struct SpawnTreeResponse {
74 pub root_job: SpawnTreeNode,
75 pub total_nodes: u32,
76 pub max_depth: u32,
77 pub spawn_operations: Vec<SpawnOperationInfo>,
78}
79
80#[derive(Debug, Serialize)]
82pub struct SpawnStatsResponse {
83 pub total_spawn_operations: u64,
84 pub avg_children_per_spawn: f64,
85 pub max_children_in_spawn: u32,
86 pub spawn_success_rate: f64,
87 pub recent_spawn_count: u64,
88 pub queue_breakdown: HashMap<String, SpawnQueueStats>,
89}
90
91#[derive(Debug, Serialize)]
93pub struct SpawnQueueStats {
94 pub spawn_operations: u64,
95 pub avg_children: f64,
96 pub success_rate: f64,
97}
98
99#[derive(Debug, Serialize)]
101pub struct JobInfo {
102 pub id: Uuid,
103 pub queue_name: String,
104 pub status: String,
105 pub priority: String,
106 pub created_at: DateTime<Utc>,
107 pub scheduled_at: Option<DateTime<Utc>>,
108 pub started_at: Option<DateTime<Utc>>,
109 pub completed_at: Option<DateTime<Utc>>,
110 pub has_spawn_config: bool,
111 pub spawn_operation_id: Option<String>,
112}
113
114pub fn spawn_routes<T>(
116 _queue: Arc<T>,
117) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone
118where
119 T: DatabaseQueue + Send + Sync + 'static,
120{
121 warp::path("spawn")
123 .and(warp::path("info"))
124 .and(warp::get())
125 .map(|| {
126 let response = ApiResponse::success(serde_json::json!({
127 "message": "Spawn API endpoints are available",
128 "endpoints": [
129 "GET /api/jobs/{id}/children - List spawned child jobs",
130 "GET /api/jobs/{id}/parent - Get parent job information",
131 "GET /api/jobs/{id}/spawn-tree - Get complete spawn hierarchy",
132 "GET /api/spawn/operations - List spawn operations",
133 "GET /api/spawn/operations/{id} - Get spawn operation details",
134 "POST /api/jobs/{id}/spawn - Manually trigger spawn operation",
135 "GET /api/spawn/stats - Get spawn operation statistics"
136 ],
137 "status": "placeholder_implementation"
138 }));
139 warp::reply::json(&response)
140 })
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_spawn_children_params() {
149 let params = SpawnChildrenParams {
150 include_grandchildren: Some(true),
151 status_filter: Some("pending".to_string()),
152 depth: Some(3),
153 };
154
155 assert_eq!(params.include_grandchildren, Some(true));
156 assert_eq!(params.status_filter, Some("pending".to_string()));
157 assert_eq!(params.depth, Some(3));
158 }
159
160 #[test]
161 fn test_spawn_tree_params() {
162 let params = SpawnTreeParams {
163 format: Some("mermaid".to_string()),
164 max_depth: Some(5),
165 include_config: Some(true),
166 };
167
168 assert_eq!(params.format, Some("mermaid".to_string()));
169 assert_eq!(params.max_depth, Some(5));
170 assert_eq!(params.include_config, Some(true));
171 }
172
173 #[test]
174 fn test_spawn_tree_response_structure() {
175 let tree_response = SpawnTreeResponse {
176 root_job: SpawnTreeNode {
177 id: Uuid::new_v4(),
178 queue_name: "test_queue".to_string(),
179 status: "completed".to_string(),
180 priority: "high".to_string(),
181 created_at: Utc::now(),
182 has_spawn_config: true,
183 children: vec![],
184 parent_id: None,
185 depth: 0,
186 spawn_operation_id: Some("test_op".to_string()),
187 },
188 total_nodes: 1,
189 max_depth: 0,
190 spawn_operations: vec![],
191 };
192
193 assert_eq!(tree_response.total_nodes, 1);
194 assert_eq!(tree_response.max_depth, 0);
195 assert!(tree_response.root_job.has_spawn_config);
196 }
197
198 #[test]
199 fn test_spawn_stats_response_structure() {
200 let mut queue_breakdown = HashMap::new();
201 queue_breakdown.insert(
202 "test_queue".to_string(),
203 SpawnQueueStats {
204 spawn_operations: 10,
205 avg_children: 3.5,
206 success_rate: 95.0,
207 },
208 );
209
210 let stats_response = SpawnStatsResponse {
211 total_spawn_operations: 50,
212 avg_children_per_spawn: 3.2,
213 max_children_in_spawn: 15,
214 spawn_success_rate: 97.5,
215 recent_spawn_count: 12,
216 queue_breakdown,
217 };
218
219 assert_eq!(stats_response.total_spawn_operations, 50);
220 assert_eq!(stats_response.avg_children_per_spawn, 3.2);
221 assert_eq!(stats_response.spawn_success_rate, 97.5);
222 assert_eq!(stats_response.queue_breakdown.len(), 1);
223 }
224}