Skip to main content

sz_rust_workflow/capability/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2024-2026 SZ-Rust Team
3//
4use std::sync::Arc;
5
6use async_trait::async_trait;
7use serde_json::{json, Value};
8use sz_rust_addons_loader::capability_hook::CapabilityHook;
9use sz_rust_capability::{CapResult, Capability, CapabilityRegistry, CapabilitySource};
10
11use crate::WorkflowState;
12
13pub const WORKFLOW_CAPABILITY_NAMES: [&str; 3] = [
14    "workflow.health_check",
15    "workflow.list_definitions",
16    "workflow.list_instances",
17];
18
19pub struct WorkflowPlugin {
20    state: WorkflowState,
21}
22
23impl WorkflowPlugin {
24    pub fn new(state: WorkflowState) -> Self {
25        Self { state }
26    }
27}
28
29impl CapabilityHook for WorkflowPlugin {
30    fn register_capabilities(&self, registry: &CapabilityRegistry) -> CapResult<Vec<String>> {
31        let caps: Vec<Arc<dyn Capability>> = vec![
32            Arc::new(HealthCheckCapability::new(self.state.clone())),
33            Arc::new(ListDefinitionsCapability::new()),
34            Arc::new(ListInstancesCapability::new()),
35        ];
36        let mut names = Vec::with_capacity(caps.len());
37        for cap in caps {
38            let name = cap.name().to_string();
39            registry.register(cap);
40            names.push(name);
41        }
42        Ok(names)
43    }
44
45    fn capability_names(&self) -> Vec<String> {
46        WORKFLOW_CAPABILITY_NAMES
47            .iter()
48            .map(|s| s.to_string())
49            .collect()
50    }
51}
52
53pub struct HealthCheckCapability {
54    state: WorkflowState,
55}
56
57impl HealthCheckCapability {
58    pub fn new(state: WorkflowState) -> Self {
59        Self { state }
60    }
61}
62
63#[async_trait]
64impl Capability for HealthCheckCapability {
65    fn name(&self) -> &'static str {
66        "workflow.health_check"
67    }
68
69    fn description(&self) -> &'static str {
70        "workflow 引擎健康检查"
71    }
72
73    fn schema(&self) -> Value {
74        json!({"type": "object", "properties": {}})
75    }
76
77    fn source(&self) -> CapabilitySource {
78        CapabilitySource::Plugin
79    }
80
81    fn tags(&self) -> &'static [&'static str] {
82        &["workflow", "health", "read"]
83    }
84
85    fn requires_confirmation(&self) -> bool {
86        false
87    }
88
89    async fn call(&self, _params: Value) -> CapResult<Value> {
90        Ok(json!({
91            "code": 1,
92            "msg": "success",
93            "data": {
94                "plugin": "workflow",
95                "status": "active",
96                "engine": "WorkflowEngine",
97                "version": self.state.version
98            }
99        }))
100    }
101}
102
103pub struct ListDefinitionsCapability;
104
105impl Default for ListDefinitionsCapability {
106    fn default() -> Self {
107        Self::new()
108    }
109}
110
111impl ListDefinitionsCapability {
112    pub fn new() -> Self {
113        Self
114    }
115}
116
117#[async_trait]
118impl Capability for ListDefinitionsCapability {
119    fn name(&self) -> &'static str {
120        "workflow.list_definitions"
121    }
122
123    fn description(&self) -> &'static str {
124        "列出工作流定义"
125    }
126
127    fn schema(&self) -> Value {
128        json!({"type": "object", "properties": {}})
129    }
130
131    fn source(&self) -> CapabilitySource {
132        CapabilitySource::Plugin
133    }
134
135    fn tags(&self) -> &'static [&'static str] {
136        &["workflow", "definition", "read"]
137    }
138
139    fn requires_confirmation(&self) -> bool {
140        false
141    }
142
143    async fn call(&self, _params: Value) -> CapResult<Value> {
144        Ok(json!({
145            "code": 1,
146            "msg": "success",
147            "data": {
148                "definitions": [],
149                "total": 0
150            }
151        }))
152    }
153}
154
155pub struct ListInstancesCapability;
156
157impl Default for ListInstancesCapability {
158    fn default() -> Self {
159        Self::new()
160    }
161}
162
163impl ListInstancesCapability {
164    pub fn new() -> Self {
165        Self
166    }
167}
168
169#[async_trait]
170impl Capability for ListInstancesCapability {
171    fn name(&self) -> &'static str {
172        "workflow.list_instances"
173    }
174
175    fn description(&self) -> &'static str {
176        "列出工作流实例"
177    }
178
179    fn schema(&self) -> Value {
180        json!({"type": "object", "properties": {}})
181    }
182
183    fn source(&self) -> CapabilitySource {
184        CapabilitySource::Plugin
185    }
186
187    fn tags(&self) -> &'static [&'static str] {
188        &["workflow", "instance", "read"]
189    }
190
191    fn requires_confirmation(&self) -> bool {
192        false
193    }
194
195    async fn call(&self, _params: Value) -> CapResult<Value> {
196        let engine = crate::WorkflowEngine::new(
197            crate::WorkflowConfig::default(),
198            crate::WorkflowDeps::default_for_test(),
199        );
200        let page = crate::PageRequest::default();
201        let pending_tasks = engine
202            .query_tasks("", page)
203            .await
204            .map(|r| r.total)
205            .unwrap_or(0);
206        Ok(json!({
207            "code": 1,
208            "msg": "success",
209            "data": {
210                "instances": [],
211                "total": 0,
212                "pending_tasks": pending_tasks
213            }
214        }))
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221
222    #[test]
223    fn test_workflow_capability_names() {
224        assert_eq!(WORKFLOW_CAPABILITY_NAMES.len(), 3);
225        assert!(WORKFLOW_CAPABILITY_NAMES.contains(&"workflow.health_check"));
226        assert!(WORKFLOW_CAPABILITY_NAMES.contains(&"workflow.list_definitions"));
227        assert!(WORKFLOW_CAPABILITY_NAMES.contains(&"workflow.list_instances"));
228    }
229
230    #[tokio::test]
231    async fn test_register_capabilities() {
232        let registry = CapabilityRegistry::new();
233        let plugin = WorkflowPlugin::new(WorkflowState::default());
234        let names = plugin.register_capabilities(&registry).unwrap();
235        assert_eq!(names.len(), 3);
236    }
237
238    #[tokio::test]
239    async fn test_health_check_capability() {
240        let cap = HealthCheckCapability::new(WorkflowState::default());
241        let result = cap.call(json!({})).await.unwrap();
242        assert_eq!(result["code"], 1);
243        assert_eq!(result["data"]["plugin"], "workflow");
244    }
245
246    #[tokio::test]
247    async fn test_list_definitions_capability() {
248        let cap = ListDefinitionsCapability::new();
249        let result = cap.call(json!({})).await.unwrap();
250        assert_eq!(result["code"], 1);
251    }
252
253    #[tokio::test]
254    async fn test_list_instances_capability() {
255        let cap = ListInstancesCapability::new();
256        let result = cap.call(json!({})).await.unwrap();
257        assert_eq!(result["code"], 1);
258    }
259
260    #[test]
261    fn test_health_check_capability_metadata() {
262        let cap = HealthCheckCapability::new(WorkflowState::default());
263        assert_eq!(cap.name(), "workflow.health_check");
264        assert!(!cap.description().is_empty());
265        assert_eq!(cap.source(), CapabilitySource::Plugin);
266        assert!(cap.tags().contains(&"workflow"));
267        assert!(!cap.requires_confirmation());
268    }
269
270    #[test]
271    fn test_list_definitions_capability_metadata() {
272        let cap = ListDefinitionsCapability::new();
273        assert_eq!(cap.name(), "workflow.list_definitions");
274        assert!(!cap.description().is_empty());
275        assert_eq!(cap.source(), CapabilitySource::Plugin);
276        assert!(cap.tags().contains(&"definition"));
277        assert!(!cap.requires_confirmation());
278    }
279
280    #[test]
281    fn test_list_instances_capability_metadata() {
282        let cap = ListInstancesCapability::new();
283        assert_eq!(cap.name(), "workflow.list_instances");
284        assert!(!cap.description().is_empty());
285        assert_eq!(cap.source(), CapabilitySource::Plugin);
286        assert!(cap.tags().contains(&"instance"));
287        assert!(!cap.requires_confirmation());
288    }
289
290    #[test]
291    fn test_workflow_plugin_capability_names() {
292        let plugin = WorkflowPlugin::new(WorkflowState::default());
293        let names = plugin.capability_names();
294        assert_eq!(names.len(), 3);
295        assert!(names.contains(&"workflow.health_check".to_string()));
296    }
297}