oxigdal_workflow/versioning/
migration.rs1use crate::engine::WorkflowDefinition;
4use crate::error::{Result, WorkflowError};
5use serde::{Deserialize, Serialize};
6
7pub struct WorkflowMigration;
9
10impl WorkflowMigration {
11 pub fn new() -> Self {
13 Self
14 }
15
16 pub fn migrate(
18 &self,
19 from: WorkflowDefinition,
20 to: WorkflowDefinition,
21 ) -> Result<WorkflowDefinition> {
22 let plan = self.create_migration_plan(&from, &to)?;
24
25 self.execute_migration_plan(from, plan)
27 }
28
29 fn create_migration_plan(
31 &self,
32 from: &WorkflowDefinition,
33 to: &WorkflowDefinition,
34 ) -> Result<MigrationPlan> {
35 let mut steps = Vec::new();
36
37 if from.version != to.version {
39 steps.push(MigrationStep::UpdateVersion {
40 from: from.version.clone(),
41 to: to.version.clone(),
42 });
43 }
44
45 if from.dag.task_count() != to.dag.task_count() {
47 steps.push(MigrationStep::UpdateTaskCount {
48 from: from.dag.task_count(),
49 to: to.dag.task_count(),
50 });
51 }
52
53 Ok(MigrationPlan {
55 steps,
56 requires_downtime: false,
57 estimated_duration_secs: 0,
58 })
59 }
60
61 fn execute_migration_plan(
63 &self,
64 mut workflow: WorkflowDefinition,
65 plan: MigrationPlan,
66 ) -> Result<WorkflowDefinition> {
67 for step in plan.steps {
68 workflow = self.execute_migration_step(workflow, step)?;
69 }
70
71 Ok(workflow)
72 }
73
74 fn execute_migration_step(
76 &self,
77 mut workflow: WorkflowDefinition,
78 step: MigrationStep,
79 ) -> Result<WorkflowDefinition> {
80 match step {
81 MigrationStep::UpdateVersion { to, .. } => {
82 workflow.version = to;
83 }
84 MigrationStep::UpdateTaskCount { .. } => {
85 }
87 MigrationStep::UpdateMetadata => {
88 }
90 MigrationStep::Custom { .. } => {
91 }
93 }
94
95 Ok(workflow)
96 }
97
98 pub fn validate_migration(
100 &self,
101 from: &WorkflowDefinition,
102 to: &WorkflowDefinition,
103 ) -> Result<()> {
104 if from.id != to.id {
106 return Err(WorkflowError::versioning(
107 "Cannot migrate between different workflows",
108 ));
109 }
110
111 Ok(())
112 }
113}
114
115impl Default for WorkflowMigration {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct MigrationPlan {
124 pub steps: Vec<MigrationStep>,
126 pub requires_downtime: bool,
128 pub estimated_duration_secs: u64,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub enum MigrationStep {
135 UpdateVersion {
137 from: String,
139 to: String,
141 },
142 UpdateTaskCount {
144 from: usize,
146 to: usize,
148 },
149 UpdateMetadata,
151 Custom {
153 name: String,
155 description: String,
157 },
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::dag::WorkflowDag;
164
165 #[test]
166 fn test_migration_creation() {
167 let migration = WorkflowMigration::new();
168
169 let from = WorkflowDefinition {
170 id: "test".to_string(),
171 name: "Test".to_string(),
172 description: None,
173 version: "1.0.0".to_string(),
174 dag: WorkflowDag::new(),
175 };
176
177 let to = WorkflowDefinition {
178 id: "test".to_string(),
179 name: "Test".to_string(),
180 description: None,
181 version: "2.0.0".to_string(),
182 dag: WorkflowDag::new(),
183 };
184
185 assert!(migration.validate_migration(&from, &to).is_ok());
186 }
187
188 #[test]
189 fn test_migration_plan() {
190 let migration = WorkflowMigration::new();
191
192 let from = WorkflowDefinition {
193 id: "test".to_string(),
194 name: "Test".to_string(),
195 description: None,
196 version: "1.0.0".to_string(),
197 dag: WorkflowDag::new(),
198 };
199
200 let to = WorkflowDefinition {
201 id: "test".to_string(),
202 name: "Test".to_string(),
203 description: None,
204 version: "2.0.0".to_string(),
205 dag: WorkflowDag::new(),
206 };
207
208 let plan = migration.create_migration_plan(&from, &to).expect("Failed");
209
210 assert!(!plan.steps.is_empty());
211 }
212
213 #[test]
214 fn test_invalid_migration() {
215 let migration = WorkflowMigration::new();
216
217 let from = WorkflowDefinition {
218 id: "test1".to_string(),
219 name: "Test1".to_string(),
220 description: None,
221 version: "1.0.0".to_string(),
222 dag: WorkflowDag::new(),
223 };
224
225 let to = WorkflowDefinition {
226 id: "test2".to_string(),
227 name: "Test2".to_string(),
228 description: None,
229 version: "2.0.0".to_string(),
230 dag: WorkflowDag::new(),
231 };
232
233 assert!(migration.validate_migration(&from, &to).is_err());
234 }
235}