feat(wfe-deno): Deno bindings for the WFE workflow engine
This commit is contained in:
312
wfe-deno/src/ops/builder.rs
Normal file
312
wfe-deno/src/ops/builder.rs
Normal file
@@ -0,0 +1,312 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use wfe_core::builder::WorkflowBuilder;
|
||||
use wfe_core::models::ErrorBehavior;
|
||||
|
||||
use crate::state::WfeState;
|
||||
|
||||
/// Internal builder state that tracks the WorkflowBuilder and current step.
|
||||
///
|
||||
/// We don't use StepBuilder (pub(crate)) — instead we work directly with
|
||||
/// WorkflowBuilder's public `steps` field, `add_step`, and `wire_outcome`.
|
||||
pub struct JsBuilderState {
|
||||
pub wb: WorkflowBuilder<serde_json::Value>,
|
||||
pub current_step: Option<usize>,
|
||||
}
|
||||
|
||||
/// Create a new WorkflowBuilder and return its handle.
|
||||
#[op2(fast)]
|
||||
#[smi]
|
||||
pub fn op_builder_create(state: &mut OpState) -> u32 {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let id = wfe.alloc_builder_id();
|
||||
wfe.builders.insert(
|
||||
id,
|
||||
JsBuilderState {
|
||||
wb: WorkflowBuilder::<serde_json::Value>::new(),
|
||||
current_step: None,
|
||||
},
|
||||
);
|
||||
id
|
||||
}
|
||||
|
||||
/// Add the first step via `start_with`.
|
||||
#[op2(fast)]
|
||||
pub fn op_builder_start_with(
|
||||
state: &mut OpState,
|
||||
#[smi] handle: u32,
|
||||
#[string] step_type: String,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let bs = get_builder(wfe, handle)?;
|
||||
|
||||
if bs.current_step.is_some() {
|
||||
return Err(deno_error::JsErrorBox::generic(
|
||||
"start_with already called on this builder",
|
||||
));
|
||||
}
|
||||
|
||||
let step_id = bs.wb.add_step(&step_type);
|
||||
bs.current_step = Some(step_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Chain the next step via `then`.
|
||||
#[op2(fast)]
|
||||
pub fn op_builder_then(
|
||||
state: &mut OpState,
|
||||
#[smi] handle: u32,
|
||||
#[string] step_type: String,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let bs = get_builder(wfe, handle)?;
|
||||
|
||||
let prev_id = bs
|
||||
.current_step
|
||||
.ok_or_else(|| deno_error::JsErrorBox::generic("call start_with before then"))?;
|
||||
|
||||
let next_id = bs.wb.add_step(&step_type);
|
||||
bs.wb.wire_outcome(prev_id, next_id, None);
|
||||
bs.current_step = Some(next_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the current step's name.
|
||||
#[op2(fast)]
|
||||
pub fn op_builder_name(
|
||||
state: &mut OpState,
|
||||
#[smi] handle: u32,
|
||||
#[string] name: String,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let bs = get_builder(wfe, handle)?;
|
||||
let step_id = current_step(bs)?;
|
||||
bs.wb.steps[step_id].name = Some(name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the current step's JSON config.
|
||||
#[op2]
|
||||
pub fn op_builder_config(
|
||||
state: &mut OpState,
|
||||
#[smi] handle: u32,
|
||||
#[serde] config: serde_json::Value,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let bs = get_builder(wfe, handle)?;
|
||||
let step_id = current_step(bs)?;
|
||||
bs.wb.steps[step_id].step_config = Some(config);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the current step's error behavior.
|
||||
#[op2]
|
||||
pub fn op_builder_on_error(
|
||||
state: &mut OpState,
|
||||
#[smi] handle: u32,
|
||||
#[serde] behavior: serde_json::Value,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let bs = get_builder(wfe, handle)?;
|
||||
let step_id = current_step(bs)?;
|
||||
let eb = parse_error_behavior(&behavior)?;
|
||||
bs.wb.steps[step_id].error_behavior = Some(eb);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Chain a delay step after the current step.
|
||||
#[op2(fast)]
|
||||
pub fn op_builder_delay(
|
||||
state: &mut OpState,
|
||||
#[smi] handle: u32,
|
||||
#[number] ms: u64,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let bs = get_builder(wfe, handle)?;
|
||||
let prev_id = current_step(bs)?;
|
||||
|
||||
let delay_type = std::any::type_name::<wfe_core::primitives::delay::DelayStep>();
|
||||
let next_id = bs.wb.add_step(delay_type);
|
||||
bs.wb.steps[next_id].step_config = Some(serde_json::json!({
|
||||
"duration_millis": ms,
|
||||
}));
|
||||
bs.wb.wire_outcome(prev_id, next_id, None);
|
||||
bs.current_step = Some(next_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Chain a wait-for-event step after the current step.
|
||||
#[op2(fast)]
|
||||
pub fn op_builder_wait_for(
|
||||
state: &mut OpState,
|
||||
#[smi] handle: u32,
|
||||
#[string] event_name: String,
|
||||
#[string] event_key: String,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let bs = get_builder(wfe, handle)?;
|
||||
let prev_id = current_step(bs)?;
|
||||
|
||||
let wait_type = std::any::type_name::<wfe_core::primitives::wait_for::WaitForStep>();
|
||||
let next_id = bs.wb.add_step(wait_type);
|
||||
bs.wb.steps[next_id].step_config = Some(serde_json::json!({
|
||||
"event_name": event_name,
|
||||
"event_key": event_key,
|
||||
}));
|
||||
bs.wb.wire_outcome(prev_id, next_id, None);
|
||||
bs.current_step = Some(next_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build the workflow definition and return it as JSON. Consumes the builder.
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_builder_build(
|
||||
state: &mut OpState,
|
||||
#[smi] handle: u32,
|
||||
#[string] id: String,
|
||||
#[smi] version: u32,
|
||||
) -> Result<serde_json::Value, deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let bs = wfe
|
||||
.builders
|
||||
.remove(&handle)
|
||||
.ok_or_else(|| deno_error::JsErrorBox::generic("invalid builder handle"))?;
|
||||
|
||||
let def = bs.wb.build(&id, version);
|
||||
serde_json::to_value(&def)
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("serialization failed: {e}")))
|
||||
}
|
||||
|
||||
/// Build the workflow definition and register it with the host. Consumes the builder.
|
||||
#[op2]
|
||||
pub async fn op_builder_register(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
#[smi] handle: u32,
|
||||
#[string] id: String,
|
||||
#[smi] version: u32,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let (def, host) = {
|
||||
let mut s = state.borrow_mut();
|
||||
let wfe = s.borrow_mut::<WfeState>();
|
||||
let bs = wfe
|
||||
.builders
|
||||
.remove(&handle)
|
||||
.ok_or_else(|| deno_error::JsErrorBox::generic("invalid builder handle"))?;
|
||||
let def = bs.wb.build(&id, version);
|
||||
let host = wfe.host()?.clone();
|
||||
(def, host)
|
||||
};
|
||||
|
||||
host.register_workflow_definition(def).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_builder(
|
||||
wfe: &mut WfeState,
|
||||
handle: u32,
|
||||
) -> Result<&mut JsBuilderState, deno_error::JsErrorBox> {
|
||||
wfe.builders
|
||||
.get_mut(&handle)
|
||||
.ok_or_else(|| deno_error::JsErrorBox::generic("invalid builder handle"))
|
||||
}
|
||||
|
||||
fn current_step(bs: &JsBuilderState) -> Result<usize, deno_error::JsErrorBox> {
|
||||
bs.current_step
|
||||
.ok_or_else(|| deno_error::JsErrorBox::generic("no current step — call start_with first"))
|
||||
}
|
||||
|
||||
fn parse_error_behavior(
|
||||
value: &serde_json::Value,
|
||||
) -> Result<ErrorBehavior, deno_error::JsErrorBox> {
|
||||
match value.as_str() {
|
||||
Some("suspend") => Ok(ErrorBehavior::Suspend),
|
||||
Some("terminate") => Ok(ErrorBehavior::Terminate),
|
||||
Some("compensate") => Ok(ErrorBehavior::Compensate),
|
||||
_ => {
|
||||
if let Some(retry) = value.get("retry") {
|
||||
let interval = retry
|
||||
.get("interval")
|
||||
.and_then(|v| v.as_u64())
|
||||
.unwrap_or(60_000);
|
||||
let max_retries = retry
|
||||
.get("maxRetries")
|
||||
.and_then(|v| v.as_u64())
|
||||
.unwrap_or(3) as u32;
|
||||
Ok(ErrorBehavior::Retry {
|
||||
interval: Duration::from_millis(interval),
|
||||
max_retries,
|
||||
})
|
||||
} else {
|
||||
Err(deno_error::JsErrorBox::generic(format!(
|
||||
"invalid error behavior: {value}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn parse_suspend_behavior() {
|
||||
let eb = parse_error_behavior(&serde_json::json!("suspend")).unwrap();
|
||||
assert!(matches!(eb, ErrorBehavior::Suspend));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_terminate_behavior() {
|
||||
let eb = parse_error_behavior(&serde_json::json!("terminate")).unwrap();
|
||||
assert!(matches!(eb, ErrorBehavior::Terminate));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_compensate_behavior() {
|
||||
let eb = parse_error_behavior(&serde_json::json!("compensate")).unwrap();
|
||||
assert!(matches!(eb, ErrorBehavior::Compensate));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_retry_behavior() {
|
||||
let eb = parse_error_behavior(
|
||||
&serde_json::json!({"retry": {"interval": 5000, "maxRetries": 5}}),
|
||||
)
|
||||
.unwrap();
|
||||
match eb {
|
||||
ErrorBehavior::Retry {
|
||||
interval,
|
||||
max_retries,
|
||||
} => {
|
||||
assert_eq!(interval, Duration::from_millis(5000));
|
||||
assert_eq!(max_retries, 5);
|
||||
}
|
||||
_ => panic!("expected Retry"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_retry_behavior_defaults() {
|
||||
let eb = parse_error_behavior(&serde_json::json!({"retry": {}})).unwrap();
|
||||
match eb {
|
||||
ErrorBehavior::Retry {
|
||||
interval,
|
||||
max_retries,
|
||||
} => {
|
||||
assert_eq!(interval, Duration::from_millis(60_000));
|
||||
assert_eq!(max_retries, 3);
|
||||
}
|
||||
_ => panic!("expected Retry"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_invalid_behavior_returns_error() {
|
||||
let result = parse_error_behavior(&serde_json::json!("nonsense"));
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
22
wfe-deno/src/ops/event.rs
Normal file
22
wfe-deno/src/ops/event.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
|
||||
use crate::state::WfeState;
|
||||
|
||||
/// Publish an event to the workflow host for matching subscriptions.
|
||||
#[op2]
|
||||
pub async fn op_publish_event(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
#[string] event_name: String,
|
||||
#[string] event_key: String,
|
||||
#[serde] data: serde_json::Value,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let host = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
wfe.host()?.clone()
|
||||
};
|
||||
host.publish_event(&event_name, &event_key, data)
|
||||
.await
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("publish_event failed: {e}")))
|
||||
}
|
||||
62
wfe-deno/src/ops/host.rs
Normal file
62
wfe-deno/src/ops/host.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
|
||||
use crate::state::WfeState;
|
||||
|
||||
/// Create a WorkflowHost with in-memory providers and store it in state.
|
||||
#[op2(fast)]
|
||||
pub fn op_host_create(state: &mut OpState) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
if wfe.host.is_some() {
|
||||
return Err(deno_error::JsErrorBox::generic(
|
||||
"WorkflowHost already created",
|
||||
));
|
||||
}
|
||||
|
||||
let persistence = Arc::new(wfe_core::test_support::InMemoryPersistenceProvider::new());
|
||||
let lock = Arc::new(wfe_core::test_support::InMemoryLockProvider::new());
|
||||
let queue = Arc::new(wfe_core::test_support::InMemoryQueueProvider::new());
|
||||
let lifecycle = Arc::new(wfe_core::test_support::InMemoryLifecyclePublisher::new());
|
||||
|
||||
let host = wfe::WorkflowHostBuilder::new()
|
||||
.use_persistence(persistence)
|
||||
.use_lock_provider(lock)
|
||||
.use_queue_provider(queue)
|
||||
.use_lifecycle(lifecycle)
|
||||
.build()
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("Failed to build host: {e}")))?;
|
||||
|
||||
wfe.host = Some(Arc::new(host));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start the WorkflowHost background tasks.
|
||||
#[op2]
|
||||
pub async fn op_host_start(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let host = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
wfe.host()?.clone()
|
||||
};
|
||||
host.start()
|
||||
.await
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("Failed to start host: {e}")))
|
||||
}
|
||||
|
||||
/// Stop the WorkflowHost gracefully.
|
||||
#[op2]
|
||||
pub async fn op_host_stop(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let host = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
wfe.host()?.clone()
|
||||
};
|
||||
host.stop().await;
|
||||
Ok(())
|
||||
}
|
||||
43
wfe-deno/src/ops/mod.rs
Normal file
43
wfe-deno/src/ops/mod.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
pub mod builder;
|
||||
pub mod event;
|
||||
pub mod host;
|
||||
pub mod step;
|
||||
pub mod workflow;
|
||||
|
||||
deno_core::extension!(
|
||||
wfe_deno_ext,
|
||||
ops = [
|
||||
// Host lifecycle
|
||||
host::op_host_create,
|
||||
host::op_host_start,
|
||||
host::op_host_stop,
|
||||
// Workflow management
|
||||
workflow::op_start_workflow,
|
||||
workflow::op_suspend_workflow,
|
||||
workflow::op_resume_workflow,
|
||||
workflow::op_terminate_workflow,
|
||||
workflow::op_get_workflow,
|
||||
// Builder
|
||||
builder::op_builder_create,
|
||||
builder::op_builder_start_with,
|
||||
builder::op_builder_then,
|
||||
builder::op_builder_name,
|
||||
builder::op_builder_config,
|
||||
builder::op_builder_on_error,
|
||||
builder::op_builder_delay,
|
||||
builder::op_builder_wait_for,
|
||||
builder::op_builder_build,
|
||||
builder::op_builder_register,
|
||||
// Step execution bridge
|
||||
step::op_register_step,
|
||||
step::op_step_executor_poll,
|
||||
step::op_step_executor_respond,
|
||||
// Events
|
||||
event::op_publish_event,
|
||||
],
|
||||
esm_entry_point = "ext:wfe-deno/bootstrap.js",
|
||||
esm = [
|
||||
"ext:wfe-deno/bootstrap.js" = "src/js/bootstrap.js",
|
||||
"ext:wfe-deno/wfe.js" = "src/js/wfe.js",
|
||||
],
|
||||
);
|
||||
107
wfe-deno/src/ops/step.rs
Normal file
107
wfe-deno/src/ops/step.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
|
||||
use crate::bridge::JsStepBody;
|
||||
use crate::state::WfeState;
|
||||
|
||||
/// Register a JS step type with the host.
|
||||
///
|
||||
/// On the Rust side this creates a factory that produces `JsStepBody` instances.
|
||||
/// On the JS side the caller also registers the actual function via
|
||||
/// `__wfe_registerStepFunction(stepType, fn)`.
|
||||
#[op2]
|
||||
pub async fn op_register_step(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
#[string] step_type: String,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let (host, tx) = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
(wfe.host()?.clone(), wfe.step_request_tx.clone())
|
||||
};
|
||||
let counter = Arc::new(std::sync::atomic::AtomicU32::new(0));
|
||||
|
||||
host.register_step_factory(
|
||||
&step_type,
|
||||
move || Box::new(JsStepBody::new(tx.clone(), counter.clone())),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Poll for a step execution request from the Rust executor.
|
||||
///
|
||||
/// This is an async op that blocks until a step needs to be executed.
|
||||
/// Returns `{ requestId, stepType, context }` or null on shutdown.
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub async fn op_step_executor_poll(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
) -> Result<serde_json::Value, deno_error::JsErrorBox> {
|
||||
// Take the receiver out of state (only the first call gets it).
|
||||
let mut rx = {
|
||||
let mut s = state.borrow_mut();
|
||||
let wfe = s.borrow_mut::<WfeState>();
|
||||
match wfe.step_request_rx.take() {
|
||||
Some(rx) => rx,
|
||||
None => {
|
||||
return Err(deno_error::JsErrorBox::generic(
|
||||
"step executor poll already active",
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Wait for the next request.
|
||||
match rx.recv().await {
|
||||
Some(req) => {
|
||||
let request_id = req.request_id;
|
||||
let result = serde_json::json!({
|
||||
"requestId": request_id,
|
||||
"stepType": req.step_type,
|
||||
"context": req.context,
|
||||
});
|
||||
|
||||
// Store the response sender for op_step_executor_respond.
|
||||
{
|
||||
let mut s = state.borrow_mut();
|
||||
let wfe = s.borrow_mut::<WfeState>();
|
||||
wfe.inflight.insert(request_id, req.response_tx);
|
||||
// Put the receiver back so we can poll again.
|
||||
wfe.step_request_rx = Some(rx);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
None => {
|
||||
// Channel closed — shutdown.
|
||||
Ok(serde_json::Value::Null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a step execution result back to the Rust executor.
|
||||
#[op2]
|
||||
pub fn op_step_executor_respond(
|
||||
state: &mut OpState,
|
||||
#[smi] request_id: u32,
|
||||
#[serde] result: Option<serde_json::Value>,
|
||||
#[string] error: Option<String>,
|
||||
) -> Result<(), deno_error::JsErrorBox> {
|
||||
let wfe = state.borrow_mut::<WfeState>();
|
||||
let tx = wfe.inflight.remove(&request_id).ok_or_else(|| {
|
||||
deno_error::JsErrorBox::generic(format!("no inflight request with id {request_id}"))
|
||||
})?;
|
||||
|
||||
let response = match error {
|
||||
Some(err) => Err(err),
|
||||
None => Ok(result.unwrap_or(serde_json::json!({"proceed": true}))),
|
||||
};
|
||||
|
||||
// Ignore send failure — the executor may have timed out.
|
||||
let _ = tx.send(response);
|
||||
Ok(())
|
||||
}
|
||||
91
wfe-deno/src/ops/workflow.rs
Normal file
91
wfe-deno/src/ops/workflow.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
|
||||
use crate::state::WfeState;
|
||||
|
||||
/// Start a new workflow instance. Returns the workflow instance ID.
|
||||
#[op2]
|
||||
#[string]
|
||||
pub async fn op_start_workflow(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
#[string] definition_id: String,
|
||||
#[smi] version: u32,
|
||||
#[serde] data: serde_json::Value,
|
||||
) -> Result<String, deno_error::JsErrorBox> {
|
||||
let host = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
wfe.host()?.clone()
|
||||
};
|
||||
host.start_workflow(&definition_id, version, data)
|
||||
.await
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("start_workflow failed: {e}")))
|
||||
}
|
||||
|
||||
/// Suspend a running workflow. Returns true if suspension succeeded.
|
||||
#[op2]
|
||||
pub async fn op_suspend_workflow(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
#[string] id: String,
|
||||
) -> Result<bool, deno_error::JsErrorBox> {
|
||||
let host = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
wfe.host()?.clone()
|
||||
};
|
||||
host.suspend_workflow(&id)
|
||||
.await
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("suspend_workflow failed: {e}")))
|
||||
}
|
||||
|
||||
/// Resume a suspended workflow. Returns true if resumption succeeded.
|
||||
#[op2]
|
||||
pub async fn op_resume_workflow(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
#[string] id: String,
|
||||
) -> Result<bool, deno_error::JsErrorBox> {
|
||||
let host = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
wfe.host()?.clone()
|
||||
};
|
||||
host.resume_workflow(&id)
|
||||
.await
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("resume_workflow failed: {e}")))
|
||||
}
|
||||
|
||||
/// Terminate a workflow. Returns true if termination succeeded.
|
||||
#[op2]
|
||||
pub async fn op_terminate_workflow(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
#[string] id: String,
|
||||
) -> Result<bool, deno_error::JsErrorBox> {
|
||||
let host = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
wfe.host()?.clone()
|
||||
};
|
||||
host.terminate_workflow(&id)
|
||||
.await
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("terminate_workflow failed: {e}")))
|
||||
}
|
||||
|
||||
/// Get a workflow instance by ID. Returns the serialized WorkflowInstance.
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub async fn op_get_workflow(
|
||||
state: std::rc::Rc<std::cell::RefCell<OpState>>,
|
||||
#[string] id: String,
|
||||
) -> Result<serde_json::Value, deno_error::JsErrorBox> {
|
||||
let host = {
|
||||
let s = state.borrow();
|
||||
let wfe = s.borrow::<WfeState>();
|
||||
wfe.host()?.clone()
|
||||
};
|
||||
let instance = host
|
||||
.get_workflow(&id)
|
||||
.await
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("get_workflow failed: {e}")))?;
|
||||
serde_json::to_value(&instance)
|
||||
.map_err(|e| deno_error::JsErrorBox::generic(format!("serialization failed: {e}")))
|
||||
}
|
||||
Reference in New Issue
Block a user