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?;
73
74 self.emit(
78 namespace,
79 WorkflowBusEvent::WorkflowStarted {
80 workflow_id: workflow_id.to_string(),
81 },
82 )
83 .await;
84
85 Ok(wf)
86 }
87
88 pub async fn get_workflow(&self, id: &str) -> Result<Option<WorkflowRecord>> {
89 self.store.get_workflow(id).await
90 }
91
92 pub async fn list_workflows(
93 &self,
94 namespace: &str,
95 status: Option<WorkflowStatus>,
96 workflow_type: Option<&str>,
97 search_attrs_filter: Option<&str>,
98 limit: i64,
99 offset: i64,
100 ) -> Result<Vec<WorkflowRecord>> {
101 self.store
102 .list_workflows(
103 namespace,
104 status,
105 workflow_type,
106 search_attrs_filter,
107 limit,
108 offset,
109 )
110 .await
111 }
112
113 pub async fn upsert_search_attributes(
114 &self,
115 workflow_id: &str,
116 patch_json: &str,
117 ) -> Result<()> {
118 self.store
119 .upsert_search_attributes(workflow_id, patch_json)
120 .await
121 }
122
123 pub async fn cancel_workflow(&self, id: &str, reason: Option<&str>) -> Result<bool> {
124 let wf = self.store.get_workflow(id).await?;
125 match wf {
126 None => Ok(false),
127 Some(wf) => {
128 let status = WorkflowStatus::from_str(&wf.status)
129 .map_err(|e| anyhow::anyhow!(e))?;
130 if status.is_terminal() {
131 return Ok(false);
132 }
133
134 self.store.cancel_pending_activities(id).await?;
146 self.store.cancel_pending_timers(id).await?;
147
148 let payload = reason.map(|r| {
153 serde_json::json!({ "reason": r }).to_string()
154 });
155
156 let seq = self.store.get_event_count(id).await? as i32 + 1;
157 self.store
158 .append_event(&WorkflowEvent {
159 id: None,
160 workflow_id: id.to_string(),
161 seq,
162 event_type: "WorkflowCancelRequested".to_string(),
163 payload,
164 timestamp: timestamp_now(),
165 })
166 .await?;
167
168 self.mark_and_emit_needs_dispatch(id).await?;
169
170 let children = self.store.list_child_workflows(id).await?;
174 for child in children {
175 Box::pin(self.cancel_workflow(&child.id, reason)).await?;
176 }
177
178 if matches!(status, WorkflowStatus::Pending) {
184 self.finalise_cancellation(id).await?;
185 }
186
187 Ok(true)
188 }
189 }
190 }
191
192 pub async fn terminate_workflow(&self, id: &str, reason: Option<&str>) -> Result<bool> {
193 let wf = self.store.get_workflow(id).await?;
194 match wf {
195 None => Ok(false),
196 Some(wf) => {
197 let status = WorkflowStatus::from_str(&wf.status)
198 .map_err(|e| anyhow::anyhow!(e))?;
199 if status.is_terminal() {
200 return Ok(false);
201 }
202
203 self.store
204 .update_workflow_status(
205 id,
206 WorkflowStatus::Failed,
207 None,
208 Some(reason.unwrap_or("terminated")),
209 )
210 .await?;
211
212 self.emit(
215 &wf.namespace,
216 WorkflowBusEvent::WorkflowTerminated {
217 workflow_id: id.to_string(),
218 },
219 )
220 .await;
221
222 Ok(true)
223 }
224 }
225 }
226
227 pub async fn finalise_cancellation(&self, workflow_id: &str) -> Result<()> {
232 if let Some(wf) = self.store.get_workflow(workflow_id).await?
234 && wf.status == "CANCELLED"
235 {
236 return Ok(());
237 }
238 self.store
239 .update_workflow_status(workflow_id, WorkflowStatus::Cancelled, None, None)
240 .await?;
241 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
242 self.store
243 .append_event(&WorkflowEvent {
244 id: None,
245 workflow_id: workflow_id.to_string(),
246 seq: event_seq,
247 event_type: "WorkflowCancelled".to_string(),
248 payload: None,
249 timestamp: timestamp_now(),
250 })
251 .await?;
252 let ns = self
253 .store
254 .get_workflow(workflow_id)
255 .await?
256 .map(|w| w.namespace)
257 .unwrap_or_default();
258 self.emit(
259 &ns,
260 WorkflowBusEvent::WorkflowCancelled {
261 workflow_id: workflow_id.to_string(),
262 },
263 )
264 .await;
265 Ok(())
266 }
267
268 pub async fn complete_workflow(&self, workflow_id: &str, result: Option<&str>) -> Result<()> {
273 self.store
274 .update_workflow_status(workflow_id, WorkflowStatus::Completed, result, None)
275 .await?;
276 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
277 self.store
278 .append_event(&WorkflowEvent {
279 id: None,
280 workflow_id: workflow_id.to_string(),
281 seq: event_seq,
282 event_type: "WorkflowCompleted".to_string(),
283 payload: result.map(String::from),
284 timestamp: timestamp_now(),
285 })
286 .await?;
287 self.notify_parent_of_child_outcome(
288 workflow_id,
289 "ChildWorkflowCompleted",
290 serde_json::json!({
291 "child_workflow_id": workflow_id,
292 "result": result.and_then(|s| serde_json::from_str::<serde_json::Value>(s).ok()),
293 }),
294 )
295 .await?;
296 let ns = self
297 .store
298 .get_workflow(workflow_id)
299 .await?
300 .map(|w| w.namespace)
301 .unwrap_or_default();
302 self.emit(
303 &ns,
304 WorkflowBusEvent::WorkflowCompleted {
305 workflow_id: workflow_id.to_string(),
306 },
307 )
308 .await;
309 Ok(())
310 }
311
312 pub async fn fail_workflow(&self, workflow_id: &str, error: &str) -> Result<()> {
315 self.store
316 .update_workflow_status(workflow_id, WorkflowStatus::Failed, None, Some(error))
317 .await?;
318 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
319 self.store
320 .append_event(&WorkflowEvent {
321 id: None,
322 workflow_id: workflow_id.to_string(),
323 seq: event_seq,
324 event_type: "WorkflowFailed".to_string(),
325 payload: Some(serde_json::json!({"error": error}).to_string()),
326 timestamp: timestamp_now(),
327 })
328 .await?;
329 self.notify_parent_of_child_outcome(
330 workflow_id,
331 "ChildWorkflowFailed",
332 serde_json::json!({
333 "child_workflow_id": workflow_id,
334 "error": error,
335 }),
336 )
337 .await?;
338 let ns = self
339 .store
340 .get_workflow(workflow_id)
341 .await?
342 .map(|w| w.namespace)
343 .unwrap_or_default();
344 self.emit(
345 &ns,
346 WorkflowBusEvent::WorkflowFailed {
347 workflow_id: workflow_id.to_string(),
348 },
349 )
350 .await;
351 Ok(())
352 }
353
354 async fn notify_parent_of_child_outcome(
358 &self,
359 child_workflow_id: &str,
360 event_type: &str,
361 payload: serde_json::Value,
362 ) -> Result<()> {
363 let Some(child) = self.store.get_workflow(child_workflow_id).await? else {
364 return Ok(());
365 };
366 let Some(parent_id) = child.parent_id else {
367 return Ok(());
368 };
369 let event_seq = self.store.get_event_count(&parent_id).await? as i32 + 1;
370 self.store
371 .append_event(&WorkflowEvent {
372 id: None,
373 workflow_id: parent_id.clone(),
374 seq: event_seq,
375 event_type: event_type.to_string(),
376 payload: Some(payload.to_string()),
377 timestamp: timestamp_now(),
378 })
379 .await?;
380 self.mark_and_emit_needs_dispatch(&parent_id).await?;
381 Ok(())
382 }
383}