diff --git a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/enumeration/KpiKind.kt b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/enumeration/KpiKind.kt index 4a0539c21ae7e6e1cdadb7c59b64033d58dc361e..67f339a1d8a011d24da3b9b7f45c3307fe1c5e0d 100644 --- a/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/enumeration/KpiKind.kt +++ b/app/backend/src/main/kotlin/de/fraunhofer/iem/app/kpi/enumeration/KpiKind.kt @@ -7,22 +7,18 @@ import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiCalculationResult import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiResultNode fun KpiResultNode.toViewModel(): KPITreeResponseDto { - var additionalWeight = 0.0 - val score = when (this.kpiResult) { - is KpiCalculationResult.Success -> (this.kpiResult as KpiCalculationResult.Success).score - is KpiCalculationResult.Incomplete -> { - additionalWeight = (this.kpiResult as KpiCalculationResult.Incomplete).additionalWeights - (this.kpiResult as KpiCalculationResult.Incomplete).score - } - + val score = when (val result = this.kpiResult) { + is KpiCalculationResult.Success -> result.score + is KpiCalculationResult.Incomplete -> result.score else -> -1 } val isEmpty = score == -1 + return this.kpiId.toViewModel(score, this.children.map { KPITreeChildResponseDto( kpi = it.target.toViewModel(), - plannedWeight = it.weight, - actualWeight = it.weight + additionalWeight + plannedWeight = it.plannedWeight, + actualWeight = it.actualWeight ) }, isEmpty) diff --git a/app/gradle/libs.versions.toml b/app/gradle/libs.versions.toml index eb30cf3ac912666c397f1c95e2419218593e58ed..0009636e6f4fe600a8c28a7a2f2c184a44f4fb99 100644 --- a/app/gradle/libs.versions.toml +++ b/app/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "2.0.10" springBoot = "3.3.2" kotlinCoroutine = "1.9.0-RC" -ktor = "3.0.0-beta-2" +ktor = "2.3.12" kpiCalculator = "0.0.2-SNAPSHOT" kotlinJackson = "2.17.1" springOpenApi = "2.5.0" diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculator.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculator.kt index 5f2abb90267c3351abbdfdd9d54363f15a79699c..8ae82a12fcb80cbdfe37bd96f1f8187b28060a78 100644 --- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculator.kt +++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculator.kt @@ -81,7 +81,7 @@ object KpiCalculator { calculationStrategy = KpiStrategyId.RAW_VALUE_STRATEGY, parent = parent ) - newNode.setScore(rawValue.score) + newNode.setResult(rawValue.score) newNode } diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiCalculationNode.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiCalculationNode.kt index a83b32752f3f04d2af89de3bdc15bc828362c9d2..252bfbfe6c9c9e272f89f6030e7234ea66e2b11a 100644 --- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiCalculationNode.kt +++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiCalculationNode.kt @@ -17,9 +17,9 @@ internal class KpiCalculationNode( val parent: KpiCalculationNode? ) { - private var score: KpiCalculationResult = KpiCalculationResult.Empty() - fun setScore(score: Int) { - this.score = KpiCalculationResult.Success(score) + private var result: KpiCalculationResult = KpiCalculationResult.Empty() + fun setResult(result: Int) { + this.result = KpiCalculationResult.Success(result) } @@ -28,7 +28,7 @@ internal class KpiCalculationNode( get() = _hierarchyEdges fun addChild(node: KpiCalculationNode, weight: Double) { - _hierarchyEdges.add(KpiHierarchyEdge(to = node, from = this, weight = weight)) + _hierarchyEdges.add(KpiHierarchyEdge(to = node, from = this, plannedWeight = weight)) } fun removeChild(node: KpiCalculationNode) { @@ -36,16 +36,16 @@ internal class KpiCalculationNode( } fun getWeight(node: KpiCalculationNode): Double? { - return hierarchyEdges.find { it.to == node }?.weight + return hierarchyEdges.find { it.to == node }?.actualWeight } fun calculateKpi(): KpiCalculationResult { val strategyData = hierarchyEdges.map { - Pair(it.to.score, it.weight) + Pair(it.to.result, it.actualWeight) } - score = when (calculationStrategy) { + result = when (calculationStrategy) { KpiStrategyId.RAW_VALUE_STRATEGY -> - score + result KpiStrategyId.RATIO_STRATEGY -> RatioKPICalculationStrategy.calculateKpi(strategyData) @@ -56,8 +56,41 @@ internal class KpiCalculationNode( KpiStrategyId.MAXIMUM_STRATEGY -> MaximumKPICalculationStrategy.calculateKpi(strategyData) } + updateEdgeWeights(result) + return result + } + + private fun updateEdgeWeights(result: KpiCalculationResult) { + val updatedEdges = hierarchyEdges.map { edge -> + + if (result is KpiCalculationResult.Success) { + return@map edge + } + + val targetResult = edge.to.result + + if (result is KpiCalculationResult.Incomplete + && (targetResult !is KpiCalculationResult.Empty + && targetResult !is KpiCalculationResult.Error) + ) { + return@map KpiHierarchyEdge( + from = this, + to = edge.to, + plannedWeight = edge.plannedWeight, + actualWeight = edge.plannedWeight + result.additionalWeights + ) + } + + return@map KpiHierarchyEdge( + from = this, + to = edge.to, + plannedWeight = edge.plannedWeight, + actualWeight = 0.0 + ) + } - return score + _hierarchyEdges.clear() + _hierarchyEdges.addAll(updatedEdges) } companion object { @@ -65,11 +98,12 @@ internal class KpiCalculationNode( return KpiResultNode( kpiId = node.kind, strategyType = node.calculationStrategy, - kpiResult = node.score, + kpiResult = node.result, children = node.hierarchyEdges.map { KpiResultEdge( target = to(it.to), - weight = it.weight + plannedWeight = it.plannedWeight, + actualWeight = it.actualWeight ) } ) @@ -87,7 +121,7 @@ internal class KpiCalculationNode( KpiHierarchyEdge( to = from(child.target, calcNode), from = calcNode, - weight = child.weight + plannedWeight = child.weight ) } calcNode._hierarchyEdges.addAll(children) diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiHierarchyEdge.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiHierarchyEdge.kt index 03c1a0df664aa42b3c5c32edd90ea2a1205a33d1..1bb42bed8c442daab9e4f4ba8e0c1e1944621b67 100644 --- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiHierarchyEdge.kt +++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/hierarchy/KpiHierarchyEdge.kt @@ -3,5 +3,6 @@ package de.fraunhofer.iem.kpiCalculator.core.hierarchy internal data class KpiHierarchyEdge( val from: KpiCalculationNode, val to: KpiCalculationNode, - val weight: Double, + val plannedWeight: Double, + val actualWeight: Double = plannedWeight, ) diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/MaximumKPICalculationStrategy.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/MaximumKPICalculationStrategy.kt index 164808214247d34b2d1684269258822bf2a936e4..a1b42e0416944aa240e4f934cb353902e8e1e80c 100644 --- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/MaximumKPICalculationStrategy.kt +++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/MaximumKPICalculationStrategy.kt @@ -2,7 +2,7 @@ package de.fraunhofer.iem.kpiCalculator.core.strategy import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiCalculationResult -object MaximumKPICalculationStrategy : KpiCalculationStrategy { +internal object MaximumKPICalculationStrategy : KpiCalculationStrategy { override fun calculateKpi( successScores: List<Pair<KpiCalculationResult.Success, Double>>, failed: List<Pair<KpiCalculationResult, Double>>, diff --git a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/RatioKPICalculationStrategy.kt b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/RatioKPICalculationStrategy.kt index 10a03cd00c7f4b03a58ed5c8cb9868e54895f3a7..2a94ee3291a896e565920f7e3153cc3767d481fd 100644 --- a/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/RatioKPICalculationStrategy.kt +++ b/kpi-calculator/core/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/core/strategy/RatioKPICalculationStrategy.kt @@ -2,7 +2,7 @@ package de.fraunhofer.iem.kpiCalculator.core.strategy import de.fraunhofer.iem.kpiCalculator.model.kpi.hierarchy.KpiCalculationResult -object RatioKPICalculationStrategy : KpiCalculationStrategy { +internal object RatioKPICalculationStrategy : KpiCalculationStrategy { /** * Returns smallerValue / biggerValue, regardless in which order the values are given. */ diff --git a/kpi-calculator/core/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculatorTest.kt b/kpi-calculator/core/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculatorTest.kt index 4b8a41036525ae9bb09f5a0989f3e678fb171713..e80a516b4d8d090495d02419c9fcc726df5b38ab 100644 --- a/kpi-calculator/core/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculatorTest.kt +++ b/kpi-calculator/core/src/test/kotlin/de/fraunhofer/iem/kpiCalculator/core/KpiCalculatorTest.kt @@ -183,6 +183,12 @@ class KpiCalculatorTest { if (result is KpiCalculationResult.Incomplete) { assertEquals(85, result.score) + val sastResult = res.rootNode.children.find { it.target.kpiId == KpiId.SAST_USAGE } ?: fail() + assertEquals(0.0, sastResult.actualWeight) + val vulnerabilityEdges = res.rootNode.children.filter { it.target.kpiId == KpiId.VULNERABILITY_SCORE } + assertEquals(2, vulnerabilityEdges.size) + assertEquals(vulnerabilityEdges.first().actualWeight, 0.5) + assertEquals(vulnerabilityEdges[1].actualWeight, 0.5) } else { fail() } diff --git a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/hierarchy/DefaultHierarchy.kt b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/hierarchy/DefaultHierarchy.kt index 33150b5802d7245fae37db19af0f32d4b7ecd78c..d9a79f6aa4b6353ef47ab8c64fca780e74f39d62 100644 --- a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/hierarchy/DefaultHierarchy.kt +++ b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/hierarchy/DefaultHierarchy.kt @@ -89,9 +89,6 @@ object DefaultHierarchy { children = listOf() ) - //XXX: this is different as for all other KPIs as we don't know how many children there will be - // there is one child for every found vulnerability. This needs to be kept in mind during - // mapping of hierarchy to data. val maxDepVulnerability = KpiNode( kpiId = KpiId.MAXIMAL_VULNERABILITY, strategyType = KpiStrategyId.MAXIMUM_STRATEGY, diff --git a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/hierarchy/KpiHierarchy.kt b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/hierarchy/KpiHierarchy.kt index 147dfead0dcb063bdcb50afaaf8a61ecf52484fe..0505b5f1ff3c0cd4f591f43e924a110e24819f34 100644 --- a/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/hierarchy/KpiHierarchy.kt +++ b/kpi-calculator/model/src/main/kotlin/de/fraunhofer/iem/kpiCalculator/model/kpi/hierarchy/KpiHierarchy.kt @@ -38,7 +38,7 @@ data class KpiResultNode( ) @Serializable -data class KpiResultEdge(val target: KpiResultNode, val weight: Double) +data class KpiResultEdge(val target: KpiResultNode, val plannedWeight: Double, val actualWeight: Double) @Serializable sealed class KpiCalculationResult {