From de66fef2d667f42cd84c8c7f7f0d040183611744 Mon Sep 17 00:00:00 2001 From: Sienna Meridian Satterwhite Date: Sun, 5 Apr 2026 12:44:00 +0100 Subject: [PATCH] feat(wfe-core): add add_step_typed() and make wire_outcome public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds WorkflowBuilder::add_step_typed() for adding named, configured steps directly — needed for parallel branch closures in the CLI. Makes wire_outcome() public so callers can wire custom step graphs. --- Cargo.toml | 20 +++++------ wfe-core/src/builder/workflow_builder.rs | 45 +++++++++++++++++++++++- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b1ddb24..e331855 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["wfe-core", "wfe-sqlite", "wfe-postgres", "wfe-opensearch", "wfe-valk resolver = "2" [workspace.package] -version = "1.6.1" +version = "1.6.2" edition = "2024" license = "MIT" repository = "https://src.sunbeam.pt/studio/wfe" @@ -38,15 +38,15 @@ redis = { version = "0.27", features = ["tokio-comp", "connection-manager"] } opensearch = "2" # Internal crates -wfe-core = { version = "1.6.1", path = "wfe-core", registry = "sunbeam" } -wfe-sqlite = { version = "1.6.1", path = "wfe-sqlite", registry = "sunbeam" } -wfe-postgres = { version = "1.6.1", path = "wfe-postgres", registry = "sunbeam" } -wfe-opensearch = { version = "1.6.1", path = "wfe-opensearch", registry = "sunbeam" } -wfe-valkey = { version = "1.6.1", path = "wfe-valkey", registry = "sunbeam" } -wfe-yaml = { version = "1.6.1", path = "wfe-yaml", registry = "sunbeam" } -wfe-buildkit = { version = "1.6.1", path = "wfe-buildkit", registry = "sunbeam" } -wfe-containerd = { version = "1.6.1", path = "wfe-containerd", registry = "sunbeam" } -wfe-rustlang = { version = "1.6.1", path = "wfe-rustlang", registry = "sunbeam" } +wfe-core = { version = "1.6.2", path = "wfe-core", registry = "sunbeam" } +wfe-sqlite = { version = "1.6.2", path = "wfe-sqlite", registry = "sunbeam" } +wfe-postgres = { version = "1.6.2", path = "wfe-postgres", registry = "sunbeam" } +wfe-opensearch = { version = "1.6.2", path = "wfe-opensearch", registry = "sunbeam" } +wfe-valkey = { version = "1.6.2", path = "wfe-valkey", registry = "sunbeam" } +wfe-yaml = { version = "1.6.2", path = "wfe-yaml", registry = "sunbeam" } +wfe-buildkit = { version = "1.6.2", path = "wfe-buildkit", registry = "sunbeam" } +wfe-containerd = { version = "1.6.2", path = "wfe-containerd", registry = "sunbeam" } +wfe-rustlang = { version = "1.6.2", path = "wfe-rustlang", registry = "sunbeam" } # YAML serde_yaml = "0.9" diff --git a/wfe-core/src/builder/workflow_builder.rs b/wfe-core/src/builder/workflow_builder.rs index bd7f548..64b4d6d 100644 --- a/wfe-core/src/builder/workflow_builder.rs +++ b/wfe-core/src/builder/workflow_builder.rs @@ -61,8 +61,23 @@ impl WorkflowBuilder { id } + /// Add a typed step with an optional name and config. + /// Convenience for use inside `parallel` branch closures. + pub fn add_step_typed( + &mut self, + name: &str, + config: Option, + ) -> usize { + let id = self.add_step(std::any::type_name::()); + self.steps[id].name = Some(name.to_string()); + if let Some(cfg) = config { + self.steps[id].step_config = Some(cfg); + } + id + } + /// Wire an outcome from `from_step` to `to_step`. - pub(crate) fn wire_outcome(&mut self, from_step: usize, to_step: usize, value: Option) { + pub fn wire_outcome(&mut self, from_step: usize, to_step: usize, value: Option) { if let Some(step) = self.steps.get_mut(from_step) { step.outcomes.push(StepOutcome { next_step: to_step, @@ -386,6 +401,34 @@ mod tests { assert_eq!(def.steps[0].step_type, def.steps[1].step_type); } + #[test] + fn add_step_typed_sets_name_and_config() { + let cfg = serde_json::json!({"namespace": "ory"}); + let mut builder = WorkflowBuilder::::new(); + let id = builder.add_step_typed::("apply-ory", Some(cfg.clone())); + assert_eq!(builder.steps[id].name, Some("apply-ory".into())); + assert_eq!(builder.steps[id].step_config, Some(cfg)); + assert!(builder.steps[id].step_type.contains("StepA")); + } + + #[test] + fn add_step_typed_without_config() { + let mut builder = WorkflowBuilder::::new(); + let id = builder.add_step_typed::("my-step", None); + assert_eq!(builder.steps[id].name, Some("my-step".into())); + assert_eq!(builder.steps[id].step_config, None); + } + + #[test] + fn wire_outcome_connects_steps() { + let mut builder = WorkflowBuilder::::new(); + let id0 = builder.add_step_typed::("first", None); + let id1 = builder.add_step_typed::("second", None); + builder.wire_outcome(id0, id1, None); + assert_eq!(builder.steps[id0].outcomes.len(), 1); + assert_eq!(builder.steps[id0].outcomes[0].next_step, id1); + } + #[test] fn inline_step_via_then_fn() { let def = WorkflowBuilder::::new()