Skip to content
Draft
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
35 changes: 35 additions & 0 deletions api-check-ignore.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,39 @@
<from>*</from>
<to>*</to>
</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>
21 changes: 15 additions & 6 deletions src/main/scala/org/camunda/dmn/DmnEngine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ object DmnEngine {
class DmnEngine(configuration: DmnEngine.Configuration =
DmnEngine.Configuration(),
auditLogListeners: List[AuditLogListener] = List.empty,
clock: FeelEngineClock = FeelEngineClock.SystemClock) {
clock: FeelEngineClock = FeelEngineClock.SystemClock,
dmnRepository: DmnRepository = StatelessDmnRepository) {

import DmnEngine._

Expand All @@ -158,15 +159,18 @@ class DmnEngine(configuration: DmnEngine.Configuration =
feelUnaryTestsParser = feelEngine.parseUnaryTests(_).toEither.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 @@ -176,7 +180,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 All @@ -196,7 +202,10 @@ class DmnEngine(configuration: DmnEngine.Configuration =
}

def parse(stream: InputStream): Either[Failure, ParsedDmn] =
parser.parse(stream)
parser.parse(stream).map { parsedDmn =>
dmnRepository.put(parsedDmn)
parsedDmn
}

def eval(dmn: ParsedDmn,
decisionId: String,
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
61 changes: 44 additions & 17 deletions src/main/scala/org/camunda/dmn/evaluation/DecisionEvaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@ package org.camunda.dmn.evaluation

import org.camunda.dmn.DmnEngine._
import org.camunda.dmn.FunctionalHelper._
import org.camunda.feel.syntaxtree.{Val, ValFunction}
import org.camunda.dmn.parser.{
ParsedDecision,
ParsedDecisionLogic,
ParsedBusinessKnowledgeModel
}
import org.camunda.feel.syntaxtree.{Val, ValContext, ValFunction}
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 @@ -46,8 +44,9 @@ class DecisionEvaluator(
.flatMap(functions => {

val decisionEvaluationContext = context.copy(
variables = context.variables ++ decisionResults ++ functions,
currentElement = decision)
variables = context.variables
++ decisionResults ++ functions,
currentElement = decision)

eval(decision.logic, decisionEvaluationContext)
.flatMap(
Expand All @@ -61,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