diff --git a/api/expression-parser.api b/api/expression-parser.api index 759b6b2..6c4fac2 100644 --- a/api/expression-parser.api +++ b/api/expression-parser.api @@ -17,6 +17,7 @@ public final class org/hisp/dhis/lib/expression/Expression { } public final class org/hisp/dhis/lib/expression/ExpressionMode : java/lang/Enum { + public static final field ANDROID_CUSTOM_INTENT_EXPRESSION Lorg/hisp/dhis/lib/expression/ExpressionMode; public static final field INDICATOR_EXPRESSION Lorg/hisp/dhis/lib/expression/ExpressionMode; public static final field PREDICTOR_GENERATOR_EXPRESSION Lorg/hisp/dhis/lib/expression/ExpressionMode; public static final field PREDICTOR_SKIP_TEST Lorg/hisp/dhis/lib/expression/ExpressionMode; @@ -361,6 +362,10 @@ public final class org/hisp/dhis/lib/expression/ast/Nodes$AggregationTypeNode : public fun (Lorg/hisp/dhis/lib/expression/ast/NodeType;Ljava/lang/String;)V } +public final class org/hisp/dhis/lib/expression/ast/Nodes$AndroidCustomIntentNode : org/hisp/dhis/lib/expression/ast/Nodes$SimpleNode { + public fun (Lorg/hisp/dhis/lib/expression/ast/NodeType;Ljava/lang/String;)V +} + public final class org/hisp/dhis/lib/expression/ast/Nodes$ArgumentNode : org/hisp/dhis/lib/expression/ast/Nodes$ComplexNode { public fun (Lorg/hisp/dhis/lib/expression/ast/NodeType;Ljava/lang/String;)V public fun getValueType ()Lorg/hisp/dhis/lib/expression/spi/ValueType; @@ -1304,6 +1309,17 @@ public final class org/hisp/dhis/lib/expression/spi/AggregationType : java/lang/ public static fun values ()[Lorg/hisp/dhis/lib/expression/spi/AggregationType; } +public final class org/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable : java/lang/Enum { + public static final field orgunit_code Lorg/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable; + public static final field orgunit_id Lorg/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable; + public static final field orgunit_path Lorg/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable; + public static final field user_id Lorg/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable; + public static final field user_username Lorg/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lorg/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable; + public static fun values ()[Lorg/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable; +} + public final class org/hisp/dhis/lib/expression/spi/DataItem { public fun (Lorg/hisp/dhis/lib/expression/spi/DataItemType;Lorg/hisp/dhis/lib/expression/spi/ID;Ljava/util/List;Ljava/util/List;Lorg/hisp/dhis/lib/expression/spi/QueryModifiers;)V public fun (Lorg/hisp/dhis/lib/expression/spi/DataItemType;Lorg/hisp/dhis/lib/expression/spi/ID;Lorg/hisp/dhis/lib/expression/spi/QueryModifiers;)V @@ -1327,6 +1343,7 @@ public final class org/hisp/dhis/lib/expression/spi/DataItem { } public final class org/hisp/dhis/lib/expression/spi/DataItemType : java/lang/Enum { + public static final field ANDROID_CUSTOM_INTENT Lorg/hisp/dhis/lib/expression/spi/DataItemType; public static final field ATTRIBUTE Lorg/hisp/dhis/lib/expression/spi/DataItemType; public static final field CONSTANT Lorg/hisp/dhis/lib/expression/spi/DataItemType; public static final field Companion Lorg/hisp/dhis/lib/expression/spi/DataItemType$Companion; @@ -1504,6 +1521,7 @@ public final class org/hisp/dhis/lib/expression/spi/ID { } public final class org/hisp/dhis/lib/expression/spi/IDType : java/lang/Enum { + public static final field AndroidCustomIntent Lorg/hisp/dhis/lib/expression/spi/IDType; public static final field AttributeOptionComboUID Lorg/hisp/dhis/lib/expression/spi/IDType; public static final field AttributeUID Lorg/hisp/dhis/lib/expression/spi/IDType; public static final field CategoryOptionComboUID Lorg/hisp/dhis/lib/expression/spi/IDType; @@ -1744,6 +1762,7 @@ public final class org/hisp/dhis/lib/expression/syntax/Expr$Companion { public final class org/hisp/dhis/lib/expression/syntax/ExpressionGrammar { public static final field INSTANCE Lorg/hisp/dhis/lib/expression/syntax/ExpressionGrammar; + public final fun getAndroidCustomIntentMode ()Ljava/util/List; public final fun getIndicatorExpressionMode ()Ljava/util/List; public final fun getPredictorExpressionMode ()Ljava/util/List; public final fun getPredictorSkipTestMode ()Ljava/util/List; diff --git a/build.gradle.kts b/build.gradle.kts index abaa1b4..b761bea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ repositories { mavenCentral() } -version = "1.2.0-SNAPSHOT" +version = "1.2.1-SNAPSHOT" group = "org.hisp.dhis.lib.expression" if (project.hasProperty("removeSnapshotSuffix")) { diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ExpressionMode.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ExpressionMode.kt index df41a9f..7b97129 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ExpressionMode.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ExpressionMode.kt @@ -38,7 +38,14 @@ enum class ExpressionMode( ValueType.BOOLEAN, ValueType.STRING, ValueType.NUMBER, - ValueType.DATE); + ValueType.DATE), + + // android custom intent request parameters + ANDROID_CUSTOM_INTENT_EXPRESSION( + ExpressionGrammar.AndroidCustomIntentMode, + ValueType.BOOLEAN, + ValueType.STRING, + ValueType.NUMBER); internal val resultTypes: Set = setOf(*resultTypes) diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Nodes.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Nodes.kt index 06a6bc3..ad81fc5 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Nodes.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Nodes.kt @@ -427,6 +427,10 @@ object Nodes { SimpleNode(type, rawValue, ProgramVariable::valueOf, rethrowAs( ProgramVariable::class.simpleName, ProgramVariable.entries, ProgramVariable::name)) + class AndroidCustomIntentNode(type: NodeType, rawValue: String) : + SimpleNode(type, rawValue, AndroidCustomIntentVariable::valueOf, rethrowAs( + AndroidCustomIntentVariable::class.simpleName, AndroidCustomIntentVariable.entries, AndroidCustomIntentVariable::name)) + class NamedValueNode(type: NodeType, rawValue: String) : SimpleNode(type, rawValue, NamedValue::valueOf, rethrowAs( NamedValue::class.simpleName, NamedValue.entries, NamedValue::name)) diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable.kt new file mode 100644 index 0000000..978381a --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/AndroidCustomIntentVariable.kt @@ -0,0 +1,12 @@ +package org.hisp.dhis.lib.expression.spi + +import kotlin.js.JsExport + +@JsExport +enum class AndroidCustomIntentVariable { + orgunit_code, + orgunit_id, + orgunit_path, + user_id, + user_username +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/DataItemType.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/DataItemType.kt index 4df89b1..d740981 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/DataItemType.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/DataItemType.kt @@ -36,7 +36,8 @@ enum class DataItemType(internal val symbol: String, private val parameterTypes: INDICATOR("N", IDType.IndicatorUID), ORG_UNIT_GROUP("OUG", IDType.OrganisationUnitGroupUID), REPORTING_RATE("R", IDType.DataSetUID, IDType.ReportingRateType), - PROGRAM_VARIABLE("V", IDType.ProgramVariableName); + PROGRAM_VARIABLE("V", IDType.ProgramVariableName), + ANDROID_CUSTOM_INTENT("VAR", IDType.AndroidCustomIntent); constructor(symbol: String, vararg parameterTypes: IDType) : this( symbol, listOf>(listOf(*parameterTypes))) diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/IDType.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/IDType.kt index 3e5f189..4339ec8 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/IDType.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/IDType.kt @@ -4,6 +4,7 @@ import kotlin.js.JsExport @JsExport enum class IDType { + AndroidCustomIntent, AttributeUID, AttributeOptionComboUID, CategoryOptionUID, @@ -24,6 +25,6 @@ enum class IDType { ReportingRateType; fun isUID(): Boolean { - return this != ProgramVariableName && this != ReportingRateType; + return this != ProgramVariableName && this != ReportingRateType && this != AndroidCustomIntent } } \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/ExpressionGrammar.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/ExpressionGrammar.kt index 77206ac..9f8261f 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/ExpressionGrammar.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/ExpressionGrammar.kt @@ -5,6 +5,7 @@ import org.hisp.dhis.lib.expression.ast.NamedFunction import org.hisp.dhis.lib.expression.ast.Node import org.hisp.dhis.lib.expression.ast.NodeType import org.hisp.dhis.lib.expression.ast.Nodes.AggregationTypeNode +import org.hisp.dhis.lib.expression.ast.Nodes.AndroidCustomIntentNode import org.hisp.dhis.lib.expression.ast.Nodes.ProgramVariableNode import org.hisp.dhis.lib.expression.ast.Nodes.ReportingRateTypeNode import org.hisp.dhis.lib.expression.spi.DataItemType @@ -150,6 +151,7 @@ object ExpressionGrammar { private val OUG_BRACE = item(DataItemType.ORG_UNIT_GROUP, UID) private val N_BRACE = item(DataItemType.INDICATOR, UID) private val V_BRACE = variable(DataItemType.PROGRAM_VARIABLE, IDENTIFIER.by(Node.Factory.new(::ProgramVariableNode))) + private val ACI_BRACE = variable(DataItemType.ANDROID_CUSTOM_INTENT, IDENTIFIER.by(Node.Factory.new(::AndroidCustomIntentNode))) private val CommonDataItems = listOf(HASH_BRACE, A_BRACE, C_BRACE, D_BRACE, I_BRACE, R_BRACE, OUG_BRACE) /* @@ -197,6 +199,15 @@ object ExpressionGrammar { RuleEngineD2Functions, listOf(HASH_BRACE, A_BRACE, C_BRACE, V_BRACE)) + val AndroidCustomIntentMode = concat( + CommonBooleanFunctions, + CommonConstants, + CommonNumberFunctions, + CommonSameFunctions, + CommonD2Functions, + RuleEngineD2Functions, + listOf(ACI_BRACE)) + /* Block expressions */ diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/AndroidCustomIntentExpressionTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/AndroidCustomIntentExpressionTest.kt new file mode 100644 index 0000000..350f061 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/AndroidCustomIntentExpressionTest.kt @@ -0,0 +1,69 @@ +package org.hisp.dhis.lib.expression + +import org.hisp.dhis.lib.expression.spi.ExpressionData +import org.hisp.dhis.lib.expression.spi.ParseException +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** + * Tests that the Android Custom Intent mode + */ +internal class AndroidCustomIntentExpressionTest { + + @Test + fun testLiteralValues() { + assertEquals("faJk7SeA5e", evaluate("'faJk7SeA5e'")) + assertEquals(5.0, evaluate("5")) + assertEquals(5.5, evaluate("5.5")) + assertEquals(true, evaluate("true")) + } + + @Test + fun testSecondLevelOrgunit() { + val data = ExpressionData().copy( + programVariableValues = mapOf( + "orgunit_path" to "/ImspTQPwCqd/O6uvpzGd5pu/YuQRtpLP10I/g8upMTyEZGZ" + ) + ) + + assertEquals("O6uvpzGd5pu", evaluate("d2:split(VAR{orgunit_path}, '/', 2)", data)) + } + + @Test + fun testConcatenateUserOrgunit() { + val data = ExpressionData().copy( + programVariableValues = mapOf( + "user_username" to "admin", + "orgunit_code" to "OUC32" + ) + ) + + assertEquals( + "admin_OUC32", + evaluate("d2:concatenate(VAR{user_username}, '_', VAR{orgunit_code})", data) + ) + } + + @Test + fun testValidateExpression() { + validate("VAR{orgunit_code}", valid = true) + validate("VAR{invalid_var}", valid = false) + } + + companion object { + private fun evaluate(expression: String, data: ExpressionData = ExpressionData()): Any? { + return Expression(expression, ExpressionMode.ANDROID_CUSTOM_INTENT_EXPRESSION).evaluate(data) + } + + private fun validate(expression: String, valid: Boolean) { + try { + Expression(expression, ExpressionMode.ANDROID_CUSTOM_INTENT_EXPRESSION).validate(mapOf()) + assertTrue(valid) + } catch (e: ParseException) { + assertFalse(valid) + } + } + } +}