ironflow_api/entities/
run.rs1use std::collections::HashMap;
4
5use chrono::{DateTime, Utc};
6use ironflow_store::models::{Run, RunStatus, TriggerKind};
7use rust_decimal::Decimal;
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10
11use super::{CreatedBy, StepResponse};
12
13#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
24#[derive(Debug, Serialize, Deserialize)]
25pub struct RunResponse {
26 pub id: Uuid,
28 pub workflow_name: String,
30 pub status: RunStatus,
32 pub trigger: TriggerKind,
34 pub error: Option<String>,
36 pub retry_count: u32,
38 pub max_retries: u32,
40 #[cfg_attr(feature = "openapi", schema(value_type = f64))]
42 pub cost_usd: Decimal,
43 pub duration_ms: u64,
45 pub created_at: DateTime<Utc>,
47 pub updated_at: DateTime<Utc>,
49 pub started_at: Option<DateTime<Utc>>,
51 pub completed_at: Option<DateTime<Utc>>,
53 pub handler_version: Option<String>,
55 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
57 pub labels: HashMap<String, String>,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
60 pub scheduled_at: Option<DateTime<Utc>>,
61 pub created_by: CreatedBy,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub idempotency_key: Option<String>,
66 #[cfg_attr(feature = "openapi", schema(value_type = Option<f64>))]
68 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub max_cost_usd: Option<Decimal>,
70}
71
72impl From<Run> for RunResponse {
73 fn from(run: Run) -> Self {
74 let created_by = CreatedBy::from(&run);
75 RunResponse {
76 id: run.id,
77 workflow_name: run.workflow_name,
78 status: run.status.state,
79 trigger: run.trigger,
80 error: run.error,
81 retry_count: run.retry_count,
82 max_retries: run.max_retries,
83 cost_usd: run.cost_usd,
84 duration_ms: run.duration_ms,
85 created_at: run.created_at,
86 updated_at: run.updated_at,
87 started_at: run.started_at,
88 completed_at: run.completed_at,
89 handler_version: run.handler_version,
90 labels: run.labels,
91 scheduled_at: run.scheduled_at,
92 created_by,
93 idempotency_key: run.idempotency_key,
94 max_cost_usd: run.max_cost_usd,
95 }
96 }
97}
98
99#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
101#[derive(Debug, Serialize)]
102pub struct RunDetailResponse {
103 pub run: RunResponse,
105 pub steps: Vec<StepResponse>,
107 pub payload: serde_json::Value,
109}
110
111#[cfg_attr(feature = "openapi", derive(utoipa::IntoParams, utoipa::ToSchema))]
113#[derive(Debug, Deserialize)]
114pub struct ListRunsQuery {
115 pub workflow: Option<String>,
117 pub status: Option<RunStatus>,
119 pub has_steps: Option<bool>,
124 pub label: Option<String>,
126 pub created_by: Option<Uuid>,
130 pub page: Option<u32>,
132 pub per_page: Option<u32>,
134}
135
136impl ListRunsQuery {
137 pub fn parse_labels(&self) -> Option<HashMap<String, String>> {
139 self.label.as_ref().and_then(|raw| {
140 let mut map = HashMap::new();
141 for entry in raw.split(',') {
142 let entry = entry.trim();
143 if let Some((k, v)) = entry.split_once(':') {
144 map.insert(k.to_string(), v.to_string());
145 }
146 }
147 if map.is_empty() { None } else { Some(map) }
148 })
149 }
150}