use chrono::{DateTime, Utc};
use hammerwork::queue::DatabaseQueue;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use uuid::Uuid;
use warp::{Filter, Rejection, Reply};
use super::ApiResponse;
#[derive(Debug, Deserialize)]
pub struct SpawnChildrenParams {
pub include_grandchildren: Option<bool>,
pub status_filter: Option<String>,
pub depth: Option<u32>,
}
#[derive(Debug, Deserialize)]
pub struct SpawnTreeParams {
pub format: Option<String>,
pub max_depth: Option<u32>,
pub include_config: Option<bool>,
}
#[derive(Debug, Serialize)]
pub struct SpawnOperationInfo {
pub operation_id: String,
pub parent_job_id: Uuid,
pub spawned_job_ids: Vec<Uuid>,
pub spawned_at: DateTime<Utc>,
pub spawn_count: u32,
pub success_count: u32,
pub failed_count: u32,
pub pending_count: u32,
pub config: Option<serde_json::Value>,
}
#[derive(Debug, Serialize)]
pub struct SpawnTreeNode {
pub id: Uuid,
pub queue_name: String,
pub status: String,
pub priority: String,
pub created_at: DateTime<Utc>,
pub has_spawn_config: bool,
pub children: Vec<SpawnTreeNode>,
pub parent_id: Option<Uuid>,
pub depth: u32,
pub spawn_operation_id: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct SpawnTreeResponse {
pub root_job: SpawnTreeNode,
pub total_nodes: u32,
pub max_depth: u32,
pub spawn_operations: Vec<SpawnOperationInfo>,
}
#[derive(Debug, Serialize)]
pub struct SpawnStatsResponse {
pub total_spawn_operations: u64,
pub avg_children_per_spawn: f64,
pub max_children_in_spawn: u32,
pub spawn_success_rate: f64,
pub recent_spawn_count: u64,
pub queue_breakdown: HashMap<String, SpawnQueueStats>,
}
#[derive(Debug, Serialize)]
pub struct SpawnQueueStats {
pub spawn_operations: u64,
pub avg_children: f64,
pub success_rate: f64,
}
#[derive(Debug, Serialize)]
pub struct JobInfo {
pub id: Uuid,
pub queue_name: String,
pub status: String,
pub priority: String,
pub created_at: DateTime<Utc>,
pub scheduled_at: Option<DateTime<Utc>>,
pub started_at: Option<DateTime<Utc>>,
pub completed_at: Option<DateTime<Utc>>,
pub has_spawn_config: bool,
pub spawn_operation_id: Option<String>,
}
pub fn spawn_routes<T>(
_queue: Arc<T>,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone
where
T: DatabaseQueue + Send + Sync + 'static,
{
warp::path("spawn")
.and(warp::path("info"))
.and(warp::get())
.map(|| {
let response = ApiResponse::success(serde_json::json!({
"message": "Spawn API endpoints are available",
"endpoints": [
"GET /api/jobs/{id}/children - List spawned child jobs",
"GET /api/jobs/{id}/parent - Get parent job information",
"GET /api/jobs/{id}/spawn-tree - Get complete spawn hierarchy",
"GET /api/spawn/operations - List spawn operations",
"GET /api/spawn/operations/{id} - Get spawn operation details",
"POST /api/jobs/{id}/spawn - Manually trigger spawn operation",
"GET /api/spawn/stats - Get spawn operation statistics"
],
"status": "placeholder_implementation"
}));
warp::reply::json(&response)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spawn_children_params() {
let params = SpawnChildrenParams {
include_grandchildren: Some(true),
status_filter: Some("pending".to_string()),
depth: Some(3),
};
assert_eq!(params.include_grandchildren, Some(true));
assert_eq!(params.status_filter, Some("pending".to_string()));
assert_eq!(params.depth, Some(3));
}
#[test]
fn test_spawn_tree_params() {
let params = SpawnTreeParams {
format: Some("mermaid".to_string()),
max_depth: Some(5),
include_config: Some(true),
};
assert_eq!(params.format, Some("mermaid".to_string()));
assert_eq!(params.max_depth, Some(5));
assert_eq!(params.include_config, Some(true));
}
#[test]
fn test_spawn_tree_response_structure() {
let tree_response = SpawnTreeResponse {
root_job: SpawnTreeNode {
id: Uuid::new_v4(),
queue_name: "test_queue".to_string(),
status: "completed".to_string(),
priority: "high".to_string(),
created_at: Utc::now(),
has_spawn_config: true,
children: vec![],
parent_id: None,
depth: 0,
spawn_operation_id: Some("test_op".to_string()),
},
total_nodes: 1,
max_depth: 0,
spawn_operations: vec![],
};
assert_eq!(tree_response.total_nodes, 1);
assert_eq!(tree_response.max_depth, 0);
assert!(tree_response.root_job.has_spawn_config);
}
#[test]
fn test_spawn_stats_response_structure() {
let mut queue_breakdown = HashMap::new();
queue_breakdown.insert(
"test_queue".to_string(),
SpawnQueueStats {
spawn_operations: 10,
avg_children: 3.5,
success_rate: 95.0,
},
);
let stats_response = SpawnStatsResponse {
total_spawn_operations: 50,
avg_children_per_spawn: 3.2,
max_children_in_spawn: 15,
spawn_success_rate: 97.5,
recent_spawn_count: 12,
queue_breakdown,
};
assert_eq!(stats_response.total_spawn_operations, 50);
assert_eq!(stats_response.avg_children_per_spawn, 3.2);
assert_eq!(stats_response.spawn_success_rate, 97.5);
assert_eq!(stats_response.queue_breakdown.len(), 1);
}
}