From ee5052a3da8b97a35c7aa2911d8a1bc82988ad89 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Fri, 1 Aug 2025 07:35:56 -0500 Subject: [PATCH 01/39] note --- missing-drafts.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 missing-drafts.md diff --git a/missing-drafts.md b/missing-drafts.md new file mode 100644 index 00000000..90dfe28e --- /dev/null +++ b/missing-drafts.md @@ -0,0 +1,10 @@ +# Missing Draft Implementations + +This file tracks the `WIO` subtypes that are missing from the `DraftBuilder` API. + +- [ ] `Fork` (for conditional logic) +- [ ] `Parallel` (for concurrent execution) +- [ ] `HandleError` / `HandleErrorWith` (for error handling) +- [ ] `Checkpoint` (for saving progress) +- [ ] `Pure` (for simple value-based steps) +- [ ] `FlatMap` / `AndThen` (for sequential composition - may not require explicit draft helpers) From c8522b5debfb38967796d27c079a55f533772559 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sun, 3 Aug 2025 09:58:42 -0500 Subject: [PATCH 02/39] draft for WIO.Fork --- .../wio/builders/DraftBuilder.scala | 9 ++++++++- .../scala/workflows4s/wio/WIODraftTest.scala | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index 44fa31b5..4ae6ae33 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -26,7 +26,7 @@ object DraftBuilder { None, ), ) - def timer(name: String = null, duration: FiniteDuration = null)(using autoName: sourcecode.Name): WIO.Timer[Ctx, Any, Nothing, Nothing] = + def timer(name: String = null, duration: FiniteDuration = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = WIO.Timer( Option(duration) match { case Some(value) => WIO.Timer.DurationSource.Static(value.toJava) @@ -47,6 +47,13 @@ object DraftBuilder { ), ) + def choice(name: String = null)(branches: (String, WIO.Draft[Ctx])*)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = { + val branchWios = branches.map { case (branchName, wio) => + WIO.Branch(_ => None, wio, Some(branchName)) + } + WIO.Fork(branchWios.toVector, getEffectiveName(name, autoName).some, None) + } + def forEach(forEach: WIO.Draft[Ctx], name: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = { val effName = getEffectiveName(name, autoName).some WIO.ForEach(_ => ???, forEach, () => ???, null, _ => ???, (_, _, _) => ???, (_, _) => ???, None, null, WIOMeta.ForEach(effName)) diff --git a/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala b/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala index a2660379..4f894fd8 100644 --- a/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala +++ b/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala @@ -88,5 +88,25 @@ class WIODraftTest extends AnyFreeSpec with Matchers with OptionValues with Eith case _ => fail("Expected Sequence model") } } + + "should create a fork with correct branches" in { + val approve = WIO.draft.step("Approve") + val reject = WIO.draft.step("Reject") + val wio = WIO.draft.choice("Review")( + "Approved" -> approve, + "Rejected" -> reject, + ) + val model = wio.toProgress.toModel + + model match { + case WIOModel.Fork(branches, meta) => + meta.name shouldBe Some("Review") + branches.length shouldBe 2 + + branches.head shouldBe WIOModel.RunIO(WIOMeta.RunIO(Some("Approve"), None, None)) + branches(1) shouldBe WIOModel.RunIO(WIOMeta.RunIO(Some("Reject"), None, None)) + case _ => fail("Expected Fork model") + } + } } } From 6acefc692fcc7d5a54b38355b27ac170905cccbf Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Mon, 4 Aug 2025 11:03:44 -0500 Subject: [PATCH 03/39] draft for WIO.Parallel --- .../workflows4s/wio/builders/DraftBuilder.scala | 11 +++++++++++ .../scala/workflows4s/wio/WIODraftTest.scala | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index 4ae6ae33..27e7a29d 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -70,6 +70,17 @@ object DraftBuilder { base.transformInput((_: Any) => ???).map(_ => ???) } + def parallel(elements: WIO.Draft[Ctx]*): WIO.Draft[Ctx] = { + val parallelElements = elements.map { element => + WIO.Parallel.Element(element.map(_ => ???), (interimState: WCState[Ctx], _: WCState[Ctx]) => interimState) + } + WIO.Parallel[Ctx, Any, Nothing, WCState[Ctx], WCState[Ctx]]( + elements = parallelElements, + formResult = _ => ???, + initialInterimState = (_: Any) => ??? + ).transformInput((_: Any) => ???).map(_ => ???) + } + } } diff --git a/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala b/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala index 4f894fd8..e11480d7 100644 --- a/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala +++ b/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala @@ -108,5 +108,22 @@ class WIODraftTest extends AnyFreeSpec with Matchers with OptionValues with Eith case _ => fail("Expected Fork model") } } + + "should create a parallel step with multiple elements" in { + val step1: Draft[Ctx] = WIO.draft.step("task1") + val step2: Draft[Ctx] = WIO.draft.step("task2") + val step3: Draft[Ctx] = WIO.draft.step("task3") + val parallel: Draft[Ctx] = WIO.draft.parallel(step1, step2, step3) + val model = parallel.toProgress.toModel + + model match { + case WIOModel.Parallel(elements) => + elements.length shouldBe 3 + elements(0) shouldBe WIOModel.RunIO(WIOMeta.RunIO(Some("task1"), None, None)) + elements(1) shouldBe WIOModel.RunIO(WIOMeta.RunIO(Some("task2"), None, None)) + elements(2) shouldBe WIOModel.RunIO(WIOMeta.RunIO(Some("task3"), None, None)) + case _ => fail("Expected Parallel model") + } + } } } From 582734a9fe672c22cb2fac76efb559ca74fc4f18 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Wed, 6 Aug 2025 11:23:52 -0500 Subject: [PATCH 04/39] draft doc for signal example code --- website/docs/operations/02-await-signal.mdx | 12 +++++++++++ .../example/docs/DraftSignalExample.scala | 21 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala diff --git a/website/docs/operations/02-await-signal.mdx b/website/docs/operations/02-await-signal.mdx index 13fcea15..fe1aedd6 100644 --- a/website/docs/operations/02-await-signal.mdx +++ b/website/docs/operations/02-await-signal.mdx @@ -9,6 +9,18 @@ Signal handling is essential for workflows that need to pause and wait for exter +## Drafting Support + +When drafting workflows that need signal handling, use `WIO.draft.signal()`: + +```scala file=./main/scala/workflows4s/example/docs/DraftSignalExample.scala start=start_draft end=end_draft +``` + +The draft signal operation simplifies the API by: +- Not requiring signal type parameters +- Not requiring explicit event handling +- Automatically generating names from the context if not provided + # Unhandled Signals The Workflows4s API allows arbitrary signals to be sent to a workflow instance. While this provides flexibility, it also diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala new file mode 100644 index 00000000..d55e5e8c --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala @@ -0,0 +1,21 @@ +package workflows4s.example.docs + +object DraftSignalExample { + + // start_draft + object DraftContext extends workflows4s.wio.WorkflowContext { + // No need to define State or Event + } + + import DraftContext._ + + // Create a signal operation + val awaitApproval = WIO.draft.signal("Approval Required", error = "Rejected") + + // Use it in a workflow + val workflow = WIO.draft.step("Submit PR") >>> + awaitApproval >>> + WIO.draft.step("Merge PR") + // end_draft + +} \ No newline at end of file From 37748898da1c04ad677a6b1d4442d999b4c1fbae Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Wed, 6 Aug 2025 13:05:31 -0500 Subject: [PATCH 05/39] draft doc for timer timer timer --- website/docs/operations/02.1-timers.mdx | 11 +++++++++++ .../example/docs/DraftTimerExample.scala | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala diff --git a/website/docs/operations/02.1-timers.mdx b/website/docs/operations/02.1-timers.mdx index 2c6cd504..19f8d563 100644 --- a/website/docs/operations/02.1-timers.mdx +++ b/website/docs/operations/02.1-timers.mdx @@ -12,3 +12,14 @@ This operation enables time-based coordination, allowing workflows to pause for ``` +## Drafting Support + +When drafting workflows that need time-based operations, use `WIO.draft.timer()`: + +```scala file=./main/scala/workflows4s/example/docs/DraftTimerExample.scala start=start_draft end=end_draft +``` + +The draft timer operation simplifies the API by: +- Not requiring explicit event handling +- Making duration parameter optional (can be determined at runtime) +- Automatically generating names from the context if not provided diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala new file mode 100644 index 00000000..c47a6e62 --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala @@ -0,0 +1,16 @@ +package workflows4s.example.docs + +import scala.concurrent.duration._ + +object DraftTimerExample { + object DraftContext extends workflows4s.wio.WorkflowContext { + // No need to define State or Event + } + + import DraftContext._ + + // start_draft + // Create timer operation with draft API + val waitForReview = WIO.draft.timer("Wait for Review", duration = 24.hours) + // end_draft +} \ No newline at end of file From 835b31ec07138fb4f52fce330829580c4c3fd639 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Thu, 7 Aug 2025 08:09:50 -0500 Subject: [PATCH 06/39] draft doc for loop --- website/docs/operations/05-loops.mdx | 17 +++++++++++ .../example/docs/DraftLoopExample.scala | 29 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala diff --git a/website/docs/operations/05-loops.mdx b/website/docs/operations/05-loops.mdx index 3c273cbf..964c11c6 100644 --- a/website/docs/operations/05-loops.mdx +++ b/website/docs/operations/05-loops.mdx @@ -17,3 +17,20 @@ In the future this will be detected by the [linter](https://github.com/business4 ``` + +## Drafting Support + +When drafting workflows that need loops, use `WIO.draft.repeat()` to create conditional loops with a simpler API: + +```scala file=./main/scala/workflows4s/example/docs/DraftLoopExample.scala start=start_draft end=end_draft +``` + +The draft repeat operation simplifies the API by: +- Not requiring explicit event handling +- Making condition functions optional (can be determined at runtime) +- Providing optional error handling through `onRestart` +- Automatically generating names from the context if not provided + +:::tip +Always add a timer step to avoid busy loops, as shown in the example above. +::: diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala new file mode 100644 index 00000000..760ac8df --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala @@ -0,0 +1,29 @@ +package workflows4s.example.docs + +import scala.concurrent.duration._ + +object DraftLoopExample { + object DraftContext extends workflows4s.wio.WorkflowContext { + // No need to define State or Event + } + + import DraftContext._ + + // start_draft + // Create a retry workflow with a timer to avoid busy loops + val processStep = WIO.draft.step("Process Item") + val waitStep = WIO.draft.timer("Wait before retry", 5.seconds) + + val retryWorkflow = WIO.draft.repeat("Check Success", "Done", "Retry")( + processStep >>> waitStep + ) + + // Add custom retry behavior + val handleError = WIO.draft.step("Handle Error") + val retryWithHandler = WIO.draft.repeat("Check Success", "Done", "Retry")( + processStep >>> waitStep, + onRestart = handleError + ) + // end_draft +} + From e5e0dacf75d6037a253a7df00d281f9d8a1d7feb Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Thu, 7 Aug 2025 08:14:47 -0500 Subject: [PATCH 07/39] draft doc for fork --- website/docs/operations/06-fork.mdx | 13 ++++++++ .../example/docs/DraftForkExample.scala | 32 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala diff --git a/website/docs/operations/06-fork.mdx b/website/docs/operations/06-fork.mdx index 52ebefbd..1944cb97 100644 --- a/website/docs/operations/06-fork.mdx +++ b/website/docs/operations/06-fork.mdx @@ -9,3 +9,16 @@ It's equivalent to `if` instructions but allow for static rendering of the workf ``` + +## Drafting Support + +When drafting workflows that need conditional branching, use `WIO.draft.choice()` to create forks with a simpler API: + +```scala file=./main/scala/workflows4s/example/docs/DraftForkExample.scala start=start_draft end=end_draft +``` + +The draft choice operation simplifies the API by: +- Not requiring explicit condition functions +- Not requiring explicit event handling +- Automatically generating names from the context if not provided +- Providing a more intuitive interface for creating conditional branches diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala new file mode 100644 index 00000000..77a6ef03 --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala @@ -0,0 +1,32 @@ +package workflows4s.example.docs + +object DraftForkExample { + object DraftContext extends workflows4s.wio.WorkflowContext { + // No need to define State or Event + } + + import DraftContext._ + + // start_draft + // Create a simple approval workflow with conditional branching + val approveStep = WIO.draft.step("Approve") + val rejectStep = WIO.draft.step("Reject") + + val approvalWorkflow = WIO.draft.choice("Review Decision")( + "Approved" -> approveStep, + "Rejected" -> rejectStep + ) + + // Create a more complex workflow with multiple branches + val processStep = WIO.draft.step("Process") + val notifyStep = WIO.draft.step("Notify") + val archiveStep = WIO.draft.step("Archive") + + val complexWorkflow = WIO.draft.choice("Action Type")( + "Process" -> (processStep >>> notifyStep), + "Archive" -> archiveStep, + "Skip" -> WIO.draft.step("Skip Processing") + ) + // end_draft +} + From 7d01613451d1db83a025a9eef52bf0eaa60aaed9 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Thu, 7 Aug 2025 08:17:31 -0500 Subject: [PATCH 08/39] draft doc for parallel --- website/docs/operations/08-parallel.mdx | 15 +++++++++++- .../example/docs/DraftParallelExample.scala | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala diff --git a/website/docs/operations/08-parallel.mdx b/website/docs/operations/08-parallel.mdx index a923697e..35e99fab 100644 --- a/website/docs/operations/08-parallel.mdx +++ b/website/docs/operations/08-parallel.mdx @@ -12,4 +12,17 @@ Workflow's state is continously updated after each step completion (doesn't wait ```scala file=./main/scala/workflows4s/example/docs/ParallelExample.scala start=start_doc end=end_doc ``` - \ No newline at end of file + + +## Drafting Support + +When drafting workflows that need parallel execution, use `WIO.draft.parallel()` to run multiple branches concurrently with a simpler API: + +```scala file=./main/scala/workflows4s/example/docs/DraftParallelExample.scala start=start_draft end=end_draft +``` + +The draft parallel operation simplifies the API by: +- Not requiring explicit interim state or result construction +- Not requiring explicit event handling +- Automatically generating names from the context if not provided +- Providing a more intuitive interface for running steps in parallel \ No newline at end of file diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala new file mode 100644 index 00000000..16d60936 --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala @@ -0,0 +1,24 @@ +package workflows4s.example.docs + +object DraftParallelExample { + object DraftContext extends workflows4s.wio.WorkflowContext { + // No need to define State or Event + } + + import DraftContext._ + + // start_draft + // Create a simple parallel workflow + val stepA = WIO.draft.step("Task A") + val stepB = WIO.draft.step("Task B") + val stepC = WIO.draft.step("Task C") + + val parallelWorkflow = WIO.draft.parallel(stepA, stepB, stepC) + + // Create a parallel workflow with timers and signals + val timerStep = WIO.draft.timer("Wait 1") + val signalStep = WIO.draft.signal("External Approval") + val parallelWithWaits = WIO.draft.parallel(stepA, timerStep, signalStep) + // end_draft +} + From 2d6c495669d1cabb6e65451516944cfc2488972d Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Fri, 8 Aug 2025 07:55:35 -0500 Subject: [PATCH 09/39] add draft-signal example with OperationOutputs --- website/docs/operations/02-await-signal.mdx | 2 + .../src/test/resources/docs/draft-signal.bpmn | 82 +++++++++++++++++++ .../src/test/resources/docs/draft-signal.json | 31 +++++++ .../test/resources/docs/draft-signal.mermaid | 10 +++ .../src/test/resources/docs/draft-signal.svg | 4 + .../example/docs/ExamplesTest.scala | 1 + 6 files changed, 130 insertions(+) create mode 100644 workflows4s-example/src/test/resources/docs/draft-signal.bpmn create mode 100644 workflows4s-example/src/test/resources/docs/draft-signal.json create mode 100644 workflows4s-example/src/test/resources/docs/draft-signal.mermaid create mode 100644 workflows4s-example/src/test/resources/docs/draft-signal.svg diff --git a/website/docs/operations/02-await-signal.mdx b/website/docs/operations/02-await-signal.mdx index fe1aedd6..7c200b11 100644 --- a/website/docs/operations/02-await-signal.mdx +++ b/website/docs/operations/02-await-signal.mdx @@ -16,6 +16,8 @@ When drafting workflows that need signal handling, use `WIO.draft.signal()`: ```scala file=./main/scala/workflows4s/example/docs/DraftSignalExample.scala start=start_draft end=end_draft ``` + + The draft signal operation simplifies the API by: - Not requiring signal type parameters - Not requiring explicit event handling diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.bpmn b/workflows4s-example/src/test/resources/docs/draft-signal.bpmn new file mode 100644 index 00000000..9e1d7370 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-signal.bpmn @@ -0,0 +1,82 @@ + + + + + sequenceFlow_7 + + + sequenceFlow_7 + sequenceFlow_8 + + + + sequenceFlow_8 + sequenceFlow_9 + + + + + sequenceFlow_9 + sequenceFlow_10 + + + + + + + sequenceFlow_10 + sequenceFlow_11 + + + + sequenceFlow_11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.json b/workflows4s-example/src/test/resources/docs/draft-signal.json new file mode 100644 index 00000000..48a54f23 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-signal.json @@ -0,0 +1,31 @@ +{ + "steps" : [ + { + "meta" : { + "name" : "Submit PR", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "meta" : { + "signalName" : "Approval Required", + "operationName" : null, + "error" : { + "name" : "Rejected" + } + }, + "_type" : "HandleSignal" + }, + { + "meta" : { + "name" : "Merge PR", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + } + ], + "_type" : "Sequence" +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.mermaid b/workflows4s-example/src/test/resources/docs/draft-signal.mermaid new file mode 100644 index 00000000..41010a19 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-signal.mermaid @@ -0,0 +1,10 @@ +flowchart TD +node0@{ shape: circle, label: "Start"} +node1["Submit PR"] +node0 --> node1 +node2@{ shape: stadium, label: "fa:fa-envelope Approval Required"} +node1 --> node2 +node3["Handle Approval Required"] +node2 --> node3 +node4["Merge PR"] +node3 --> node4 diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.svg b/workflows4s-example/src/test/resources/docs/draft-signal.svg new file mode 100644 index 00000000..31ea158c --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-signal.svg @@ -0,0 +1,4 @@ + + + +Submit PRApproval RequiredHandle "Approval Required"Merge PRRejected \ No newline at end of file diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 33260ef1..491aa732 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -15,6 +15,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("run-io-description", RunIOExample.doThingsWithDescription), ExampleConfig("timer", TimerExample.waitForInput), ExampleConfig("handle-signal", HandleSignalExample.doThings), + ExampleConfig("draft-signal", DraftSignalExample.workflow), ExampleConfig("and-then", SequencingExample.sequence1), ExampleConfig("flat-map", SequencingExample.Dynamic.sequence1), ExampleConfig("handle-error-with", HandleErrorExample.errorHandled), From 785bfb3ce5015c2c392eb2567c9c81dd02a8ff79 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Fri, 8 Aug 2025 08:02:54 -0500 Subject: [PATCH 10/39] add draft-timer example with OperationOutputs --- website/docs/operations/02.1-timers.mdx | 2 + .../src/test/resources/docs/draft-timer.bpmn | 41 +++++++++++++++++++ .../src/test/resources/docs/draft-timer.json | 8 ++++ .../test/resources/docs/draft-timer.mermaid | 4 ++ .../src/test/resources/docs/draft-timer.svg | 4 ++ .../example/docs/ExamplesTest.scala | 1 + 6 files changed, 60 insertions(+) create mode 100644 workflows4s-example/src/test/resources/docs/draft-timer.bpmn create mode 100644 workflows4s-example/src/test/resources/docs/draft-timer.json create mode 100644 workflows4s-example/src/test/resources/docs/draft-timer.mermaid create mode 100644 workflows4s-example/src/test/resources/docs/draft-timer.svg diff --git a/website/docs/operations/02.1-timers.mdx b/website/docs/operations/02.1-timers.mdx index 19f8d563..bc671e40 100644 --- a/website/docs/operations/02.1-timers.mdx +++ b/website/docs/operations/02.1-timers.mdx @@ -19,6 +19,8 @@ When drafting workflows that need time-based operations, use `WIO.draft.timer()` ```scala file=./main/scala/workflows4s/example/docs/DraftTimerExample.scala start=start_draft end=end_draft ``` + + The draft timer operation simplifies the API by: - Not requiring explicit event handling - Making duration parameter optional (can be determined at runtime) diff --git a/workflows4s-example/src/test/resources/docs/draft-timer.bpmn b/workflows4s-example/src/test/resources/docs/draft-timer.bpmn new file mode 100644 index 00000000..497413ff --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-timer.bpmn @@ -0,0 +1,41 @@ + + + + + sequenceFlow_3 + + + sequenceFlow_3 + sequenceFlow_4 + + 24h + + + + + sequenceFlow_4 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflows4s-example/src/test/resources/docs/draft-timer.json b/workflows4s-example/src/test/resources/docs/draft-timer.json new file mode 100644 index 00000000..11212569 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-timer.json @@ -0,0 +1,8 @@ +{ + "meta" : { + "duration" : "PT24H", + "releaseAt" : null, + "name" : "Wait for Review" + }, + "_type" : "Timer" +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-timer.mermaid b/workflows4s-example/src/test/resources/docs/draft-timer.mermaid new file mode 100644 index 00000000..59fd12f4 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-timer.mermaid @@ -0,0 +1,4 @@ +flowchart TD +node0@{ shape: circle, label: "Start"} +node1@{ shape: stadium, label: "fa:fa-clock Wait for Review (24h)"} +node0 --> node1 diff --git a/workflows4s-example/src/test/resources/docs/draft-timer.svg b/workflows4s-example/src/test/resources/docs/draft-timer.svg new file mode 100644 index 00000000..5180ed0b --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-timer.svg @@ -0,0 +1,4 @@ + + + +Wait for Review \ No newline at end of file diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 491aa732..1db3ef5b 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -14,6 +14,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("run-io-error", RunIOExample.doThingsWithError), ExampleConfig("run-io-description", RunIOExample.doThingsWithDescription), ExampleConfig("timer", TimerExample.waitForInput), + ExampleConfig("draft-timer", DraftTimerExample.waitForReview), ExampleConfig("handle-signal", HandleSignalExample.doThings), ExampleConfig("draft-signal", DraftSignalExample.workflow), ExampleConfig("and-then", SequencingExample.sequence1), From 3d2484fb7398e497b225104dbeb9d5c4f260df7c Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Fri, 8 Aug 2025 08:21:40 -0500 Subject: [PATCH 11/39] add draft-loop example with OperationOutputs --- website/docs/operations/05-loops.mdx | 2 + .../example/docs/DraftLoopExample.scala | 20 ++-- .../src/test/resources/docs/draft-loop.bpmn | 113 ++++++++++++++++++ .../src/test/resources/docs/draft-loop.json | 37 ++++++ .../test/resources/docs/draft-loop.mermaid | 12 ++ .../src/test/resources/docs/draft-loop.svg | 4 + .../example/docs/ExamplesTest.scala | 1 + 7 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 workflows4s-example/src/test/resources/docs/draft-loop.bpmn create mode 100644 workflows4s-example/src/test/resources/docs/draft-loop.json create mode 100644 workflows4s-example/src/test/resources/docs/draft-loop.mermaid create mode 100644 workflows4s-example/src/test/resources/docs/draft-loop.svg diff --git a/website/docs/operations/05-loops.mdx b/website/docs/operations/05-loops.mdx index 964c11c6..1319bae5 100644 --- a/website/docs/operations/05-loops.mdx +++ b/website/docs/operations/05-loops.mdx @@ -25,6 +25,8 @@ When drafting workflows that need loops, use `WIO.draft.repeat()` to create cond ```scala file=./main/scala/workflows4s/example/docs/DraftLoopExample.scala start=start_draft end=end_draft ``` + + The draft repeat operation simplifies the API by: - Not requiring explicit event handling - Making condition functions optional (can be determined at runtime) diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala index 760ac8df..a02d52bb 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala @@ -10,19 +10,17 @@ object DraftLoopExample { import DraftContext._ // start_draft - // Create a retry workflow with a timer to avoid busy loops + // Create a draft loop with a timer to avoid busy loops val processStep = WIO.draft.step("Process Item") - val waitStep = WIO.draft.timer("Wait before retry", 5.seconds) + val waitStep = WIO.draft.timer("Wait before retry", duration = 1.minute) - val retryWorkflow = WIO.draft.repeat("Check Success", "Done", "Retry")( - processStep >>> waitStep - ) - - // Add custom retry behavior - val handleError = WIO.draft.step("Handle Error") - val retryWithHandler = WIO.draft.repeat("Check Success", "Done", "Retry")( - processStep >>> waitStep, - onRestart = handleError + val loop = WIO.draft.repeat( + conditionName = "Is processing complete?", + releaseBranchName = "Yes", + restartBranchName = "No" + )( + body = processStep >>> waitStep, + onRestart = WIO.draft.step("Reset for retry") ) // end_draft } diff --git a/workflows4s-example/src/test/resources/docs/draft-loop.bpmn b/workflows4s-example/src/test/resources/docs/draft-loop.bpmn new file mode 100644 index 00000000..37890b5b --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-loop.bpmn @@ -0,0 +1,113 @@ + + + + + sequenceFlow_8 + + + sequenceFlow_8 + sequenceFlow_14 + sequenceFlow_9 + + + + sequenceFlow_9 + sequenceFlow_10 + + + + sequenceFlow_10 + sequenceFlow_11 + + 1m + + + + + sequenceFlow_11 + sequenceFlow_12 + sequenceFlow_13 + + + + + + + sequenceFlow_12 + sequenceFlow_15 + + + + + + sequenceFlow_13 + sequenceFlow_14 + + + + sequenceFlow_15 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflows4s-example/src/test/resources/docs/draft-loop.json b/workflows4s-example/src/test/resources/docs/draft-loop.json new file mode 100644 index 00000000..df5a0fee --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-loop.json @@ -0,0 +1,37 @@ +{ + "base" : { + "steps" : [ + { + "meta" : { + "name" : "Process Item", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "meta" : { + "duration" : "PT1M", + "releaseAt" : null, + "name" : "Wait before retry" + }, + "_type" : "Timer" + } + ], + "_type" : "Sequence" + }, + "onRestart" : { + "meta" : { + "name" : "Reset for retry", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + "meta" : { + "conditionName" : "Is processing complete?", + "exitBranchName" : "Yes", + "restartBranchName" : "No" + }, + "_type" : "Loop" +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-loop.mermaid b/workflows4s-example/src/test/resources/docs/draft-loop.mermaid new file mode 100644 index 00000000..f7649d18 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-loop.mermaid @@ -0,0 +1,12 @@ +flowchart TD +node0:::executed@{ shape: circle, label: "Start"} +node1["Process Item"] +node0 --> node1 +node2@{ shape: stadium, label: "fa:fa-clock Wait before retry (1m)"} +node1 --> node2 +node3@{ shape: hex, label: "Is processing complete?"} +node2 --> node3 +node4["Reset for retry"] +node3 -->|"No"| node4 +node4 --> node1 +classDef executed fill:#0e0 diff --git a/workflows4s-example/src/test/resources/docs/draft-loop.svg b/workflows4s-example/src/test/resources/docs/draft-loop.svg new file mode 100644 index 00000000..788924be --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-loop.svg @@ -0,0 +1,4 @@ + + + +Process ItemWait before retryIs processing complete?Reset for retryYesNo \ No newline at end of file diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 1db3ef5b..3bd3dddb 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -22,6 +22,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("handle-error-with", HandleErrorExample.errorHandled), ExampleConfig("simple-loop", LoopExample.Simple.loop), ExampleConfig("loop", LoopExample.loop), + ExampleConfig("draft-loop", DraftLoopExample.loop), ExampleConfig("fork", ForkExample.fork), ExampleConfig("parallel", ParallelExample.parallel), ExampleConfig("interruption-signal", InterruptionExample.interruptedThroughSignal), From 48d854802d075a5b5fac0e73ac465be84f08f6d7 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Fri, 8 Aug 2025 11:36:37 -0700 Subject: [PATCH 12/39] add draft-parallel example with OperationOutputs --- website/docs/operations/08-parallel.mdx | 2 + .../test/resources/docs/draft-parallel.bpmn | 102 ++++++++++++++++++ .../test/resources/docs/draft-parallel.json | 29 +++++ .../resources/docs/draft-parallel.mermaid | 14 +++ .../test/resources/docs/draft-parallel.svg | 4 + .../example/docs/ExamplesTest.scala | 1 + 6 files changed, 152 insertions(+) create mode 100644 workflows4s-example/src/test/resources/docs/draft-parallel.bpmn create mode 100644 workflows4s-example/src/test/resources/docs/draft-parallel.json create mode 100644 workflows4s-example/src/test/resources/docs/draft-parallel.mermaid create mode 100644 workflows4s-example/src/test/resources/docs/draft-parallel.svg diff --git a/website/docs/operations/08-parallel.mdx b/website/docs/operations/08-parallel.mdx index 35e99fab..1a960cb6 100644 --- a/website/docs/operations/08-parallel.mdx +++ b/website/docs/operations/08-parallel.mdx @@ -21,6 +21,8 @@ When drafting workflows that need parallel execution, use `WIO.draft.parallel()` ```scala file=./main/scala/workflows4s/example/docs/DraftParallelExample.scala start=start_draft end=end_draft ``` + + The draft parallel operation simplifies the API by: - Not requiring explicit interim state or result construction - Not requiring explicit event handling diff --git a/workflows4s-example/src/test/resources/docs/draft-parallel.bpmn b/workflows4s-example/src/test/resources/docs/draft-parallel.bpmn new file mode 100644 index 00000000..c3c2b382 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-parallel.bpmn @@ -0,0 +1,102 @@ + + + + + sequenceFlow_4 + + + sequenceFlow_4 + sequenceFlow_5 + sequenceFlow_7 + sequenceFlow_9 + + + + sequenceFlow_5 + sequenceFlow_6 + + + + sequenceFlow_6 + sequenceFlow_8 + sequenceFlow_10 + sequenceFlow_11 + + + + sequenceFlow_7 + sequenceFlow_8 + + + + + sequenceFlow_9 + sequenceFlow_10 + + + + + sequenceFlow_11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflows4s-example/src/test/resources/docs/draft-parallel.json b/workflows4s-example/src/test/resources/docs/draft-parallel.json new file mode 100644 index 00000000..81208edc --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-parallel.json @@ -0,0 +1,29 @@ +{ + "elements" : [ + { + "meta" : { + "name" : "Task A", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "meta" : { + "name" : "Task B", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "meta" : { + "name" : "Task C", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + } + ], + "_type" : "Parallel" +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-parallel.mermaid b/workflows4s-example/src/test/resources/docs/draft-parallel.mermaid new file mode 100644 index 00000000..ca52d9a0 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-parallel.mermaid @@ -0,0 +1,14 @@ +flowchart TD +node0@{ shape: circle, label: "Start"} +node1@{ shape: fork, label: ""} +node0 --> node1 +node2["Task A"] +node1 --> node2 +node3["Task B"] +node1 --> node3 +node4["Task C"] +node1 --> node4 +node5@{ shape: fork, label: ""} +node2 --> node5 +node3 --> node5 +node4 --> node5 diff --git a/workflows4s-example/src/test/resources/docs/draft-parallel.svg b/workflows4s-example/src/test/resources/docs/draft-parallel.svg new file mode 100644 index 00000000..349cca2d --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-parallel.svg @@ -0,0 +1,4 @@ + + + +Task ATask BTask C \ No newline at end of file diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 3bd3dddb..28100497 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -25,6 +25,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("draft-loop", DraftLoopExample.loop), ExampleConfig("fork", ForkExample.fork), ExampleConfig("parallel", ParallelExample.parallel), + ExampleConfig("draft-parallel", DraftParallelExample.parallelWorkflow), ExampleConfig("interruption-signal", InterruptionExample.interruptedThroughSignal), ExampleConfig("checkpoint", CheckpointExample.checkpoint.checkpointed, technical = true), ExampleConfig("recovery", CheckpointExample.recovery.myWorkflow, technical = true), From fad5870947c2949f399925fe70babba500c32a1b Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sun, 10 Aug 2025 07:32:16 -0500 Subject: [PATCH 13/39] rm --- missing-drafts.md | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 missing-drafts.md diff --git a/missing-drafts.md b/missing-drafts.md deleted file mode 100644 index 90dfe28e..00000000 --- a/missing-drafts.md +++ /dev/null @@ -1,10 +0,0 @@ -# Missing Draft Implementations - -This file tracks the `WIO` subtypes that are missing from the `DraftBuilder` API. - -- [ ] `Fork` (for conditional logic) -- [ ] `Parallel` (for concurrent execution) -- [ ] `HandleError` / `HandleErrorWith` (for error handling) -- [ ] `Checkpoint` (for saving progress) -- [ ] `Pure` (for simple value-based steps) -- [ ] `FlatMap` / `AndThen` (for sequential composition - may not require explicit draft helpers) From 8038dd348855f38adff38443b6f097b6c2787e2b Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sun, 10 Aug 2025 07:54:22 -0500 Subject: [PATCH 14/39] update drafting support doc for handle errors --- website/docs/operations/04-handle-errors.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/docs/operations/04-handle-errors.mdx b/website/docs/operations/04-handle-errors.mdx index 60851d65..407212b5 100644 --- a/website/docs/operations/04-handle-errors.mdx +++ b/website/docs/operations/04-handle-errors.mdx @@ -12,3 +12,7 @@ Errors defined and handled should be domain errors and not technical ones. Domai ``` + +## Drafting Support + +For Error Handling, no dedicated draft API is required. Drafting can be done using the standard API. From d627af134066aa54e2265a007519fec50a3b8be6 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sun, 10 Aug 2025 07:55:58 -0500 Subject: [PATCH 15/39] add drafting support note for Sequencing Operations --- website/docs/operations/03-sequence-operations.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/docs/operations/03-sequence-operations.mdx b/website/docs/operations/03-sequence-operations.mdx index 5c50153b..a867143f 100644 --- a/website/docs/operations/03-sequence-operations.mdx +++ b/website/docs/operations/03-sequence-operations.mdx @@ -28,3 +28,7 @@ Dynamic steps cannot be rendered statically. Consider using [Forks](/docs/operat ``` + +## Drafting Support + +For Sequencing Operations, no dedicated draft API is required. Drafting can be done using the standard API. From ec9ddbcfcacc46a571c518f4858d35e7ad3c291b Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sun, 10 Aug 2025 07:59:55 -0500 Subject: [PATCH 16/39] add drafting support notes for operations --- website/docs/operations/10-checkpoints.mdx | 6 +++++- website/docs/operations/11-retry.mdx | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/website/docs/operations/10-checkpoints.mdx b/website/docs/operations/10-checkpoints.mdx index 6bc1b4e2..28a1bf09 100644 --- a/website/docs/operations/10-checkpoints.mdx +++ b/website/docs/operations/10-checkpoints.mdx @@ -49,4 +49,8 @@ In practice, checkpointing and recovery are often used together to enable workfl 1. Version 1 of a workflow includes a segment that is checkpointed 2. Version 2 of the workflow removes that segment but includes a recovery mechanism -3. When a workflow instance created with version 1 is resumed using version 2, the recovery mechanism processes the checkpoint event, allowing the workflow to continue without the removed segment \ No newline at end of file +3. When a workflow instance created with version 1 is resumed using version 2, the recovery mechanism processes the checkpoint event, allowing the workflow to continue without the removed segment + +## Drafting Support + +For Checkpointing and Recovery, no dedicated draft API is required. Drafting can be done using the standard API. \ No newline at end of file diff --git a/website/docs/operations/11-retry.mdx b/website/docs/operations/11-retry.mdx index 9c4c3b19..e505aa15 100644 --- a/website/docs/operations/11-retry.mdx +++ b/website/docs/operations/11-retry.mdx @@ -57,4 +57,8 @@ If you see it as a major limitation, please reach out. Use workflow-level retries for retry schedules spanning **minutes to hours or days** -For short-lived retries (e.g., retrying within milliseconds or seconds), prefer handling them directly inside the `IO` operation using libraries like [`cats-retry`](https://github.com/cats-effect/cats-retry). \ No newline at end of file +For short-lived retries (e.g., retrying within milliseconds or seconds), prefer handling them directly inside the `IO` operation using libraries like [`cats-retry`](https://github.com/cats-effect/cats-retry). + +## Drafting Support + +For Retrying Operations, no dedicated draft API is required. Drafting can be done using the standard API. \ No newline at end of file From 244361776df9ebd70b29ef40d9c3d10aced7659b Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Mon, 18 Aug 2025 07:28:26 -0500 Subject: [PATCH 17/39] add drafting support for interuption update docs --- website/docs/operations/07-Interrupting.mdx | 15 ++++++++ .../wio/builders/DraftBuilder.scala | 34 +++++++++++++++++++ .../docs/DraftInterruptionExample.scala | 28 +++++++++++++++ .../example/docs/ExamplesTest.scala | 1 + 4 files changed, 78 insertions(+) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala diff --git a/website/docs/operations/07-Interrupting.mdx b/website/docs/operations/07-Interrupting.mdx index c2256f22..2265480a 100644 --- a/website/docs/operations/07-Interrupting.mdx +++ b/website/docs/operations/07-Interrupting.mdx @@ -9,3 +9,18 @@ This allows selecting an alternative path based on such a signal. ``` + +## Drafting Support + +When drafting workflows that need interruption handling, use `WIO.draft.interruptionSignal()` and `WIO.draft.interruptionTimeout()`: + +```scala file=./main/scala/workflows4s/example/docs/DraftInterruptionExample.scala start=start_draft end=end_draft +``` + + + +The draft interruption operations simplify the API by: +- Not requiring signal type parameters or event handling for signals +- Not requiring duration source configuration for timeouts +- Automatically generating names from the context if not provided +- Providing a simple interface for creating interruptible workflows diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index 27e7a29d..b6d3b014 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -81,6 +81,40 @@ object DraftBuilder { ).transformInput((_: Any) => ???).map(_ => ???) } + def interruptionSignal( + signalName: String = null, + operationName: String = null, + error: String = null + )(using autoName: sourcecode.Name): WIO.Interruption[Ctx, Nothing, Nothing] = { + val draftSignalHandling = WIO.HandleSignal( + draftSignal, + SignalHandler[Unit, Unit, WCState[Ctx]]((_, _) => ???), + dummyEventHandler[WCEvent[Ctx], Unit], + WIO.HandleSignal.Meta( + Option(error).map(ErrorMeta.Present(_)).getOrElse(ErrorMeta.noError), + Option(signalName).getOrElse(getEffectiveName(null, autoName)), + Option(operationName) + ), + ).transformInput((_: WCState[Ctx]) => ???).map(_ => ???) + WIO.Interruption(draftSignalHandling, WIO.HandleInterruption.InterruptionType.Signal) + } + + def interruptionTimeout( + timerName: String = null, + duration: FiniteDuration = null + )(using autoName: sourcecode.Name): WIO.Interruption[Ctx, Nothing, Nothing] = { + val draftTimer = WIO.Timer( + Option(duration) match { + case Some(value) => WIO.Timer.DurationSource.Static(value.toJava) + case None => WIO.Timer.DurationSource.Dynamic(_ => ???) + }, + dummyEventHandler[WCEvent[Ctx], WIO.Timer.Started], + Option(timerName).orElse(getEffectiveName(null, autoName).some), + dummyEventHandler[WCEvent[Ctx], WIO.Timer.Released], + ).transformInput((_: WCState[Ctx]) => ???).map(_ => ???) + WIO.Interruption(draftTimer, WIO.HandleInterruption.InterruptionType.Timer) + } + } } diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala new file mode 100644 index 00000000..26b8488e --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala @@ -0,0 +1,28 @@ +package workflows4s.example.docs + +import scala.concurrent.duration.* + +object DraftInterruptionExample { + + // start_draft + object DraftContext extends workflows4s.wio.WorkflowContext { + // No need to define State or Event + } + + import DraftContext._ + + // Create signal and timeout interruptions + val urgentProcessing = WIO.draft.interruptionSignal("Urgent Processing Request") + val processingTimeout = WIO.draft.interruptionTimeout("Processing Deadline", 2.hours) + + // Document processing workflow that can be interrupted + val documentProcessingWorkflow = + WIO.draft.step("Validate Document") >>> + WIO.draft.step("Extract Content") + .interruptWith(urgentProcessing) // Can be interrupted for urgent processing + .interruptWith(processingTimeout) >>> // Must complete within 2 hours + WIO.draft.step("Generate Report") + // end_draft + +} + diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 28100497..598ad6b7 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -27,6 +27,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("parallel", ParallelExample.parallel), ExampleConfig("draft-parallel", DraftParallelExample.parallelWorkflow), ExampleConfig("interruption-signal", InterruptionExample.interruptedThroughSignal), + ExampleConfig("draft-interruption", DraftInterruptionExample.documentProcessingWorkflow), ExampleConfig("checkpoint", CheckpointExample.checkpoint.checkpointed, technical = true), ExampleConfig("recovery", CheckpointExample.recovery.myWorkflow, technical = true), ExampleConfig("pure", PureExample.doThings), From 2fe524955eb83e23f59c8b56aff9ace6647834ff Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Wed, 20 Aug 2025 12:34:47 -0500 Subject: [PATCH 18/39] Update website/docs/operations/02.1-timers.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Voytek Pituła --- website/docs/operations/02.1-timers.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/operations/02.1-timers.mdx b/website/docs/operations/02.1-timers.mdx index bc671e40..207a72e3 100644 --- a/website/docs/operations/02.1-timers.mdx +++ b/website/docs/operations/02.1-timers.mdx @@ -23,5 +23,5 @@ When drafting workflows that need time-based operations, use `WIO.draft.timer()` The draft timer operation simplifies the API by: - Not requiring explicit event handling -- Making duration parameter optional (can be determined at runtime) +- Making duration parameter optional - Automatically generating names from the context if not provided From 88c38114a6e71a7df6aeeb0212f3c687182b9a16 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Wed, 20 Aug 2025 20:32:26 -0500 Subject: [PATCH 19/39] Update website/docs/operations/05-loops.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Voytek Pituła --- website/docs/operations/05-loops.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/operations/05-loops.mdx b/website/docs/operations/05-loops.mdx index 1319bae5..b75e8397 100644 --- a/website/docs/operations/05-loops.mdx +++ b/website/docs/operations/05-loops.mdx @@ -30,7 +30,7 @@ When drafting workflows that need loops, use `WIO.draft.repeat()` to create cond The draft repeat operation simplifies the API by: - Not requiring explicit event handling - Making condition functions optional (can be determined at runtime) -- Providing optional error handling through `onRestart` +- Making return branch optional - Automatically generating names from the context if not provided :::tip From 1e415772c5e03c7bfb9d6161aed03762624090ca Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Wed, 20 Aug 2025 20:32:36 -0500 Subject: [PATCH 20/39] Update website/docs/operations/05-loops.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Voytek Pituła --- website/docs/operations/05-loops.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/operations/05-loops.mdx b/website/docs/operations/05-loops.mdx index b75e8397..1ba03efb 100644 --- a/website/docs/operations/05-loops.mdx +++ b/website/docs/operations/05-loops.mdx @@ -29,7 +29,7 @@ When drafting workflows that need loops, use `WIO.draft.repeat()` to create cond The draft repeat operation simplifies the API by: - Not requiring explicit event handling -- Making condition functions optional (can be determined at runtime) +- Making condition functions optional - Making return branch optional - Automatically generating names from the context if not provided From 1e52e578148d977cb4f51e1f689e5d5f5dfa4b1e Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Wed, 20 Aug 2025 20:38:05 -0500 Subject: [PATCH 21/39] remove redundant tips --- website/docs/operations/05-loops.mdx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/website/docs/operations/05-loops.mdx b/website/docs/operations/05-loops.mdx index 1ba03efb..c53680b1 100644 --- a/website/docs/operations/05-loops.mdx +++ b/website/docs/operations/05-loops.mdx @@ -32,7 +32,3 @@ The draft repeat operation simplifies the API by: - Making condition functions optional - Making return branch optional - Automatically generating names from the context if not provided - -:::tip -Always add a timer step to avoid busy loops, as shown in the example above. -::: From f3454256f43ee0ec459f7b10e03439cf98b428fa Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sat, 23 Aug 2025 19:00:30 -0500 Subject: [PATCH 22/39] update fork draft --- website/docs/operations/06-fork.mdx | 6 +- .../example/docs/DraftForkExample.scala | 12 --- .../src/test/resources/docs/draft-choice.bpmn | 86 +++++++++++++++++++ .../src/test/resources/docs/draft-choice.json | 32 +++++++ .../test/resources/docs/draft-choice.mermaid | 8 ++ .../src/test/resources/docs/draft-choice.svg | 4 + .../example/docs/ExamplesTest.scala | 1 + 7 files changed, 134 insertions(+), 15 deletions(-) create mode 100644 workflows4s-example/src/test/resources/docs/draft-choice.bpmn create mode 100644 workflows4s-example/src/test/resources/docs/draft-choice.json create mode 100644 workflows4s-example/src/test/resources/docs/draft-choice.mermaid create mode 100644 workflows4s-example/src/test/resources/docs/draft-choice.svg diff --git a/website/docs/operations/06-fork.mdx b/website/docs/operations/06-fork.mdx index 1944cb97..ee5eb184 100644 --- a/website/docs/operations/06-fork.mdx +++ b/website/docs/operations/06-fork.mdx @@ -17,8 +17,8 @@ When drafting workflows that need conditional branching, use `WIO.draft.choice() ```scala file=./main/scala/workflows4s/example/docs/DraftForkExample.scala start=start_draft end=end_draft ``` + + The draft choice operation simplifies the API by: - Not requiring explicit condition functions -- Not requiring explicit event handling -- Automatically generating names from the context if not provided -- Providing a more intuitive interface for creating conditional branches +- Automatically generating names from the context if not provided \ No newline at end of file diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala index 77a6ef03..9520a1a9 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala @@ -8,7 +8,6 @@ object DraftForkExample { import DraftContext._ // start_draft - // Create a simple approval workflow with conditional branching val approveStep = WIO.draft.step("Approve") val rejectStep = WIO.draft.step("Reject") @@ -16,17 +15,6 @@ object DraftForkExample { "Approved" -> approveStep, "Rejected" -> rejectStep ) - - // Create a more complex workflow with multiple branches - val processStep = WIO.draft.step("Process") - val notifyStep = WIO.draft.step("Notify") - val archiveStep = WIO.draft.step("Archive") - - val complexWorkflow = WIO.draft.choice("Action Type")( - "Process" -> (processStep >>> notifyStep), - "Archive" -> archiveStep, - "Skip" -> WIO.draft.step("Skip Processing") - ) // end_draft } diff --git a/workflows4s-example/src/test/resources/docs/draft-choice.bpmn b/workflows4s-example/src/test/resources/docs/draft-choice.bpmn new file mode 100644 index 00000000..f9149c52 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-choice.bpmn @@ -0,0 +1,86 @@ + + + + + sequenceFlow_5 + + + sequenceFlow_5 + sequenceFlow_6 + sequenceFlow_8 + + + + + + + sequenceFlow_6 + sequenceFlow_7 + + + sequenceFlow_7 + sequenceFlow_9 + sequenceFlow_10 + + + + + + + sequenceFlow_8 + sequenceFlow_9 + + + + sequenceFlow_10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflows4s-example/src/test/resources/docs/draft-choice.json b/workflows4s-example/src/test/resources/docs/draft-choice.json new file mode 100644 index 00000000..0f78e027 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-choice.json @@ -0,0 +1,32 @@ +{ + "branches" : [ + { + "meta" : { + "name" : "Approve", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "meta" : { + "name" : "Reject", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + } + ], + "meta" : { + "name" : "Review Decision", + "branches" : [ + { + "name" : "Approved" + }, + { + "name" : "Rejected" + } + ] + }, + "_type" : "Fork" +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-choice.mermaid b/workflows4s-example/src/test/resources/docs/draft-choice.mermaid new file mode 100644 index 00000000..cd59e09e --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-choice.mermaid @@ -0,0 +1,8 @@ +flowchart TD +node0@{ shape: circle, label: "Start"} +node1@{ shape: hex, label: "Review Decision"} +node0 --> node1 +node2["Approve"] +node1 -->|"Approved"| node2 +node3["Reject"] +node1 -->|"Rejected"| node3 diff --git a/workflows4s-example/src/test/resources/docs/draft-choice.svg b/workflows4s-example/src/test/resources/docs/draft-choice.svg new file mode 100644 index 00000000..e26bbf57 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-choice.svg @@ -0,0 +1,4 @@ + + + +Review DecisionApproveRejectApprovedRejected \ No newline at end of file diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 598ad6b7..25505328 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -24,6 +24,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("loop", LoopExample.loop), ExampleConfig("draft-loop", DraftLoopExample.loop), ExampleConfig("fork", ForkExample.fork), + ExampleConfig("draft-choice", DraftForkExample.approvalWorkflow), ExampleConfig("parallel", ParallelExample.parallel), ExampleConfig("draft-parallel", DraftParallelExample.parallelWorkflow), ExampleConfig("interruption-signal", InterruptionExample.interruptedThroughSignal), From fadcc556222220439f6ce4b915b6677d83612cab Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sat, 23 Aug 2025 19:15:39 -0500 Subject: [PATCH 23/39] add missing draft-interruption.json --- .../resources/docs/draft-interruption.json | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 workflows4s-example/src/test/resources/docs/draft-interruption.json diff --git a/workflows4s-example/src/test/resources/docs/draft-interruption.json b/workflows4s-example/src/test/resources/docs/draft-interruption.json new file mode 100644 index 00000000..65389912 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-interruption.json @@ -0,0 +1,53 @@ +{ + "steps" : [ + { + "meta" : { + "name" : "Validate Document", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "base" : { + "base" : { + "meta" : { + "name" : "Extract Content", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + "trigger" : { + "meta" : { + "signalName" : "Urgent Processing Request", + "operationName" : null, + "error" : null + }, + "_type" : "HandleSignal" + }, + "handler" : null, + "_type" : "Interruptible" + }, + "trigger" : { + "meta" : { + "duration" : "PT2H", + "releaseAt" : null, + "name" : "Processing Deadline" + }, + "_type" : "Timer" + }, + "handler" : null, + "_type" : "Interruptible" + }, + { + "meta" : { + "name" : "Generate Report", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + } + ], + "_type" : "Sequence" +} \ No newline at end of file From 3096632daee7c17a20a3a6f4f8b2b8dbcf450de7 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sat, 23 Aug 2025 19:16:03 -0500 Subject: [PATCH 24/39] add draft-interruption.bpmn --- .../resources/docs/draft-interruption.bpmn | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 workflows4s-example/src/test/resources/docs/draft-interruption.bpmn diff --git a/workflows4s-example/src/test/resources/docs/draft-interruption.bpmn b/workflows4s-example/src/test/resources/docs/draft-interruption.bpmn new file mode 100644 index 00000000..e2777f4b --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-interruption.bpmn @@ -0,0 +1,149 @@ + + + + + sequenceFlow_10 + + + sequenceFlow_10 + sequenceFlow_16 + + + + sequenceFlow_16 + + sequenceFlow_13 + + + sequenceFlow_13 + + sequenceFlow_11 + + + sequenceFlow_11 + sequenceFlow_12 + + + + sequenceFlow_12 + + + + + + sequenceFlow_14 + + + + sequenceFlow_14 + sequenceFlow_15 + + + + sequenceFlow_15 + + + + + + sequenceFlow_17 + + + + + + sequenceFlow_17 + sequenceFlow_18 + + + + sequenceFlow_18 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 89e18bd9b4d0d75bf7de3d849e0b457347c92582 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Sat, 23 Aug 2025 19:16:34 -0500 Subject: [PATCH 25/39] add draft-interruption.mermaid --- .../resources/docs/draft-interruption.mermaid | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 workflows4s-example/src/test/resources/docs/draft-interruption.mermaid diff --git a/workflows4s-example/src/test/resources/docs/draft-interruption.mermaid b/workflows4s-example/src/test/resources/docs/draft-interruption.mermaid new file mode 100644 index 00000000..423695a0 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-interruption.mermaid @@ -0,0 +1,20 @@ +flowchart TD +node0@{ shape: circle, label: "Start"} +node1["Validate Document"] +node0 --> node1 +subgraph node2 [" "] +subgraph node3 [" "] +node4["Extract Content"] +node1 --> node4 +end +node9@{ shape: stadium, label: "fa:fa-envelope Urgent Processing Request"} +node3 --> node9 +node10["Handle Urgent Processing Request"] +node9 --> node10 +end +node14@{ shape: stadium, label: "fa:fa-clock Processing Deadline (2h)"} +node2 --> node14 +node15["Generate Report"] +node14 --> node15 +node10 --> node15 +node4 --> node15 From df15e2fe594c37b9413628f6679b2bb70e0a577d Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Thu, 28 Aug 2025 06:14:16 -0500 Subject: [PATCH 26/39] simplify example --- .../docs/DraftInterruptionExample.scala | 5 +- .../resources/docs/draft-interruption.bpmn | 128 ++++++++---------- .../resources/docs/draft-interruption.json | 8 -- .../resources/docs/draft-interruption.mermaid | 4 - .../resources/docs/draft-interruption.svg | 4 + 5 files changed, 64 insertions(+), 85 deletions(-) create mode 100644 workflows4s-example/src/test/resources/docs/draft-interruption.svg diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala index 26b8488e..e94a4c72 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala @@ -4,13 +4,13 @@ import scala.concurrent.duration.* object DraftInterruptionExample { - // start_draft object DraftContext extends workflows4s.wio.WorkflowContext { // No need to define State or Event } import DraftContext._ + // start_draft // Create signal and timeout interruptions val urgentProcessing = WIO.draft.interruptionSignal("Urgent Processing Request") val processingTimeout = WIO.draft.interruptionTimeout("Processing Deadline", 2.hours) @@ -20,8 +20,7 @@ object DraftInterruptionExample { WIO.draft.step("Validate Document") >>> WIO.draft.step("Extract Content") .interruptWith(urgentProcessing) // Can be interrupted for urgent processing - .interruptWith(processingTimeout) >>> // Must complete within 2 hours - WIO.draft.step("Generate Report") + .interruptWith(processingTimeout) // Must complete within 2 hours // end_draft } diff --git a/workflows4s-example/src/test/resources/docs/draft-interruption.bpmn b/workflows4s-example/src/test/resources/docs/draft-interruption.bpmn index e2777f4b..f40eea09 100644 --- a/workflows4s-example/src/test/resources/docs/draft-interruption.bpmn +++ b/workflows4s-example/src/test/resources/docs/draft-interruption.bpmn @@ -1,149 +1,137 @@ - - sequenceFlow_10 + + sequenceFlow_9 - sequenceFlow_10 - sequenceFlow_16 + sequenceFlow_9 + sequenceFlow_15 - + - sequenceFlow_16 - - sequenceFlow_13 + sequenceFlow_15 + + sequenceFlow_12 - sequenceFlow_13 - - sequenceFlow_11 + sequenceFlow_12 + + sequenceFlow_10 - sequenceFlow_11 - sequenceFlow_12 + sequenceFlow_10 + sequenceFlow_11 - - - sequenceFlow_12 + + + sequenceFlow_11 - + - - - sequenceFlow_14 - + + + sequenceFlow_13 + - sequenceFlow_14 - sequenceFlow_15 + sequenceFlow_13 + sequenceFlow_14 - - - sequenceFlow_15 + + + sequenceFlow_14 - + - - - sequenceFlow_17 + + + sequenceFlow_16 - + - - sequenceFlow_17 - sequenceFlow_18 - - - - sequenceFlow_18 + + sequenceFlow_16 - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - - - - - - diff --git a/workflows4s-example/src/test/resources/docs/draft-interruption.json b/workflows4s-example/src/test/resources/docs/draft-interruption.json index 65389912..1aefa461 100644 --- a/workflows4s-example/src/test/resources/docs/draft-interruption.json +++ b/workflows4s-example/src/test/resources/docs/draft-interruption.json @@ -39,14 +39,6 @@ }, "handler" : null, "_type" : "Interruptible" - }, - { - "meta" : { - "name" : "Generate Report", - "error" : null, - "description" : null - }, - "_type" : "RunIO" } ], "_type" : "Sequence" diff --git a/workflows4s-example/src/test/resources/docs/draft-interruption.mermaid b/workflows4s-example/src/test/resources/docs/draft-interruption.mermaid index 423695a0..b3b8c1da 100644 --- a/workflows4s-example/src/test/resources/docs/draft-interruption.mermaid +++ b/workflows4s-example/src/test/resources/docs/draft-interruption.mermaid @@ -14,7 +14,3 @@ node9 --> node10 end node14@{ shape: stadium, label: "fa:fa-clock Processing Deadline (2h)"} node2 --> node14 -node15["Generate Report"] -node14 --> node15 -node10 --> node15 -node4 --> node15 diff --git a/workflows4s-example/src/test/resources/docs/draft-interruption.svg b/workflows4s-example/src/test/resources/docs/draft-interruption.svg new file mode 100644 index 00000000..8b1ed751 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-interruption.svg @@ -0,0 +1,4 @@ + + + +Validate DocumentExtract ContentHandle Urgent Processing RequestUrgent Processing RequestProcessing Deadline \ No newline at end of file From f5f1a4875d3c51ad7b99b2680a5d09a7f53dd8c3 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Thu, 28 Aug 2025 06:45:22 -0500 Subject: [PATCH 27/39] simplify example simplify --- .../example/docs/DraftSignalExample.scala | 8 +-- .../src/test/resources/docs/draft-signal.bpmn | 72 ++++++++----------- .../src/test/resources/docs/draft-signal.json | 36 ++++------ .../test/resources/docs/draft-signal.mermaid | 2 - .../src/test/resources/docs/draft-signal.svg | 2 +- 5 files changed, 48 insertions(+), 72 deletions(-) diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala index d55e5e8c..bad6afbd 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala @@ -2,20 +2,18 @@ package workflows4s.example.docs object DraftSignalExample { - // start_draft object DraftContext extends workflows4s.wio.WorkflowContext { // No need to define State or Event } import DraftContext._ + // start_draft // Create a signal operation val awaitApproval = WIO.draft.signal("Approval Required", error = "Rejected") // Use it in a workflow - val workflow = WIO.draft.step("Submit PR") >>> - awaitApproval >>> - WIO.draft.step("Merge PR") + val workflow = WIO.draft.step("Submit PR") >>> awaitApproval // end_draft -} \ No newline at end of file +} diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.bpmn b/workflows4s-example/src/test/resources/docs/draft-signal.bpmn index 9e1d7370..a5a957b4 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.bpmn +++ b/workflows4s-example/src/test/resources/docs/draft-signal.bpmn @@ -1,82 +1,70 @@ - + - - sequenceFlow_7 + + sequenceFlow_6 - sequenceFlow_7 - sequenceFlow_8 + sequenceFlow_6 + sequenceFlow_7 - + - sequenceFlow_8 - sequenceFlow_9 - + sequenceFlow_7 + sequenceFlow_8 + - + - sequenceFlow_9 - sequenceFlow_10 + sequenceFlow_8 + sequenceFlow_9 - - + + - - sequenceFlow_10 - sequenceFlow_11 - - - - sequenceFlow_11 + + sequenceFlow_9 - + - + - - + + - + - + - + - + - + - + - + - - + + - + - - - - - - - diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.json b/workflows4s-example/src/test/resources/docs/draft-signal.json index 48a54f23..dd4e51ab 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.json +++ b/workflows4s-example/src/test/resources/docs/draft-signal.json @@ -1,31 +1,23 @@ { - "steps" : [ + "steps": [ { - "meta" : { - "name" : "Submit PR", - "error" : null, - "description" : null + "meta": { + "name": "Submit PR", + "error": null, + "description": null }, - "_type" : "RunIO" + "_type": "RunIO" }, { - "meta" : { - "signalName" : "Approval Required", - "operationName" : null, - "error" : { - "name" : "Rejected" + "meta": { + "signalName": "Approval Required", + "operationName": null, + "error": { + "name": "Rejected" } }, - "_type" : "HandleSignal" - }, - { - "meta" : { - "name" : "Merge PR", - "error" : null, - "description" : null - }, - "_type" : "RunIO" + "_type": "HandleSignal" } ], - "_type" : "Sequence" -} \ No newline at end of file + "_type": "Sequence" +} diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.mermaid b/workflows4s-example/src/test/resources/docs/draft-signal.mermaid index 41010a19..9e452da7 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.mermaid +++ b/workflows4s-example/src/test/resources/docs/draft-signal.mermaid @@ -6,5 +6,3 @@ node2@{ shape: stadium, label: "fa:fa-envelope Approval Required"} node1 --> node2 node3["Handle Approval Required"] node2 --> node3 -node4["Merge PR"] -node3 --> node4 diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.svg b/workflows4s-example/src/test/resources/docs/draft-signal.svg index 31ea158c..c538e6ae 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.svg +++ b/workflows4s-example/src/test/resources/docs/draft-signal.svg @@ -1,4 +1,4 @@ -Submit PRApproval RequiredHandle "Approval Required"Merge PRRejected \ No newline at end of file +Submit PRApproval RequiredHandle "Approval Required"Rejected \ No newline at end of file From 111ef8a1465ca087c75f680d3832840e0ce89185 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Thu, 28 Aug 2025 07:15:25 -0500 Subject: [PATCH 28/39] simplify example --- .../example/docs/DraftParallelExample.scala | 5 ---- .../test/resources/docs/draft-parallel.svg | 2 +- .../src/test/resources/docs/draft-signal.bpmn | 2 +- .../src/test/resources/docs/draft-signal.json | 28 +++++++++---------- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala index 16d60936..6bdeafae 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala @@ -14,11 +14,6 @@ object DraftParallelExample { val stepC = WIO.draft.step("Task C") val parallelWorkflow = WIO.draft.parallel(stepA, stepB, stepC) - - // Create a parallel workflow with timers and signals - val timerStep = WIO.draft.timer("Wait 1") - val signalStep = WIO.draft.signal("External Approval") - val parallelWithWaits = WIO.draft.parallel(stepA, timerStep, signalStep) // end_draft } diff --git a/workflows4s-example/src/test/resources/docs/draft-parallel.svg b/workflows4s-example/src/test/resources/docs/draft-parallel.svg index 349cca2d..42d44466 100644 --- a/workflows4s-example/src/test/resources/docs/draft-parallel.svg +++ b/workflows4s-example/src/test/resources/docs/draft-parallel.svg @@ -1,4 +1,4 @@ -Task ATask BTask C \ No newline at end of file +Task ATask BTask C \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.bpmn b/workflows4s-example/src/test/resources/docs/draft-signal.bpmn index a5a957b4..d199bbcd 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.bpmn +++ b/workflows4s-example/src/test/resources/docs/draft-signal.bpmn @@ -1,5 +1,5 @@ - + sequenceFlow_6 diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.json b/workflows4s-example/src/test/resources/docs/draft-signal.json index dd4e51ab..c7e35d18 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.json +++ b/workflows4s-example/src/test/resources/docs/draft-signal.json @@ -1,23 +1,23 @@ { - "steps": [ + "steps" : [ { - "meta": { - "name": "Submit PR", - "error": null, - "description": null + "meta" : { + "name" : "Submit PR", + "error" : null, + "description" : null }, - "_type": "RunIO" + "_type" : "RunIO" }, { - "meta": { - "signalName": "Approval Required", - "operationName": null, - "error": { - "name": "Rejected" + "meta" : { + "signalName" : "Approval Required", + "operationName" : null, + "error" : { + "name" : "Rejected" } }, - "_type": "HandleSignal" + "_type" : "HandleSignal" } ], - "_type": "Sequence" -} + "_type" : "Sequence" +} \ No newline at end of file From ab51add1f39983289ebfb2322a6b5af8ba8f8d69 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Mon, 1 Sep 2025 06:54:04 -0500 Subject: [PATCH 29/39] make use of existing DraftWorkflowContext --- .../scala/workflows4s/example/docs/DraftForkExample.scala | 7 ++----- .../example/docs/DraftInterruptionExample.scala | 6 +----- .../scala/workflows4s/example/docs/DraftLoopExample.scala | 5 +---- .../workflows4s/example/docs/DraftParallelExample.scala | 6 +----- .../workflows4s/example/docs/DraftSignalExample.scala | 6 +----- .../workflows4s/example/docs/DraftTimerExample.scala | 8 +++----- 6 files changed, 9 insertions(+), 29 deletions(-) diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala index 9520a1a9..01f1a839 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala @@ -1,11 +1,8 @@ package workflows4s.example.docs object DraftForkExample { - object DraftContext extends workflows4s.wio.WorkflowContext { - // No need to define State or Event - } - import DraftContext._ + import workflows4s.wio.DraftWorkflowContext._ // start_draft val approveStep = WIO.draft.step("Approve") @@ -16,5 +13,5 @@ object DraftForkExample { "Rejected" -> rejectStep ) // end_draft -} +} diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala index e94a4c72..ff30b858 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala @@ -4,11 +4,7 @@ import scala.concurrent.duration.* object DraftInterruptionExample { - object DraftContext extends workflows4s.wio.WorkflowContext { - // No need to define State or Event - } - - import DraftContext._ + import workflows4s.wio.DraftWorkflowContext._ // start_draft // Create signal and timeout interruptions diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala index a02d52bb..815ecbb0 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala @@ -3,11 +3,8 @@ package workflows4s.example.docs import scala.concurrent.duration._ object DraftLoopExample { - object DraftContext extends workflows4s.wio.WorkflowContext { - // No need to define State or Event - } - import DraftContext._ + import workflows4s.wio.DraftWorkflowContext._ // start_draft // Create a draft loop with a timer to avoid busy loops diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala index 6bdeafae..ff44f95f 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala @@ -1,11 +1,7 @@ package workflows4s.example.docs object DraftParallelExample { - object DraftContext extends workflows4s.wio.WorkflowContext { - // No need to define State or Event - } - - import DraftContext._ + import workflows4s.wio.DraftWorkflowContext._ // start_draft // Create a simple parallel workflow diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala index bad6afbd..923e377e 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala @@ -2,11 +2,7 @@ package workflows4s.example.docs object DraftSignalExample { - object DraftContext extends workflows4s.wio.WorkflowContext { - // No need to define State or Event - } - - import DraftContext._ + import workflows4s.wio.DraftWorkflowContext._ // start_draft // Create a signal operation diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala index c47a6e62..d9dbc641 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala @@ -1,16 +1,14 @@ package workflows4s.example.docs -import scala.concurrent.duration._ +import scala.concurrent.duration.* object DraftTimerExample { - object DraftContext extends workflows4s.wio.WorkflowContext { - // No need to define State or Event - } - import DraftContext._ + import workflows4s.wio.DraftWorkflowContext._ // start_draft // Create timer operation with draft API val waitForReview = WIO.draft.timer("Wait for Review", duration = 24.hours) // end_draft + } \ No newline at end of file From fb5ae3911d339b902401967f6314456a1f4a5aec Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Thu, 11 Sep 2025 06:50:27 -0500 Subject: [PATCH 30/39] draft support for checkpoint wio --- website/docs/operations/10-checkpoints.mdx | 8 ++- .../wio/builders/DraftBuilder.scala | 24 +++++++ .../example/docs/DraftCheckpointExample.scala | 16 +++++ .../test/resources/docs/draft-checkpoint.bpmn | 62 +++++++++++++++++++ .../test/resources/docs/draft-checkpoint.json | 29 +++++++++ .../resources/docs/draft-checkpoint.mermaid | 8 +++ .../example/docs/ExamplesTest.scala | 1 + 7 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala create mode 100644 workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn create mode 100644 workflows4s-example/src/test/resources/docs/draft-checkpoint.json create mode 100644 workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid diff --git a/website/docs/operations/10-checkpoints.mdx b/website/docs/operations/10-checkpoints.mdx index 28a1bf09..949a020f 100644 --- a/website/docs/operations/10-checkpoints.mdx +++ b/website/docs/operations/10-checkpoints.mdx @@ -53,4 +53,10 @@ In practice, checkpointing and recovery are often used together to enable workfl ## Drafting Support -For Checkpointing and Recovery, no dedicated draft API is required. Drafting can be done using the standard API. \ No newline at end of file +When drafting workflows that need checkpointing and recovery, you can use the draft API. + +### Draft Checkpoint +```scala file=./main/scala/workflows4s/example/docs/DraftCheckpointExample.scala start=start_draft_checkpoint end=end_draft_recovery + +``` + diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index b6d3b014..beee1bfd 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -81,6 +81,30 @@ object DraftBuilder { ).transformInput((_: Any) => ???).map(_ => ???) } + def checkpoint(name: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = { + WIO.RunIO( + _ => ???, + dummyEventHandler, + WIO.RunIO.Meta( + ErrorMeta.noError, + getEffectiveName(name, autoName).some, + None, + ), + ) + } + + def recovery(name: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = { + WIO.RunIO( + _ => ???, + dummyEventHandler, + WIO.RunIO.Meta( + ErrorMeta.noError, + getEffectiveName(name, autoName).some, + None, + ), + ) + } + def interruptionSignal( signalName: String = null, operationName: String = null, diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala new file mode 100644 index 00000000..10a2d78d --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala @@ -0,0 +1,16 @@ +package workflows4s.example.docs + +import workflows4s.wio.DraftWorkflowContext._ + +object DraftCheckpointExample { + + // start_draft_checkpoint + val v1 = WIO.draft.checkpoint("CP") >>> WIO.draft.step("A") >>> WIO.draft.step("B") + // end_draft_checkpoint + + // start_draft_recovery + val v2 = WIO.draft.recovery("RC") >>> WIO.draft.step("C") + // end_draft_recovery +} + + diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn b/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn new file mode 100644 index 00000000..c6144170 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn @@ -0,0 +1,62 @@ + + + + + sequenceFlow_4 + + + sequenceFlow_4 + sequenceFlow_5 + + + + sequenceFlow_5 + sequenceFlow_6 + + + + sequenceFlow_6 + sequenceFlow_7 + + + + sequenceFlow_7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.json b/workflows4s-example/src/test/resources/docs/draft-checkpoint.json new file mode 100644 index 00000000..77671cba --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.json @@ -0,0 +1,29 @@ +{ + "steps" : [ + { + "meta" : { + "name" : "CP", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "meta" : { + "name" : "A", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "meta" : { + "name" : "B", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + } + ], + "_type" : "Sequence" +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid b/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid new file mode 100644 index 00000000..033c4d82 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid @@ -0,0 +1,8 @@ +flowchart TD +node0@{ shape: circle, label: "Start"} +node1["CP"] +node0 --> node1 +node2["A"] +node1 --> node2 +node3["B"] +node2 --> node3 diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 25505328..4eb75321 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -31,6 +31,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("draft-interruption", DraftInterruptionExample.documentProcessingWorkflow), ExampleConfig("checkpoint", CheckpointExample.checkpoint.checkpointed, technical = true), ExampleConfig("recovery", CheckpointExample.recovery.myWorkflow, technical = true), + ExampleConfig("draft-checkpoint", DraftCheckpointExample.v1, technical = true), ExampleConfig("pure", PureExample.doThings), ExampleConfig("pure-error", PureExample.doThingsWithError), ExampleConfig("pull-request-draft", PullRequestWorkflowDraft.workflow), From b413bb410f6cdf0d3d05ea718310fb089d7febf4 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Fri, 12 Sep 2025 06:07:02 -0500 Subject: [PATCH 31/39] draft support for retry --- website/docs/operations/11-retry.mdx | 10 +++- .../wio/builders/DraftBuilder.scala | 7 +++ .../example/docs/DraftRetryExample.scala | 18 +++++++ .../src/test/resources/docs/draft-retry.bpmn | 50 +++++++++++++++++++ .../src/test/resources/docs/draft-retry.json | 32 ++++++++++++ .../test/resources/docs/draft-retry.mermaid | 8 +++ .../src/test/resources/docs/draft-retry.svg | 4 ++ .../example/docs/ExamplesTest.scala | 1 + 8 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala create mode 100644 workflows4s-example/src/test/resources/docs/draft-retry.bpmn create mode 100644 workflows4s-example/src/test/resources/docs/draft-retry.json create mode 100644 workflows4s-example/src/test/resources/docs/draft-retry.mermaid create mode 100644 workflows4s-example/src/test/resources/docs/draft-retry.svg diff --git a/website/docs/operations/11-retry.mdx b/website/docs/operations/11-retry.mdx index e505aa15..855b351a 100644 --- a/website/docs/operations/11-retry.mdx +++ b/website/docs/operations/11-retry.mdx @@ -61,4 +61,12 @@ For short-lived retries (e.g., retrying within milliseconds or seconds), prefer ## Drafting Support -For Retrying Operations, no dedicated draft API is required. Drafting can be done using the standard API. \ No newline at end of file +When drafting workflows that need retry handling, use `WIO.draft.retry()`: + +```scala file=./main/scala/workflows4s/example/docs/DraftRetryExample.scala start=start_draft end=end_draft +``` + + +The draft retry operation simplifies the API by: +- Not requiring explicit retry logic implementation +- Automatically generating names from the context if not provided diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index beee1bfd..26ab63e0 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -139,6 +139,13 @@ object DraftBuilder { WIO.Interruption(draftTimer, WIO.HandleInterruption.InterruptionType.Timer) } + def retry(base: WIO.Draft[Ctx]): WIO.Draft[Ctx] = { + WIO.Retry( + base, + (_: Throwable, _: WCState[Ctx], _: java.time.Instant) => ??? + ).transformInput((_: Any) => ???).map(_ => ???) + } + } } diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala new file mode 100644 index 00000000..523f45a8 --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala @@ -0,0 +1,18 @@ +package workflows4s.example.docs + +object DraftRetryExample { + + import workflows4s.wio.DraftWorkflowContext._ + + // start_draft + // Create a step that might fail and needs retry + val apiCall = WIO.draft.step("Call External API") + + // Wrap it with retry logic for visualization + val withRetry = WIO.draft.retry(apiCall) + + // Use it in a workflow + val workflow = WIO.draft.step("Prepare Request") >>> withRetry >>> WIO.draft.step("Process Response") + // end_draft + +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-retry.bpmn b/workflows4s-example/src/test/resources/docs/draft-retry.bpmn new file mode 100644 index 00000000..18362c6e --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-retry.bpmn @@ -0,0 +1,50 @@ + + + + + sequenceFlow_3 + + + sequenceFlow_3 + sequenceFlow_4 + + + + sequenceFlow_4 + sequenceFlow_5 + + + + sequenceFlow_5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflows4s-example/src/test/resources/docs/draft-retry.json b/workflows4s-example/src/test/resources/docs/draft-retry.json new file mode 100644 index 00000000..d1eb5370 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-retry.json @@ -0,0 +1,32 @@ +{ + "steps" : [ + { + "meta" : { + "name" : "Prepare Request", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + { + "base" : { + "meta" : { + "name" : "Call External API", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + }, + "_type" : "Retried" + }, + { + "meta" : { + "name" : "Process Response", + "error" : null, + "description" : null + }, + "_type" : "RunIO" + } + ], + "_type" : "Sequence" +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-retry.mermaid b/workflows4s-example/src/test/resources/docs/draft-retry.mermaid new file mode 100644 index 00000000..ced22055 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-retry.mermaid @@ -0,0 +1,8 @@ +flowchart TD +node0@{ shape: circle, label: "Start"} +node1["Prepare Request"] +node0 --> node1 +node2["fa:fa-redo Call External API"] +node1 --> node2 +node3["Process Response"] +node2 --> node3 diff --git a/workflows4s-example/src/test/resources/docs/draft-retry.svg b/workflows4s-example/src/test/resources/docs/draft-retry.svg new file mode 100644 index 00000000..2a071d03 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-retry.svg @@ -0,0 +1,4 @@ + + + +Prepare RequestProcess Response \ No newline at end of file diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 4eb75321..3d8da052 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -39,6 +39,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("for-each-draft", ForEachExample.draft.forEachDraft), ExampleConfig("for-each", ForEachExample.real.forEachStep), ExampleConfig("retry", RetryExample.withRetry), + ExampleConfig("draft-retry", DraftRetryExample.workflow), ) "examples" - { From 0723506787d7d161b42efa4ed714da6700866087 Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Fri, 12 Sep 2025 06:16:06 -0500 Subject: [PATCH 32/39] scalafmt --- .../wio/builders/DraftBuilder.scala | 74 +++++++++++-------- .../example/docs/DraftCheckpointExample.scala | 4 +- .../example/docs/DraftForkExample.scala | 8 +- .../docs/DraftInterruptionExample.scala | 14 ++-- .../example/docs/DraftLoopExample.scala | 13 ++-- .../example/docs/DraftParallelExample.scala | 3 +- .../example/docs/DraftRetryExample.scala | 8 +- .../example/docs/DraftSignalExample.scala | 2 +- .../example/docs/DraftTimerExample.scala | 4 +- 9 files changed, 69 insertions(+), 61 deletions(-) diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index 26ab63e0..daabd82c 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -16,7 +16,7 @@ object DraftBuilder { def draft: DraftBuilderStep1 = DraftBuilderStep1() class DraftBuilderStep1 { - def signal(name: String = null, error: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = WIO.HandleSignal( + def signal(name: String = null, error: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = WIO.HandleSignal( draftSignal, SignalHandler[Unit, Unit, Any]((_, _) => ???), dummyEventHandler, @@ -74,11 +74,14 @@ object DraftBuilder { val parallelElements = elements.map { element => WIO.Parallel.Element(element.map(_ => ???), (interimState: WCState[Ctx], _: WCState[Ctx]) => interimState) } - WIO.Parallel[Ctx, Any, Nothing, WCState[Ctx], WCState[Ctx]]( - elements = parallelElements, - formResult = _ => ???, - initialInterimState = (_: Any) => ??? - ).transformInput((_: Any) => ???).map(_ => ???) + WIO + .Parallel[Ctx, Any, Nothing, WCState[Ctx], WCState[Ctx]]( + elements = parallelElements, + formResult = _ => ???, + initialInterimState = (_: Any) => ???, + ) + .transformInput((_: Any) => ???) + .map(_ => ???) } def checkpoint(name: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = { @@ -108,42 +111,51 @@ object DraftBuilder { def interruptionSignal( signalName: String = null, operationName: String = null, - error: String = null + error: String = null, )(using autoName: sourcecode.Name): WIO.Interruption[Ctx, Nothing, Nothing] = { - val draftSignalHandling = WIO.HandleSignal( - draftSignal, - SignalHandler[Unit, Unit, WCState[Ctx]]((_, _) => ???), - dummyEventHandler[WCEvent[Ctx], Unit], - WIO.HandleSignal.Meta( - Option(error).map(ErrorMeta.Present(_)).getOrElse(ErrorMeta.noError), - Option(signalName).getOrElse(getEffectiveName(null, autoName)), - Option(operationName) - ), - ).transformInput((_: WCState[Ctx]) => ???).map(_ => ???) + val draftSignalHandling = WIO + .HandleSignal( + draftSignal, + SignalHandler[Unit, Unit, WCState[Ctx]]((_, _) => ???), + dummyEventHandler[WCEvent[Ctx], Unit], + WIO.HandleSignal.Meta( + Option(error).map(ErrorMeta.Present(_)).getOrElse(ErrorMeta.noError), + Option(signalName).getOrElse(getEffectiveName(null, autoName)), + Option(operationName), + ), + ) + .transformInput((_: WCState[Ctx]) => ???) + .map(_ => ???) WIO.Interruption(draftSignalHandling, WIO.HandleInterruption.InterruptionType.Signal) } def interruptionTimeout( timerName: String = null, - duration: FiniteDuration = null + duration: FiniteDuration = null, )(using autoName: sourcecode.Name): WIO.Interruption[Ctx, Nothing, Nothing] = { - val draftTimer = WIO.Timer( - Option(duration) match { - case Some(value) => WIO.Timer.DurationSource.Static(value.toJava) - case None => WIO.Timer.DurationSource.Dynamic(_ => ???) - }, - dummyEventHandler[WCEvent[Ctx], WIO.Timer.Started], - Option(timerName).orElse(getEffectiveName(null, autoName).some), - dummyEventHandler[WCEvent[Ctx], WIO.Timer.Released], - ).transformInput((_: WCState[Ctx]) => ???).map(_ => ???) + val draftTimer = WIO + .Timer( + Option(duration) match { + case Some(value) => WIO.Timer.DurationSource.Static(value.toJava) + case None => WIO.Timer.DurationSource.Dynamic(_ => ???) + }, + dummyEventHandler[WCEvent[Ctx], WIO.Timer.Started], + Option(timerName).orElse(getEffectiveName(null, autoName).some), + dummyEventHandler[WCEvent[Ctx], WIO.Timer.Released], + ) + .transformInput((_: WCState[Ctx]) => ???) + .map(_ => ???) WIO.Interruption(draftTimer, WIO.HandleInterruption.InterruptionType.Timer) } def retry(base: WIO.Draft[Ctx]): WIO.Draft[Ctx] = { - WIO.Retry( - base, - (_: Throwable, _: WCState[Ctx], _: java.time.Instant) => ??? - ).transformInput((_: Any) => ???).map(_ => ???) + WIO + .Retry( + base, + (_: Throwable, _: WCState[Ctx], _: java.time.Instant) => ???, + ) + .transformInput((_: Any) => ???) + .map(_ => ???) } } diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala index 10a2d78d..ef62e123 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala @@ -1,6 +1,6 @@ package workflows4s.example.docs -import workflows4s.wio.DraftWorkflowContext._ +import workflows4s.wio.DraftWorkflowContext.* object DraftCheckpointExample { @@ -12,5 +12,3 @@ object DraftCheckpointExample { val v2 = WIO.draft.recovery("RC") >>> WIO.draft.step("C") // end_draft_recovery } - - diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala index 01f1a839..d7999cd8 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala @@ -2,15 +2,15 @@ package workflows4s.example.docs object DraftForkExample { - import workflows4s.wio.DraftWorkflowContext._ + import workflows4s.wio.DraftWorkflowContext.* // start_draft val approveStep = WIO.draft.step("Approve") - val rejectStep = WIO.draft.step("Reject") - + val rejectStep = WIO.draft.step("Reject") + val approvalWorkflow = WIO.draft.choice("Review Decision")( "Approved" -> approveStep, - "Rejected" -> rejectStep + "Rejected" -> rejectStep, ) // end_draft diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala index ff30b858..6ce508cd 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala @@ -4,20 +4,20 @@ import scala.concurrent.duration.* object DraftInterruptionExample { - import workflows4s.wio.DraftWorkflowContext._ + import workflows4s.wio.DraftWorkflowContext.* // start_draft // Create signal and timeout interruptions - val urgentProcessing = WIO.draft.interruptionSignal("Urgent Processing Request") + val urgentProcessing = WIO.draft.interruptionSignal("Urgent Processing Request") val processingTimeout = WIO.draft.interruptionTimeout("Processing Deadline", 2.hours) // Document processing workflow that can be interrupted - val documentProcessingWorkflow = + val documentProcessingWorkflow = WIO.draft.step("Validate Document") >>> - WIO.draft.step("Extract Content") - .interruptWith(urgentProcessing) // Can be interrupted for urgent processing - .interruptWith(processingTimeout) // Must complete within 2 hours + WIO.draft + .step("Extract Content") + .interruptWith(urgentProcessing) // Can be interrupted for urgent processing + .interruptWith(processingTimeout) // Must complete within 2 hours // end_draft } - diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala index 815ecbb0..70feb868 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala @@ -1,24 +1,23 @@ package workflows4s.example.docs -import scala.concurrent.duration._ +import scala.concurrent.duration.* object DraftLoopExample { - import workflows4s.wio.DraftWorkflowContext._ + import workflows4s.wio.DraftWorkflowContext.* // start_draft // Create a draft loop with a timer to avoid busy loops val processStep = WIO.draft.step("Process Item") - val waitStep = WIO.draft.timer("Wait before retry", duration = 1.minute) - + val waitStep = WIO.draft.timer("Wait before retry", duration = 1.minute) + val loop = WIO.draft.repeat( conditionName = "Is processing complete?", releaseBranchName = "Yes", - restartBranchName = "No" + restartBranchName = "No", )( body = processStep >>> waitStep, - onRestart = WIO.draft.step("Reset for retry") + onRestart = WIO.draft.step("Reset for retry"), ) // end_draft } - diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala index ff44f95f..045481e4 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala @@ -1,7 +1,7 @@ package workflows4s.example.docs object DraftParallelExample { - import workflows4s.wio.DraftWorkflowContext._ + import workflows4s.wio.DraftWorkflowContext.* // start_draft // Create a simple parallel workflow @@ -12,4 +12,3 @@ object DraftParallelExample { val parallelWorkflow = WIO.draft.parallel(stepA, stepB, stepC) // end_draft } - diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala index 523f45a8..41a4da2b 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala @@ -2,17 +2,17 @@ package workflows4s.example.docs object DraftRetryExample { - import workflows4s.wio.DraftWorkflowContext._ + import workflows4s.wio.DraftWorkflowContext.* // start_draft // Create a step that might fail and needs retry val apiCall = WIO.draft.step("Call External API") - + // Wrap it with retry logic for visualization val withRetry = WIO.draft.retry(apiCall) - + // Use it in a workflow val workflow = WIO.draft.step("Prepare Request") >>> withRetry >>> WIO.draft.step("Process Response") // end_draft -} \ No newline at end of file +} diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala index 923e377e..6465ecbb 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala @@ -2,7 +2,7 @@ package workflows4s.example.docs object DraftSignalExample { - import workflows4s.wio.DraftWorkflowContext._ + import workflows4s.wio.DraftWorkflowContext.* // start_draft // Create a signal operation diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala index d9dbc641..510c5759 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala @@ -4,11 +4,11 @@ import scala.concurrent.duration.* object DraftTimerExample { - import workflows4s.wio.DraftWorkflowContext._ + import workflows4s.wio.DraftWorkflowContext.* // start_draft // Create timer operation with draft API val waitForReview = WIO.draft.timer("Wait for Review", duration = 24.hours) // end_draft -} \ No newline at end of file +} From e759d0df394854e54bdf24155c234f59b86dabca Mon Sep 17 00:00:00 2001 From: Watson Dinh Date: Fri, 12 Sep 2025 06:33:06 -0500 Subject: [PATCH 33/39] scalafmt --- .../test/scala/workflows4s/wio/WIODraftTest.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala b/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala index e11480d7..4cba4814 100644 --- a/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala +++ b/workflows4s-core/src/test/scala/workflows4s/wio/WIODraftTest.scala @@ -92,7 +92,7 @@ class WIODraftTest extends AnyFreeSpec with Matchers with OptionValues with Eith "should create a fork with correct branches" in { val approve = WIO.draft.step("Approve") val reject = WIO.draft.step("Reject") - val wio = WIO.draft.choice("Review")( + val wio = WIO.draft.choice("Review")( "Approved" -> approve, "Rejected" -> reject, ) @@ -110,11 +110,11 @@ class WIODraftTest extends AnyFreeSpec with Matchers with OptionValues with Eith } "should create a parallel step with multiple elements" in { - val step1: Draft[Ctx] = WIO.draft.step("task1") - val step2: Draft[Ctx] = WIO.draft.step("task2") - val step3: Draft[Ctx] = WIO.draft.step("task3") + val step1: Draft[Ctx] = WIO.draft.step("task1") + val step2: Draft[Ctx] = WIO.draft.step("task2") + val step3: Draft[Ctx] = WIO.draft.step("task3") val parallel: Draft[Ctx] = WIO.draft.parallel(step1, step2, step3) - val model = parallel.toProgress.toModel + val model = parallel.toProgress.toModel model match { case WIOModel.Parallel(elements) => @@ -122,7 +122,7 @@ class WIODraftTest extends AnyFreeSpec with Matchers with OptionValues with Eith elements(0) shouldBe WIOModel.RunIO(WIOMeta.RunIO(Some("task1"), None, None)) elements(1) shouldBe WIOModel.RunIO(WIOMeta.RunIO(Some("task2"), None, None)) elements(2) shouldBe WIOModel.RunIO(WIOMeta.RunIO(Some("task3"), None, None)) - case _ => fail("Expected Parallel model") + case _ => fail("Expected Parallel model") } } } From c7eb205caf09ea4411da957cd0911b67b15d40f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pitu=C5=82a?= Date: Wed, 24 Sep 2025 20:29:10 +0200 Subject: [PATCH 34/39] simplify docs, improve checkpoints, recovery and retries support --- website/docs/operations/01-run-code.mdx | 9 ++- website/docs/operations/02-await-signal.mdx | 9 +-- website/docs/operations/02.1-timers.mdx | 10 +-- .../{9-for-each.mdx => 05-for-each.mdx} | 6 +- website/docs/operations/05-loops.mdx | 10 +-- website/docs/operations/06-fork.mdx | 10 +-- website/docs/operations/07-Interrupting.mdx | 12 +-- website/docs/operations/08-parallel.mdx | 12 +-- website/docs/operations/10-checkpoints.mdx | 10 ++- website/docs/operations/11-retry.mdx | 8 +- website/docs/operations/20-drafting.mdx | 20 +++++ .../wio/builders/DraftBuilder.scala | 37 +++------- .../example/docs/DraftCheckpointExample.scala | 14 ---- .../example/docs/DraftRetryExample.scala | 18 ----- .../docs/draft/DraftCheckpointExample.scala | 20 +++++ .../docs/{ => draft}/DraftForkExample.scala | 2 +- .../DraftInterruptionExample.scala | 2 +- .../docs/{ => draft}/DraftLoopExample.scala | 2 +- .../{ => draft}/DraftParallelExample.scala | 2 +- .../docs/draft/DraftRetryExample.scala | 17 +++++ .../docs/{ => draft}/DraftSignalExample.scala | 6 +- .../example/docs/draft/DraftStepExample.scala | 12 +++ .../docs/{ => draft}/DraftTimerExample.scala | 4 +- .../src/test/resources/docs/and-then.svg | 2 +- .../src/test/resources/docs/checkpoint.svg | 2 +- .../test/resources/docs/draft-checkpoint.bpmn | 56 ++++---------- .../test/resources/docs/draft-checkpoint.json | 34 ++------- .../resources/docs/draft-checkpoint.mermaid | 9 +-- .../src/test/resources/docs/draft-choice.svg | 2 +- .../resources/docs/draft-interruption.svg | 2 +- .../src/test/resources/docs/draft-loop.svg | 2 +- .../test/resources/docs/draft-parallel.svg | 2 +- .../src/test/resources/docs/draft-retry.svg | 2 +- .../src/test/resources/docs/draft-signal.bpmn | 74 ++++++++----------- .../src/test/resources/docs/draft-signal.json | 27 ++----- .../test/resources/docs/draft-signal.mermaid | 6 +- .../src/test/resources/docs/draft-signal.svg | 2 +- .../src/test/resources/docs/draft-timer.bpmn | 2 +- .../src/test/resources/docs/draft-timer.json | 2 +- .../test/resources/docs/draft-timer.mermaid | 2 +- .../src/test/resources/docs/draft-timer.svg | 2 +- .../src/test/resources/docs/flat-map.svg | 2 +- .../test/resources/docs/for-each-draft.svg | 2 +- .../src/test/resources/docs/for-each.svg | 2 +- .../src/test/resources/docs/fork.svg | 2 +- .../test/resources/docs/handle-error-with.svg | 2 +- .../src/test/resources/docs/handle-signal.svg | 2 +- .../resources/docs/interruption-signal.svg | 2 +- .../src/test/resources/docs/loop.svg | 2 +- .../src/test/resources/docs/parallel.svg | 2 +- .../resources/docs/pull-request-draft.svg | 2 +- .../src/test/resources/docs/pull-request.svg | 2 +- .../src/test/resources/docs/pure-error.svg | 2 +- .../src/test/resources/docs/pure.svg | 2 +- .../src/test/resources/docs/recovery.svg | 2 +- .../src/test/resources/docs/retry.svg | 2 +- .../resources/docs/run-io-description.svg | 2 +- .../src/test/resources/docs/run-io-error.svg | 2 +- .../src/test/resources/docs/run-io.svg | 2 +- .../src/test/resources/docs/simple-loop.svg | 2 +- .../src/test/resources/docs/timer.svg | 2 +- .../example/docs/ExamplesTest.scala | 6 +- 62 files changed, 228 insertions(+), 300 deletions(-) rename website/docs/operations/{9-for-each.mdx => 05-for-each.mdx} (96%) create mode 100644 website/docs/operations/20-drafting.mdx delete mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala delete mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftCheckpointExample.scala rename workflows4s-example/src/main/scala/workflows4s/example/docs/{ => draft}/DraftForkExample.scala (89%) rename workflows4s-example/src/main/scala/workflows4s/example/docs/{ => draft}/DraftInterruptionExample.scala (94%) rename workflows4s-example/src/main/scala/workflows4s/example/docs/{ => draft}/DraftLoopExample.scala (93%) rename workflows4s-example/src/main/scala/workflows4s/example/docs/{ => draft}/DraftParallelExample.scala (89%) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftRetryExample.scala rename workflows4s-example/src/main/scala/workflows4s/example/docs/{ => draft}/DraftSignalExample.scala (55%) create mode 100644 workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftStepExample.scala rename workflows4s-example/src/main/scala/workflows4s/example/docs/{ => draft}/DraftTimerExample.scala (63%) diff --git a/website/docs/operations/01-run-code.mdx b/website/docs/operations/01-run-code.mdx index 3798fb72..f9b8f770 100644 --- a/website/docs/operations/01-run-code.mdx +++ b/website/docs/operations/01-run-code.mdx @@ -39,7 +39,7 @@ These operations need special handling because they shouldn't be re-executed dur :::info -Workflows4s currently supports onyl cats-effect IO. If you're interested in other effect systems, please check [here](https://github.com/business4s/workflows4s/issues/59). +Workflows4s currently supports only cats-effect IO. If you're interested in other effect systems, please check [here](https://github.com/business4s/workflows4s/issues/59). ::: @@ -69,3 +69,10 @@ You can add descriptions to RunIO operations to provide additional context in di ``` + +## Drafting support + +Executing logic comes with [drafting support](20-drafting.mdx). + +```scala file=./main/scala/workflows4s/example/docs/draft/DraftStepExample.scala start=start_doc end=end_doc +``` diff --git a/website/docs/operations/02-await-signal.mdx b/website/docs/operations/02-await-signal.mdx index 7c200b11..1ae2c484 100644 --- a/website/docs/operations/02-await-signal.mdx +++ b/website/docs/operations/02-await-signal.mdx @@ -11,18 +11,13 @@ Signal handling is essential for workflows that need to pause and wait for exter ## Drafting Support -When drafting workflows that need signal handling, use `WIO.draft.signal()`: +Awaiting signals come with [drafting support](20-drafting.mdx). -```scala file=./main/scala/workflows4s/example/docs/DraftSignalExample.scala start=start_draft end=end_draft +```scala file=./main/scala/workflows4s/example/docs/draft/DraftSignalExample.scala start=start_draft end=end_draft ``` -The draft signal operation simplifies the API by: -- Not requiring signal type parameters -- Not requiring explicit event handling -- Automatically generating names from the context if not provided - # Unhandled Signals The Workflows4s API allows arbitrary signals to be sent to a workflow instance. While this provides flexibility, it also diff --git a/website/docs/operations/02.1-timers.mdx b/website/docs/operations/02.1-timers.mdx index 207a72e3..218a6ad8 100644 --- a/website/docs/operations/02.1-timers.mdx +++ b/website/docs/operations/02.1-timers.mdx @@ -12,16 +12,12 @@ This operation enables time-based coordination, allowing workflows to pause for ``` + ## Drafting Support -When drafting workflows that need time-based operations, use `WIO.draft.timer()`: +Awaiting time comes with [drafting support](20-drafting.mdx). -```scala file=./main/scala/workflows4s/example/docs/DraftTimerExample.scala start=start_draft end=end_draft +```scala file=./main/scala/workflows4s/example/docs/draft/DraftTimerExample.scala start=start_draft end=end_draft ``` - -The draft timer operation simplifies the API by: -- Not requiring explicit event handling -- Making duration parameter optional -- Automatically generating names from the context if not provided diff --git a/website/docs/operations/9-for-each.mdx b/website/docs/operations/05-for-each.mdx similarity index 96% rename from website/docs/operations/9-for-each.mdx rename to website/docs/operations/05-for-each.mdx index b391e51d..3bd279b3 100644 --- a/website/docs/operations/9-for-each.mdx +++ b/website/docs/operations/05-for-each.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 9.1 +sidebar_position: 5.1 --- import OperationOutputs from '@site/src/components/OperationOutputs'; @@ -23,9 +23,9 @@ This element is one of the most complicated ones and requires you to configure q -### Draft Mode +## Drafting Support -For quick prototyping, you can use the draft API: +Iterating comes with [drafting support](20-drafting.mdx). ```scala file=./main/scala/workflows4s/example/docs/ForEachExample.scala start=draft_start end=draft_end ``` diff --git a/website/docs/operations/05-loops.mdx b/website/docs/operations/05-loops.mdx index c53680b1..a1fbcaa4 100644 --- a/website/docs/operations/05-loops.mdx +++ b/website/docs/operations/05-loops.mdx @@ -20,15 +20,9 @@ In the future this will be detected by the [linter](https://github.com/business4 ## Drafting Support -When drafting workflows that need loops, use `WIO.draft.repeat()` to create conditional loops with a simpler API: +Loops come with [drafting support](20-drafting.mdx). -```scala file=./main/scala/workflows4s/example/docs/DraftLoopExample.scala start=start_draft end=end_draft +```scala file=./main/scala/workflows4s/example/docs/draft/DraftLoopExample.scala start=start_draft end=end_draft ``` - -The draft repeat operation simplifies the API by: -- Not requiring explicit event handling -- Making condition functions optional -- Making return branch optional -- Automatically generating names from the context if not provided diff --git a/website/docs/operations/06-fork.mdx b/website/docs/operations/06-fork.mdx index ee5eb184..980ea701 100644 --- a/website/docs/operations/06-fork.mdx +++ b/website/docs/operations/06-fork.mdx @@ -12,13 +12,9 @@ It's equivalent to `if` instructions but allow for static rendering of the workf ## Drafting Support -When drafting workflows that need conditional branching, use `WIO.draft.choice()` to create forks with a simpler API: +Branching comes with [drafting support](20-drafting.mdx). -```scala file=./main/scala/workflows4s/example/docs/DraftForkExample.scala start=start_draft end=end_draft +```scala file=./main/scala/workflows4s/example/docs/draft/DraftForkExample.scala start=start_draft end=end_draft ``` - - -The draft choice operation simplifies the API by: -- Not requiring explicit condition functions -- Automatically generating names from the context if not provided \ No newline at end of file + \ No newline at end of file diff --git a/website/docs/operations/07-Interrupting.mdx b/website/docs/operations/07-Interrupting.mdx index 2265480a..02c4fcd0 100644 --- a/website/docs/operations/07-Interrupting.mdx +++ b/website/docs/operations/07-Interrupting.mdx @@ -12,15 +12,9 @@ This allows selecting an alternative path based on such a signal. ## Drafting Support -When drafting workflows that need interruption handling, use `WIO.draft.interruptionSignal()` and `WIO.draft.interruptionTimeout()`: +Interruptions come with [drafting support](20-drafting.mdx). -```scala file=./main/scala/workflows4s/example/docs/DraftInterruptionExample.scala start=start_draft end=end_draft +```scala file=./main/scala/workflows4s/example/docs/draft/DraftInterruptionExample.scala start=start_draft end=end_draft ``` - - -The draft interruption operations simplify the API by: -- Not requiring signal type parameters or event handling for signals -- Not requiring duration source configuration for timeouts -- Automatically generating names from the context if not provided -- Providing a simple interface for creating interruptible workflows + \ No newline at end of file diff --git a/website/docs/operations/08-parallel.mdx b/website/docs/operations/08-parallel.mdx index 1a960cb6..21e33db4 100644 --- a/website/docs/operations/08-parallel.mdx +++ b/website/docs/operations/08-parallel.mdx @@ -16,15 +16,9 @@ Workflow's state is continously updated after each step completion (doesn't wait ## Drafting Support -When drafting workflows that need parallel execution, use `WIO.draft.parallel()` to run multiple branches concurrently with a simpler API: +Parallel flows come with [drafting support](20-drafting.mdx). -```scala file=./main/scala/workflows4s/example/docs/DraftParallelExample.scala start=start_draft end=end_draft +```scala file=./main/scala/workflows4s/example/docs/draft/DraftParallelExample.scala start=start_draft end=end_draft ``` - - -The draft parallel operation simplifies the API by: -- Not requiring explicit interim state or result construction -- Not requiring explicit event handling -- Automatically generating names from the context if not provided -- Providing a more intuitive interface for running steps in parallel \ No newline at end of file + \ No newline at end of file diff --git a/website/docs/operations/10-checkpoints.mdx b/website/docs/operations/10-checkpoints.mdx index 949a020f..1a14c6e9 100644 --- a/website/docs/operations/10-checkpoints.mdx +++ b/website/docs/operations/10-checkpoints.mdx @@ -53,10 +53,14 @@ In practice, checkpointing and recovery are often used together to enable workfl ## Drafting Support -When drafting workflows that need checkpointing and recovery, you can use the draft API. +Checkpointing and recovery come with [drafting support](20-drafting.mdx). ### Draft Checkpoint -```scala file=./main/scala/workflows4s/example/docs/DraftCheckpointExample.scala start=start_draft_checkpoint end=end_draft_recovery +```scala file=./main/scala/workflows4s/example/docs/draft/DraftCheckpointExample.scala start=start_draft_checkpoint end=end_draft_checkpoint +``` + +### Draft Checkpoint +```scala file=./main/scala/workflows4s/example/docs/draft/DraftCheckpointExample.scala start=start_draft_recovery end=end_draft_recovery ``` - + diff --git a/website/docs/operations/11-retry.mdx b/website/docs/operations/11-retry.mdx index 855b351a..877385bd 100644 --- a/website/docs/operations/11-retry.mdx +++ b/website/docs/operations/11-retry.mdx @@ -61,12 +61,8 @@ For short-lived retries (e.g., retrying within milliseconds or seconds), prefer ## Drafting Support -When drafting workflows that need retry handling, use `WIO.draft.retry()`: +Retries come with [drafting support](20-drafting.mdx). -```scala file=./main/scala/workflows4s/example/docs/DraftRetryExample.scala start=start_draft end=end_draft +```scala file=./main/scala/workflows4s/example/docs/draft/DraftRetryExample.scala start=start_draft end=end_draft ``` - -The draft retry operation simplifies the API by: -- Not requiring explicit retry logic implementation -- Automatically generating names from the context if not provided diff --git a/website/docs/operations/20-drafting.mdx b/website/docs/operations/20-drafting.mdx new file mode 100644 index 00000000..865955dd --- /dev/null +++ b/website/docs/operations/20-drafting.mdx @@ -0,0 +1,20 @@ +# Drafting + +Workflows4s comes with a dedicated API for creating renderable but NOT runnable workflow definitions. +Its goal is to allow defining the structure of a workflow that can be discussed with the team or stakeholders, +before committing more time to the actual implementation. + +The entire drafting API is exposed under `WIO.draft` and documented alongside specific operations. + +## Details + +Drafts use exactly the same model under the hood as real definitions, but filled with dummy logic. + +The drafting support simplifies the API in the following ways: + +* Not requiring type parameters +* Not requiring event handling +* Not requiring run logic +* Automatically generating names from the context if not provided explicitly +* Making as many parameters as possible optional + diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index daabd82c..66189640 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -13,9 +13,9 @@ object DraftBuilder { trait Step0[Ctx <: WorkflowContext]() { - def draft: DraftBuilderStep1 = DraftBuilderStep1() + val draft: DraftBuilderStep1.type = DraftBuilderStep1 - class DraftBuilderStep1 { + object DraftBuilderStep1 { def signal(name: String = null, error: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = WIO.HandleSignal( draftSignal, SignalHandler[Unit, Unit, Any]((_, _) => ???), @@ -84,29 +84,7 @@ object DraftBuilder { .map(_ => ???) } - def checkpoint(name: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = { - WIO.RunIO( - _ => ???, - dummyEventHandler, - WIO.RunIO.Meta( - ErrorMeta.noError, - getEffectiveName(name, autoName).some, - None, - ), - ) - } - - def recovery(name: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = { - WIO.RunIO( - _ => ???, - dummyEventHandler, - WIO.RunIO.Meta( - ErrorMeta.noError, - getEffectiveName(name, autoName).some, - None, - ), - ) - } + def recovery: WIO.Draft[Ctx] = WIO.Recovery(dummyEventHandler) def interruptionSignal( signalName: String = null, @@ -157,6 +135,15 @@ object DraftBuilder { .transformInput((_: Any) => ???) .map(_ => ???) } + def checkpoint(base: WIO.Draft[Ctx]): WIO.Draft[Ctx] = WIO.Checkpoint(base, (_, _) => ???, dummyEventHandler) + + + object syntax { + extension (base: WIO.Draft[Ctx]) { + def draftCheckpointed: WIO.Draft[Ctx] = checkpoint(base) + def draftRetry: WIO.Draft[Ctx] = retry(base) + } + } } diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala deleted file mode 100644 index ef62e123..00000000 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftCheckpointExample.scala +++ /dev/null @@ -1,14 +0,0 @@ -package workflows4s.example.docs - -import workflows4s.wio.DraftWorkflowContext.* - -object DraftCheckpointExample { - - // start_draft_checkpoint - val v1 = WIO.draft.checkpoint("CP") >>> WIO.draft.step("A") >>> WIO.draft.step("B") - // end_draft_checkpoint - - // start_draft_recovery - val v2 = WIO.draft.recovery("RC") >>> WIO.draft.step("C") - // end_draft_recovery -} diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala deleted file mode 100644 index 41a4da2b..00000000 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftRetryExample.scala +++ /dev/null @@ -1,18 +0,0 @@ -package workflows4s.example.docs - -object DraftRetryExample { - - import workflows4s.wio.DraftWorkflowContext.* - - // start_draft - // Create a step that might fail and needs retry - val apiCall = WIO.draft.step("Call External API") - - // Wrap it with retry logic for visualization - val withRetry = WIO.draft.retry(apiCall) - - // Use it in a workflow - val workflow = WIO.draft.step("Prepare Request") >>> withRetry >>> WIO.draft.step("Process Response") - // end_draft - -} diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftCheckpointExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftCheckpointExample.scala new file mode 100644 index 00000000..ae546766 --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftCheckpointExample.scala @@ -0,0 +1,20 @@ +package workflows4s.example.docs.draft + +import workflows4s.wio.DraftWorkflowContext.* + +object DraftCheckpointExample { + + // start_draft_checkpoint + val base = WIO.draft.step() + + val checkpointed = WIO.draft.checkpoint(base) + + // or with a postfix application + import WIO.draft.syntax.* + val checkpointed2 = base.draftCheckpointed + // end_draft_checkpoint + + // start_draft_recovery + val recovery = WIO.draft.recovery + // end_draft_recovery +} diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftForkExample.scala similarity index 89% rename from workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala rename to workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftForkExample.scala index d7999cd8..f9cbac56 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftForkExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftForkExample.scala @@ -1,4 +1,4 @@ -package workflows4s.example.docs +package workflows4s.example.docs.draft object DraftForkExample { diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftInterruptionExample.scala similarity index 94% rename from workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala rename to workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftInterruptionExample.scala index 6ce508cd..fcd46bb9 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftInterruptionExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftInterruptionExample.scala @@ -1,4 +1,4 @@ -package workflows4s.example.docs +package workflows4s.example.docs.draft import scala.concurrent.duration.* diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftLoopExample.scala similarity index 93% rename from workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala rename to workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftLoopExample.scala index 70feb868..fd35152d 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftLoopExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftLoopExample.scala @@ -1,4 +1,4 @@ -package workflows4s.example.docs +package workflows4s.example.docs.draft import scala.concurrent.duration.* diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftParallelExample.scala similarity index 89% rename from workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala rename to workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftParallelExample.scala index 045481e4..eaa4ae2d 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftParallelExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftParallelExample.scala @@ -1,4 +1,4 @@ -package workflows4s.example.docs +package workflows4s.example.docs.draft object DraftParallelExample { import workflows4s.wio.DraftWorkflowContext.* diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftRetryExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftRetryExample.scala new file mode 100644 index 00000000..795015c1 --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftRetryExample.scala @@ -0,0 +1,17 @@ +package workflows4s.example.docs.draft + +object DraftRetryExample { + + import workflows4s.wio.DraftWorkflowContext.* + + // start_draft + val apiCall = WIO.draft.step() + + val withRetry = WIO.draft.retry(apiCall) + + // or with a postfix application + import WIO.draft.syntax.* + val withRetry2 = apiCall.draftRetry + // end_draft + +} diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftSignalExample.scala similarity index 55% rename from workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala rename to workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftSignalExample.scala index 6465ecbb..4de8b2e0 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftSignalExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftSignalExample.scala @@ -1,15 +1,11 @@ -package workflows4s.example.docs +package workflows4s.example.docs.draft object DraftSignalExample { import workflows4s.wio.DraftWorkflowContext.* // start_draft - // Create a signal operation val awaitApproval = WIO.draft.signal("Approval Required", error = "Rejected") - - // Use it in a workflow - val workflow = WIO.draft.step("Submit PR") >>> awaitApproval // end_draft } diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftStepExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftStepExample.scala new file mode 100644 index 00000000..d1c92e85 --- /dev/null +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftStepExample.scala @@ -0,0 +1,12 @@ +package workflows4s.example.docs.draft + +import workflows4s.wio.DraftWorkflowContext.* + +object DraftStepExample { + + // start_doc + val basic = WIO.draft.step() + + val withError = WIO.draft.step(error = "MyError") + // end_doc +} diff --git a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftTimerExample.scala similarity index 63% rename from workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala rename to workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftTimerExample.scala index 510c5759..8d37bf92 100644 --- a/workflows4s-example/src/main/scala/workflows4s/example/docs/DraftTimerExample.scala +++ b/workflows4s-example/src/main/scala/workflows4s/example/docs/draft/DraftTimerExample.scala @@ -1,4 +1,4 @@ -package workflows4s.example.docs +package workflows4s.example.docs.draft import scala.concurrent.duration.* @@ -8,7 +8,7 @@ object DraftTimerExample { // start_draft // Create timer operation with draft API - val waitForReview = WIO.draft.timer("Wait for Review", duration = 24.hours) + val waitForReview = WIO.draft.timer(duration = 24.hours) // end_draft } diff --git a/workflows4s-example/src/test/resources/docs/and-then.svg b/workflows4s-example/src/test/resources/docs/and-then.svg index dae3e306..6f41522d 100644 --- a/workflows4s-example/src/test/resources/docs/and-then.svg +++ b/workflows4s-example/src/test/resources/docs/and-then.svg @@ -1,4 +1,4 @@ -Step1Step2Step3 \ No newline at end of file +Step1Step2Step3 \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/checkpoint.svg b/workflows4s-example/src/test/resources/docs/checkpoint.svg index 9f45b80f..ea42daf7 100644 --- a/workflows4s-example/src/test/resources/docs/checkpoint.svg +++ b/workflows4s-example/src/test/resources/docs/checkpoint.svg @@ -1,4 +1,4 @@ -My Workflow \ No newline at end of file +My Workflow \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn b/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn index c6144170..a339b520 100644 --- a/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn @@ -1,62 +1,38 @@ - - sequenceFlow_4 + + sequenceFlow_2 - - sequenceFlow_4 - sequenceFlow_5 + + sequenceFlow_2 + sequenceFlow_3 - - - sequenceFlow_5 - sequenceFlow_6 - - - - sequenceFlow_6 - sequenceFlow_7 - - - - sequenceFlow_7 + + + sequenceFlow_3 - + - - + + - + - + - - + + - + - - - - - - - - - - - - - - diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.json b/workflows4s-example/src/test/resources/docs/draft-checkpoint.json index 77671cba..72891d89 100644 --- a/workflows4s-example/src/test/resources/docs/draft-checkpoint.json +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.json @@ -1,29 +1,11 @@ { - "steps" : [ - { - "meta" : { - "name" : "CP", - "error" : null, - "description" : null - }, - "_type" : "RunIO" + "base" : { + "meta" : { + "name" : "A", + "error" : null, + "description" : null }, - { - "meta" : { - "name" : "A", - "error" : null, - "description" : null - }, - "_type" : "RunIO" - }, - { - "meta" : { - "name" : "B", - "error" : null, - "description" : null - }, - "_type" : "RunIO" - } - ], - "_type" : "Sequence" + "_type" : "RunIO" + }, + "_type" : "Checkpoint" } \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid b/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid index 033c4d82..30db1260 100644 --- a/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid @@ -1,8 +1,7 @@ flowchart TD node0@{ shape: circle, label: "Start"} -node1["CP"] -node0 --> node1 +node1:::checkpoint +subgraph node1 ["Checkpoint"] node2["A"] -node1 --> node2 -node3["B"] -node2 --> node3 +node0 --> node2 +end diff --git a/workflows4s-example/src/test/resources/docs/draft-choice.svg b/workflows4s-example/src/test/resources/docs/draft-choice.svg index e26bbf57..441d0ad7 100644 --- a/workflows4s-example/src/test/resources/docs/draft-choice.svg +++ b/workflows4s-example/src/test/resources/docs/draft-choice.svg @@ -1,4 +1,4 @@ -Review DecisionApproveRejectApprovedRejected \ No newline at end of file +Review DecisionApproveRejectApprovedRejected \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-interruption.svg b/workflows4s-example/src/test/resources/docs/draft-interruption.svg index 8b1ed751..de1088a3 100644 --- a/workflows4s-example/src/test/resources/docs/draft-interruption.svg +++ b/workflows4s-example/src/test/resources/docs/draft-interruption.svg @@ -1,4 +1,4 @@ -Validate DocumentExtract ContentHandle Urgent Processing RequestUrgent Processing RequestProcessing Deadline \ No newline at end of file +Validate DocumentExtract ContentHandle Urgent Processing RequestUrgent Processing RequestProcessing Deadline \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-loop.svg b/workflows4s-example/src/test/resources/docs/draft-loop.svg index 788924be..59e20e1d 100644 --- a/workflows4s-example/src/test/resources/docs/draft-loop.svg +++ b/workflows4s-example/src/test/resources/docs/draft-loop.svg @@ -1,4 +1,4 @@ -Process ItemWait before retryIs processing complete?Reset for retryYesNo \ No newline at end of file +Process ItemWait before retryIs processing complete?Reset for retryYesNo \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-parallel.svg b/workflows4s-example/src/test/resources/docs/draft-parallel.svg index 42d44466..44383a68 100644 --- a/workflows4s-example/src/test/resources/docs/draft-parallel.svg +++ b/workflows4s-example/src/test/resources/docs/draft-parallel.svg @@ -1,4 +1,4 @@ -Task ATask BTask C \ No newline at end of file +Task ATask BTask C \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-retry.svg b/workflows4s-example/src/test/resources/docs/draft-retry.svg index 2a071d03..43259b4b 100644 --- a/workflows4s-example/src/test/resources/docs/draft-retry.svg +++ b/workflows4s-example/src/test/resources/docs/draft-retry.svg @@ -1,4 +1,4 @@ -Prepare RequestProcess Response \ No newline at end of file +Prepare RequestProcess Response \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.bpmn b/workflows4s-example/src/test/resources/docs/draft-signal.bpmn index d199bbcd..c653151e 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.bpmn +++ b/workflows4s-example/src/test/resources/docs/draft-signal.bpmn @@ -1,70 +1,58 @@ - - sequenceFlow_6 + + sequenceFlow_5 - - sequenceFlow_6 - sequenceFlow_7 - - - sequenceFlow_7 - sequenceFlow_8 - + sequenceFlow_5 + sequenceFlow_6 + - - - sequenceFlow_8 - sequenceFlow_9 + + + sequenceFlow_6 + sequenceFlow_7 - - + + - - sequenceFlow_9 + + sequenceFlow_7 - + - + - - + + - - + + - + - - + + - - - + + + - - + + + + + - + - - - - - - - - - - diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.json b/workflows4s-example/src/test/resources/docs/draft-signal.json index c7e35d18..21d44c66 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.json +++ b/workflows4s-example/src/test/resources/docs/draft-signal.json @@ -1,23 +1,10 @@ { - "steps" : [ - { - "meta" : { - "name" : "Submit PR", - "error" : null, - "description" : null - }, - "_type" : "RunIO" - }, - { - "meta" : { - "signalName" : "Approval Required", - "operationName" : null, - "error" : { - "name" : "Rejected" - } - }, - "_type" : "HandleSignal" + "meta" : { + "signalName" : "Approval Required", + "operationName" : null, + "error" : { + "name" : "Rejected" } - ], - "_type" : "Sequence" + }, + "_type" : "HandleSignal" } \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.mermaid b/workflows4s-example/src/test/resources/docs/draft-signal.mermaid index 9e452da7..479d1b65 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.mermaid +++ b/workflows4s-example/src/test/resources/docs/draft-signal.mermaid @@ -1,8 +1,6 @@ flowchart TD node0@{ shape: circle, label: "Start"} -node1["Submit PR"] +node1@{ shape: stadium, label: "fa:fa-envelope Approval Required"} node0 --> node1 -node2@{ shape: stadium, label: "fa:fa-envelope Approval Required"} +node2["Handle Approval Required"] node1 --> node2 -node3["Handle Approval Required"] -node2 --> node3 diff --git a/workflows4s-example/src/test/resources/docs/draft-signal.svg b/workflows4s-example/src/test/resources/docs/draft-signal.svg index c538e6ae..5bada152 100644 --- a/workflows4s-example/src/test/resources/docs/draft-signal.svg +++ b/workflows4s-example/src/test/resources/docs/draft-signal.svg @@ -1,4 +1,4 @@ -Submit PRApproval RequiredHandle "Approval Required"Rejected \ No newline at end of file +Approval RequiredHandle "Approval Required"Rejected \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-timer.bpmn b/workflows4s-example/src/test/resources/docs/draft-timer.bpmn index 497413ff..c87e13f1 100644 --- a/workflows4s-example/src/test/resources/docs/draft-timer.bpmn +++ b/workflows4s-example/src/test/resources/docs/draft-timer.bpmn @@ -4,7 +4,7 @@ sequenceFlow_3 - + sequenceFlow_3 sequenceFlow_4 diff --git a/workflows4s-example/src/test/resources/docs/draft-timer.json b/workflows4s-example/src/test/resources/docs/draft-timer.json index 11212569..462bf3c7 100644 --- a/workflows4s-example/src/test/resources/docs/draft-timer.json +++ b/workflows4s-example/src/test/resources/docs/draft-timer.json @@ -2,7 +2,7 @@ "meta" : { "duration" : "PT24H", "releaseAt" : null, - "name" : "Wait for Review" + "name" : "Wait For Review" }, "_type" : "Timer" } \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-timer.mermaid b/workflows4s-example/src/test/resources/docs/draft-timer.mermaid index 59fd12f4..6390148e 100644 --- a/workflows4s-example/src/test/resources/docs/draft-timer.mermaid +++ b/workflows4s-example/src/test/resources/docs/draft-timer.mermaid @@ -1,4 +1,4 @@ flowchart TD node0@{ shape: circle, label: "Start"} -node1@{ shape: stadium, label: "fa:fa-clock Wait for Review (24h)"} +node1@{ shape: stadium, label: "fa:fa-clock Wait For Review (24h)"} node0 --> node1 diff --git a/workflows4s-example/src/test/resources/docs/draft-timer.svg b/workflows4s-example/src/test/resources/docs/draft-timer.svg index 5180ed0b..a7da734f 100644 --- a/workflows4s-example/src/test/resources/docs/draft-timer.svg +++ b/workflows4s-example/src/test/resources/docs/draft-timer.svg @@ -1,4 +1,4 @@ -Wait for Review \ No newline at end of file +Wait For Review \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/flat-map.svg b/workflows4s-example/src/test/resources/docs/flat-map.svg index 8478c55e..fc5b8733 100644 --- a/workflows4s-example/src/test/resources/docs/flat-map.svg +++ b/workflows4s-example/src/test/resources/docs/flat-map.svg @@ -1,4 +1,4 @@ -Step1<Dynamic> \ No newline at end of file +Step1<Dynamic> \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/for-each-draft.svg b/workflows4s-example/src/test/resources/docs/for-each-draft.svg index 87a24ec8..ef92cd39 100644 --- a/workflows4s-example/src/test/resources/docs/for-each-draft.svg +++ b/workflows4s-example/src/test/resources/docs/for-each-draft.svg @@ -1,4 +1,4 @@ -For Each DraftSub Workflow \ No newline at end of file +For Each DraftSub Workflow \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/for-each.svg b/workflows4s-example/src/test/resources/docs/for-each.svg index 3efac08e..abe1e3a3 100644 --- a/workflows4s-example/src/test/resources/docs/for-each.svg +++ b/workflows4s-example/src/test/resources/docs/for-each.svg @@ -1,4 +1,4 @@ -For Each StepElement Workflow \ No newline at end of file +For Each StepElement Workflow \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/fork.svg b/workflows4s-example/src/test/resources/docs/fork.svg index 9e64f1a7..280348d3 100644 --- a/workflows4s-example/src/test/resources/docs/fork.svg +++ b/workflows4s-example/src/test/resources/docs/fork.svg @@ -1,4 +1,4 @@ -Is counter positive?Do ADo BYesNo \ No newline at end of file +Is counter positive?Do ADo BYesNo \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/handle-error-with.svg b/workflows4s-example/src/test/resources/docs/handle-error-with.svg index 258b1569..80b97c31 100644 --- a/workflows4s-example/src/test/resources/docs/handle-error-with.svg +++ b/workflows4s-example/src/test/resources/docs/handle-error-with.svg @@ -1,4 +1,4 @@ -Do ThingsMy ErrorHandle That Nasty ErrorMy Error \ No newline at end of file +Do ThingsMy ErrorHandle That Nasty ErrorMy Error \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/handle-signal.svg b/workflows4s-example/src/test/resources/docs/handle-signal.svg index a7f84d70..7d39bd21 100644 --- a/workflows4s-example/src/test/resources/docs/handle-signal.svg +++ b/workflows4s-example/src/test/resources/docs/handle-signal.svg @@ -1,4 +1,4 @@ -My RequestDo Things \ No newline at end of file +My RequestDo Things \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/interruption-signal.svg b/workflows4s-example/src/test/resources/docs/interruption-signal.svg index b48c2a39..9b0b36dc 100644 --- a/workflows4s-example/src/test/resources/docs/interruption-signal.svg +++ b/workflows4s-example/src/test/resources/docs/interruption-signal.svg @@ -1,4 +1,4 @@ -Do AHandle My RequestDo BMy Request \ No newline at end of file +Do AHandle My RequestDo BMy Request \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/loop.svg b/workflows4s-example/src/test/resources/docs/loop.svg index f150c5b3..185b3606 100644 --- a/workflows4s-example/src/test/resources/docs/loop.svg +++ b/workflows4s-example/src/test/resources/docs/loop.svg @@ -1,4 +1,4 @@ -Step1Is everything done?Step2Yes!No \ No newline at end of file +Step1Is everything done?Step2Yes!No \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/parallel.svg b/workflows4s-example/src/test/resources/docs/parallel.svg index 2697439d..0f667de7 100644 --- a/workflows4s-example/src/test/resources/docs/parallel.svg +++ b/workflows4s-example/src/test/resources/docs/parallel.svg @@ -1,4 +1,4 @@ -Do ADo B \ No newline at end of file +Do ADo B \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/pull-request-draft.svg b/workflows4s-example/src/test/resources/docs/pull-request-draft.svg index 4c40421f..a451ac13 100644 --- a/workflows4s-example/src/test/resources/docs/pull-request-draft.svg +++ b/workflows4s-example/src/test/resources/docs/pull-request-draft.svg @@ -1,4 +1,4 @@ -Create PRHandle "CreatePR"Run PipelineAwait ReviewHandle "Await Review"Merge PRRejectedCritical IssueClose PR \ No newline at end of file +Create PRHandle "CreatePR"Run PipelineAwait ReviewHandle "Await Review"Merge PRRejectedCritical IssueClose PR \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/pull-request.svg b/workflows4s-example/src/test/resources/docs/pull-request.svg index 8018b8b0..a80217f5 100644 --- a/workflows4s-example/src/test/resources/docs/pull-request.svg +++ b/workflows4s-example/src/test/resources/docs/pull-request.svg @@ -1,4 +1,4 @@ -Create RequestCreate PRRun PipelineReview RequestProcess ReviewMerge PRReview RejectedPipeline FailedCommit Not FoundClose PRPRError \ No newline at end of file +Create RequestCreate PRRun PipelineReview RequestProcess ReviewMerge PRReview RejectedPipeline FailedCommit Not FoundClose PRPRError \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/pure-error.svg b/workflows4s-example/src/test/resources/docs/pure-error.svg index 14c268af..989181bf 100644 --- a/workflows4s-example/src/test/resources/docs/pure-error.svg +++ b/workflows4s-example/src/test/resources/docs/pure-error.svg @@ -1,4 +1,4 @@ -Do Things With ErrorMy Error \ No newline at end of file +Do Things With ErrorMy Error \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/pure.svg b/workflows4s-example/src/test/resources/docs/pure.svg index 0e3c986e..17c1ce91 100644 --- a/workflows4s-example/src/test/resources/docs/pure.svg +++ b/workflows4s-example/src/test/resources/docs/pure.svg @@ -1,4 +1,4 @@ -Do Things \ No newline at end of file +Do Things \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/recovery.svg b/workflows4s-example/src/test/resources/docs/recovery.svg index 0df9515a..79e67bac 100644 --- a/workflows4s-example/src/test/resources/docs/recovery.svg +++ b/workflows4s-example/src/test/resources/docs/recovery.svg @@ -1,4 +1,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/retry.svg b/workflows4s-example/src/test/resources/docs/retry.svg index 2cfe7824..56988c43 100644 --- a/workflows4s-example/src/test/resources/docs/retry.svg +++ b/workflows4s-example/src/test/resources/docs/retry.svg @@ -1,4 +1,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/run-io-description.svg b/workflows4s-example/src/test/resources/docs/run-io-description.svg index c4369f71..dc7931a2 100644 --- a/workflows4s-example/src/test/resources/docs/run-io-description.svg +++ b/workflows4s-example/src/test/resources/docs/run-io-description.svg @@ -1,4 +1,4 @@ -Do Things With DescriptionThis operation increments the counter by processing an event \ No newline at end of file +Do Things With DescriptionThis operation increments the counter by one \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/run-io-error.svg b/workflows4s-example/src/test/resources/docs/run-io-error.svg index 7287d2e2..c661e4af 100644 --- a/workflows4s-example/src/test/resources/docs/run-io-error.svg +++ b/workflows4s-example/src/test/resources/docs/run-io-error.svg @@ -1,4 +1,4 @@ -Do Things With ErrorMy Error \ No newline at end of file +Do Things With ErrorMy Error \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/run-io.svg b/workflows4s-example/src/test/resources/docs/run-io.svg index 52192557..a37f60d2 100644 --- a/workflows4s-example/src/test/resources/docs/run-io.svg +++ b/workflows4s-example/src/test/resources/docs/run-io.svg @@ -1,4 +1,4 @@ -Do Things \ No newline at end of file +Do Things \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/simple-loop.svg b/workflows4s-example/src/test/resources/docs/simple-loop.svg index 4df6d1aa..439e4eb6 100644 --- a/workflows4s-example/src/test/resources/docs/simple-loop.svg +++ b/workflows4s-example/src/test/resources/docs/simple-loop.svg @@ -1,4 +1,4 @@ -Step1 \ No newline at end of file +Step1 \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/timer.svg b/workflows4s-example/src/test/resources/docs/timer.svg index b418947e..a3275bb0 100644 --- a/workflows4s-example/src/test/resources/docs/timer.svg +++ b/workflows4s-example/src/test/resources/docs/timer.svg @@ -1,4 +1,4 @@ -Wait For Input \ No newline at end of file +Wait For Input \ No newline at end of file diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 3d8da052..e4328a52 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -2,6 +2,7 @@ package workflows4s.example.docs import org.scalatest.freespec.AnyFreeSpec import workflows4s.example.TestUtils +import workflows4s.example.docs.draft.{DraftCheckpointExample, DraftForkExample, DraftInterruptionExample, DraftLoopExample, DraftParallelExample, DraftRetryExample, DraftSignalExample, DraftTimerExample} import workflows4s.example.docs.pullrequest.{PullRequestWorkflow, PullRequestWorkflowDraft} import workflows4s.wio.WIO @@ -16,7 +17,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("timer", TimerExample.waitForInput), ExampleConfig("draft-timer", DraftTimerExample.waitForReview), ExampleConfig("handle-signal", HandleSignalExample.doThings), - ExampleConfig("draft-signal", DraftSignalExample.workflow), + ExampleConfig("draft-signal", DraftSignalExample.awaitApproval), ExampleConfig("and-then", SequencingExample.sequence1), ExampleConfig("flat-map", SequencingExample.Dynamic.sequence1), ExampleConfig("handle-error-with", HandleErrorExample.errorHandled), @@ -31,7 +32,8 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("draft-interruption", DraftInterruptionExample.documentProcessingWorkflow), ExampleConfig("checkpoint", CheckpointExample.checkpoint.checkpointed, technical = true), ExampleConfig("recovery", CheckpointExample.recovery.myWorkflow, technical = true), - ExampleConfig("draft-checkpoint", DraftCheckpointExample.v1, technical = true), + ExampleConfig("draft-checkpoint", DraftCheckpointExample.checkpointed, technical = true), + ExampleConfig("draft-recovery", DraftCheckpointExample.recovery, technical = true), ExampleConfig("pure", PureExample.doThings), ExampleConfig("pure-error", PureExample.doThingsWithError), ExampleConfig("pull-request-draft", PullRequestWorkflowDraft.workflow), From 659415d946d69760eca11d7d6c2513ea4cf55993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pitu=C5=82a?= Date: Wed, 24 Sep 2025 20:30:18 +0200 Subject: [PATCH 35/39] missing files --- .../test/resources/docs/draft-checkpoint.svg | 4 +++ .../test/resources/docs/draft-recovery.bpmn | 26 +++++++++++++++++++ .../test/resources/docs/draft-recovery.json | 3 +++ .../resources/docs/draft-recovery.mermaid | 4 +++ .../test/resources/docs/draft-recovery.svg | 4 +++ 5 files changed, 41 insertions(+) create mode 100644 workflows4s-example/src/test/resources/docs/draft-checkpoint.svg create mode 100644 workflows4s-example/src/test/resources/docs/draft-recovery.bpmn create mode 100644 workflows4s-example/src/test/resources/docs/draft-recovery.json create mode 100644 workflows4s-example/src/test/resources/docs/draft-recovery.mermaid create mode 100644 workflows4s-example/src/test/resources/docs/draft-recovery.svg diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.svg b/workflows4s-example/src/test/resources/docs/draft-checkpoint.svg new file mode 100644 index 00000000..2253c6b7 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.svg @@ -0,0 +1,4 @@ + + + +A \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-recovery.bpmn b/workflows4s-example/src/test/resources/docs/draft-recovery.bpmn new file mode 100644 index 00000000..32b7bea5 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-recovery.bpmn @@ -0,0 +1,26 @@ + + + + + sequenceFlow_1 + + + sequenceFlow_1 + + + + + + + + + + + + + + + + + + diff --git a/workflows4s-example/src/test/resources/docs/draft-recovery.json b/workflows4s-example/src/test/resources/docs/draft-recovery.json new file mode 100644 index 00000000..2aaf490d --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-recovery.json @@ -0,0 +1,3 @@ +{ + "_type" : "Recovery" +} \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-recovery.mermaid b/workflows4s-example/src/test/resources/docs/draft-recovery.mermaid new file mode 100644 index 00000000..7037df50 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-recovery.mermaid @@ -0,0 +1,4 @@ +flowchart TD +node0@{ shape: circle, label: "Start"} +node1@{ shape: hexagon, label: "fa:fa-wrench State Recovery"} +node0 --> node1 diff --git a/workflows4s-example/src/test/resources/docs/draft-recovery.svg b/workflows4s-example/src/test/resources/docs/draft-recovery.svg new file mode 100644 index 00000000..39520225 --- /dev/null +++ b/workflows4s-example/src/test/resources/docs/draft-recovery.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 64fccc1b5639d5772443b8fc18f707b9ea95333a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pitu=C5=82a?= Date: Wed, 24 Sep 2025 21:14:47 +0200 Subject: [PATCH 36/39] fix tests --- .../test/resources/docs/draft-checkpoint.bpmn | 2 +- .../test/resources/docs/draft-checkpoint.json | 2 +- .../resources/docs/draft-checkpoint.mermaid | 2 +- .../src/test/resources/docs/draft-retry.bpmn | 44 +++++-------------- .../src/test/resources/docs/draft-retry.json | 37 ++++------------ .../test/resources/docs/draft-retry.mermaid | 6 +-- .../example/docs/ExamplesTest.scala | 2 +- 7 files changed, 23 insertions(+), 72 deletions(-) diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn b/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn index a339b520..97c0c746 100644 --- a/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.bpmn @@ -4,7 +4,7 @@ sequenceFlow_2 - + sequenceFlow_2 sequenceFlow_3 diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.json b/workflows4s-example/src/test/resources/docs/draft-checkpoint.json index 72891d89..ef3452ee 100644 --- a/workflows4s-example/src/test/resources/docs/draft-checkpoint.json +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.json @@ -1,7 +1,7 @@ { "base" : { "meta" : { - "name" : "A", + "name" : "Base", "error" : null, "description" : null }, diff --git a/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid b/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid index 30db1260..b9280b3c 100644 --- a/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid +++ b/workflows4s-example/src/test/resources/docs/draft-checkpoint.mermaid @@ -2,6 +2,6 @@ flowchart TD node0@{ shape: circle, label: "Start"} node1:::checkpoint subgraph node1 ["Checkpoint"] -node2["A"] +node2["Base"] node0 --> node2 end diff --git a/workflows4s-example/src/test/resources/docs/draft-retry.bpmn b/workflows4s-example/src/test/resources/docs/draft-retry.bpmn index 18362c6e..32b7bea5 100644 --- a/workflows4s-example/src/test/resources/docs/draft-retry.bpmn +++ b/workflows4s-example/src/test/resources/docs/draft-retry.bpmn @@ -1,50 +1,26 @@ - - sequenceFlow_3 + + sequenceFlow_1 - - sequenceFlow_3 - sequenceFlow_4 - - - - sequenceFlow_4 - sequenceFlow_5 - - - - sequenceFlow_5 + + sequenceFlow_1 - + - - + + - - + + - + - - - - - - - - - - - - - - diff --git a/workflows4s-example/src/test/resources/docs/draft-retry.json b/workflows4s-example/src/test/resources/docs/draft-retry.json index d1eb5370..e87d2c95 100644 --- a/workflows4s-example/src/test/resources/docs/draft-retry.json +++ b/workflows4s-example/src/test/resources/docs/draft-retry.json @@ -1,32 +1,11 @@ { - "steps" : [ - { - "meta" : { - "name" : "Prepare Request", - "error" : null, - "description" : null - }, - "_type" : "RunIO" + "base" : { + "meta" : { + "name" : "Api Call", + "error" : null, + "description" : null }, - { - "base" : { - "meta" : { - "name" : "Call External API", - "error" : null, - "description" : null - }, - "_type" : "RunIO" - }, - "_type" : "Retried" - }, - { - "meta" : { - "name" : "Process Response", - "error" : null, - "description" : null - }, - "_type" : "RunIO" - } - ], - "_type" : "Sequence" + "_type" : "RunIO" + }, + "_type" : "Retried" } \ No newline at end of file diff --git a/workflows4s-example/src/test/resources/docs/draft-retry.mermaid b/workflows4s-example/src/test/resources/docs/draft-retry.mermaid index ced22055..2c01566b 100644 --- a/workflows4s-example/src/test/resources/docs/draft-retry.mermaid +++ b/workflows4s-example/src/test/resources/docs/draft-retry.mermaid @@ -1,8 +1,4 @@ flowchart TD node0@{ shape: circle, label: "Start"} -node1["Prepare Request"] +node1["fa:fa-redo Api Call"] node0 --> node1 -node2["fa:fa-redo Call External API"] -node1 --> node2 -node3["Process Response"] -node2 --> node3 diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index e4328a52..960fc0bc 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -41,7 +41,7 @@ class ExamplesTest extends AnyFreeSpec { ExampleConfig("for-each-draft", ForEachExample.draft.forEachDraft), ExampleConfig("for-each", ForEachExample.real.forEachStep), ExampleConfig("retry", RetryExample.withRetry), - ExampleConfig("draft-retry", DraftRetryExample.workflow), + ExampleConfig("draft-retry", DraftRetryExample.withRetry), ) "examples" - { From 351b077ddc7d93978ba2f349e68bbe7fce24afe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pitu=C5=82a?= Date: Wed, 24 Sep 2025 21:19:38 +0200 Subject: [PATCH 37/39] scalafmt --- .../workflows4s/wio/builders/DraftBuilder.scala | 5 ++--- .../workflows4s/example/docs/ExamplesTest.scala | 12 ++++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index 66189640..eacecea8 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -126,7 +126,7 @@ object DraftBuilder { WIO.Interruption(draftTimer, WIO.HandleInterruption.InterruptionType.Timer) } - def retry(base: WIO.Draft[Ctx]): WIO.Draft[Ctx] = { + def retry(base: WIO.Draft[Ctx]): WIO.Draft[Ctx] = { WIO .Retry( base, @@ -137,11 +137,10 @@ object DraftBuilder { } def checkpoint(base: WIO.Draft[Ctx]): WIO.Draft[Ctx] = WIO.Checkpoint(base, (_, _) => ???, dummyEventHandler) - object syntax { extension (base: WIO.Draft[Ctx]) { def draftCheckpointed: WIO.Draft[Ctx] = checkpoint(base) - def draftRetry: WIO.Draft[Ctx] = retry(base) + def draftRetry: WIO.Draft[Ctx] = retry(base) } } diff --git a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala index 960fc0bc..a991afbe 100644 --- a/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala +++ b/workflows4s-example/src/test/scala/workflows4s/example/docs/ExamplesTest.scala @@ -2,7 +2,16 @@ package workflows4s.example.docs import org.scalatest.freespec.AnyFreeSpec import workflows4s.example.TestUtils -import workflows4s.example.docs.draft.{DraftCheckpointExample, DraftForkExample, DraftInterruptionExample, DraftLoopExample, DraftParallelExample, DraftRetryExample, DraftSignalExample, DraftTimerExample} +import workflows4s.example.docs.draft.{ + DraftCheckpointExample, + DraftForkExample, + DraftInterruptionExample, + DraftLoopExample, + DraftParallelExample, + DraftRetryExample, + DraftSignalExample, + DraftTimerExample, +} import workflows4s.example.docs.pullrequest.{PullRequestWorkflow, PullRequestWorkflowDraft} import workflows4s.wio.WIO @@ -59,7 +68,6 @@ class ExamplesTest extends AnyFreeSpec { "render progress" in { val instance = PullRequestWorkflow.run TestUtils.renderDocsProgressExample(instance, "pull-request-completed") - } } From 0701c9f6a4dda7f7e8ef53f1e4a088e04af97559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pitu=C5=82a?= Date: Wed, 24 Sep 2025 21:30:02 +0200 Subject: [PATCH 38/39] bring back ability to do .toInterruption --- .../src/main/scala/workflows4s/wio/builders/DraftBuilder.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index eacecea8..a984d66d 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -26,7 +26,7 @@ object DraftBuilder { None, ), ) - def timer(name: String = null, duration: FiniteDuration = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = + def timer(name: String = null, duration: FiniteDuration = null)(using autoName: sourcecode.Name): WIO.Timer[Ctx, Any, Nothing, Nothing] = WIO.Timer( Option(duration) match { case Some(value) => WIO.Timer.DurationSource.Static(value.toJava) From 0a41c13d162594df53a2fc13bade2176f09be2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pitu=C5=82a?= Date: Wed, 24 Sep 2025 21:34:35 +0200 Subject: [PATCH 39/39] scalafmt --- .../wio/builders/DraftBuilder.scala | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala index a984d66d..3aaa9304 100644 --- a/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala +++ b/workflows4s-core/src/main/scala/workflows4s/wio/builders/DraftBuilder.scala @@ -16,16 +16,17 @@ object DraftBuilder { val draft: DraftBuilderStep1.type = DraftBuilderStep1 object DraftBuilderStep1 { - def signal(name: String = null, error: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = WIO.HandleSignal( - draftSignal, - SignalHandler[Unit, Unit, Any]((_, _) => ???), - dummyEventHandler, - WIO.HandleSignal.Meta( - Option(error).map(ErrorMeta.Present(_)).getOrElse(ErrorMeta.noError), - getEffectiveName(name, autoName), - None, - ), - ) + def signal(name: String = null, error: String = null)(using autoName: sourcecode.Name): WIO.Draft[Ctx] = + WIO.HandleSignal( + draftSignal, + SignalHandler[Unit, Unit, Any]((_, _) => ???), + dummyEventHandler, + WIO.HandleSignal.Meta( + Option(error).map(ErrorMeta.Present(_)).getOrElse(ErrorMeta.noError), + getEffectiveName(name, autoName), + None, + ), + ) def timer(name: String = null, duration: FiniteDuration = null)(using autoName: sourcecode.Name): WIO.Timer[Ctx, Any, Nothing, Nothing] = WIO.Timer( Option(duration) match {