1#![doc(html_root_url = "https://docs.rs/nestrs-workflows/1.7.1")]
11
12mod module;
13mod options;
14mod store;
15
16pub use module::WorkflowsModule;
17pub use options::WorkflowsOptions;
18pub use store::{
19 InMemoryWorkflowStore, SharedWorkflowStore, WorkflowInstance, WorkflowStatus, WorkflowStore,
20};
21
22pub use nestrs_core::{DynamicModule, Injectable, ProviderRegistry};
23use serde_json::Value;
24use std::sync::Arc;
25
26pub struct WorkflowsService {
27 store: SharedWorkflowStore,
28}
29
30impl WorkflowsService {
31 pub fn from_options(opts: &WorkflowsOptions) -> Self {
32 Self {
33 store: opts.store.clone(),
34 }
35 }
36
37 pub async fn start(&self, name: &str, input: Value) -> WorkflowInstance {
38 self.store.create(name, input).await
39 }
40
41 pub async fn get(&self, id: &str) -> Option<WorkflowInstance> {
42 self.store.get(id).await
43 }
44
45 pub async fn complete_step(
47 &self,
48 id: &str,
49 step: &str,
50 result: Value,
51 ) -> Result<WorkflowInstance, String> {
52 self.store.complete_step(id, step, result).await
53 }
54
55 pub async fn wait_for_approval(&self, id: &str) -> Result<WorkflowInstance, String> {
57 self.store
58 .set_status(id, WorkflowStatus::WaitingApproval)
59 .await
60 }
61
62 pub async fn approve(&self, id: &str) -> Result<WorkflowInstance, String> {
63 self.store.set_status(id, WorkflowStatus::Running).await
64 }
65}
66
67impl Injectable for WorkflowsService {
68 fn construct(_registry: &nestrs_core::ProviderRegistry) -> Arc<Self> {
69 panic!("WorkflowsService must be provided by WorkflowsModule::register(...)");
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use serde_json::json;
77
78 #[tokio::test]
79 async fn survives_logical_restart() {
80 let store = InMemoryWorkflowStore::shared();
81 let svc = WorkflowsService {
82 store: store.clone(),
83 };
84 let inst = svc.start("order", json!({"id": 1})).await;
85 svc.complete_step(&inst.id, "charge", json!({"ok": true}))
86 .await
87 .unwrap();
88 let svc2 = WorkflowsService { store };
90 let loaded = svc2.get(&inst.id).await.expect("loaded");
91 assert!(loaded.steps.contains_key("charge"));
92 assert_eq!(loaded.status, WorkflowStatus::Running);
93 }
94}