Skip to content

Commit

Permalink
Merge pull request #341 from camunda-community-hub/286-decision-evalu…
Browse files Browse the repository at this point in the history
…ation

feat: Add GraphQL API for decision evaluation
  • Loading branch information
saig0 authored Mar 2, 2023
2 parents 32566ba + abcbf97 commit 2f5160a
Show file tree
Hide file tree
Showing 19 changed files with 533 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.zeebe.zeeqs.data.entity

import javax.persistence.*

@Entity
data class DecisionEvaluation(
@Id @Column(name = "key_") val key: Long,
val decisionKey: Long,
val decisionRequirementsKey: Long,
@Lob val decisionOutput: String,
@Enumerated(EnumType.STRING) var state: DecisionEvaluationState = DecisionEvaluationState.EVALUATED,
val evaluationTime: Long,
val failedDecisionId: String? = null,
@Lob val evaluationFailureMessage: String? = null,
val processInstanceKey: Long? = null,
val elementInstanceKey: Long? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.zeebe.zeeqs.data.entity

import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Lob

@Entity
data class DecisionEvaluationInput(
@Id val id: String,
val inputId: String,
val inputName: String,
@Lob @Column(name = "value_") val value: String,
val evaluatedDecisionId: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.zeebe.zeeqs.data.entity

import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Lob

@Entity
data class DecisionEvaluationOutput(
@Id val id: String,
val outputId: String,
val outputName: String,
@Lob @Column(name = "value_") val value: String,
val evaluatedDecisionId: String,
val ruleId: String,
val ruleIndex: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.zeebe.zeeqs.data.entity

enum class DecisionEvaluationState {
EVALUATED, FAILED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.zeebe.zeeqs.data.entity

import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Lob

@Entity
data class EvaluatedDecision(
@Id val id: String,
val decisionKey: Long,
@Lob val decisionOutput: String,
val decisionEvaluationKey: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.zeebe.zeeqs.data.repository

import io.zeebe.zeeqs.data.entity.DecisionEvaluationInput
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.stereotype.Repository

@Repository
interface DecisionEvaluationInputRepository :
PagingAndSortingRepository<DecisionEvaluationInput, String> {

fun findAllByEvaluatedDecisionId(evaluatedDecisionId: String): List<DecisionEvaluationInput>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.zeebe.zeeqs.data.repository

import io.zeebe.zeeqs.data.entity.DecisionEvaluationOutput
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.stereotype.Repository

@Repository
interface DecisionEvaluationOutputRepository :
PagingAndSortingRepository<DecisionEvaluationOutput, String> {

fun findAllByEvaluatedDecisionId(evaluatedDecisionId: String): List<DecisionEvaluationOutput>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.zeebe.zeeqs.data.repository

import io.zeebe.zeeqs.data.entity.DecisionEvaluation
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.stereotype.Repository

@Repository
interface DecisionEvaluationRepository : PagingAndSortingRepository<DecisionEvaluation, Long> {

fun findAllByDecisionKey(decisionKey: Long): List<DecisionEvaluation>

fun countByDecisionKey(decisionKey: Long): Long

fun findAllByProcessInstanceKey(processInstanceKey: Long): List<DecisionEvaluation>

fun findAllByElementInstanceKey(elementInstanceKey: Long): List<DecisionEvaluation>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ package io.zeebe.zeeqs.data.repository
import io.zeebe.zeeqs.data.entity.Decision
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.stereotype.Repository
import java.util.*

@Repository
interface DecisionRepository : PagingAndSortingRepository<Decision, Long> {

fun findAllByDecisionRequirementsKey(decisionRequirementsKey: Long): List<Decision>

fun findByDecisionRequirementsKeyAndDecisionId(
decisionRequirementsKey: Long,
decisionId: String
): Optional<Decision>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.zeebe.zeeqs.data.repository

import io.zeebe.zeeqs.data.entity.EvaluatedDecision
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.stereotype.Repository

@Repository
interface EvaluatedDecisionRepository :
PagingAndSortingRepository<EvaluatedDecision, String> {

fun findAllByDecisionEvaluationKey(decisionEvaluationKey: Long): List<EvaluatedDecision>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.zeebe.zeeqs.graphql.resolvers.connection

import io.zeebe.zeeqs.data.entity.DecisionEvaluation

class DecisionEvaluationConnection(
val getItems: () -> List<DecisionEvaluation>,
val getCount: () -> Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.zeebe.zeeqs.graphql.resolvers.connection

import io.zeebe.zeeqs.data.entity.DecisionEvaluation
import org.springframework.graphql.data.method.annotation.SchemaMapping
import org.springframework.stereotype.Controller

@Controller
class DecisionEvaluationConnectionResolver {

@SchemaMapping(typeName = "DecisionEvaluationConnection", field = "nodes")
fun nodes(connection: DecisionEvaluationConnection): List<DecisionEvaluation> {
return connection.getItems()
}

@SchemaMapping(typeName = "DecisionEvaluationConnection", field = "totalCount")
fun totalCount(connection: DecisionEvaluationConnection): Long {
return connection.getCount()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package io.zeebe.zeeqs.graphql.resolvers.type

import io.zeebe.zeeqs.data.entity.*
import io.zeebe.zeeqs.data.repository.*
import org.springframework.data.repository.findByIdOrNull
import org.springframework.graphql.data.method.annotation.SchemaMapping
import org.springframework.stereotype.Controller
import kotlin.jvm.optionals.getOrNull

@Controller
class DecisionEvaluationResolver(
private val evaluatedDecisionRepository: EvaluatedDecisionRepository,
private val decisionEvaluationInputRepository: DecisionEvaluationInputRepository,
private val decisionEvaluationOutputRepository: DecisionEvaluationOutputRepository,
private val decisionRepository: DecisionRepository,
private val processInstanceRepository: ProcessInstanceRepository,
private val elementInstanceRepository: ElementInstanceRepository
) {

@SchemaMapping(typeName = "DecisionEvaluation", field = "decision")
fun decision(decisionEvaluation: DecisionEvaluation): Decision? {
return decisionRepository.findByIdOrNull(decisionEvaluation.decisionKey)
}

@SchemaMapping(typeName = "DecisionEvaluation", field = "failedDecision")
fun failedDecision(decisionEvaluation: DecisionEvaluation): Decision? {
return decisionEvaluation.failedDecisionId?.let {
decisionRepository.findByDecisionRequirementsKeyAndDecisionId(
decisionRequirementsKey = decisionEvaluation.decisionRequirementsKey,
decisionId = it
).getOrNull()
}
}

@SchemaMapping(typeName = "DecisionEvaluation", field = "processInstance")
fun processInstance(decisionEvaluation: DecisionEvaluation): ProcessInstance? {
return decisionEvaluation.processInstanceKey?.let {
processInstanceRepository.findByIdOrNull(it)
}
}

@SchemaMapping(typeName = "DecisionEvaluation", field = "elementInstance")
fun elementInstance(decisionEvaluation: DecisionEvaluation): ElementInstance? {
return decisionEvaluation.elementInstanceKey?.let {
elementInstanceRepository.findByIdOrNull(it)
}
}

@SchemaMapping(typeName = "DecisionEvaluation", field = "evaluatedDecisions")
fun evaluatedDecisions(decisionEvaluation: DecisionEvaluation): List<EvaluatedDecision> {
return evaluatedDecisionRepository.findAllByDecisionEvaluationKey(
decisionEvaluationKey = decisionEvaluation.key
)
}

@SchemaMapping(typeName = "EvaluatedDecision", field = "decision")
fun evaluatedDecision(evaluatedDecision: EvaluatedDecision): Decision? {
return decisionRepository.findByIdOrNull(evaluatedDecision.decisionKey)
}

@SchemaMapping(typeName = "EvaluatedDecision", field = "inputs")
fun evaluatedInputs(evaluatedDecision: EvaluatedDecision): List<DecisionEvaluationInput> {
return decisionEvaluationInputRepository.findAllByEvaluatedDecisionId(
evaluatedDecisionId = evaluatedDecision.id
)
}

@SchemaMapping(typeName = "EvaluatedDecision", field = "matchedRules")
fun matchedRules(evaluatedDecision: EvaluatedDecision): List<DecisionEvaluationMatchedRule> {
return decisionEvaluationOutputRepository.findAllByEvaluatedDecisionId(
evaluatedDecisionId = evaluatedDecision.id
)
.groupBy { MatchedRule(ruleId = it.ruleId, ruleIndex = it.ruleIndex) }
.map { (rule, outputs) ->
DecisionEvaluationMatchedRule(
ruleId = rule.ruleId,
ruleIndex = rule.ruleIndex,
outputs = outputs
)
}
}

data class MatchedRule(
val ruleId: String,
val ruleIndex: Int
)

data class DecisionEvaluationMatchedRule(
val ruleId: String,
val ruleIndex: Int,
val outputs: List<DecisionEvaluationOutput>
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@ package io.zeebe.zeeqs.graphql.resolvers.type

import io.zeebe.zeeqs.data.entity.Decision
import io.zeebe.zeeqs.data.entity.DecisionRequirements
import io.zeebe.zeeqs.data.repository.DecisionEvaluationRepository
import io.zeebe.zeeqs.data.repository.DecisionRequirementsRepository
import io.zeebe.zeeqs.graphql.resolvers.connection.DecisionEvaluationConnection
import org.springframework.data.repository.findByIdOrNull
import org.springframework.graphql.data.method.annotation.SchemaMapping
import org.springframework.stereotype.Controller

@Controller
class DecisionResolver(
private val decisionRequirementsRepository: DecisionRequirementsRepository
private val decisionRequirementsRepository: DecisionRequirementsRepository,
private val decisionEvaluationRepository: DecisionEvaluationRepository
) {

@SchemaMapping(typeName = "Decision", field = "decisionRequirements")
fun decisionRequirements(decision: Decision): DecisionRequirements? {
return decisionRequirementsRepository.findByIdOrNull(decision.decisionRequirementsKey)
}

@SchemaMapping(typeName = "Decision", field = "evaluations")
fun evaluations(decision: Decision): DecisionEvaluationConnection {
return DecisionEvaluationConnection(
getItems = { decisionEvaluationRepository.findAllByDecisionKey(decisionKey = decision.key) },
getCount = { decisionEvaluationRepository.countByDecisionKey(decisionKey = decision.key) }
)
}

}
2 changes: 2 additions & 0 deletions graphql-api/src/main/resources/graphql/Decision.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type Decision {
version: Int!
# The decision requirements graph that contains the decision.
decisionRequirements: DecisionRequirements
# The evaluations of the decision.
evaluations: DecisionEvaluationConnection!
}

type DecisionConnection {
Expand Down
76 changes: 76 additions & 0 deletions graphql-api/src/main/resources/graphql/DecisionEvaluation.graphqls
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# An evaluation of a DMN decision.
type DecisionEvaluation {
# The unique key of the decision evaluation.
key: ID!
# The evaluated decision (i.e. the root/called decision).
decision: Decision
# The output of the evaluation (i.e. the decision result).
decisionOutput: String!
# The state of the evaluation (i.e. evaluation was successful or with failures).
state: DecisionEvaluationState!
# All evaluated decisions (i.e. the root decision and all required decisions).
evaluatedDecisions: [EvaluatedDecision!]
# The failure message if the evaluation was not successful.
evaluationFailureMessage: String
# The decision that caused the failure if the evaluation was not successful.
failedDecision: Decision
# The related process instance if the decision was called from a BPMN process.
processInstance: ProcessInstance
# The related element instance of the business rule task if the decision was called from a BPMN process.
elementInstance: ElementInstance
}

# An intermediate result of a decision evaluation.
type EvaluatedDecision {
# The evaluated decision.
decision: Decision
# The output of the decision evaluation.
decisionOutput: String!
# The evaluated inputs if the decision is a decision table.
inputs: [DecisionEvaluationInput!]
# The matched rules if the decision is a decision table.
matchedRules: [DecisionEvaluationMatchedRule!]
}

# An evaluated input of a decision table.
type DecisionEvaluationInput {
# The id of the input.
inputId: String!
# The name of the input.
inputName: String!
# The value of the evaluated input.
value: String!
}

# A matched rule of a decision table evaluation.
type DecisionEvaluationMatchedRule {
# The id of the rule.
ruleId: String!
# The index of the rule.
ruleIndex: Int!
# The evaluated outputs of the rule.
outputs: [DecisionEvaluationOutput!]
}

# An evaluated output of a decision table.
type DecisionEvaluationOutput {
# The id of the output.
outputId: String!
# The name of the output.
outputName: String!
# The value of the evaluated output.
value: String!
}

# The state of a decision evaluation.
enum DecisionEvaluationState {
# The decision was evaluated successfully.
EVALUATED,
# The decision evaluation failed.
FAILED
}

type DecisionEvaluationConnection {
totalCount: Int!
nodes: [DecisionEvaluation!]!
}
Loading

0 comments on commit 2f5160a

Please sign in to comment.