1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! [`Operation`] trait — user-defined step operations.
//!
//! Implement this trait to create custom step types that integrate into the
//! workflow lifecycle. Common use cases include API clients (GitLab, Gmail,
//! Slack) that need full step tracking (persistence, duration, error handling).
//!
//! # How it works
//!
//! 1. Implement [`Operation`] on your type.
//! 2. Call [`WorkflowContext::operation()`](crate::context::WorkflowContext::operation)
//! inside a [`WorkflowHandler`](crate::handler::WorkflowHandler).
//! 3. The engine handles the full step lifecycle: create step record, transition
//! to Running, execute, persist output/duration, mark Completed or Failed.
//!
//! # Examples
//!
//! ```no_run
//! use ironflow_engine::operation::Operation;
//! use ironflow_engine::error::EngineError;
//! use serde_json::{Value, json};
//! use std::future::Future;
//! use std::pin::Pin;
//!
//! struct CreateGitlabIssue {
//! project_id: u64,
//! title: String,
//! }
//!
//! impl Operation for CreateGitlabIssue {
//! fn kind(&self) -> &str {
//! "gitlab"
//! }
//!
//! fn execute(&self) -> Pin<Box<dyn Future<Output = Result<Value, EngineError>> + Send + '_>> {
//! Box::pin(async move {
//! // Call the GitLab API here (e.g. via the `gitlab` crate).
//! Ok(json!({"issue_id": 42, "url": "https://gitlab.com/issues/42"}))
//! })
//! }
//! }
//! ```
use Future;
use Pin;
use Value;
use crateEngineError;
/// A user-defined operation that integrates into the workflow step lifecycle.
///
/// Implement this trait for custom integrations (GitLab, Gmail, Slack, etc.)
/// that need full step tracking when executed via
/// [`WorkflowContext::operation()`](crate::context::WorkflowContext::operation).
///
/// # Contract
///
/// - [`kind()`](Operation::kind) returns a short, lowercase identifier stored
/// as [`StepKind::Custom`](ironflow_store::entities::StepKind::Custom) in
/// the database (e.g. `"gitlab"`, `"gmail"`, `"slack"`).
/// - [`execute()`](Operation::execute) performs the operation and returns
/// a JSON [`Value`] on success. The engine persists this as the step output.
///
/// # Examples
///
/// ```no_run
/// use ironflow_engine::operation::Operation;
/// use ironflow_engine::error::EngineError;
/// use serde_json::{Value, json};
/// use std::future::Future;
/// use std::pin::Pin;
///
/// struct SendSlackMessage {
/// channel: String,
/// text: String,
/// }
///
/// impl Operation for SendSlackMessage {
/// fn kind(&self) -> &str { "slack" }
///
/// fn execute(&self) -> Pin<Box<dyn Future<Output = Result<Value, EngineError>> + Send + '_>> {
/// Box::pin(async move {
/// // Post to Slack API ...
/// Ok(json!({"ok": true, "ts": "1234567890.123456"}))
/// })
/// }
/// }
/// ```