Skip to content

Commit

Permalink
Improve forks/branches rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
Krever committed Apr 16, 2024
1 parent 3bd0801 commit ccbaf6c
Show file tree
Hide file tree
Showing 11 changed files with 340 additions and 277 deletions.
16 changes: 12 additions & 4 deletions src/main/scala/workflow4s/wio/WIO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package workflow4s.wio

import cats.effect.IO
import workflow4s.wio.WIO.Timer.DurationSource
import workflow4s.wio.builders.{AwaitBuilder, HandleSignalBuilder, InterruptionBuilder, LoopBuilder, WIOBuilderMethods}
import workflow4s.wio.builders.{AwaitBuilder, BranchBuilder, ForkBuilder, HandleSignalBuilder, InterruptionBuilder, LoopBuilder, WIOBuilderMethods}
import workflow4s.wio.internal.WorkflowEmbedding.EventEmbedding
import workflow4s.wio.internal.{EventHandler, SignalHandler, WIOUtils, WorkflowEmbedding}

Expand All @@ -19,7 +19,9 @@ trait WorkflowContext { ctx: WorkflowContext =>
extends WIOBuilderMethods[ctx.type]
with HandleSignalBuilder.Step0[ctx.type]
with LoopBuilder.Step0[ctx.type]
with AwaitBuilder.Step0[ctx.type] {
with AwaitBuilder.Step0[ctx.type]
with ForkBuilder.Step0[ctx.type]
with BranchBuilder.Step0[ctx.type] {
type Branch[-In, +Err, +Out <: State] = workflow4s.wio.WIO.Branch[In, Err, Out, ctx.type]
type Interruption[+Err, +Out <: State] = workflow4s.wio.WIO.Interruption[ctx.type, Err, Out, ?, ?]

Expand Down Expand Up @@ -134,7 +136,8 @@ object WIO {
)
}

case class Fork[Ctx <: WorkflowContext, -In, +Err, +Out <: WCState[Ctx]](branches: Vector[Branch[In, Err, Out, Ctx]]) extends WIO[In, Err, Out, Ctx]
case class Fork[Ctx <: WorkflowContext, -In, +Err, +Out <: WCState[Ctx]](branches: Vector[Branch[In, Err, Out, Ctx]], name: Option[String])
extends WIO[In, Err, Out, Ctx]

case class Embedded[Ctx <: WorkflowContext, -In, +Err, InnerCtx <: WorkflowContext, InnerOut <: WCState[InnerCtx], MappingOutput[_ <: WCState[
InnerCtx,
Expand Down Expand Up @@ -186,14 +189,17 @@ object WIO {

// -----

def build[Ctx <: WorkflowContext]: WIOBuilderMethods[Ctx] = new WIOBuilderMethods[Ctx] {}
def build[Ctx <: WorkflowContext]: WIOBuilderMethods[Ctx] with BranchBuilder.Step0[Ctx] = new WIOBuilderMethods[Ctx]
with BranchBuilder.Step0[Ctx] {}

trait Branch[-In, +Err, +Out <: WCState[Ctx], Ctx <: WorkflowContext] {
type I // Intermediate

def condition: In => Option[I]

def wio: WIO[(In, I), Err, Out, Ctx]

def name: Option[String]
}

/*
Expand All @@ -218,11 +224,13 @@ object WIO {
def apply[Ctx <: WorkflowContext, In, T, Err, Out <: WCState[Ctx]](
cond: In => Option[T],
wio0: WIO[(In, T), Err, Out, Ctx],
name0: Option[String],
): Branch[In, Err, Out, Ctx] = {
new Branch[In, Err, Out, Ctx] {
override type I = T
override def condition: In => Option[I] = cond
override def wio: WIO[(In, I), Err, Out, Ctx] = wio0
override def name: Option[String] = name0
}
}
}
Expand Down
33 changes: 33 additions & 0 deletions src/main/scala/workflow4s/wio/builders/BranchBuilder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package workflow4s.wio.builders

import cats.implicits.catsSyntaxOptionId
import workflow4s.wio.{WIO, *}
import workflow4s.wio.model.ModelUtils

object BranchBuilder {

trait Step0[Ctx <: WorkflowContext]() {

def branch[In]: BranchBuilderStep1[In] = BranchBuilderStep1()

case class BranchBuilderStep1[In]() {

def when[Err, Out <: WCState[Ctx]](cond: In => Boolean)(wio: WIO[In, Err, Out, Ctx]): Step2[Unit, Err, Out] =
Step2(cond.andThen(Option.when(_)(())), wio.transformInput((x: (In, Any)) => x._1), None)

def create[T, Err, Out <: WCState[Ctx]](cond: In => Option[T])(wio: WIO[(In, T), Err, Out, Ctx]): Step2[T, Err, Out] =
Step2(cond, wio, None)

case class Step2[T, Err, Out <: WCState[Ctx]](cond: In => Option[T], wio: WIO[(In, T), Err, Out, Ctx], name: Option[String]) {

def named(name: String): Step2[T, Err, Out] = this.copy(name = name.some)
def autoNamed()(using n: sourcecode.Name): Step2[T, Err, Out] = named(ModelUtils.prettifyName(n.value))

def done: WIO.Branch[In, Err, Out, Ctx] = WIO.Branch(cond, wio, name)
}

}

}

}
51 changes: 51 additions & 0 deletions src/main/scala/workflow4s/wio/builders/ForkBuilder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package workflow4s.wio.builders

import cats.implicits.catsSyntaxOptionId
import workflow4s.wio.*
import workflow4s.wio.WIO.Timer
import workflow4s.wio.WIO.Timer.DurationSource
import workflow4s.wio.internal.EventHandler
import workflow4s.wio.model.ModelUtils

import java.time.Instant
import scala.jdk.DurationConverters.*
import scala.reflect.ClassTag

object ForkBuilder {

trait Step0[Ctx <: WorkflowContext]() {

def fork[In]: ForkBuilder[In, Nothing, Nothing] = ForkBuilder(Vector(), None)

case class ForkBuilder[-In, +Err, +Out <: WCState[Ctx]](branches: Vector[WIO.Branch[In, Err, Out, Ctx]], name: Option[String]) {
def onSome[T, Err1 >: Err, Out1 >: Out <: WCState[Ctx], In1 <: In](cond: In1 => Option[T])(
wio: WIO[(In1, T), Err1, Out1, Ctx],
name: String = null,
): ForkBuilder[In1, Err1, Out1] = addBranch(WIO.Branch(cond, wio, Option(name)))

// here we can add some APIs for exhaustive handling of Booleans or Eithers.

def matchCondition[T, Err1 >: Err, Out1 >: Out <: WCState[Ctx], In1 <: In](condition: In1 => Boolean, name: String = null)(
onTrue: WIO[In1, Err1, Out1, Ctx],
onFalse: WIO[In1, Err1, Out1, Ctx],
): WIO[In1, Err1, Out1, Ctx] = WIO.Fork(
Vector(
WIO.build[Ctx].branch[In1].when(condition)(onTrue).named("Yes").done,
WIO.build[Ctx].branch[In1].when(condition.andThen(!_))(onFalse).named("No").done,
),
Option(name),
)

def addBranch[T, Err1 >: Err, Out1 >: Out <: WCState[Ctx], In1 <: In](
b: WIO.Branch[In1, Err1, Out1, Ctx],
): ForkBuilder[In1, Err1, Out1] = this.copy(branches = branches.appended(b))

def named(name: String): ForkBuilder[In, Err, Out] = this.copy(name = name.some)
def autoNamed()(using n: sourcecode.Name): ForkBuilder[In, Err, Out] = named(ModelUtils.prettifyName(n.value))

def done: WIO[In, Err, Out, Ctx] = WIO.Fork(branches, name)
}

}

}
23 changes: 0 additions & 23 deletions src/main/scala/workflow4s/wio/builders/WIOBuilderMethods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,30 +57,7 @@ trait WIOBuilderMethods[Ctx <: WorkflowContext] {
def apply[Err](value: Err)(implicit ct: ErrorMeta[Err]): WIO[In, Err, Nothing, Ctx] = WIO.Pure(s => Left(value), ct)
}

def fork[In]: ForkBuilder[In, Nothing, Nothing] = ForkBuilder(Vector())

// can be removed and replaced with direct instance of WIO.Fork?
case class ForkBuilder[-In, +Err, +Out <: WCState[Ctx]](branches: Vector[Branch[In, Err, Out, Ctx]]) {
def branch[T, Err1 >: Err, Out1 >: Out <: WCState[Ctx], In1 <: In](cond: In1 => Option[T])(
wio: WIO[(In1, T), Err1, Out1, Ctx],
): ForkBuilder[In1, Err1, Out1] = addBranch(Branch(cond, wio))

def addBranch[T, Err1 >: Err, Out1 >: Out <: WCState[Ctx], In1 <: In](
b: Branch[In1, Err1, Out1, Ctx],
): ForkBuilder[In1, Err1, Out1] = ForkBuilder(branches.appended(b))

def done: WIO[In, Err, Out, Ctx] = WIO.Fork(branches)
}

def branch[In]: BranchBuilder[In] = BranchBuilder()

case class BranchBuilder[In]() {
def when[Err, Out <: WCState[Ctx]](cond: In => Boolean)(wio: WIO[In, Err, Out, Ctx]): Branch[In, Err, Out, Ctx] =
Branch(cond.andThen(Option.when(_)(())), wio.transformInput((x: (In, Any)) => x._1))

def create[T, Err, Out <: WCState[Ctx]](cond: In => Option[T])(wio: WIO[(In, T), Err, Out, Ctx]): Branch[In, Err, Out, Ctx] =
Branch(cond, wio)
}

def embed[In, Err, Out <: WCState[InnerCtx], InnerCtx <: WorkflowContext, OS[_ <: WCState[InnerCtx]] <: WCState[Ctx]](
wio: WIO[In, Err, Out, InnerCtx],
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/workflow4s/wio/model/WIOModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object WIOModel {
restartBranchName: Option[String],
onRestart: Option[WIOModel],
) extends WIOModel
case class Fork(branches: Vector[Branch]) extends WIOModel
case class Fork(branches: Vector[Branch], name: Option[String]) extends WIOModel
case class Interruptible(base: WIOModel, trigger: HandleSignal, flow: Option[WIOModel]) extends WIOModel
case class Timer(duration: Option[Duration], name: Option[String]) extends WIOModel

Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/workflow4s/wio/model/WIOModelInterpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ object WIOModelInterpreter {
def onLoop[Out1 <: WCState[Ctx]](wio: WIO.Loop[Ctx, In, Err, Out1, Out]): Result =
WIOModel.Loop(recurse(wio.loop), wio.meta.conditionName, wio.meta.releaseBranchName, wio.meta.restartBranchName, wio.onRestart.map(recurse(_)))
def onFork(wio: WIO.Fork[Ctx, In, Err, Out]): Result =
WIOModel.Fork(wio.branches.map(x => WIOModel.Branch(recurse(x.wio), None)))
WIOModel.Fork(wio.branches.map(x => WIOModel.Branch(recurse(x.wio), x.name)), wio.name)
def onEmbedded[InnerCtx <: WorkflowContext, InnerOut <: WCState[InnerCtx], MappingOutput[_] <: WCState[Ctx]](
wio: WIO.Embedded[Ctx, In, Err, InnerCtx, InnerOut, MappingOutput],
): Result = {
Expand Down Expand Up @@ -121,7 +121,7 @@ object WIOModelInterpreter {
case x @ WIOModel.Noop => handleRaw(x) // does it make any sense?, can noop be element in sequence?
case x @ WIOModel.Pure(name, errorMeta) => handleRaw(x)
case x: WIOModel.Loop => stripFirst(x.base, toBeStrpped).map(y => x.copy(base = y))
case x @ WIOModel.Fork(branches) => handleRaw(x)
case x @ WIOModel.Fork(_, _) => handleRaw(x)
case WIOModel.Interruptible(base, trigger, flow) => stripFirst(base, toBeStrpped).map(WIOModel.Interruptible(_, trigger, flow))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ object BPMNConverter {
)
.connectTo(loopStartGwId)
.moveToNode(nextTaskTempNodeId)
case WIOModel.Fork(branches) =>
val base = builder.exclusiveGateway()
case WIOModel.Fork(branches, name) =>
val base = builder.exclusiveGateway().name(name.orNull)
val gwId = base.getElement.getId
val (resultBuilder, Some(endGwId)) = {
branches.zipWithIndex.foldLeft[(Builder, Option[String])](base -> None)({ case ((builder1, endGw), (branch, idx)) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ object ChecksEngine extends ChecksEngine {
private def getDecision: WIO[ChecksState.Executed, Nothing, ChecksState.Decided] = {
WIO
.fork[ChecksState.Executed]
.addBranch(requiresReviewBranch)
.addBranch(decidedBySystemBranch) // TODO if/else api
.done
.matchCondition(_.requiresReview, "Requires review?")(
onTrue = handleReview,
onFalse = systemDecision,
)
}

private def refreshChecksUntilAllComplete: WIO[ChecksInput, Nothing, ChecksState.Executed] = {
Expand Down Expand Up @@ -73,20 +74,13 @@ object ChecksEngine extends ChecksEngine {
.handleEvent((state, evt) => state.addResults(evt.results))
.autoNamed()

private val decidedBySystemBranch: WIO.Branch[ChecksState.Executed, Nothing, ChecksState.Decided] =
WIO
.branch[ChecksState.Executed]
.when(!_.requiresReview)(
WIO.pure.make(st => {
val decision =
if (st.isRejected) Decision.RejectedBySystem()
else Decision.ApprovedBySystem()
st.asDecided(decision)
}),
)

private val requiresReviewBranch: WIO.Branch[ChecksState.Executed, Nothing, ChecksState.Decided] =
WIO.branch[ChecksState.Executed].when(_.requiresReview)(handleReview)
private val systemDecision: WIO[ChecksState.Executed, Nothing, ChecksState.Decided] =
WIO.pure.make(st => {
val decision =
if (st.isRejected) Decision.RejectedBySystem()
else Decision.ApprovedBySystem()
st.asDecided(decision)
})

private def handleReview: WIO[ChecksState.Executed, Nothing, ChecksState.Decided] = WIO
.handleSignal(Signals.review)
Expand All @@ -102,5 +96,4 @@ object ChecksEngine extends ChecksEngine {
.voidResponse
.done


}
Loading

0 comments on commit ccbaf6c

Please sign in to comment.