Skip to main content

backbone_bucket/application/workflows/
share_creation_workflow.rs

1//! ShareCreation flow implementation
2//!
3//! Generated by metaphor-schema
4//!
5//! Complete workflow for creating and distributing file shares:
6//! - Permission validation
7//! - Share record creation
8//! - URL generation
9//! - Notification delivery (in-app and email)
10
11use serde::{Deserialize, Serialize};
12use std::sync::Arc;
13use chrono;
14use backbone_core::flow::{WorkflowStep, WorkflowContext};
15
16/// Error type for flow execution
17#[derive(Debug, Clone, thiserror::Error)]
18pub enum FlowError {
19    #[error("No current step to execute")]
20    NoCurrentStep,
21
22    #[error("Step execution failed: {0}")]
23    StepFailed(String),
24
25    #[error("Condition evaluation failed: {0}")]
26    ConditionFailed(String),
27
28    #[error("Compensation failed: {0}")]
29    CompensationFailed(String),
30
31    #[error("Flow timed out")]
32    Timeout,
33
34    #[error("Flow cancelled")]
35    Cancelled,
36
37    #[error("Invalid state transition: {from} -> {to}")]
38    InvalidTransition { from: String, to: String },
39}
40
41/// Execution status for ShareCreation flow
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(rename_all = "snake_case")]
44pub enum ShareCreationFlowStatus {
45    /// Flow is pending execution
46    Pending,
47    /// Flow is currently running
48    Running,
49    /// Flow is waiting for an event or condition
50    Waiting,
51    /// Flow completed successfully
52    Completed,
53    /// Flow failed
54    Failed,
55    /// Flow was cancelled
56    Cancelled,
57    /// Flow is compensating (rolling back)
58    Compensating,
59}
60
61/// Steps in ShareCreation flow
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
63#[serde(rename_all = "snake_case")]
64pub enum ShareCreationFlowStep {
65    /// validate_file
66    ValidateFile,
67    /// check_share_permission
68    CheckSharePermission,
69    /// check_manage_permission
70    CheckManagePermission,
71    /// check_share_type
72    CheckShareType,
73    /// validate_public_share
74    ValidatePublicShare,
75    /// validate_user_share
76    ValidateUserShare,
77    /// validate_email_share
78    ValidateEmailShare,
79    /// check_existing_share
80    CheckExistingShare,
81    /// update_existing_share
82    UpdateExistingShare,
83    /// create_share_record
84    CreateShareRecord,
85    /// generate_share_url
86    GenerateShareUrl,
87    /// determine_notification
88    DetermineNotification,
89    /// notify_user
90    NotifyUser,
91    /// send_email_invitation
92    SendEmailInvitation,
93    /// log_share
94    LogShare,
95    /// complete
96    Complete,
97    /// file_not_found
98    FileNotFound,
99    /// permission_denied
100    PermissionDenied,
101    /// failed_permission
102    FailedPermission,
103    /// public_sharing_disabled
104    PublicSharingDisabled,
105    /// user_not_found
106    UserNotFound,
107    /// invalid_config
108    InvalidConfig,
109}
110
111/// Instance of ShareCreation flow execution
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct ShareCreationFlowInstance {
114    /// Unique instance ID
115    pub id: String,
116    /// Current status
117    pub status: ShareCreationFlowStatus,
118    /// Current step
119    pub current_step: Option<ShareCreationFlowStep>,
120    /// Flow context (variables)
121    pub context: serde_json::Value,
122    /// Completed steps
123    pub completed_steps: Vec<ShareCreationFlowStep>,
124    /// Error if failed
125    pub error: Option<String>,
126    /// Created timestamp
127    pub created_at: chrono::DateTime<chrono::Utc>,
128    /// Updated timestamp
129    pub updated_at: chrono::DateTime<chrono::Utc>,
130}
131
132impl ShareCreationFlowInstance {
133    /// Create a new flow instance
134    pub fn new(id: impl Into<String>) -> Self {
135        let now = chrono::Utc::now();
136        Self {
137            id: id.into(),
138            status: ShareCreationFlowStatus::Pending,
139            current_step: None,
140            context: serde_json::json!({}),
141            completed_steps: Vec::new(),
142            error: None,
143            created_at: now,
144            updated_at: now,
145        }
146    }
147
148    /// Check if flow is complete
149    pub fn is_complete(&self) -> bool {
150        matches!(
151            self.status,
152            ShareCreationFlowStatus::Completed | ShareCreationFlowStatus::Failed | ShareCreationFlowStatus::Cancelled
153        )
154    }
155
156    /// Check if flow is running
157    pub fn is_running(&self) -> bool {
158        matches!(
159            self.status,
160            ShareCreationFlowStatus::Running | ShareCreationFlowStatus::Waiting
161        )
162    }
163
164    /// Set a context variable
165    pub fn set_context(&mut self, key: &str, value: serde_json::Value) {
166        if let serde_json::Value::Object(ref mut map) = self.context {
167            map.insert(key.to_string(), value);
168        }
169        self.updated_at = chrono::Utc::now();
170    }
171
172    /// Get a context variable
173    pub fn get_context(&self, key: &str) -> Option<&serde_json::Value> {
174        if let serde_json::Value::Object(ref map) = self.context {
175            map.get(key)
176        } else {
177            None
178        }
179    }
180}
181
182// Phase 1: FlowInstance satisfies backbone_core::flow::WorkflowContext.
183// The entity() accessor requires the entity to be carried in the instance;
184// add `pub entity: ShareCreation` in the // <<< CUSTOM section and remove the todo!.
185#[allow(unused_variables)]
186impl WorkflowContext<ShareCreationFlowInstance> for ShareCreationFlowInstance {
187    fn entity(&self) -> &ShareCreationFlowInstance { self }
188    fn set_var(&mut self, key: &str, value: serde_json::Value) {
189        self.set_context(key, value);
190    }
191    fn get_var(&self, key: &str) -> Option<&serde_json::Value> {
192        self.get_context(key)
193    }
194}
195
196/// Step handler trait for ShareCreation flow
197#[async_trait::async_trait]
198pub trait ShareCreationStepHandler: Send + Sync {
199    /// Handle validate_file step
200    async fn handle_validate_file(
201        &self,
202        instance: &mut ShareCreationFlowInstance,
203    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
204
205    /// Evaluate condition for check_share_permission step
206    async fn evaluate_check_share_permission(
207        &self,
208        instance: &ShareCreationFlowInstance,
209    ) -> Result<ShareCreationFlowStep, FlowError>;
210
211    /// Handle check_manage_permission step
212    async fn handle_check_manage_permission(
213        &self,
214        instance: &mut ShareCreationFlowInstance,
215    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
216
217    /// Evaluate condition for check_share_type step
218    async fn evaluate_check_share_type(
219        &self,
220        instance: &ShareCreationFlowInstance,
221    ) -> Result<ShareCreationFlowStep, FlowError>;
222
223    /// Handle validate_public_share step
224    async fn handle_validate_public_share(
225        &self,
226        instance: &mut ShareCreationFlowInstance,
227    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
228
229    /// Handle validate_user_share step
230    async fn handle_validate_user_share(
231        &self,
232        instance: &mut ShareCreationFlowInstance,
233    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
234
235    /// Handle validate_email_share step
236    async fn handle_validate_email_share(
237        &self,
238        instance: &mut ShareCreationFlowInstance,
239    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
240
241    /// Handle check_existing_share step
242    async fn handle_check_existing_share(
243        &self,
244        instance: &mut ShareCreationFlowInstance,
245    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
246
247    /// Handle update_existing_share step
248    async fn handle_update_existing_share(
249        &self,
250        instance: &mut ShareCreationFlowInstance,
251    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
252
253    /// Handle create_share_record step
254    async fn handle_create_share_record(
255        &self,
256        instance: &mut ShareCreationFlowInstance,
257    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
258
259    /// Handle generate_share_url step
260    async fn handle_generate_share_url(
261        &self,
262        instance: &mut ShareCreationFlowInstance,
263    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
264
265    /// Evaluate condition for determine_notification step
266    async fn evaluate_determine_notification(
267        &self,
268        instance: &ShareCreationFlowInstance,
269    ) -> Result<ShareCreationFlowStep, FlowError>;
270
271    /// Handle notify_user step
272    async fn handle_notify_user(
273        &self,
274        instance: &mut ShareCreationFlowInstance,
275    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
276
277    /// Handle send_email_invitation step
278    async fn handle_send_email_invitation(
279        &self,
280        instance: &mut ShareCreationFlowInstance,
281    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
282
283    /// Handle log_share step
284    async fn handle_log_share(
285        &self,
286        instance: &mut ShareCreationFlowInstance,
287    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
288
289    /// Handle terminal step complete
290    async fn handle_complete(
291        &self,
292        instance: &mut ShareCreationFlowInstance,
293    ) -> Result<(), FlowError>;
294
295    /// Handle terminal step file_not_found
296    async fn handle_file_not_found(
297        &self,
298        instance: &mut ShareCreationFlowInstance,
299    ) -> Result<(), FlowError>;
300
301    /// Handle permission_denied step
302    async fn handle_permission_denied(
303        &self,
304        instance: &mut ShareCreationFlowInstance,
305    ) -> Result<Option<ShareCreationFlowStep>, FlowError>;
306
307    /// Handle terminal step failed_permission
308    async fn handle_failed_permission(
309        &self,
310        instance: &mut ShareCreationFlowInstance,
311    ) -> Result<(), FlowError>;
312
313    /// Handle terminal step public_sharing_disabled
314    async fn handle_public_sharing_disabled(
315        &self,
316        instance: &mut ShareCreationFlowInstance,
317    ) -> Result<(), FlowError>;
318
319    /// Handle terminal step user_not_found
320    async fn handle_user_not_found(
321        &self,
322        instance: &mut ShareCreationFlowInstance,
323    ) -> Result<(), FlowError>;
324
325    /// Handle terminal step invalid_config
326    async fn handle_invalid_config(
327        &self,
328        instance: &mut ShareCreationFlowInstance,
329    ) -> Result<(), FlowError>;
330
331}
332
333/// Executor for ShareCreation flow
334pub struct ShareCreationFlowExecutor<H: ShareCreationStepHandler> {
335    handler: Arc<H>,
336}
337
338impl<H: ShareCreationStepHandler> ShareCreationFlowExecutor<H> {
339    /// Create a new flow executor
340    pub fn new(handler: Arc<H>) -> Self {
341        Self { handler }
342    }
343
344    /// Start a new flow instance
345    pub async fn start(&self, instance_id: impl Into<String>) -> Result<ShareCreationFlowInstance, FlowError> {
346        let mut instance = ShareCreationFlowInstance::new(instance_id);
347        instance.status = ShareCreationFlowStatus::Running;
348        instance.current_step = Some(ShareCreationFlowStep::ValidateFile);
349        Ok(instance)
350    }
351
352    /// Execute the current step
353    pub async fn execute_step(&self, instance: &mut ShareCreationFlowInstance) -> Result<(), FlowError> {
354        let current_step = match instance.current_step {
355            Some(step) => step,
356            None => return Err(FlowError::NoCurrentStep),
357        };
358
359        let next_step = match current_step {
360            ShareCreationFlowStep::ValidateFile => {
361                self.handler.handle_validate_file(instance).await?
362            }
363            ShareCreationFlowStep::CheckSharePermission => {
364                let next = self.handler.evaluate_check_share_permission(instance).await?;
365                Some(next)
366            }
367            ShareCreationFlowStep::CheckManagePermission => {
368                self.handler.handle_check_manage_permission(instance).await?
369            }
370            ShareCreationFlowStep::CheckShareType => {
371                let next = self.handler.evaluate_check_share_type(instance).await?;
372                Some(next)
373            }
374            ShareCreationFlowStep::ValidatePublicShare => {
375                self.handler.handle_validate_public_share(instance).await?
376            }
377            ShareCreationFlowStep::ValidateUserShare => {
378                self.handler.handle_validate_user_share(instance).await?
379            }
380            ShareCreationFlowStep::ValidateEmailShare => {
381                self.handler.handle_validate_email_share(instance).await?
382            }
383            ShareCreationFlowStep::CheckExistingShare => {
384                self.handler.handle_check_existing_share(instance).await?
385            }
386            ShareCreationFlowStep::UpdateExistingShare => {
387                self.handler.handle_update_existing_share(instance).await?
388            }
389            ShareCreationFlowStep::CreateShareRecord => {
390                self.handler.handle_create_share_record(instance).await?
391            }
392            ShareCreationFlowStep::GenerateShareUrl => {
393                self.handler.handle_generate_share_url(instance).await?
394            }
395            ShareCreationFlowStep::DetermineNotification => {
396                let next = self.handler.evaluate_determine_notification(instance).await?;
397                Some(next)
398            }
399            ShareCreationFlowStep::NotifyUser => {
400                self.handler.handle_notify_user(instance).await?
401            }
402            ShareCreationFlowStep::SendEmailInvitation => {
403                self.handler.handle_send_email_invitation(instance).await?
404            }
405            ShareCreationFlowStep::LogShare => {
406                self.handler.handle_log_share(instance).await?
407            }
408            ShareCreationFlowStep::Complete => {
409                self.handler.handle_complete(instance).await?;
410                None // Terminal step
411            }
412            ShareCreationFlowStep::FileNotFound => {
413                self.handler.handle_file_not_found(instance).await?;
414                None // Terminal step
415            }
416            ShareCreationFlowStep::PermissionDenied => {
417                self.handler.handle_permission_denied(instance).await?
418            }
419            ShareCreationFlowStep::FailedPermission => {
420                self.handler.handle_failed_permission(instance).await?;
421                None // Terminal step
422            }
423            ShareCreationFlowStep::PublicSharingDisabled => {
424                self.handler.handle_public_sharing_disabled(instance).await?;
425                None // Terminal step
426            }
427            ShareCreationFlowStep::UserNotFound => {
428                self.handler.handle_user_not_found(instance).await?;
429                None // Terminal step
430            }
431            ShareCreationFlowStep::InvalidConfig => {
432                self.handler.handle_invalid_config(instance).await?;
433                None // Terminal step
434            }
435        };
436
437        // Mark current step as completed
438        instance.completed_steps.push(current_step);
439
440        // Move to next step
441        match next_step {
442            Some(next) => {
443                instance.current_step = Some(next);
444            }
445            None => {
446                instance.current_step = None;
447                instance.status = ShareCreationFlowStatus::Completed;
448            }
449        }
450
451        instance.updated_at = chrono::Utc::now();
452        Ok(())
453    }
454
455    /// Run the flow to completion
456    pub async fn run(&self, instance: &mut ShareCreationFlowInstance) -> Result<(), FlowError> {
457        while !instance.is_complete() && instance.status != ShareCreationFlowStatus::Waiting {
458            self.execute_step(instance).await?;
459        }
460        Ok(())
461    }
462
463    /// Cancel the flow
464    pub fn cancel(&self, instance: &mut ShareCreationFlowInstance) {
465        instance.status = ShareCreationFlowStatus::Cancelled;
466        instance.updated_at = chrono::Utc::now();
467    }
468
469    /// Mark flow as failed
470    pub fn fail(&self, instance: &mut ShareCreationFlowInstance, error: impl Into<String>) {
471        instance.status = ShareCreationFlowStatus::Failed;
472        instance.error = Some(error.into());
473        instance.updated_at = chrono::Utc::now();
474    }
475}
476
477#[cfg(test)]
478mod tests {
479    use super::*;
480
481    #[test]
482    fn test_flow_instance_creation() {
483        let instance = ShareCreationFlowInstance::new("test-1");
484        assert_eq!(instance.id, "test-1");
485        assert_eq!(instance.status, ShareCreationFlowStatus::Pending);
486        assert!(!instance.is_complete());
487    }
488
489    #[test]
490    fn test_flow_context() {
491        let mut instance = ShareCreationFlowInstance::new("test-2");
492        instance.set_context("key", serde_json::json!("value"));
493        let value = instance.get_context("key");
494        assert_eq!(value, Some(&serde_json::json!("value")));
495    }
496
497    #[test]
498    fn test_flow_status_transitions() {
499        let mut instance = ShareCreationFlowInstance::new("test-3");
500        assert!(!instance.is_running());
501
502        instance.status = ShareCreationFlowStatus::Running;
503        assert!(instance.is_running());
504        assert!(!instance.is_complete());
505
506        instance.status = ShareCreationFlowStatus::Completed;
507        assert!(instance.is_complete());
508        assert!(!instance.is_running());
509    }
510}