kibana_sync/kibana/workflows/
manifest.rs1use crate::{Result, ResultContext};
18use serde::{Deserialize, Serialize};
19use std::path::Path;
20
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
23pub struct WorkflowEntry {
24 pub id: String,
26 pub name: String,
28}
29
30impl WorkflowEntry {
31 pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
33 Self {
34 id: id.into(),
35 name: name.into(),
36 }
37 }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42pub struct WorkflowsManifest {
43 pub workflows: Vec<WorkflowEntry>,
45}
46
47impl WorkflowsManifest {
48 pub fn new() -> Self {
50 Self {
51 workflows: Vec::new(),
52 }
53 }
54
55 pub fn with_workflows(workflows: Vec<WorkflowEntry>) -> Self {
57 Self { workflows }
58 }
59
60 pub fn add_workflow(&mut self, workflow: WorkflowEntry) -> bool {
64 if !self.workflows.iter().any(|w| w.id == workflow.id) {
66 self.workflows.push(workflow);
67 true
68 } else {
69 false
70 }
71 }
72
73 pub fn remove_workflow_by_id(&mut self, workflow_id: &str) -> bool {
75 if let Some(pos) = self.workflows.iter().position(|w| w.id == workflow_id) {
76 self.workflows.remove(pos);
77 true
78 } else {
79 false
80 }
81 }
82
83 pub fn remove_workflow_by_name(&mut self, workflow_name: &str) -> bool {
85 if let Some(pos) = self.workflows.iter().position(|w| w.name == workflow_name) {
86 self.workflows.remove(pos);
87 true
88 } else {
89 false
90 }
91 }
92
93 pub fn contains_id(&self, workflow_id: &str) -> bool {
95 self.workflows.iter().any(|w| w.id == workflow_id)
96 }
97
98 pub fn contains_name(&self, workflow_name: &str) -> bool {
100 self.workflows.iter().any(|w| w.name == workflow_name)
101 }
102
103 pub fn get_by_id(&self, workflow_id: &str) -> Option<&WorkflowEntry> {
105 self.workflows.iter().find(|w| w.id == workflow_id)
106 }
107
108 pub fn get_by_name(&self, workflow_name: &str) -> Option<&WorkflowEntry> {
110 self.workflows.iter().find(|w| w.name == workflow_name)
111 }
112
113 pub fn count(&self) -> usize {
115 self.workflows.len()
116 }
117
118 pub fn read(path: impl AsRef<Path>) -> Result<Self> {
120 let content = std::fs::read_to_string(path.as_ref()).with_context(|| {
121 format!(
122 "Failed to read workflows manifest: {}",
123 path.as_ref().display()
124 )
125 })?;
126
127 let manifest: Self = yaml_serde::from_str(&content)
128 .with_context(|| "Failed to parse workflows manifest YAML")?;
129
130 Ok(manifest)
131 }
132
133 pub fn write(&self, path: impl AsRef<Path>) -> Result<()> {
135 if let Some(parent) = path.as_ref().parent() {
137 std::fs::create_dir_all(parent)?;
138 }
139
140 let yaml = yaml_serde::to_string(self)
141 .with_context(|| "Failed to serialize workflows manifest to YAML")?;
142
143 std::fs::write(path.as_ref(), yaml).with_context(|| {
144 format!(
145 "Failed to write workflows manifest: {}",
146 path.as_ref().display()
147 )
148 })?;
149
150 Ok(())
151 }
152}
153
154impl Default for WorkflowsManifest {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use tempfile::TempDir;
164
165 #[test]
166 fn test_new_manifest() {
167 let manifest = WorkflowsManifest::new();
168 assert_eq!(manifest.count(), 0);
169 }
170
171 #[test]
172 fn test_with_workflows() {
173 let manifest = WorkflowsManifest::with_workflows(vec![
174 WorkflowEntry::new("wf1", "workflow1"),
175 WorkflowEntry::new("wf2", "workflow2"),
176 ]);
177 assert_eq!(manifest.count(), 2);
178 assert!(manifest.contains_id("wf1"));
179 assert!(manifest.contains_name("workflow1"));
180 assert!(manifest.contains_id("wf2"));
181 assert!(manifest.contains_name("workflow2"));
182 }
183
184 #[test]
185 fn test_add_workflow() {
186 let mut manifest = WorkflowsManifest::new();
187 manifest.add_workflow(WorkflowEntry::new("test-id", "test"));
188 assert_eq!(manifest.count(), 1);
189 assert!(manifest.contains_id("test-id"));
190 assert!(manifest.contains_name("test"));
191
192 manifest.add_workflow(WorkflowEntry::new("test-id", "test"));
194 assert_eq!(manifest.count(), 1);
195 }
196
197 #[test]
198 fn test_remove_workflow_by_id() {
199 let mut manifest = WorkflowsManifest::with_workflows(vec![
200 WorkflowEntry::new("wf1", "workflow1"),
201 WorkflowEntry::new("wf2", "workflow2"),
202 ]);
203
204 assert!(manifest.remove_workflow_by_id("wf1"));
205 assert_eq!(manifest.count(), 1);
206 assert!(!manifest.contains_id("wf1"));
207
208 assert!(!manifest.remove_workflow_by_id("nonexistent"));
210 }
211
212 #[test]
213 fn test_remove_workflow_by_name() {
214 let mut manifest = WorkflowsManifest::with_workflows(vec![
215 WorkflowEntry::new("wf1", "workflow1"),
216 WorkflowEntry::new("wf2", "workflow2"),
217 ]);
218
219 assert!(manifest.remove_workflow_by_name("workflow1"));
220 assert_eq!(manifest.count(), 1);
221 assert!(!manifest.contains_name("workflow1"));
222
223 assert!(!manifest.remove_workflow_by_name("nonexistent"));
225 }
226
227 #[test]
228 fn test_get_by_id() {
229 let manifest = WorkflowsManifest::with_workflows(vec![
230 WorkflowEntry::new("wf1", "workflow1"),
231 WorkflowEntry::new("wf2", "workflow2"),
232 ]);
233
234 let entry = manifest.get_by_id("wf1").unwrap();
235 assert_eq!(entry.id, "wf1");
236 assert_eq!(entry.name, "workflow1");
237
238 assert!(manifest.get_by_id("nonexistent").is_none());
239 }
240
241 #[test]
242 fn test_get_by_name() {
243 let manifest = WorkflowsManifest::with_workflows(vec![
244 WorkflowEntry::new("wf1", "workflow1"),
245 WorkflowEntry::new("wf2", "workflow2"),
246 ]);
247
248 let entry = manifest.get_by_name("workflow1").unwrap();
249 assert_eq!(entry.id, "wf1");
250 assert_eq!(entry.name, "workflow1");
251
252 assert!(manifest.get_by_name("nonexistent").is_none());
253 }
254
255 #[test]
256 fn test_read_write() {
257 let temp_dir = TempDir::new().unwrap();
258 let manifest_path = temp_dir.path().join("manifest").join("workflows.yml");
259
260 let original = WorkflowsManifest::with_workflows(vec![
261 WorkflowEntry::new("wf1", "workflow1"),
262 WorkflowEntry::new("wf2", "workflow2"),
263 WorkflowEntry::new("wf3", "workflow3"),
264 ]);
265
266 original.write(&manifest_path).unwrap();
268 assert!(manifest_path.exists());
269
270 let loaded = WorkflowsManifest::read(&manifest_path).unwrap();
272 assert_eq!(loaded, original);
273 assert_eq!(loaded.count(), 3);
274 }
275
276 #[test]
277 fn test_yaml_format() {
278 let manifest = WorkflowsManifest::with_workflows(vec![
279 WorkflowEntry::new("wf1", "workflow1"),
280 WorkflowEntry::new("wf2", "workflow2"),
281 ]);
282 let yaml = yaml_serde::to_string(&manifest).unwrap();
283
284 assert!(yaml.contains("workflows:"));
285 assert!(yaml.contains("id: wf1"));
286 assert!(yaml.contains("name: workflow1"));
287 assert!(yaml.contains("id: wf2"));
288 assert!(yaml.contains("name: workflow2"));
289 }
290}