Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package dev.bpmcrafters.processengineapi.test
import com.tngtech.jgiven.Stage
import com.tngtech.jgiven.annotation.ExpectedScenarioState
import com.tngtech.jgiven.annotation.ProvidedScenarioState
import dev.bpmcrafters.processengineapi.decision.DecisionByRefEvaluationCommand
import dev.bpmcrafters.processengineapi.decision.DecisionEvaluationResult
import dev.bpmcrafters.processengineapi.process.StartProcessByDefinitionAtElementCmd
import dev.bpmcrafters.processengineapi.process.StartProcessByDefinitionCmd
import dev.bpmcrafters.processengineapi.process.StartProcessByMessageCmd
Expand Down Expand Up @@ -32,6 +34,13 @@ class BaseGivenWhenStage : Stage<BaseGivenWhenStage>() {
@ProvidedScenarioState
lateinit var taskSubscription: TaskSubscription

@ProvidedScenarioState
var decisionResult: DecisionEvaluationResult? = null

@ProvidedScenarioState
var throwableCaught: Throwable? = null


fun `start process by definition`(definitionKey: String) = step {
instanceId = processTestHelper.getStartProcessApi().startProcess(
StartProcessByDefinitionCmd(
Expand Down Expand Up @@ -144,5 +153,18 @@ class BaseGivenWhenStage : Stage<BaseGivenWhenStage>() {
)
).get()

fun `evaluate decision by ref key with payload`(decisionDefinitionId: String, payload: Map<String, Any>) = step {
processTestHelper.getEvaluateDecisionApi().evaluateDecision(
DecisionByRefEvaluationCommand(
decisionRef = decisionDefinitionId,
payload = payload,
restrictions = mapOf(),
)
).handle { res, ex ->
this.throwableCaught = ex
this.decisionResult = res
}.get()
}


}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package dev.bpmcrafters.processengineapi.test

import com.tngtech.jgiven.Stage
import com.tngtech.jgiven.annotation.As
import com.tngtech.jgiven.annotation.ExpectedScenarioState
import dev.bpmcrafters.processengineapi.decision.DecisionEvaluationResult
import io.toolisticon.testing.jgiven.JGivenKotlinStage
import io.toolisticon.testing.jgiven.step
import org.assertj.core.api.Assertions.assertThat
import org.awaitility.Awaitility.await
import java.util.function.Function
import kotlin.reflect.KClass

@JGivenKotlinStage
class BaseThenStage : Stage<BaseThenStage>() {
Expand All @@ -22,6 +26,15 @@ class BaseThenStage : Stage<BaseThenStage>() {
@ExpectedScenarioState
lateinit var processTestHelper: ProcessTestHelper

@ExpectedScenarioState
lateinit var decisionResult: DecisionEvaluationResult

@ExpectedScenarioState
var throwableCaught: Throwable? = null

@ExpectedScenarioState
var interpretedDecisionResult: Any? = null

fun `we should have a running process`() = step {
val process = processTestHelper.getProcessInformation(instanceId)
assertThat(process).isNotNull()
Expand Down Expand Up @@ -59,4 +72,26 @@ class BaseThenStage : Stage<BaseThenStage>() {
await().untilAsserted { assertThat(externalTaskId).isNull() }
}

fun `we should have thrown`(clazz: KClass<out Throwable>) = step {
assertThat(throwableCaught).describedAs { "no exception found, but result was " + this.decisionResult }.isNotNull
assertThat(throwableCaught).isInstanceOf(clazz.java)
}

fun `evaluation result interpreted as `(interpretation: Function<DecisionEvaluationResult, Any?>) = step {
try {
this.interpretedDecisionResult = interpretation.apply(this.decisionResult)
} catch (e: Exception) {
this.throwableCaught = e
}
}

@As("should fail interpretation with $")
fun `should interpretation fail`(clazz: KClass<out Throwable>) = `we should have thrown`(clazz)

fun `interpreted result is`(expectedResult: Any?) = step {
assertThat(this.throwableCaught).isNull()
assertThat(this.interpretedDecisionResult).isEqualTo(expectedResult)
}


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.bpmcrafters.processengineapi.test

import dev.bpmcrafters.processengineapi.decision.EvaluateDecisionApi
import dev.bpmcrafters.processengineapi.process.ProcessInformation
import dev.bpmcrafters.processengineapi.process.StartProcessApi
import dev.bpmcrafters.processengineapi.task.ServiceTaskCompletionApi
Expand All @@ -16,6 +17,8 @@ interface ProcessTestHelper {

fun getServiceTaskCompletionApi(): ServiceTaskCompletionApi

fun getEvaluateDecisionApi(): EvaluateDecisionApi

fun triggerPullingUserTaskDeliveryManually()

fun subscribeForUserTasks()
Expand Down
10 changes: 10 additions & 0 deletions engine-adapter/c7-embedded-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
<artifactId>camunda-engine</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<scope>provided</scope>
</dependency>

<!-- For JGiven Testing Stage as a part of compiled code -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.bpmcrafters.processengineapi.adapter.c7.embedded.decision

import com.fasterxml.jackson.databind.ObjectMapper
import dev.bpmcrafters.processengineapi.decision.DecisionEvaluationOutput
import org.camunda.bpm.dmn.engine.DmnDecisionResultEntries

/**
* Delegating output.
*/
data class DelegatingDmnDecisionEvaluationOutput(
private val objectMapper: ObjectMapper,
val entries: DmnDecisionResultEntries,
) : DecisionEvaluationOutput {

override fun <T : Any> asType(type: Class<T>): T? {
try {
if (entries.isEmpty()) {
return null
} else if (entries.keys.size == 1) {
return objectMapper.convertValue(entries.values.first(), type)
}
return objectMapper.convertValue(entries, type)
} catch (e: Exception) {
throw IllegalStateException("Can't deserialize into ${type.name} decision output: ${asMap()}", e)
}
}

override fun asMap(): Map<String, Any?> {
return entries.entryMap
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.bpmcrafters.processengineapi.adapter.c7.embedded.decision

import com.fasterxml.jackson.databind.ObjectMapper
import dev.bpmcrafters.processengineapi.decision.DecisionEvaluationOutput
import dev.bpmcrafters.processengineapi.decision.DecisionEvaluationResult
import org.camunda.bpm.dmn.engine.DmnDecisionResult

/**
* Delegating result.
*/
data class DelegatingDmnDecisionResult(
val dmnDecisionResult: DmnDecisionResult,
private val objectMapper: ObjectMapper
) : DecisionEvaluationResult {

override fun asSingle(): DecisionEvaluationOutput {
return DelegatingDmnDecisionEvaluationOutput(objectMapper, dmnDecisionResult.singleResult)
}

override fun asList(): List<DecisionEvaluationOutput> {
return dmnDecisionResult.map { DelegatingDmnDecisionEvaluationOutput(objectMapper, it) }
}

override fun meta(): Map<String, String> = mapOf(
"single-result" to if (dmnDecisionResult.resultList.size == 1) {
"true"
} else {
"false"
},
"result-count" to "${dmnDecisionResult.resultList.size}"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package dev.bpmcrafters.processengineapi.adapter.c7.embedded.decision

import com.fasterxml.jackson.databind.ObjectMapper
import dev.bpmcrafters.processengineapi.CommonRestrictions
import dev.bpmcrafters.processengineapi.MetaInfo
import dev.bpmcrafters.processengineapi.MetaInfoAware
import dev.bpmcrafters.processengineapi.adapter.c7.embedded.shared.EngineCommandExecutor
import dev.bpmcrafters.processengineapi.decision.*
import io.github.oshai.kotlinlogging.KotlinLogging
import org.camunda.bpm.dmn.engine.DmnDecisionResult
import org.camunda.bpm.dmn.engine.DmnDecisionResultEntries
import org.camunda.bpm.engine.DecisionService
import org.camunda.bpm.engine.dmn.DecisionsEvaluationBuilder
import java.util.concurrent.CompletableFuture

private val logger = KotlinLogging.logger {}

class EvaluateDecisionApiImpl(
private val decisionService: DecisionService,
private val objectMapper: ObjectMapper,
private val commandExecutor: EngineCommandExecutor
) : EvaluateDecisionApi {

override fun evaluateDecision(command: DecisionEvaluationCommand): CompletableFuture<DecisionEvaluationResult> {
return when (command) {
is DecisionByRefEvaluationCommand -> {
logger.debug {
"PROCESS-ENGINE-C7-EMBEDDED-061: Evaluating decision by reference ${command.decisionRef}"
}
commandExecutor.execute {
val result = decisionService
.evaluateDecisionByKey(command.decisionRef)
.applyRestrictions(ensureSupported(command.restrictionSupplier.get()))
.variables(command.payloadSupplier.get())
.evaluate()
if (result.size == 0) {
NoDecisionResult
} else {
DelegatingDmnDecisionResult(result, objectMapper)
}
}
}

else -> throw UnsupportedOperationException("Evaluate Decision command of type ${command::class.qualifiedName} is not implemented yet")
}
}

override fun getSupportedRestrictions(): Set<String> = setOf(
CommonRestrictions.TENANT_ID,
CommonRestrictions.WITHOUT_TENANT_ID,
)

fun DecisionsEvaluationBuilder.applyRestrictions(restrictions: Map<String, String>): DecisionsEvaluationBuilder = this.apply {
restrictions
.forEach { (key, value) ->
when (key) {
CommonRestrictions.TENANT_ID -> this.decisionDefinitionTenantId(value).apply {
require(!restrictions.containsKey(CommonRestrictions.WITHOUT_TENANT_ID)) {
"Illegal restriction combination. ${CommonRestrictions.TENANT_ID} " +
"and ${CommonRestrictions.WITHOUT_TENANT_ID} can't be provided in the same time because they are mutually exclusive."
}
}

CommonRestrictions.WITHOUT_TENANT_ID -> this.decisionDefinitionWithoutTenantId().apply {
require(!restrictions.containsKey(CommonRestrictions.TENANT_ID)) {
"Illegal restriction combination. ${CommonRestrictions.TENANT_ID} " +
"and ${CommonRestrictions.WITHOUT_TENANT_ID} can't be provided in the same time because they are mutually exclusive."
}
}
}
}
}

override fun meta(instance: MetaInfoAware): MetaInfo {
TODO("Not yet implemented")
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dev.bpmcrafters.processengineapi.adapter.c7.embedded.decision

import dev.bpmcrafters.processengineapi.decision.DecisionEvaluationOutput
import dev.bpmcrafters.processengineapi.decision.DecisionEvaluationResult

/**
* No output.
*/
object NoDecisionResult : DecisionEvaluationResult {
override fun asSingle(): DecisionEvaluationOutput = throw IllegalStateException("No decision result")

override fun asList(): List<DecisionEvaluationOutput> = listOf()

override fun meta(): Map<String, String> = mapOf("single-result" to "false", "result-count" to "0")

}
Loading