Skip to content

Commit 3af0336

Browse files
committed
Working Worker C8 example.
1 parent 0ef0d75 commit 3af0336

File tree

36 files changed

+768
-255
lines changed

36 files changed

+768
-255
lines changed

02-bpmn/src/main/scala/camundala/bpmn/exports.scala

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,32 @@ case class GeneralVariables(
100100
end GeneralVariables
101101

102102
object GeneralVariables:
103-
given InOutCodec[GeneralVariables] = deriveCodec
103+
given InOutCodec[GeneralVariables] = CirceCodec.from(decoder, deriveInOutEncoder)
104104
given ApiSchema[GeneralVariables] = deriveApiSchema
105+
106+
implicit val decoder: Decoder[GeneralVariables] = new Decoder[GeneralVariables] :
107+
final def apply(c: HCursor): Decoder.Result[GeneralVariables] =
108+
for
109+
servicesMocked <- c.downField("servicesMocked").as[Option[Boolean]].map(_.getOrElse(false))
110+
mockedWorkers <- c.downField("mockedWorkers").as[Option[Seq[String]]].map(_.getOrElse(Seq.empty))
111+
outputMock <- c.downField("outputMock").as[Option[Json]]
112+
outputServiceMock <- c.downField("outputServiceMock").as[Option[Json]]
113+
manualOutMapping <- c.downField("manualOutMapping").as[Option[Boolean]].map(_.getOrElse(false))
114+
outputVariables <- c.downField("outputVariables").as[Option[Seq[String]]].map(_.getOrElse(Seq.empty))
115+
handledErrors <- c.downField("handledErrors").as[Option[Seq[String]]].map(_.getOrElse(Seq.empty))
116+
regexHandledErrors <- c.downField("regexHandledErrors").as[Option[Seq[String]]].map(_.getOrElse(Seq.empty))
117+
impersonateUserId <- c.downField("impersonateUserId").as[Option[String]]
118+
yield GeneralVariables(
119+
servicesMocked,
120+
mockedWorkers,
121+
outputMock,
122+
outputServiceMock,
123+
manualOutMapping,
124+
outputVariables,
125+
handledErrors,
126+
regexHandledErrors,
127+
impersonateUserId
128+
)
105129
end GeneralVariables
106130

107131
lazy val regexHandledErrorsDescr =

03-worker/src/main/scala/camundala/worker/EngineContext.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package camundala
22
package worker
33

4-
import camundala.domain.*
54
import camundala.bpmn.*
6-
import camundala.worker.CamundalaWorkerError.ServiceError
5+
import camundala.domain.*
76

87
import java.time.{LocalDate, LocalDateTime}
98
import scala.reflect.ClassTag

03-worker/src/main/scala/camundala/worker/Handler.scala

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ trait WorkerHandler:
1313
def worker: Worker[?, ?, ?]
1414
def topic: String
1515

16-
def applicationName: String
17-
def registerHandler(): Unit
16+
def projectName: String
1817
def registerHandler( register: => Unit): Unit =
19-
val appPackageName = applicationName.replace("-", ".")
18+
val appPackageName = projectName.replace("-", ".")
2019
val testMode = sys.env.get("WORKER_TEST_MODE").contains("true") // did not work with lazy val
2120
if testMode || getClass.getName.startsWith(appPackageName)
2221
then
@@ -66,6 +65,10 @@ object ValidationHandler:
6665
funct(in)
6766
end ValidationHandler
6867

68+
69+
type InitProcessFunction =
70+
EngineContext ?=> Either[InitProcessError, Map[String, Any]]
71+
6972
/** handler for Custom Process Initialisation. All the variables in the Result Map will be put on
7073
* the process.
7174
*
@@ -98,18 +101,17 @@ end ValidationHandler
98101
trait InitProcessHandler[
99102
In <: Product: InOutCodec
100103
]:
101-
def init(input: In): Either[InitProcessError, Map[String, Any]]
104+
def init(input: In): InitProcessFunction
102105
end InitProcessHandler
103-
104106
object InitProcessHandler:
105107
def apply[
106108
In <: Product: InOutCodec
107109
](
108-
funct: In => Either[InitProcessError, Map[String, Any]],
110+
funct: In => InitProcessFunction,
109111
processLabels: ProcessLabels
110112
): InitProcessHandler[In] =
111113
new InitProcessHandler[In]:
112-
override def init(in: In): Either[InitProcessError, Map[String, Any]] =
114+
override def init(in: In): InitProcessFunction =
113115
funct(in)
114116
.map:
115117
_ ++ processLabels.toMap
Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,36 @@
11
package camundala.worker
22

3-
43
import scala.concurrent.duration.*
54

6-
75
trait JobWorker:
86
def topic: String
97
def timeout: Duration = 10.seconds
108

9+
protected def errorHandled(error: CamundalaWorkerError, handledErrors: Seq[String]): Boolean =
10+
error.isMock || // if it is mocked, it is handled in the error, as it also could be a successful output
11+
handledErrors.contains(error.errorCode.toString) || handledErrors.map(
12+
_.toLowerCase
13+
).contains("catchall")
14+
15+
protected def regexMatchesAll(
16+
errorHandled: Boolean,
17+
error: CamundalaWorkerError,
18+
regexHandledErrors: Seq[String]
19+
) =
20+
errorHandled && regexHandledErrors.forall(regex =>
21+
error.errorMsg.matches(s".*$regex.*")
22+
)
1123

24+
protected def filteredOutput(
25+
outputVariables: Seq[String],
26+
allOutputs: Map[String, Any]
27+
): Map[String, Any] =
28+
outputVariables match
29+
case filter if filter.isEmpty => allOutputs
30+
case filter =>
31+
allOutputs
32+
.filter:
33+
case k -> _ => filter.contains(k)
1234

35+
end filteredOutput
36+
end JobWorker
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package camundala.worker
22

33
import zio.*
4+
import zio.ZIO.*
45

56
import scala.compiletime.uninitialized
67

78
trait WorkerApp extends ZIOAppDefault:
8-
def workerClients: Seq[WorkerClient[?]]
9-
var theWorkers: Set[JobWorker] = uninitialized
9+
def workerRegistries: Seq[WorkerRegistry[?]]
10+
protected var theWorkers: Set[JobWorker] = uninitialized
1011

12+
override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = ZioLogger.logger
13+
1114
def workers(dWorkers: (JobWorker | Seq[JobWorker])*): Unit =
1215
theWorkers = dWorkers
1316
.flatMap:
@@ -17,6 +20,6 @@ trait WorkerApp extends ZIOAppDefault:
1720

1821
override def run: ZIO[Any, Any, Any] =
1922
for
20-
_ <- Console.printLine("Starting WorkerApp")
21-
_ <- ZIO.collectAllPar(workerClients.map(_.run(theWorkers)))
23+
_ <- logInfo("Starting WorkerApp")
24+
_ <- collectAllPar(workerRegistries.map(_.register(theWorkers)))
2225
yield ()

03-worker/src/main/scala/camundala/worker/WorkerClient.scala

Lines changed: 0 additions & 10 deletions
This file was deleted.

03-worker/src/main/scala/camundala/worker/WorkerDsl.scala

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ import scala.reflect.ClassTag
99
trait WorkerDsl[In <: Product: InOutCodec, Out <: Product: InOutCodec]
1010
extends JobWorker:
1111

12-
protected def engineContext: EngineContext
13-
14-
protected def logger: WorkerLogger
15-
1612
// needed that it can be called from CSubscriptionPostProcessor
1713
def worker: Worker[In, Out, ?]
1814
def topic: String = worker.topic
@@ -171,12 +167,10 @@ private trait InitProcessDsl[
171167
InitIn <: Product: InOutCodec,
172168
InConfig <: Product: InOutCodec
173169
]:
174-
protected def engineContext: EngineContext
175-
176170
protected def customInit(in: In): InitIn
177171

178172
// by default the InConfig is initialized
179-
final def initProcess(in: In): Either[InitProcessError, Map[String, Any]] =
173+
final def initProcess(in: In)(using engineContext: EngineContext): Either[InitProcessError, Map[String, Any]] =
180174
val inConfig = in match
181175
case i: WithConfig[?] =>
182176
initConfig(
@@ -201,7 +195,7 @@ private trait InitProcessDsl[
201195
private def initConfig(
202196
optConfig: Option[InConfig],
203197
defaultConfig: InConfig
204-
): Map[String, Any] =
198+
)(using engineContext: EngineContext): Map[String, Any] =
205199
val defaultJson = defaultConfig.asJson
206200
val r = optConfig.map {
207201
config =>

03-worker/src/main/scala/camundala/worker/WorkerExecutor.scala

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,42 +16,43 @@ case class WorkerExecutor[
1616

1717
def execute(
1818
processVariables: Seq[Either[BadVariableError, (String, Option[Json])]]
19-
) =
19+
): Either[CamundalaWorkerError, Map[String, Any]] =
2020
for
21-
validatedInput <- InputValidator.validate(processVariables)
22-
initializedOutput <- Initializer.initVariables(validatedInput)
23-
mockedOutput <- OutMocker.mockedOutput(validatedInput)
21+
validatedInput <- InputValidator.validate(processVariables)
22+
initializedOutput <- Initializer.initVariables(validatedInput)(using context.engineContext)
23+
mockedOutput <- OutMocker.mockedOutput(validatedInput)
2424
// only run the work if it is not mocked
25-
output <- if mockedOutput.isEmpty then WorkRunner.run(validatedInput) else Right(mockedOutput.get)
26-
allOutputs = camundaOutputs(validatedInput, initializedOutput, output)
27-
filteredOut = filteredOutput(allOutputs)
25+
output <-
26+
if mockedOutput.isEmpty then WorkRunner.run(validatedInput) else Right(mockedOutput.get)
27+
allOutputs = camundaOutputs(validatedInput, initializedOutput, output)
28+
filteredOut = filteredOutput(allOutputs)
2829
// make MockedOutput as error if mocked
29-
_ <- if mockedOutput.isDefined then Left(MockedOutput(filteredOut)) else Right(())
30+
_ <- if mockedOutput.isDefined then Left(MockedOutput(filteredOut)) else Right(())
3031
yield filteredOut
3132

3233
object InputValidator:
33-
lazy val prototype = worker.in
34+
lazy val prototype = worker.in
3435
lazy val validationHandler = worker.validationHandler
3536

3637
def validate(
3738
inputParamsAsJson: Seq[Either[Any, (String, Option[Json])]]
3839
): Either[ValidatorError, In] =
39-
val jsonResult: Either[ValidatorError, Seq[(String, Option[Json])]] =
40+
val jsonResult: Either[ValidatorError, Seq[(String, Option[Json])]] =
4041
inputParamsAsJson
4142
.partition(_.isRight) match
4243
case (successes, failures) if failures.isEmpty =>
4344
Right(
4445
successes.collect { case Right(value) => value }
4546
)
46-
case (_, failures) =>
47+
case (_, failures) =>
4748
Left(
4849
ValidatorError(
4950
failures
5051
.collect { case Left(value) => value }
5152
.mkString("Validator Error(s):\n - ", " - ", "\n")
5253
)
5354
)
54-
val json: Either[ValidatorError, JsonObject] = jsonResult
55+
val json: Either[ValidatorError, JsonObject] = jsonResult
5556
.map(_.foldLeft(JsonObject()) { case (jsonObj, jsonKey -> jsonValue) =>
5657
if jsonValue.isDefined
5758
then jsonObj.add(jsonKey, jsonValue.get)
@@ -64,23 +65,25 @@ case class WorkerExecutor[
6465
.map(ex => ValidatorError(errorMsg = ex.errorMsg))
6566
.flatMap(in => validationHandler.map(h => h.validate(in)).getOrElse(Right(in)))
6667
)
67-
val in = toIn(json)
68+
69+
val in = toIn(json)
6870
val result = in.flatMap:
6971
case i: WithConfig[?] =>
7072
val newIn =
7173
for
72-
jsonObj: JsonObject <- json
73-
inputVariables = jsonObj.toMap
74+
jsonObj: JsonObject <- json
75+
inputVariables = jsonObj.toMap
7476
configJson: JsonObject =
7577
inputVariables.get("inConfig").getOrElse(i.defaultConfigAsJson).asObject.get
76-
newJsonConfig = worker.inConfigVariableNames
77-
.foldLeft(configJson):
78-
case (configJson, n) =>
79-
if jsonObj.contains(n)
80-
then configJson.add(n, jsonObj(n).get)
81-
else configJson
78+
newJsonConfig = worker.inConfigVariableNames
79+
.foldLeft(configJson):
80+
case (configJson, n) =>
81+
if jsonObj.contains(n)
82+
then configJson.add(n, jsonObj(n).get)
83+
else configJson
8284
yield jsonObj.add("inConfig", newJsonConfig.asJson)
8385
toIn(newIn)
86+
8487
case x =>
8588
in
8689
result
@@ -95,7 +98,7 @@ case class WorkerExecutor[
9598

9699
def initVariables(
97100
validatedInput: In
98-
): Either[InitProcessError, Map[String, Any]] =
101+
): InitProcessFunction =
99102
worker.initProcessHandler
100103
.map { vi =>
101104
vi.init(validatedInput).map(_ ++ defaultVariables)
@@ -115,10 +118,10 @@ case class WorkerExecutor[
115118
case (_, Some(outputMock), _) =>
116119
decodeMock(outputMock)
117120
// if your worker is mocked we use the default mock
118-
case (true, None, None) =>
121+
case (true, None, None) =>
119122
worker.defaultMock(in).map(Some(_))
120123
// otherwise it is not mocked or it is a service mock which is handled in service Worker during running
121-
case (_, None, _) =>
124+
case (_, None, _) =>
122125
Right(None)
123126
end mockedOutput
124127

@@ -150,7 +153,7 @@ case class WorkerExecutor[
150153
(output match
151154
case o: NoOutput =>
152155
context.toEngineObject(o)
153-
case _ =>
156+
case _ =>
154157
context.toEngineObject(output.asInstanceOf[Out])
155158
)
156159
private def filteredOutput(
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package camundala.worker
2+
3+
import zio.ZIO
4+
import zio.ZIO.*
5+
6+
trait WorkerRegistry[T <: JobWorker]:
7+
def register(workers: Set[JobWorker]): ZIO[Any, Any, Any] =
8+
logInfo(s"Registering Workers for ${getClass.getSimpleName}") *>
9+
registerWorkers(workers.collect { case w: T => w })
10+
11+
protected def registerWorkers(workers: Set[T]): ZIO[Any, Any, Any]
12+
end WorkerRegistry
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package camundala.worker
2+
3+
import org.slf4j.{Logger, LoggerFactory}
4+
import zio.*
5+
import zio.logging.*
6+
import zio.logging.backend.SLF4J
7+
8+
object ZioLogger:
9+
val logger = Runtime.removeDefaultLoggers >>> SLF4J.slf4j
10+
11+
end ZioLogger
12+
13+
case class Slf4JLogger(private val delegateLogger: Logger) extends WorkerLogger:
14+
15+
def debug(message: String): Unit =
16+
if delegateLogger.isDebugEnabled then
17+
delegateLogger.debug(message)
18+
19+
def info(message: String): Unit =
20+
if delegateLogger.isInfoEnabled then
21+
delegateLogger.info(message)
22+
23+
def warn(message: String): Unit =
24+
if delegateLogger.isWarnEnabled then
25+
delegateLogger.warn(message)
26+
27+
def error(err: CamundalaWorkerError): Unit =
28+
if delegateLogger.isErrorEnabled then
29+
delegateLogger.error(s"Error ${err.causeMsg}")
30+
end Slf4JLogger
31+
object Slf4JLogger:
32+
def logger(name: String) = Slf4JLogger(LoggerFactory.getLogger(name))
33+
end Slf4JLogger

0 commit comments

Comments
 (0)