Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Extract enums to top level and use them in Js interface #126

Merged
merged 6 commits into from
Apr 11, 2024
Merged
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
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