All Downloads are FREE. Search and download functionalities are using the official Maven repository.

camundala.simulation.custom.SEventExtensions.scala Maven / Gradle / Ivy

The newest version!
package camundala.simulation
package custom

import camundala.bpmn.*
import io.circe.*
import sttp.client3.*

trait SEventExtensions extends SimulationHelper:

  extension (sEvent: SEvent)
    def loadVariable()(using data: ScenarioData): ResultType =
      val variableName = sEvent.readyVariable
      val readyValue = sEvent.readyValue
      def loadVariable(
          processInstanceId: Any
      )(data: ScenarioData): ResultType =
        val uri =
          uri"${config.endpoint}/history/variable-instance?variableName=$variableName&processInstanceId=$processInstanceId&deserializeValues=false"
        val request = basicRequest
          .auth()
          .get(uri)
        given ScenarioData = data
        runRequest(
          request,
          s"${sEvent.inOut.getClass.getSimpleName} '${sEvent.name}' loadVariables"
        )((body, data) =>
          body.hcursor.downArray
            .downField("value")
            .as[Json]
            .flatMap { json =>
              val value = if json.isString then json.asString.get else json
              if value.toString == readyValue.toString then
                Right(
                  data
                    .info(
                      s"Variable for '${sEvent.name}' ready ($variableName = '$readyValue')"
                    )
                )
              else
                Left(
                  data
                    .info(
                      s"Variable found for '${sEvent.name}' but not ready ($variableName = '$readyValue' (result: '$value'))"
                    )
                )
            }
            .left
            .flatMap { _ =>
              sEvent.tryOrFail(loadVariable(processInstanceId))
            }
        )
      end loadVariable

      val processInstanceId = data.context.processInstanceId
      loadVariable(processInstanceId)(
        data
          .debug(s"Load - Wait for $variableName with ready value: $readyValue")
          .withRequestCount(0)
      )

  end extension

  extension (sEvent: SMessageEvent)
    def event = sEvent.inOut

    def sendMessage()(using ScenarioData): ResultType =
      for
        // default: try until it returns status 200
        given ScenarioData <- sEvent.optReadyVariable
          .map(_ => sEvent.loadVariable())
          .getOrElse(Right(summon[ScenarioData]))
        given ScenarioData <- sendMsg()
      yield summon[ScenarioData]

    def sendMsg()(using data: ScenarioData): ResultType =
      def correlate()(data: ScenarioData): ResultType =
        val processInstanceId: Option[String] =
          if sEvent.processInstanceId then Some(data.context.processInstanceId)
          else None
        val tenant = // only set if there is no processInstanceId
          if sEvent.processInstanceId then None
          else summon[SimulationConfig[?]].tenantId
        val body = CorrelateMessageIn(
          messageName = event.messageName,
          tenantId = tenant,
          processInstanceId = processInstanceId,
          processVariables = Some(event.camundaInMap)
        ).asJson.deepDropNullValues.toString
        val uri = uri"${config.endpoint}/message"

        val request = basicRequest
          .auth()
          .contentType("application/json")
          .body(body)
          .post(uri)

        given ScenarioData = data
          .debug(s"- Request body: $body")

        val response = request.send(backend)
        response.body
          .map { (body: String) =>
            summon[ScenarioData]
              .info(
                s"Message '${sEvent.name}' received"
              )
              .debug(s"- response body: $body")
          }
          .left
          .flatMap { _ =>
            sEvent.tryOrFail(correlate())
          }
      end correlate
      correlate()(data.withRequestCount(0))
    end sendMsg
  end extension // SReceiveMessageEvent

  extension (sEvent: SSignalEvent)
    def event = sEvent.inOut
    def sendSignal()(using ScenarioData): ResultType =
      for
        given ScenarioData <- sEvent.loadVariable()
        given ScenarioData <- sndSgnl()
      yield summon[ScenarioData]

    private def sndSgnl()(using
        data: ScenarioData
    ): ResultType =
      val body = SendSignalIn(
        // supports dynamic processInstanceId
        name = event.messageName.replace(SignalEvent.Dynamic_ProcessInstance, data.context.processInstanceId),
        variables = Some(event.camundaInMap)
      ).asJson.deepDropNullValues.toString
      val uri = uri"${config.endpoint}/signal"

      val request = basicRequest
        .auth()
        .contentType("application/json")
        .body(body)
        .post(uri)

      runRequest(request, s"Signal '${sEvent.name}' sent")((_, data) =>
        Right(data)
      )
    end sndSgnl
  end extension // SReceiveSignalEvent

  extension (sEvent: STimerEvent)

    def getAndExecute()(using data: ScenarioData): ResultType =
      given ScenarioData = data.withTaskId(notSet)

      for
        // default it waits until there is a job ready
        given ScenarioData <- sEvent.optReadyVariable
          .map(_ => sEvent.loadVariable())
          .getOrElse(Right(summon[ScenarioData]))
        given ScenarioData <- job()
        given ScenarioData <- executeTimer()
      yield summon[ScenarioData]
      end for
    end getAndExecute

    private def job()(using data: ScenarioData): ResultType =
      def getJob(
          processInstanceId: Any
      )(data: ScenarioData): ResultType =
        val uri =
          uri"${config.endpoint}/job?processInstanceId=$processInstanceId"
        val request = basicRequest
          .auth()
          .get(uri)

        given ScenarioData = data
          .info(
            s"TimerEvent '${sEvent.name}' get"
          )
          .debug(s"- URI: $uri")

        request
          .extractBody()
          .flatMap(body =>
            body.hcursor.downArray
              .downField("id")
              .as[String]
              .map { (jobId: String) =>
                summon[ScenarioData]
                  .withJobId(jobId)
                  .info(
                    s"TimerEvent '${sEvent.name}' ready"
                  )
                  .info(s"- jobId: $jobId")
                  .debug(s"- body: $body")
              }
              .left
              .flatMap { _ =>
                sEvent.tryOrFail(getJob(processInstanceId))
              }
          )
      end getJob

      val processInstanceId = data.context.processInstanceId
      getJob(processInstanceId)(data.withRequestCount(0))
    end job

    private def executeTimer()(using ScenarioData): ResultType =
      for
        given ScenarioData <- execEvent()
      yield summon[ScenarioData]

    private def execEvent()(using
        data: ScenarioData
    ): ResultType =

      val uri = uri"${config.endpoint}/job/${data.context.jobId}/execute"

      val request = basicRequest
        .auth()
        .contentType("application/json")
        .post(uri)

      runRequest(request, s"Timer '${sEvent.name}' sent")((_, data) =>
        Right(data)
      )
    end execEvent
  end extension // SReceiveTimerEvent

end SEventExtensions




© 2015 - 2025 Weber Informatics LLC | Privacy Policy