Skip to content

Commit

Permalink
Merge branch '71-from-poc' into 71-poc
Browse files Browse the repository at this point in the history
  • Loading branch information
saig0 committed Oct 6, 2023
2 parents caf6088 + 67853b6 commit 9ac80d0
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 192 deletions.
38 changes: 35 additions & 3 deletions api-check-ignore.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@
<!-- num method params -->
<differenceType>7004</differenceType>
<method>*$anonfun*</method>
<from>*</from>
<to>*</to>
</difference>
<difference>
<className>org/camunda/dmn/**</className>
<!-- method param type -->
<differenceType>7005</differenceType>
<method>*$anonfun*</method>
<!--don't care what the type has changed to-->
<from>*</from>
<to>*</to>
</difference>
<difference>
Expand All @@ -43,4 +40,39 @@
<differenceType>7002</differenceType>
<method>*</method>
</difference>
<!-- changes from 1.8.1 to 1.9 -->
<difference>
<className>org/camunda/dmn/DmnEngine</className>
<differenceType>7004</differenceType>
<method>DmnEngine(*</method>
</difference>
<difference>
<className>org/camunda/dmn/parser/DmnParser</className>
<differenceType>7004</differenceType>
<method>DmnParser(*</method>
</difference>
<difference>
<className>org/camunda/dmn/parser/ParsedDmn</className>
<differenceType>7004</differenceType>
<method>*</method>
</difference>
<difference>
<className>org/camunda/dmn/parser/ParsedBusinessKnowledgeModel</className>
<differenceType>2000</differenceType>
</difference>
<difference>
<className>org/camunda/dmn/parser/ParsedBusinessKnowledgeModel</className>
<differenceType>4001</differenceType>
<to>**</to>
</difference>
<difference>
<className>org/camunda/dmn/parser/ParsedDecision</className>
<differenceType>2000</differenceType>
</difference>
<difference>
<className>org/camunda/dmn/parser/ParsedDecision</className>
<differenceType>4001</differenceType>
<to>**</to>
</difference>
<!-- END changes from 1.8.1 to 1.9 -->
</differences>
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>org.camunda.bpm.extension.dmn.scala</groupId>
<artifactId>dmn-engine</artifactId>
<name>DMN Scala Engine</name>
<version>1.8.2-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>

<parent>
<groupId>org.camunda</groupId>
Expand All @@ -15,7 +15,7 @@
</parent>

<properties>
<feel.version>1.16.0</feel.version>
<feel.version>1.16.1</feel.version>
<camunda-model-api.version>7.19.0</camunda-model-api.version>
<version.java>11</version.java>
<scala.version>2.13.11</scala.version>
Expand Down
17 changes: 10 additions & 7 deletions src/main/scala/org/camunda/dmn/DmnEngine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,19 +156,20 @@ class DmnEngine(configuration: DmnEngine.Configuration =
val parser = new DmnParser(
configuration = configuration,
feelParser = feelEngine.parseExpression(_).left.map(_.message),
feelUnaryTestsParser = feelEngine.parseUnaryTests(_).left.map(_.message),
dmnRepository = dmnRepository
)
feelUnaryTestsParser = feelEngine.parseUnaryTests(_).left.map(_.message))

val decisionEval = new DecisionEvaluator(eval = this.evalExpression,
evalBkm = bkmEval.createFunction)
val decisionEval = new DecisionEvaluator(
eval = this.evalExpression,
evalBkm = bkmEval.createFunction,
repository = dmnRepository
)

val literalExpressionEval = new LiteralExpressionEvaluator(feelEngine)

val decisionTableEval = new DecisionTableEvaluator(
literalExpressionEval.evalExpression)

val bkmEval = new BusinessKnowledgeEvaluator(this.evalExpression, valueMapper)
val bkmEval = new BusinessKnowledgeEvaluator(this.evalExpression, valueMapper, dmnRepository)

val contextEval = new ContextEvaluator(this.evalExpression)

Expand All @@ -178,7 +179,9 @@ class DmnEngine(configuration: DmnEngine.Configuration =

val invocationEval = new InvocationEvaluator(
eval = literalExpressionEval.evalExpression,
evalBkm = bkmEval.eval)
evalBkm = bkmEval.eval,
repository = dmnRepository
)

val functionDefinitionEval = new FunctionDefinitionEvaluator(
literalExpressionEval.evalExpression)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,20 @@ package org.camunda.dmn.evaluation

import org.camunda.dmn.DmnEngine._
import org.camunda.dmn.FunctionalHelper._
import org.camunda.dmn.parser.{
ParsedBusinessKnowledgeModel,
ParsedDecisionLogic
}
import org.camunda.dmn.parser.{DmnRepository, EmbeddedBusinessKnowledgeModel, ExpressionFailure, ImportedBusinessKnowledgeModel, ParsedBusinessKnowledgeModel, ParsedBusinessKnowledgeModelFailure, ParsedBusinessKnowledgeModelReference, ParsedDecisionLogic}
import org.camunda.feel.syntaxtree.{Val, ValError, ValFunction}
import org.camunda.feel.valuemapper.ValueMapper

class BusinessKnowledgeEvaluator(
eval: (ParsedDecisionLogic, EvalContext) => Either[Failure, Val],
valueMapper: ValueMapper) {
valueMapper: ValueMapper,
repository: DmnRepository) {

def eval(bkm: ParsedBusinessKnowledgeModel,
context: EvalContext): Either[Failure, Val] = {

evalRequiredKnowledge(bkm.requiredBkms, context)
resolveRequiredBkms(bkm)
.flatMap(evalRequiredKnowledge(_, context))
.flatMap(functions => {

val evalContext =
Expand All @@ -43,11 +42,21 @@ class BusinessKnowledgeEvaluator(
})
}

private def resolveRequiredBkms(bkm: ParsedBusinessKnowledgeModel): Either[Failure, Iterable[ParsedBusinessKnowledgeModel]] = {
mapEither[ParsedBusinessKnowledgeModelReference, ParsedBusinessKnowledgeModel](bkm.requiredBkms, {
case ImportedBusinessKnowledgeModel(namespace, id, _) => repository.getBusinessKnowledgeModel(namespace = namespace, bkmId = id)
case ParsedBusinessKnowledgeModelFailure(_, _, failureMessage) => Left(Failure(failureMessage))
case bkm: EmbeddedBusinessKnowledgeModel => Right(bkm)
})
}

def createFunction(
bkm: ParsedBusinessKnowledgeModel,
context: EvalContext): Either[Failure, (String, ValFunction)] = {

evalRequiredKnowledge(bkm.requiredBkms, context).map(functions => {
resolveRequiredBkms(bkm)
.flatMap(evalRequiredKnowledge(_, context))
.map(functions => {

val evalContext = context.copy(variables = context.variables ++ functions,
currentElement = bkm)
Expand Down
81 changes: 41 additions & 40 deletions src/main/scala/org/camunda/dmn/evaluation/DecisionEvaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ package org.camunda.dmn.evaluation
import org.camunda.dmn.DmnEngine._
import org.camunda.dmn.FunctionalHelper._
import org.camunda.feel.syntaxtree.{Val, ValContext, ValFunction}
import org.camunda.dmn.parser.{ParsedBusinessKnowledgeModel, ParsedDecision, ParsedDecisionLogic}
import org.camunda.dmn.parser.{DmnRepository, EmbeddedBusinessKnowledgeModel, EmbeddedDecision, ImportedBusinessKnowledgeModel, ImportedDecision, ParsedBusinessKnowledgeModel, ParsedBusinessKnowledgeModelFailure, ParsedBusinessKnowledgeModelReference, ParsedDecision, ParsedDecisionFailure, ParsedDecisionLogic, ParsedDecisionReference}
import org.camunda.feel.context.Context.StaticContext

class DecisionEvaluator(
eval: (ParsedDecisionLogic, EvalContext) => Either[Failure, Val],
evalBkm: (ParsedBusinessKnowledgeModel,
EvalContext) => Either[Failure, (String, ValFunction)]) {
EvalContext) => Either[Failure, (String, ValFunction)],
repository: DmnRepository) {

def eval(decision: ParsedDecision,
context: EvalContext): Either[Failure, Val] = {
Expand All @@ -42,38 +43,10 @@ class DecisionEvaluator(
evalRequiredKnowledge(decision.requiredBkms, context)
.flatMap(functions => {

val isImported: ((String, Val)) => Boolean = {
case (name, _) => name.contains(".")
}

// todo: replace the hack to wrap the imported BKMs and decisions into a context, maybe move to the BKM evaluation logic
val importedFunctions = functions
.filter(isImported)
.map { case (name, function) =>
val Array(prefix: String, functionName: String) = name.split('.')
prefix -> ValContext(StaticContext(
variables = Map.empty,
functions = Map(functionName -> List(function))
))
}
val embeddedFunctions = functions.filterNot(isImported)

val importedDecisions = decisionResults
.filter(isImported)
.map { case (name, decisionResult) =>
val Array(prefix: String, decisionName: String) = name.split('.')
prefix -> ValContext(StaticContext(
variables = Map(decisionName -> decisionResult),
functions = Map.empty
))
}
val embeddedDecisions = decisionResults.filterNot(isImported)

val decisionEvaluationContext = context.copy(
variables = context.variables
++ embeddedDecisions ++ importedDecisions
++ embeddedFunctions ++ importedFunctions,
currentElement = decision)
++ decisionResults ++ functions,
currentElement = decision)

eval(decision.logic, decisionEvaluationContext)
.flatMap(
Expand All @@ -87,17 +60,45 @@ class DecisionEvaluator(
}

private def evalRequiredDecisions(
requiredDecisions: Iterable[ParsedDecision],
context: EvalContext): Either[Failure, List[(String, Val)]] = {
mapEither(requiredDecisions,
(d: ParsedDecision) => evalDecision(d, context))
requiredDecisions: Iterable[ParsedDecisionReference],
context: EvalContext): Either[Failure, List[(String, Val)]] = {
mapEither[ParsedDecisionReference, (String, Val)](requiredDecisions, {
case ImportedDecision(namespace, decisionId, importName) =>
repository.getDecision(namespace = namespace, decisionId = decisionId)
.flatMap(evalDecision(_, context))
.map { case (name, result) =>
importName -> ValContext(StaticContext(
variables = Map(name -> result),
functions = Map.empty
))
}

case ParsedDecisionFailure(_, _, failureMessage) => Left(Failure(failureMessage))
case decision: EmbeddedDecision => evalDecision(decision, context)
}
)
}

private def evalRequiredKnowledge(
requiredBkms: Iterable[ParsedBusinessKnowledgeModel],
context: EvalContext): Either[Failure, List[(String, ValFunction)]] = {
mapEither(requiredBkms,
(bkm: ParsedBusinessKnowledgeModel) => evalBkm(bkm, context))
requiredBkms: Iterable[ParsedBusinessKnowledgeModelReference],
context: EvalContext): Either[Failure, List[(String, Val)]] = {
mapEither[ParsedBusinessKnowledgeModelReference, (String, Val)](requiredBkms, {
case ImportedBusinessKnowledgeModel(namespace, id, importName) =>
repository.getBusinessKnowledgeModel(namespace = namespace, bkmId = id)
.flatMap(evalBkm(_, context))
.map { case (name, resultFunction) =>
importName -> ValContext(
StaticContext(
variables = Map.empty,
functions = Map(name -> List(resultFunction))
)
)
}

case ParsedBusinessKnowledgeModelFailure(_, _, failureMessage) => Left(Failure(failureMessage))
case bkm: EmbeddedBusinessKnowledgeModel => evalBkm(bkm, context)
}
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,35 @@ package org.camunda.dmn.evaluation

import org.camunda.dmn.DmnEngine._
import org.camunda.dmn.FunctionalHelper._
import org.camunda.dmn.parser.{
ParsedBusinessKnowledgeModel,
ParsedExpression,
ParsedInvocation
}
import org.camunda.dmn.parser.{DmnRepository, EmbeddedBusinessKnowledgeModel, ImportedBusinessKnowledgeModel, ParsedBusinessKnowledgeModel, ParsedBusinessKnowledgeModelFailure, ParsedBusinessKnowledgeModelReference, ParsedExpression, ParsedInvocation}
import org.camunda.feel.syntaxtree.Val

class InvocationEvaluator(
eval: (ParsedExpression, EvalContext) => Either[Failure, Val],
evalBkm: (ParsedBusinessKnowledgeModel, EvalContext) => Either[Failure, Val]) {
evalBkm: (ParsedBusinessKnowledgeModel, EvalContext) => Either[Failure, Val],
repository: DmnRepository) {

def eval(invocation: ParsedInvocation,
context: EvalContext): Either[Failure, Val] = {

val result = evalParameters(invocation.bindings, context).flatMap { p =>
val ctx = context.copy(variables = context.variables ++ p.toMap)
evalBkm(invocation.invocation, ctx)

resolveBkm(invocation.invocation).flatMap(evalBkm(_, ctx))
}

context.audit(invocation, result)
result
}

private def resolveBkm(bkmRef: ParsedBusinessKnowledgeModelReference): Either[Failure, ParsedBusinessKnowledgeModel] = {
bkmRef match {
case ImportedBusinessKnowledgeModel(namespace, id, _) => repository.getBusinessKnowledgeModel(namespace = namespace, bkmId = id)
case ParsedBusinessKnowledgeModelFailure(_, _, failureMessage) => Left(Failure(failureMessage))
case bkm: EmbeddedBusinessKnowledgeModel => Right(bkm)
}
}

private def evalParameters(
bindings: Iterable[(String, ParsedExpression)],
context: EvalContext): Either[Failure, List[(String, Any)]] = {
Expand Down
Loading

0 comments on commit 9ac80d0

Please sign in to comment.