1use std::str::FromStr;
5
6use anyhow::Result;
7
8use crate::ctx::{WorkflowCtx, inject_engine_version, timestamp_now};
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 =
128 WorkflowStatus::from_str(&wf.status).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| serde_json::json!({ "reason": r }).to_string());
152
153 let seq = self.store.get_event_count(id).await? as i32 + 1;
154 self.store
155 .append_event(&WorkflowEvent {
156 id: None,
157 workflow_id: id.to_string(),
158 seq,
159 event_type: "WorkflowCancelRequested".to_string(),
160 payload,
161 timestamp: timestamp_now(),
162 })
163 .await?;
164
165 self.mark_and_emit_needs_dispatch(id).await?;
166
167 let children = self.store.list_child_workflows(id).await?;
171 for child in children {
172 Box::pin(self.cancel_workflow(&child.id, reason)).await?;
173 }
174
175 if matches!(status, WorkflowStatus::Pending) {
181 self.finalise_cancellation(id).await?;
182 }
183
184 Ok(true)
185 }
186 }
187 }
188
189 pub async fn terminate_workflow(&self, id: &str, reason: Option<&str>) -> Result<bool> {
190 let wf = self.store.get_workflow(id).await?;
191 match wf {
192 None => Ok(false),
193 Some(wf) => {
194 let status =
195 WorkflowStatus::from_str(&wf.status).map_err(|e| anyhow::anyhow!(e))?;
196 if status.is_terminal() {
197 return Ok(false);
198 }
199
200 self.store
201 .update_workflow_status(
202 id,
203 WorkflowStatus::Failed,
204 None,
205 Some(reason.unwrap_or("terminated")),
206 )
207 .await?;
208
209 self.emit(
212 &wf.namespace,
213 WorkflowBusEvent::WorkflowTerminated {
214 workflow_id: id.to_string(),
215 },
216 )
217 .await;
218
219 Ok(true)
220 }
221 }
222 }
223
224 pub async fn finalise_cancellation(&self, workflow_id: &str) -> Result<()> {
229 if let Some(wf) = self.store.get_workflow(workflow_id).await?
231 && wf.status == "CANCELLED"
232 {
233 return Ok(());
234 }
235 self.store
236 .update_workflow_status(workflow_id, WorkflowStatus::Cancelled, None, None)
237 .await?;
238 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
239 self.store
240 .append_event(&WorkflowEvent {
241 id: None,
242 workflow_id: workflow_id.to_string(),
243 seq: event_seq,
244 event_type: "WorkflowCancelled".to_string(),
245 payload: None,
246 timestamp: timestamp_now(),
247 })
248 .await?;
249 let ns = self
250 .store
251 .get_workflow(workflow_id)
252 .await?
253 .map(|w| w.namespace)
254 .unwrap_or_default();
255 self.emit(
256 &ns,
257 WorkflowBusEvent::WorkflowCancelled {
258 workflow_id: workflow_id.to_string(),
259 },
260 )
261 .await;
262 Ok(())
263 }
264
265 pub async fn complete_workflow(&self, workflow_id: &str, result: Option<&str>) -> Result<()> {
270 self.store
271 .update_workflow_status(workflow_id, WorkflowStatus::Completed, result, None)
272 .await?;
273 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
274 self.store
275 .append_event(&WorkflowEvent {
276 id: None,
277 workflow_id: workflow_id.to_string(),
278 seq: event_seq,
279 event_type: "WorkflowCompleted".to_string(),
280 payload: result.map(String::from),
281 timestamp: timestamp_now(),
282 })
283 .await?;
284 self.notify_parent_of_child_outcome(
285 workflow_id,
286 "ChildWorkflowCompleted",
287 serde_json::json!({
288 "child_workflow_id": workflow_id,
289 "result": result.and_then(|s| serde_json::from_str::<serde_json::Value>(s).ok()),
290 }),
291 )
292 .await?;
293 let ns = self
294 .store
295 .get_workflow(workflow_id)
296 .await?
297 .map(|w| w.namespace)
298 .unwrap_or_default();
299 self.emit(
300 &ns,
301 WorkflowBusEvent::WorkflowCompleted {
302 workflow_id: workflow_id.to_string(),
303 },
304 )
305 .await;
306 Ok(())
307 }
308
309 pub async fn fail_workflow(&self, workflow_id: &str, error: &str) -> Result<()> {
312 self.store
313 .update_workflow_status(workflow_id, WorkflowStatus::Failed, None, Some(error))
314 .await?;
315 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
316 self.store
317 .append_event(&WorkflowEvent {
318 id: None,
319 workflow_id: workflow_id.to_string(),
320 seq: event_seq,
321 event_type: "WorkflowFailed".to_string(),
322 payload: Some(serde_json::json!({"error": error}).to_string()),
323 timestamp: timestamp_now(),
324 })
325 .await?;
326 self.notify_parent_of_child_outcome(
327 workflow_id,
328 "ChildWorkflowFailed",
329 serde_json::json!({
330 "child_workflow_id": workflow_id,
331 "error": error,
332 }),
333 )
334 .await?;
335 let ns = self
336 .store
337 .get_workflow(workflow_id)
338 .await?
339 .map(|w| w.namespace)
340 .unwrap_or_default();
341 self.emit(
342 &ns,
343 WorkflowBusEvent::WorkflowFailed {
344 workflow_id: workflow_id.to_string(),
345 },
346 )
347 .await;
348 Ok(())
349 }
350
351 async fn notify_parent_of_child_outcome(
355 &self,
356 child_workflow_id: &str,
357 event_type: &str,
358 payload: serde_json::Value,
359 ) -> Result<()> {
360 let Some(child) = self.store.get_workflow(child_workflow_id).await? else {
361 return Ok(());
362 };
363 let Some(parent_id) = child.parent_id else {
364 return Ok(());
365 };
366 let event_seq = self.store.get_event_count(&parent_id).await? as i32 + 1;
367 self.store
368 .append_event(&WorkflowEvent {
369 id: None,
370 workflow_id: parent_id.clone(),
371 seq: event_seq,
372 event_type: event_type.to_string(),
373 payload: Some(payload.to_string()),
374 timestamp: timestamp_now(),
375 })
376 .await?;
377 self.mark_and_emit_needs_dispatch(&parent_id).await?;
378 Ok(())
379 }
380}