1use std::str::FromStr;
5
6use anyhow::Result;
7
8use crate::ctx::{inject_engine_version, timestamp_now, WorkflowCtx};
9use crate::events::WorkflowBusEvent;
10use crate::store::WorkflowStore;
11use crate::types::*;
12
13impl<S: WorkflowStore> WorkflowCtx<S> {
14 pub async fn start_workflow(
15 &self,
16 namespace: &str,
17 workflow_type: &str,
18 workflow_id: &str,
19 input: Option<&str>,
20 task_queue: &str,
21 search_attributes: Option<&str>,
22 ) -> Result<WorkflowRecord> {
23 let now = timestamp_now();
24 let run_id = format!("run-{workflow_id}-{}", now as u64);
25
26 let stamped_attrs = inject_engine_version(search_attributes);
35
36 let wf = WorkflowRecord {
37 id: workflow_id.to_string(),
38 namespace: namespace.to_string(),
39 run_id,
40 workflow_type: workflow_type.to_string(),
41 task_queue: task_queue.to_string(),
42 status: "PENDING".to_string(),
43 input: input.map(String::from),
44 result: None,
45 error: None,
46 parent_id: None,
47 claimed_by: None,
48 search_attributes: stamped_attrs,
49 archived_at: None,
50 archive_uri: None,
51 created_at: now,
52 updated_at: now,
53 completed_at: None,
54 };
55
56 self.store.create_workflow(&wf).await?;
57
58 self.store
59 .append_event(&WorkflowEvent {
60 id: None,
61 workflow_id: workflow_id.to_string(),
62 seq: 1,
63 event_type: "WorkflowStarted".to_string(),
64 payload: input.map(String::from),
65 timestamp: now,
66 })
67 .await?;
68
69 self.mark_and_emit_needs_dispatch(workflow_id).await?;
72
73 self.emit(
77 namespace,
78 WorkflowBusEvent::WorkflowStarted {
79 workflow_id: workflow_id.to_string(),
80 },
81 )
82 .await;
83
84 Ok(wf)
85 }
86
87 pub async fn get_workflow(&self, id: &str) -> Result<Option<WorkflowRecord>> {
88 self.store.get_workflow(id).await
89 }
90
91 pub async fn list_workflows(
92 &self,
93 namespace: &str,
94 status: Option<WorkflowStatus>,
95 workflow_type: Option<&str>,
96 search_attrs_filter: Option<&str>,
97 limit: i64,
98 offset: i64,
99 ) -> Result<Vec<WorkflowRecord>> {
100 self.store
101 .list_workflows(
102 namespace,
103 status,
104 workflow_type,
105 search_attrs_filter,
106 limit,
107 offset,
108 )
109 .await
110 }
111
112 pub async fn upsert_search_attributes(
113 &self,
114 workflow_id: &str,
115 patch_json: &str,
116 ) -> Result<()> {
117 self.store
118 .upsert_search_attributes(workflow_id, patch_json)
119 .await
120 }
121
122 pub async fn cancel_workflow(&self, id: &str, reason: Option<&str>) -> Result<bool> {
123 let wf = self.store.get_workflow(id).await?;
124 match wf {
125 None => Ok(false),
126 Some(wf) => {
127 let status = WorkflowStatus::from_str(&wf.status)
128 .map_err(|e| anyhow::anyhow!(e))?;
129 if status.is_terminal() {
130 return Ok(false);
131 }
132
133 self.store.cancel_pending_activities(id).await?;
145 self.store.cancel_pending_timers(id).await?;
146
147 let payload = reason.map(|r| {
152 serde_json::json!({ "reason": r }).to_string()
153 });
154
155 let seq = self.store.get_event_count(id).await? as i32 + 1;
156 self.store
157 .append_event(&WorkflowEvent {
158 id: None,
159 workflow_id: id.to_string(),
160 seq,
161 event_type: "WorkflowCancelRequested".to_string(),
162 payload,
163 timestamp: timestamp_now(),
164 })
165 .await?;
166
167 self.mark_and_emit_needs_dispatch(id).await?;
168
169 let children = self.store.list_child_workflows(id).await?;
173 for child in children {
174 Box::pin(self.cancel_workflow(&child.id, reason)).await?;
175 }
176
177 if matches!(status, WorkflowStatus::Pending) {
183 self.finalise_cancellation(id).await?;
184 }
185
186 Ok(true)
187 }
188 }
189 }
190
191 pub async fn terminate_workflow(&self, id: &str, reason: Option<&str>) -> Result<bool> {
192 let wf = self.store.get_workflow(id).await?;
193 match wf {
194 None => Ok(false),
195 Some(wf) => {
196 let status = WorkflowStatus::from_str(&wf.status)
197 .map_err(|e| anyhow::anyhow!(e))?;
198 if status.is_terminal() {
199 return Ok(false);
200 }
201
202 self.store
203 .update_workflow_status(
204 id,
205 WorkflowStatus::Failed,
206 None,
207 Some(reason.unwrap_or("terminated")),
208 )
209 .await?;
210
211 self.emit(
214 &wf.namespace,
215 WorkflowBusEvent::WorkflowTerminated {
216 workflow_id: id.to_string(),
217 },
218 )
219 .await;
220
221 Ok(true)
222 }
223 }
224 }
225
226 pub async fn finalise_cancellation(&self, workflow_id: &str) -> Result<()> {
231 if let Some(wf) = self.store.get_workflow(workflow_id).await?
233 && wf.status == "CANCELLED"
234 {
235 return Ok(());
236 }
237 self.store
238 .update_workflow_status(workflow_id, WorkflowStatus::Cancelled, None, None)
239 .await?;
240 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
241 self.store
242 .append_event(&WorkflowEvent {
243 id: None,
244 workflow_id: workflow_id.to_string(),
245 seq: event_seq,
246 event_type: "WorkflowCancelled".to_string(),
247 payload: None,
248 timestamp: timestamp_now(),
249 })
250 .await?;
251 let ns = self
252 .store
253 .get_workflow(workflow_id)
254 .await?
255 .map(|w| w.namespace)
256 .unwrap_or_default();
257 self.emit(
258 &ns,
259 WorkflowBusEvent::WorkflowCancelled {
260 workflow_id: workflow_id.to_string(),
261 },
262 )
263 .await;
264 Ok(())
265 }
266
267 pub async fn complete_workflow(&self, workflow_id: &str, result: Option<&str>) -> Result<()> {
272 self.store
273 .update_workflow_status(workflow_id, WorkflowStatus::Completed, result, None)
274 .await?;
275 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
276 self.store
277 .append_event(&WorkflowEvent {
278 id: None,
279 workflow_id: workflow_id.to_string(),
280 seq: event_seq,
281 event_type: "WorkflowCompleted".to_string(),
282 payload: result.map(String::from),
283 timestamp: timestamp_now(),
284 })
285 .await?;
286 self.notify_parent_of_child_outcome(
287 workflow_id,
288 "ChildWorkflowCompleted",
289 serde_json::json!({
290 "child_workflow_id": workflow_id,
291 "result": result.and_then(|s| serde_json::from_str::<serde_json::Value>(s).ok()),
292 }),
293 )
294 .await?;
295 let ns = self
296 .store
297 .get_workflow(workflow_id)
298 .await?
299 .map(|w| w.namespace)
300 .unwrap_or_default();
301 self.emit(
302 &ns,
303 WorkflowBusEvent::WorkflowCompleted {
304 workflow_id: workflow_id.to_string(),
305 },
306 )
307 .await;
308 Ok(())
309 }
310
311 pub async fn fail_workflow(&self, workflow_id: &str, error: &str) -> Result<()> {
314 self.store
315 .update_workflow_status(workflow_id, WorkflowStatus::Failed, None, Some(error))
316 .await?;
317 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
318 self.store
319 .append_event(&WorkflowEvent {
320 id: None,
321 workflow_id: workflow_id.to_string(),
322 seq: event_seq,
323 event_type: "WorkflowFailed".to_string(),
324 payload: Some(serde_json::json!({"error": error}).to_string()),
325 timestamp: timestamp_now(),
326 })
327 .await?;
328 self.notify_parent_of_child_outcome(
329 workflow_id,
330 "ChildWorkflowFailed",
331 serde_json::json!({
332 "child_workflow_id": workflow_id,
333 "error": error,
334 }),
335 )
336 .await?;
337 let ns = self
338 .store
339 .get_workflow(workflow_id)
340 .await?
341 .map(|w| w.namespace)
342 .unwrap_or_default();
343 self.emit(
344 &ns,
345 WorkflowBusEvent::WorkflowFailed {
346 workflow_id: workflow_id.to_string(),
347 },
348 )
349 .await;
350 Ok(())
351 }
352
353 async fn notify_parent_of_child_outcome(
357 &self,
358 child_workflow_id: &str,
359 event_type: &str,
360 payload: serde_json::Value,
361 ) -> Result<()> {
362 let Some(child) = self.store.get_workflow(child_workflow_id).await? else {
363 return Ok(());
364 };
365 let Some(parent_id) = child.parent_id else {
366 return Ok(());
367 };
368 let event_seq = self.store.get_event_count(&parent_id).await? as i32 + 1;
369 self.store
370 .append_event(&WorkflowEvent {
371 id: None,
372 workflow_id: parent_id.clone(),
373 seq: event_seq,
374 event_type: event_type.to_string(),
375 payload: Some(payload.to_string()),
376 timestamp: timestamp_now(),
377 })
378 .await?;
379 self.mark_and_emit_needs_dispatch(&parent_id).await?;
380 Ok(())
381 }
382}