Skip to content

Commit

Permalink
feat!: Extract enums to top level and use them in Js interface (#126)
Browse files Browse the repository at this point in the history
* Use enums in Js interface

* Fix tests

* Opt-in for JsExport annotation

* Fix code review comments

* Use last snapshot version of expression-parser

* Use last snapshot version of expression-parser
  • Loading branch information
enricocolasante authored Apr 11, 2024
1 parent 63f89f0 commit c6ff765
Show file tree
Hide file tree
Showing 38 changed files with 254 additions and 280 deletions.
7 changes: 6 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,14 @@ kotlin {


sourceSets {
all {
languageSettings.apply {
optIn("kotlin.js.ExperimentalJsExport")
}
}
val commonMain by getting {
dependencies {
implementation("org.hisp.dhis.lib.expression:expression-parser:1.1.0-SNAPSHOT")
implementation("org.hisp.dhis.lib.expression:expression-parser:1.1.0-20240411.094221-16")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1")
}
}
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
`kotlin-dsl`
}

val kotlinVersion = "1.9.21"
val kotlinVersion = "1.9.22"
val dokkaVersion = "1.9.10"
val nexusPluginVersion = "1.3.0"
val npmPluginVersion = "3.4.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hisp.dhis.rules.api

import org.hisp.dhis.lib.expression.spi.ValueType
import kotlin.js.JsExport

/*
* Copyright (c) 2004-2020, University of Oslo
Expand Down Expand Up @@ -30,9 +31,7 @@ import org.hisp.dhis.lib.expression.spi.ValueType
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
* @author Zubair Asghar
*/
@JsExport
enum class ItemValueType(val value: String) {
NUMBER("1.0"),
DATE("2020-01-01"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hisp.dhis.rules.engine

import org.hisp.dhis.lib.expression.Expression
import org.hisp.dhis.lib.expression.ExpressionMode
import org.hisp.dhis.lib.expression.spi.IllegalExpressionException
import org.hisp.dhis.lib.expression.spi.ParseException
import org.hisp.dhis.lib.expression.spi.ValueType
Expand Down Expand Up @@ -51,15 +52,15 @@ internal class DefaultRuleEngine: RuleEngine {

override fun validate(expression: String, dataItemStore: Map<String, DataItem>): RuleValidationResult {
// Rule condition expression should be evaluated against Boolean
return getExpressionDescription(expression, Expression.Mode.RULE_ENGINE_CONDITION, dataItemStore)
return getExpressionDescription(expression, ExpressionMode.RULE_ENGINE_CONDITION, dataItemStore)
}

override fun validateDataFieldExpression(expression: String, dataItemStore: Map<String, DataItem>): RuleValidationResult {
// Rule action data field should be evaluated against all i.e Boolean, String, Date and Numerical value
return getExpressionDescription(expression, Expression.Mode.RULE_ENGINE_ACTION, dataItemStore)
return getExpressionDescription(expression, ExpressionMode.RULE_ENGINE_ACTION, dataItemStore)
}

private fun getExpressionDescription(expression: String, mode: Expression.Mode, dataItemStore: Map<String, DataItem>): RuleValidationResult {
private fun getExpressionDescription(expression: String, mode: ExpressionMode, dataItemStore: Map<String, DataItem>): RuleValidationResult {
return try {
val validationMap: Map<String, ValueType> = dataItemStore.mapValues { e -> e.value.valueType.toValueType() }
Expression(expression, mode, false).validate(validationMap)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hisp.dhis.rules.engine

import org.hisp.dhis.lib.expression.Expression
import org.hisp.dhis.lib.expression.ExpressionMode
import org.hisp.dhis.lib.expression.spi.ExpressionData
import org.hisp.dhis.lib.expression.spi.IllegalExpressionException
import org.hisp.dhis.rules.createLogger
Expand Down Expand Up @@ -57,7 +58,7 @@ internal class RuleConditionEvaluator {
rule.condition,
valueMap,
supplementaryData,
Expression.Mode.RULE_ENGINE_CONDITION
ExpressionMode.RULE_ENGINE_CONDITION
).toBoolean()
) {
for (action in rule.actions) {
Expand All @@ -68,7 +69,7 @@ internal class RuleConditionEvaluator {
unwrapVariableName(action.content()!!),
RuleVariableValue(
RuleValueType.TEXT,
process(action.data, valueMap, supplementaryData, Expression.Mode.RULE_ENGINE_ACTION),
process(action.data, valueMap, supplementaryData, ExpressionMode.RULE_ENGINE_ACTION),
listOf(),
null
),
Expand Down Expand Up @@ -134,7 +135,7 @@ internal class RuleConditionEvaluator {

private fun process(
condition: String?, valueMap: Map<String, RuleVariableValue>,
supplementaryData: Map<String, List<String>>, mode: Expression.Mode
supplementaryData: Map<String, List<String>>, mode: ExpressionMode
): String {
if (condition==null || condition.isEmpty()) {
return ""
Expand Down Expand Up @@ -180,15 +181,15 @@ internal class RuleConditionEvaluator {
supplementaryData: Map<String, List<String>>
): RuleEffect {
if (ruleAction.type == "ASSIGN") {
val data = process(ruleAction.data, valueMap, supplementaryData, Expression.Mode.RULE_ENGINE_ACTION)
val data = process(ruleAction.data, valueMap, supplementaryData, ExpressionMode.RULE_ENGINE_ACTION)
updateValueMap(ruleAction.field()!!, RuleVariableValue(RuleValueType.TEXT, data, listOf(), null), valueMap)
return if (data.isEmpty()) {
RuleEffect(rule.uid, ruleAction, null)
} else {
RuleEffect(rule.uid, ruleAction, data)
}
}
val data = if (!ruleAction.data.isNullOrEmpty()) process(ruleAction.data, valueMap, supplementaryData, Expression.Mode.RULE_ENGINE_ACTION) else ""
val data = if (!ruleAction.data.isNullOrEmpty()) process(ruleAction.data, valueMap, supplementaryData, ExpressionMode.RULE_ENGINE_ACTION) else ""
return RuleEffect(
rule.uid,
ruleAction,
Expand Down
2 changes: 0 additions & 2 deletions src/commonMain/kotlin/org/hisp/dhis/rules/models/Option.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package org.hisp.dhis.rules.models

import kotlin.js.ExperimentalJsExport
import kotlin.js.JsExport

@JsExport
@OptIn(ExperimentalJsExport::class)
data class Option(val name: String, val code: String)
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package org.hisp.dhis.rules.models

import kotlin.js.ExperimentalJsExport
import kotlin.js.JsExport

@JsExport
@OptIn(ExperimentalJsExport::class)
data class RuleAttributeValue(
val trackedEntityAttribute: String,
val value: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,8 @@ data class RuleEnrollment(
val programName: String,
val incidentDate: LocalDate,
val enrollmentDate: LocalDate,
val status: Status,
val status: RuleEnrollmentStatus,
val organisationUnit: String,
val organisationUnitCode: String,
val attributeValues: List<RuleAttributeValue>
) {
enum class Status {
ACTIVE,
COMPLETED,
CANCELLED
}
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.hisp.dhis.rules.models

import kotlin.js.JsExport

@JsExport
enum class RuleEnrollmentStatus {
ACTIVE,
COMPLETED,
CANCELLED
}
13 changes: 2 additions & 11 deletions src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEvent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,11 @@ data class RuleEvent(
val event: String,
val programStage: String,
val programStageName: String,
val status: Status,
val status: RuleEventStatus,
val eventDate: Instant,
val dueDate: LocalDate?,
val completedDate: LocalDate?,
val organisationUnit: String,
val organisationUnitCode: String?,
val dataValues: List<RuleDataValue>
) {
enum class Status {
ACTIVE,
COMPLETED,
SCHEDULE,
SKIPPED,
VISITED,
OVERDUE
}
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.hisp.dhis.rules.models

import kotlin.js.JsExport

@JsExport
enum class RuleEventStatus {
ACTIVE,
COMPLETED,
SCHEDULE,
SKIPPED,
VISITED,
OVERDUE
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package org.hisp.dhis.rules.models

import org.hisp.dhis.rules.api.RuleEngine
import org.hisp.dhis.rules.engine.DefaultRuleEngine
import kotlin.js.ExperimentalJsExport
import kotlin.js.JsExport
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmStatic

/*
Expand Down Expand Up @@ -36,7 +32,6 @@ import kotlin.jvm.JvmStatic
*/

@JsExport
@OptIn(ExperimentalJsExport::class)
data class RuleValidationResult(
val valid: Boolean,
val errorMessage: String? = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.hisp.dhis.rules.models

import kotlin.js.JsExport

@JsExport
enum class RuleValueType(private val defaultValue: Any) {
TEXT(""),
NUMERIC(0.0),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.hisp.dhis.rules.models

import kotlin.js.JsExport

/**
* This Enum specify the type of tracker object.
*/
@JsExport
enum class TrackerObjectType(private val type: String) {
EVENT("event"),
ENROLLMENT("enrollment")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class ConstantsValueTest {
programName = "test_program",
incidentDate = LocalDate.Companion.currentDate(),
enrollmentDate = LocalDate.Companion.currentDate(),
status = RuleEnrollment.Status.ACTIVE,
status = RuleEnrollmentStatus.ACTIVE,
organisationUnit = "test_ou",
organisationUnitCode = "test_ou_code",
attributeValues = listOf(RuleAttributeValue("test_attribute", "test_value"))
Expand All @@ -76,7 +76,7 @@ class ConstantsValueTest {
programName = "test_program",
incidentDate = LocalDate.Companion.currentDate(),
enrollmentDate = LocalDate.Companion.currentDate(),
status = RuleEnrollment.Status.ACTIVE,
status = RuleEnrollmentStatus.ACTIVE,
organisationUnit = "test_ou",
organisationUnitCode = "test_ou_code",
attributeValues = listOf(RuleAttributeValue("test_attribute", "test_value"))
Expand Down Expand Up @@ -104,7 +104,7 @@ class ConstantsValueTest {
programName = "test_program",
incidentDate = LocalDate.Companion.currentDate(),
enrollmentDate = LocalDate.Companion.currentDate(),
status = RuleEnrollment.Status.ACTIVE,
status = RuleEnrollmentStatus.ACTIVE,
organisationUnit = "test_ou",
organisationUnitCode = "test_ou_code",
attributeValues = listOf(RuleAttributeValue("test_attribute", "test_value"))
Expand All @@ -126,7 +126,7 @@ class ConstantsValueTest {
event = "test_event",
programStage = "test_program_stage",
programStageName = "",
status = RuleEvent.Status.ACTIVE,
status = RuleEventStatus.ACTIVE,
eventDate = Clock.System.now(),
dueDate = LocalDate.currentDate(),
organisationUnit = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ class ProgramRuleVariableTest {
PROGRAM_NAME,
INCIDENT_DATE,
ENROLLMENT_DATE,
RuleEnrollment.Status.ACTIVE,
RuleEnrollmentStatus.ACTIVE,
ORGANISATION_UNIT,
ORGANISATION_UNIT_CODE,
listOf(RuleAttributeValue("test_attribute", "test_value"))
Expand All @@ -246,11 +246,11 @@ class ProgramRuleVariableTest {
private val INCIDENT_DATE = LocalDate.parse(INCIDENT_DATE_STRING)
private const val PROGRAM_STAGE = "program stage"
private const val PROGRAM_STAGE_NAME = "program stage name"
private val RULE_EVENT_STATUS = RuleEvent.Status.ACTIVE
private val RULE_EVENT_STATUS = RuleEventStatus.ACTIVE
private const val ORGANISATION_UNIT = "organisation unit"
private const val ORGANISATION_UNIT_CODE = "organisation unit code"
private const val ENROLLMENT_ID = "enrollment id"
private val ENROLLMENT_STATUS = RuleEnrollment.Status.ACTIVE
private val ENROLLMENT_STATUS = RuleEnrollmentStatus.ACTIVE
private const val EVENT_ID = "event id"
private const val PROGRAM_NAME = "program name"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@ import kotlinx.datetime.LocalDate
import org.hisp.dhis.rules.RuleEngineTestUtils.getRuleEngineContext
import org.hisp.dhis.rules.api.RuleEngine
import org.hisp.dhis.rules.api.RuleEngineContext
import org.hisp.dhis.rules.models.Rule
import org.hisp.dhis.rules.models.RuleAction
import org.hisp.dhis.rules.models.RuleDataValue
import org.hisp.dhis.rules.models.RuleEvent
import org.hisp.dhis.rules.models.*
import org.hisp.dhis.rules.utils.currentDate
import kotlin.test.Test
import kotlin.test.assertEquals

// ToDo: function tests (check that function invocations are producing expected values; check nested function invocation)
// ToDo: various source type tests (referencing variables from different events)
class RuleEngineEffectTypesTest {
private fun getTestRuleEvent(status: RuleEvent.Status): RuleEvent {
private fun getTestRuleEvent(status: RuleEventStatus): RuleEvent {
return RuleEvent(
event = "test_event",
programStage = "test_program_stage",
Expand All @@ -39,7 +36,7 @@ class RuleEngineEffectTypesTest {
fun simpleConditionMustResultInAssignEffect() {
val ruleAction = RuleAction("'test_string'", "ASSIGN", mapOf(Pair("field", "test_data_element")))
val rule = Rule("true", listOf(ruleAction))
val ruleEffects = RuleEngine.getInstance().evaluate(getTestRuleEvent(RuleEvent.Status.ACTIVE), null, emptyList(), RuleEngineContext(listOf(rule)))
val ruleEffects = RuleEngine.getInstance().evaluate(getTestRuleEvent(RuleEventStatus.ACTIVE), null, emptyList(), RuleEngineContext(listOf(rule)))
assertEquals(1, ruleEffects.size)
assertEquals("test_string", ruleEffects[0].data)
assertEquals(ruleAction, ruleEffects[0].ruleAction)
Expand All @@ -49,7 +46,7 @@ class RuleEngineEffectTypesTest {
fun simpleConditionMustResultInAssignEffectMultipleExecution() {
val ruleAction = RuleAction("'test_string'", "ASSIGN", mapOf(Pair("field", "test_data_element")))
val rule = Rule("true", listOf(ruleAction))
val ruleEffects = RuleEngine.getInstance().evaluateAll(null, listOf(getTestRuleEvent(RuleEvent.Status.ACTIVE)), getRuleEngineContext(listOf(rule)))
val ruleEffects = RuleEngine.getInstance().evaluateAll(null, listOf(getTestRuleEvent(RuleEventStatus.ACTIVE)), getRuleEngineContext(listOf(rule)))
assertEquals(1, ruleEffects.size)
assertEquals("test_string", ruleEffects[0].ruleEffects[0].data)
assertEquals(ruleAction, ruleEffects[0].ruleEffects[0].ruleAction)
Expand All @@ -59,7 +56,7 @@ class RuleEngineEffectTypesTest {
fun testEnvironmentVariableExpression() {
val ruleAction = RuleAction("", "HIDEFIELD", mapOf(Pair("content", "test_action_content"), Pair("field", "test_data_element")))
val rule = Rule("V{event_status} =='COMPLETED'", listOf(ruleAction))
val ruleEffects = RuleEngine.getInstance().evaluate(getTestRuleEvent(RuleEvent.Status.COMPLETED), null, emptyList(), RuleEngineContext(listOf(rule)))
val ruleEffects = RuleEngine.getInstance().evaluate(getTestRuleEvent(RuleEventStatus.COMPLETED), null, emptyList(), RuleEngineContext(listOf(rule)))
assertEquals(1, ruleEffects.size)
assertEquals("", ruleEffects[0].data)
assertEquals(ruleAction, ruleEffects[0].ruleAction)
Expand Down
Loading

0 comments on commit c6ff765

Please sign in to comment.