wfe_core/models/
workflow_definition.rs1use std::time::Duration;
2
3use serde::{Deserialize, Serialize};
4
5use super::error_behavior::ErrorBehavior;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct WorkflowDefinition {
10 pub id: String,
11 pub version: u32,
12 pub description: Option<String>,
13 pub steps: Vec<WorkflowStep>,
14 pub default_error_behavior: ErrorBehavior,
15 #[serde(default, with = "super::option_duration_millis")]
16 pub default_error_retry_interval: Option<Duration>,
17}
18
19impl WorkflowDefinition {
20 pub fn new(id: impl Into<String>, version: u32) -> Self {
21 Self {
22 id: id.into(),
23 version,
24 description: None,
25 steps: Vec::new(),
26 default_error_behavior: ErrorBehavior::default(),
27 default_error_retry_interval: None,
28 }
29 }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct WorkflowStep {
35 pub id: usize,
36 pub name: Option<String>,
37 pub external_id: Option<String>,
38 pub step_type: String,
39 pub children: Vec<usize>,
40 pub outcomes: Vec<StepOutcome>,
41 pub error_behavior: Option<ErrorBehavior>,
42 pub compensation_step_id: Option<usize>,
43 pub do_compensate: bool,
44 #[serde(default)]
45 pub saga: bool,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub step_config: Option<serde_json::Value>,
49}
50
51impl WorkflowStep {
52 pub fn new(id: usize, step_type: impl Into<String>) -> Self {
53 Self {
54 id,
55 name: None,
56 external_id: None,
57 step_type: step_type.into(),
58 children: Vec::new(),
59 outcomes: Vec::new(),
60 error_behavior: None,
61 compensation_step_id: None,
62 do_compensate: false,
63 saga: false,
64 step_config: None,
65 }
66 }
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct StepOutcome {
72 pub next_step: usize,
73 pub label: Option<String>,
74 pub value: Option<serde_json::Value>,
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use pretty_assertions::assert_eq;
81
82 #[test]
83 fn definition_defaults() {
84 let def = WorkflowDefinition::new("test-workflow", 1);
85 assert_eq!(def.id, "test-workflow");
86 assert_eq!(def.version, 1);
87 assert!(def.steps.is_empty());
88 assert_eq!(def.default_error_behavior, ErrorBehavior::default());
89 assert!(def.default_error_retry_interval.is_none());
90 }
91
92 #[test]
93 fn step_defaults() {
94 let step = WorkflowStep::new(0, "MyStep");
95 assert_eq!(step.id, 0);
96 assert_eq!(step.step_type, "MyStep");
97 assert!(step.children.is_empty());
98 assert!(step.outcomes.is_empty());
99 assert!(step.error_behavior.is_none());
100 assert!(step.compensation_step_id.is_none());
101 }
102
103 #[test]
104 fn definition_serde_round_trip() {
105 let mut def = WorkflowDefinition::new("wf", 3);
106 let mut step = WorkflowStep::new(0, "StepA");
107 step.outcomes.push(StepOutcome {
108 next_step: 1,
109 label: Some("next".into()),
110 value: None,
111 });
112 def.steps.push(step);
113 def.steps.push(WorkflowStep::new(1, "StepB"));
114
115 let json = serde_json::to_string(&def).unwrap();
116 let deserialized: WorkflowDefinition = serde_json::from_str(&json).unwrap();
117 assert_eq!(def.id, deserialized.id);
118 assert_eq!(def.steps.len(), deserialized.steps.len());
119 assert_eq!(def.steps[0].outcomes[0].next_step, 1);
120 }
121}