Skip to main content

zlayer_types/api/
jobs.rs

1//! Job execution DTOs.
2
3use serde::{Deserialize, Serialize};
4
5use utoipa::IntoParams;
6
7/// Response after triggering a job
8#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
9pub struct TriggerJobResponse {
10    /// Unique execution ID for tracking
11    pub execution_id: String,
12    /// Human-readable message
13    pub message: String,
14}
15
16/// Job execution status response
17#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
18pub struct JobExecutionResponse {
19    /// Unique execution ID
20    pub id: String,
21    /// Name of the job
22    pub job_name: String,
23    /// Current status (pending, initializing, running, completed, failed, cancelled)
24    pub status: String,
25    /// When the job started (ISO 8601 format)
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub started_at: Option<String>,
28    /// When the job completed (ISO 8601 format)
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub completed_at: Option<String>,
31    /// Exit code (if completed/failed)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub exit_code: Option<i32>,
34    /// Captured logs
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub logs: Option<String>,
37    /// How the job was triggered
38    pub trigger: String,
39    /// Error reason (if failed)
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub error: Option<String>,
42    /// Duration in milliseconds (if completed)
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub duration_ms: Option<u64>,
45}
46
47/// Query parameters for listing executions
48#[derive(Debug, Deserialize, IntoParams)]
49pub struct ListExecutionsQuery {
50    /// Maximum number of executions to return
51    #[serde(default = "default_limit")]
52    pub limit: usize,
53    /// Filter by status (pending, running, completed, failed)
54    #[serde(default)]
55    pub status: Option<String>,
56}
57
58fn default_limit() -> usize {
59    50
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_trigger_response_serialize() {
68        let response = TriggerJobResponse {
69            execution_id: "abc-123".to_string(),
70            message: "Job triggered".to_string(),
71        };
72        let json = serde_json::to_string(&response).unwrap();
73        assert!(json.contains("abc-123"));
74        assert!(json.contains("Job triggered"));
75    }
76
77    #[test]
78    fn test_execution_response_serialize() {
79        let response = JobExecutionResponse {
80            id: "exec-123".to_string(),
81            job_name: "backup".to_string(),
82            status: "completed".to_string(),
83            started_at: Some("2025-01-25T12:00:00Z".to_string()),
84            completed_at: Some("2025-01-25T12:01:00Z".to_string()),
85            exit_code: Some(0),
86            logs: Some("Done!".to_string()),
87            trigger: "cli".to_string(),
88            error: None,
89            duration_ms: Some(5000),
90        };
91        let json = serde_json::to_string(&response).unwrap();
92        assert!(json.contains("exec-123"));
93        assert!(json.contains("backup"));
94        assert!(json.contains("completed"));
95    }
96
97    #[test]
98    fn test_list_query_defaults() {
99        let query: ListExecutionsQuery = serde_json::from_str("{}").unwrap();
100        assert_eq!(query.limit, 50);
101        assert!(query.status.is_none());
102    }
103}