ironflow_engine/config/
mod.rs1mod agent;
8mod approval;
9mod approvers;
10mod artifact;
11mod decision;
12pub mod delay;
13mod escalation;
14mod http;
15mod shell;
16mod workflow;
17
18pub use agent::{AgentStep, AgentStepConfig, Tool};
19pub use approval::ApprovalConfig;
20pub use approvers::Approvers;
21pub use artifact::{ArtifactInput, ArtifactOutput, ArtifactRef};
22pub use decision::{DEFAULT_DECISION_MODEL, DecisionConfig, NoAnswers};
23pub use delay::DelayConfig;
24pub use escalation::{EscalationPolicy, NotificationTarget};
25pub use http::HttpConfig;
26pub use ironflow_store::entities::{ApprovalRequirement, Assignee};
29pub use shell::ShellConfig;
30pub use workflow::WorkflowStepConfig;
31
32use ironflow_core::retry::RetryPolicy;
33use ironflow_store::entities::StepKind;
34use serde::{Deserialize, Serialize};
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(tag = "type", rename_all = "snake_case")]
52pub enum StepConfig {
53 Shell(ShellConfig),
55 Http(HttpConfig),
57 Agent(AgentStepConfig),
59 Workflow(WorkflowStepConfig),
61 Approval(ApprovalConfig),
63 Decision(DecisionConfig),
65 Delay(DelayConfig),
67}
68
69impl StepConfig {
70 pub fn allow_failure(&self) -> bool {
84 match self {
85 StepConfig::Shell(c) => c.allow_failure,
86 StepConfig::Http(c) => c.allow_failure,
87 StepConfig::Agent(c) => c.allow_failure,
88 StepConfig::Workflow(_)
89 | StepConfig::Approval(_)
90 | StepConfig::Decision(_)
91 | StepConfig::Delay(_) => false,
92 }
93 }
94
95 pub fn declared_outputs(&self) -> &[ArtifactOutput] {
109 match self {
110 StepConfig::Shell(c) => &c.outputs,
111 _ => &[],
112 }
113 }
114
115 pub fn retry(&self) -> Option<&RetryPolicy> {
130 match self {
131 StepConfig::Shell(c) => c.retry.as_ref(),
132 StepConfig::Http(c) => c.retry.as_ref(),
133 StepConfig::Agent(c) => c.retry.as_ref(),
134 StepConfig::Workflow(c) => c.retry.as_ref(),
135 StepConfig::Approval(_) | StepConfig::Decision(_) | StepConfig::Delay(_) => None,
136 }
137 }
138
139 pub fn kind(&self) -> StepKind {
151 match self {
152 StepConfig::Shell(_) => StepKind::Shell,
153 StepConfig::Http(_) => StepKind::Http,
154 StepConfig::Agent(_) => StepKind::Agent,
155 StepConfig::Workflow(_) => StepKind::Workflow,
156 StepConfig::Approval(_) => StepKind::Approval,
157 StepConfig::Decision(_) => StepKind::Decision,
158 StepConfig::Delay(_) => StepKind::Custom("delay".to_string()),
159 }
160 }
161}
162
163impl From<ShellConfig> for StepConfig {
164 fn from(c: ShellConfig) -> Self {
165 StepConfig::Shell(c)
166 }
167}
168
169impl From<HttpConfig> for StepConfig {
170 fn from(c: HttpConfig) -> Self {
171 StepConfig::Http(c)
172 }
173}
174
175impl From<AgentStepConfig> for StepConfig {
176 fn from(c: AgentStepConfig) -> Self {
177 StepConfig::Agent(c)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use ironflow_core::retry::RetryPolicy;
184
185 use super::*;
186
187 #[test]
188 fn retry_accessor_returns_policy_for_each_variant() {
189 let shell = StepConfig::Shell(ShellConfig::new("echo").retry_policy(RetryPolicy::new(2)));
190 assert_eq!(shell.retry().unwrap().max_retries(), 2);
191
192 let http = StepConfig::Http(HttpConfig::get("http://x").retry_policy(RetryPolicy::new(3)));
193 assert_eq!(http.retry().unwrap().max_retries(), 3);
194
195 let workflow = StepConfig::Workflow(
196 WorkflowStepConfig::new("build", serde_json::json!({}))
197 .retry_policy(RetryPolicy::new(4)),
198 );
199 assert_eq!(workflow.retry().unwrap().max_retries(), 4);
200
201 let agent = StepConfig::Agent(AgentStepConfig::new("test"));
202 assert!(agent.retry().is_none());
203
204 let approval = StepConfig::Approval(ApprovalConfig::new("ok?"));
205 assert!(approval.retry().is_none());
206 }
207
208 #[test]
209 fn serde_roundtrip() {
210 let configs = vec![
211 StepConfig::Shell(ShellConfig::new("echo test")),
212 StepConfig::Http(HttpConfig::get("http://example.com")),
213 StepConfig::Agent(AgentStepConfig::new("summarize")),
214 StepConfig::Workflow(WorkflowStepConfig::new("build", serde_json::json!({}))),
215 StepConfig::Approval(ApprovalConfig::new("Deploy to production?")),
216 StepConfig::Delay(DelayConfig::from_secs(60)),
217 ];
218
219 for config in configs {
220 let json = serde_json::to_string(&config).expect("serialize");
221 let back: StepConfig = serde_json::from_str(&json).expect("deserialize");
222 let json2 = serde_json::to_string(&back).expect("serialize2");
223 assert_eq!(json, json2);
224 }
225 }
226
227 #[test]
228 fn approval_with_escalation_roundtrips() {
229 let config = StepConfig::Approval(
230 ApprovalConfig::new("Deploy to production?")
231 .with_deadline_secs(3600)
232 .assigned_to(Assignee::group("release-managers"))
233 .on_timeout(EscalationPolicy::Chain(vec![
234 EscalationPolicy::Notify(vec![NotificationTarget::Webhook {
235 url: "https://example.com/sla".to_string(),
236 }]),
237 EscalationPolicy::AutoReject,
238 ])),
239 );
240
241 let json = serde_json::to_string(&config).expect("serialize");
242 let back: StepConfig = serde_json::from_str(&json).expect("deserialize");
243 let json2 = serde_json::to_string(&back).expect("serialize2");
244 assert_eq!(json, json2);
245
246 let StepConfig::Approval(approval) = back else {
247 panic!("expected an approval config");
248 };
249 assert_eq!(approval.effective_deadline_secs(), Some(3600));
250 assert_eq!(
251 approval.assignee(),
252 Some(&Assignee::group("release-managers"))
253 );
254 assert_eq!(approval.effective_policy().len(), 2);
255 }
256}