diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3919c9c7..1b5006df 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,20 +6,17 @@ jobs: unit-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: temurin - - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- + cache: 'gradle' + - name: Change wrapper permission + run: chmod +x ./gradlew - name: Test - run: mvn clean install -DskipTests=false --update-snapshots -q + run: ./gradlew clean allTests artifact: name: Publish - Nexus @@ -27,15 +24,17 @@ jobs: needs: unit-test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: temurin + cache: 'gradle' + - name: Change wrapper permission + run: chmod +x ./gradlew - name: Release Maven package - uses: samuelmeuli/action-maven-publish@v1 - with: - nexus_username: ${{ secrets.SONATYPE_OSSRH_USERNAME }} - nexus_password: ${{ secrets.SONATYPE_OSSRH_PASSWORD }} - maven_profiles: "branch" \ No newline at end of file + run: ./gradlew publishAllPublicationsToSonatypeRepository + env: + OSSRH_USERNAME: ${{ secrets.SONATYPE_OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.SONATYPE_OSSRH_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 529fde50..fac18d94 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -9,40 +9,38 @@ jobs: unit-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: temurin - - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- + cache: maven + - name: Change wrapper permission + run: chmod +x ./gradlew - name: Test - run: mvn clean install -DskipTests=false --update-snapshots -q + run: ./gradlew clean allTests artifact: - name: Publish - Nexus + name: Publish runs-on: ubuntu-latest needs: unit-test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: temurin - - name: Remove snapshot - run: mvn -B versions:set -DremoveSnapshot -DgenerateBackupPoms=false - - name: Release Maven package - uses: samuelmeuli/action-maven-publish@v1 - with: - nexus_username: ${{ secrets.SONATYPE_OSSRH_USERNAME }} - nexus_password: ${{ secrets.SONATYPE_OSSRH_PASSWORD }} - gpg_private_key: ${{ secrets.PGP_PRIVATE_KEY }} - gpg_passphrase: ${{ secrets.PGP_PASSPHRASE }} - maven_profiles: "master" + cache: 'gradle' + - name: Change wrapper permission + run: chmod +x ./gradlew + - name: Publish to Maven + #run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -PremoveSnapshot + run: ./gradlew assemble -PremoveSnapshot + env: + OSSRH_USERNAME: ${{ secrets.SONATYPE_OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.SONATYPE_OSSRH_PASSWORD }} + SIGNING_PRIVATE_KEY: ${{ secrets.PGP_PRIVATE_KEY }} + SIGNING_PASSWORD: ${{ secrets.PGP_PASSPHRASE }} diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 77f31584..00000000 --- a/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -apply plugin: 'java' - -repositories { - maven { - url("https://oss.sonatype.org/content/repositories/snapshots") - } - mavenCentral() -} - -dependencies { - def pomXml = new XmlSlurper().parse(new File("dhis2-rule-engine", "pom.xml")) - def pomDependencies = pomXml.dependencies.dependency - pomDependencies.each { dependency -> - def dependencySpec = "${dependency.groupId}:${dependency.artifactId}:${dependency.version}" - if (dependency.scope == 'test') { - dependencies.add 'testImplementation', dependencySpec - } else if (dependency.artifactId == 'auto-value' || dependency.artifactId == 'javax.annotation-api' && JavaVersion.current() != JavaVersion.VERSION_1_8) { - dependencies.add 'compileOnly', dependencySpec - dependencies.add 'annotationProcessor', dependencySpec - } else { - dependencies.add 'implementation', dependencySpec - } - } -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..63d1aeef --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,68 @@ +plugins { + kotlin("multiplatform") + id("maven-publish-conventions") + id("npm-publish-conventions") +} + +repositories { + mavenCentral() + maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } +} + +group = "org.hisp.dhis.rules" +version = "3.0.0-SNAPSHOT" + +val isReleaseVersion = project.hasProperty("removeSnapshot") +if (isReleaseVersion) { + version = (version as String).replace("-SNAPSHOT", "") +} + +kotlin { + jvm { + jvmToolchain(17) + withJava() + testRuns.named("test") { + executionTask.configure { + useJUnit() + } + } + } + js { + nodejs() + useEsModules() + binaries.library() + generateTypeScriptDefinitions() + } + val hostOs = System.getProperty("os.name") + val isArm64 = System.getProperty("os.arch") == "aarch64" + val isMingwX64 = hostOs.startsWith("Windows") + val nativeTarget = when { + hostOs == "Mac OS X" && isArm64 -> macosArm64("native") + hostOs == "Mac OS X" && !isArm64 -> macosX64("native") + hostOs == "Linux" && isArm64 -> linuxArm64("native") + hostOs == "Linux" && !isArm64 -> linuxX64("native") + isMingwX64 -> mingwX64("native") + else -> throw GradleException("Host OS is not supported in Kotlin/Native.") + } + + + sourceSets { + val commonMain by getting { + dependencies { + implementation("org.hisp.dhis.lib.expression:expression-parser:1.1.0-SNAPSHOT") + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1") + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + val jvmMain by getting + val jvmTest by getting + val jsMain by getting + val jsTest by getting + val nativeMain by getting + val nativeTest by getting + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..3c95aa85 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + `kotlin-dsl` +} + +val kotlinVersion = "1.9.21" +val dokkaVersion = "1.9.10" +val nexusPluginVersion = "1.3.0" +val npmPluginVersion = "3.4.1" + +repositories { + mavenCentral() + gradlePluginPortal() +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:${dokkaVersion}") + implementation("io.github.gradle-nexus:publish-plugin:${nexusPluginVersion}") + implementation("dev.petuska:npm-publish-gradle-plugin:${npmPluginVersion}") +} diff --git a/buildSrc/src/main/kotlin/Props.kt b/buildSrc/src/main/kotlin/Props.kt new file mode 100644 index 00000000..2922efc5 --- /dev/null +++ b/buildSrc/src/main/kotlin/Props.kt @@ -0,0 +1,35 @@ +object Props { + const val DESCRIPTION = "Rule Engine" + + const val REPOSITORY_TYPE = "git" + const val REPOSITORY_SYSTEM = "GitHub" + const val REPOSITORY_URL = "https://github.com/dhis2/dhis2-rule-engine.git" + + const val ORGANIZATION_NAME = "UiO" + const val ORGANIZATION_URL = "https://dhis2.org" + + const val LICENSE_NAME = "BSD-3-Clause" + const val LICENSE_URL = "https://opensource.org/license/bsd-3-clause/" + + val DEVELOPERS = listOf( + Developer( + name = "Enrico Colasante", + email = "enrico@dhis2.org", + organization = "UiO", + organizationUrl = "https://www.uio.no/" + ), + Developer( + name = "Zubair Asghar", + email = "zubair@dhis2.org", + organization = "UiO", + organizationUrl = "https://www.uio.no/" + ), + ) +} + +data class Developer( + val name: String, + val email: String, + val organization: String, + val organizationUrl: String, +) \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/maven-publish-conventions.gradle.kts b/buildSrc/src/main/kotlin/maven-publish-conventions.gradle.kts new file mode 100644 index 00000000..c9100530 --- /dev/null +++ b/buildSrc/src/main/kotlin/maven-publish-conventions.gradle.kts @@ -0,0 +1,76 @@ +plugins { + `maven-publish` + signing + id("io.github.gradle-nexus.publish-plugin") + id("org.jetbrains.dokka") +} + +val ossrhUsername: String? = System.getenv("OSSRH_USERNAME") +val ossrhPassword: String? = System.getenv("OSSRH_PASSWORD") +val signingPrivateKey: String? = System.getenv("SIGNING_PRIVATE_KEY") +val signingPassword: String? = System.getenv("SIGNING_PASSWORD") + +val isReleaseVersion = (version as String).contains("SNAPSHOT") + +val dokkaHtml = tasks.findByName("dokkaHtml")!! + +val dokkaHtmlJar = tasks.register("dokkaHtmlJar") { + dependsOn(dokkaHtml) + from(dokkaHtml.outputs) + archiveClassifier.set("docs") +} + +publishing { + publications.withType { + artifact(dokkaHtmlJar) + + pom { + name.set("rule-engine") + description.set(Props.DESCRIPTION) + + organization { + name.set(Props.ORGANIZATION_NAME) + url.set(Props.ORGANIZATION_URL) + } + developers { + Props.DEVELOPERS.map { + developer { + name.set(it.name) + email.set(it.email) + organization.set(it.organization) + organizationUrl.set(it.organizationUrl) + } + } + } + licenses { + license { + name.set(Props.LICENSE_NAME) + url.set(Props.LICENSE_URL) + } + } + scm { + url.set("lp:dhis2") + } + issueManagement { + system.set(Props.REPOSITORY_SYSTEM) + url.set(Props.REPOSITORY_URL) + } + url.set(Props.REPOSITORY_URL) + } + } +} + +nexusPublishing { + this.repositories { + sonatype { + username.set(ossrhUsername) + password.set(ossrhPassword) + } + } +} + +signing { + isRequired = isReleaseVersion + useInMemoryPgpKeys(signingPrivateKey, signingPassword) + sign(publishing.publications) +} diff --git a/buildSrc/src/main/kotlin/npm-publish-conventions.gradle.kts b/buildSrc/src/main/kotlin/npm-publish-conventions.gradle.kts new file mode 100644 index 00000000..82070d1e --- /dev/null +++ b/buildSrc/src/main/kotlin/npm-publish-conventions.gradle.kts @@ -0,0 +1,52 @@ +import dev.petuska.npm.publish.extension.domain.NpmAccess + +plugins { + id("dev.petuska.npm.publish") +} + +val npmjsToken: String? = System.getenv("NPMJS_TOKEN") + +project.afterEvaluate { + npmPublish { + access.set(NpmAccess.PUBLIC) + packages { + named("js") { + scope.set("dhis2") + readme.set(File("./README.md")) + packageJson { + "module" by "${project.name}.mjs" + "main" by "" + "exports" by { + "import" by "./${project.name}.mjs" + } + "contributors" by Props.DEVELOPERS.map { developer -> + "${developer.name} <${developer.email}>" + } + "description" by Props.DESCRIPTION + "license" by Props.LICENSE_NAME + "repository" by { + "type" by Props.REPOSITORY_TYPE + "url" by Props.REPOSITORY_URL + } + "publishConfig" by { + "access" by "public" + } + } + } + } + registries { + npmjs { + authToken.set(npmjsToken) + } + } + } + + tasks.named("assembleJsPackage") { + doLast { + val file = file("${layout.buildDirectory.get()}/packages/js/package.json") + val mainRegex = "\n \"main\": \"\"," + val removedMain = file.readText().replace(mainRegex, "") + file.writeText(removedMain) + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..033e24c4 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..ac72c34e --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..fcb6fca1 --- /dev/null +++ b/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..6689b85b --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock new file mode 100644 index 00000000..c51f96b3 --- /dev/null +++ b/kotlin-js-store/yarn.lock @@ -0,0 +1,559 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +debug@4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +format-util@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" + integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +mocha@10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +source-map-support@0.5.21: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +typescript@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/pom.xml b/pom.xml deleted file mode 100644 index d26b1928..00000000 --- a/pom.xml +++ /dev/null @@ -1,299 +0,0 @@ - - - 4.0.0 - - org.hisp.dhis.rules - rule-engine - 2.1.9 - jar - rule-engine - - Rule Engine - - - UiO - http://www.dhis2.org - - - - - Enrico Colasante - enrico@dhis2.org - UiO - http://www.uio.no/ - - - Zubair Asghar - zubair@dhis2.org - UiO - http://www.uio.no/ - - - - - - BSD - http://opensource.org/licenses/BSD-2-Clause - - - - - lp:dhis2 - - - - GitHub - https://github.com/dhis2/dhis2-rule-engine - - - https://github.com/dhis2/dhis2-rule-engine - - - - MavenCentral - Maven repository - https://repo1.maven.org/maven2 - - false - - - - ossrh - Sonatype OSS - https://oss.sonatype.org/content/repositories/snapshots - - false - - - true - always - - - - - - - ossrh - Sonatype OSS - https://oss.sonatype.org/content/repositories/snapshots - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.11.0 - - 17 - 17 - 17 - 17 - - - - org.apache.maven.plugins - maven-source-plugin - 3.3.0 - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.5.0 - - 8 - false - - - - attach-javadocs - - jar - - - - - - - - - - - install - - - true - - - - - branch - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.3.0 - - - enforce-no-releases - - enforce - - - - - No Releases Allowed! - - - true - - - - - - - - - master - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.3.0 - - - enforce-no-snapshots - - enforce - - - - - No Snapshots Allowed! - - - true - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - true - - ossrh - https://oss.sonatype.org/ - true - 20 - - - - org.apache.maven.plugins - maven-gpg-plugin - 3.1.0 - - - sign-artifacts - verify - - sign - - - - - --pinentry-mode - loopback - - - - - - - - - - - - - junit - junit - 4.13.1 - test - - - com.google.auto.value - auto-value - 1.3 - provided - - - org.assertj - assertj-core - 2.9.1 - test - - - org.mockito - mockito-core - 2.2.9 - test - - - nl.jqno.equalsverifier - equalsverifier - 2.1.6 - compile - - - javax.annotation - javax.annotation-api - 1.3.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - joda-time - joda-time - 2.10.5 - - - org.slf4j - slf4j-simple - 1.7.30 - - - org.apache.commons - commons-math3 - 3.6.1 - - - com.google.code.findbugs - jsr305 - 3.0.1 - compile - - - org.hisp.dhis.lib.expression - expression-parser - 1.0.2 - - - - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..c4debce4 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,8 @@ +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} + +rootProject.name = "rule-engine" diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/Logger.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/Logger.kt new file mode 100644 index 00000000..852ad103 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/Logger.kt @@ -0,0 +1,5 @@ +package org.hisp.dhis.rules + +data class Logger(val severe: (String) -> Unit, val fine: (String) -> Unit) + +expect fun createLogger(className: String): Logger \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/api/DataItem.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/api/DataItem.kt new file mode 100644 index 00000000..3c54ded0 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/api/DataItem.kt @@ -0,0 +1,7 @@ +package org.hisp.dhis.rules.api + +/** + * Class is place holder for program rule variable, Constant and program environment variable display name and type. + * @author Zubair Asghar + */ +data class DataItem(val displayName: String, val valueType: ItemValueType) \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/api/EnvironmentVariables.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/api/EnvironmentVariables.kt new file mode 100644 index 00000000..165dac95 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/api/EnvironmentVariables.kt @@ -0,0 +1,27 @@ +package org.hisp.dhis.rules.api + +import org.hisp.dhis.rules.utils.RuleEngineUtils + +object EnvironmentVariables { + val ENV_VARIABLES = mapOf( + Pair(RuleEngineUtils.ENV_VAR_COMPLETED_DATE, ItemValueType.DATE), + Pair(RuleEngineUtils.ENV_VAR_CURRENT_DATE, ItemValueType.DATE), + Pair(RuleEngineUtils.ENV_VAR_EVENT_DATE, ItemValueType.DATE), + Pair(RuleEngineUtils.ENV_VAR_INCIDENT_DATE, ItemValueType.DATE), + Pair(RuleEngineUtils.ENV_VAR_ENROLLMENT_DATE, ItemValueType.DATE), + Pair(RuleEngineUtils.ENV_VAR_DUE_DATE, ItemValueType.DATE), + Pair(RuleEngineUtils.ENV_VAR_EVENT_COUNT, ItemValueType.NUMBER), + Pair(RuleEngineUtils.ENV_VAR_TEI_COUNT, ItemValueType.NUMBER), + Pair(RuleEngineUtils.ENV_VAR_ENROLLMENT_COUNT, ItemValueType.NUMBER), + Pair(RuleEngineUtils.ENV_VAR_EVENT_ID, ItemValueType.NUMBER), + Pair(RuleEngineUtils.ENV_VAR_PROGRAM_STAGE_ID, ItemValueType.NUMBER), + Pair(RuleEngineUtils.ENV_VAR_ENROLLMENT_ID, ItemValueType.NUMBER), + Pair(RuleEngineUtils.ENV_VAR_ENROLLMENT_STATUS, ItemValueType.TEXT), + Pair(RuleEngineUtils.ENV_VAR_EVENT_STATUS, ItemValueType.TEXT), + Pair(RuleEngineUtils.ENV_VAR_OU, ItemValueType.TEXT), + Pair(RuleEngineUtils.ENV_VAR_OU_CODE, ItemValueType.TEXT), + Pair(RuleEngineUtils.ENV_VAR_ENVIRONMENT, ItemValueType.TEXT), + Pair(RuleEngineUtils.ENV_VAR_PROGRAM_NAME, ItemValueType.TEXT), + Pair(RuleEngineUtils.ENV_VAR_PROGRAM_STAGE_NAME, ItemValueType.TEXT) + ) +} \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/api/ItemValueType.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/api/ItemValueType.kt new file mode 100644 index 00000000..f9fdf634 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/api/ItemValueType.kt @@ -0,0 +1,50 @@ +package org.hisp.dhis.rules.api + +import org.hisp.dhis.lib.expression.spi.ValueType + +/* +* Copyright (c) 2004-2020, University of Oslo +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* Neither the name of the HISP project nor the names of its contributors may +* be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * @author Zubair Asghar + */ +enum class ItemValueType(val value: String) { + NUMBER("1.0"), + DATE("2020-01-01"), + TEXT("Sample_text_string"), + BOOLEAN("true"); + + fun toValueType(): ValueType { + return when (this) { + NUMBER -> ValueType.NUMBER + DATE -> ValueType.DATE + TEXT -> ValueType.STRING + BOOLEAN -> ValueType.BOOLEAN + } + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/api/RuleEngine.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/api/RuleEngine.kt new file mode 100644 index 00000000..d2e5852a --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/api/RuleEngine.kt @@ -0,0 +1,20 @@ +package org.hisp.dhis.rules.api + +import org.hisp.dhis.rules.engine.DefaultRuleEngine +import org.hisp.dhis.rules.models.* +import kotlin.jvm.JvmStatic + +interface RuleEngine { + fun validate(expression: String, dataItemStore: Map): RuleValidationResult + fun validateDataFieldExpression(expression: String, dataItemStore: Map): RuleValidationResult + fun evaluateAll(enrollmentTarget: RuleEnrollment?, eventsTarget: List, executionContext: RuleEngineContext): List + fun evaluate(target: RuleEnrollment, ruleEvents: List, executionContext: RuleEngineContext): List + fun evaluate(target: RuleEvent, ruleEnrollment: RuleEnrollment?, ruleEvents: List, executionContext: RuleEngineContext): List + + companion object { + @JvmStatic + fun getInstance(): RuleEngine { + return DefaultRuleEngine() + } + } +} \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/api/RuleEngineContext.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/api/RuleEngineContext.kt new file mode 100644 index 00000000..e28895cd --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/api/RuleEngineContext.kt @@ -0,0 +1,13 @@ +package org.hisp.dhis.rules.api + +import org.hisp.dhis.rules.models.Rule +import org.hisp.dhis.rules.models.RuleEnrollment +import org.hisp.dhis.rules.models.RuleEvent +import org.hisp.dhis.rules.models.RuleVariable + +data class RuleEngineContext( + val rules: List, + val ruleVariables: List = emptyList(), + val supplementaryData: Map> = emptyMap(), + val constantsValues: Map = emptyMap() +) diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/engine/DefaultRuleEngine.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/DefaultRuleEngine.kt new file mode 100644 index 00000000..bf2d4f7b --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/DefaultRuleEngine.kt @@ -0,0 +1,83 @@ +package org.hisp.dhis.rules.engine + +import org.hisp.dhis.lib.expression.Expression +import org.hisp.dhis.lib.expression.spi.IllegalExpressionException +import org.hisp.dhis.lib.expression.spi.ParseException +import org.hisp.dhis.lib.expression.spi.ValueType +import org.hisp.dhis.rules.api.DataItem +import org.hisp.dhis.rules.api.RuleEngine +import org.hisp.dhis.rules.api.RuleEngineContext +import org.hisp.dhis.rules.models.* + +internal class DefaultRuleEngine: RuleEngine { + override fun evaluate(target: RuleEvent, ruleEnrollment: RuleEnrollment?, ruleEvents: List, executionContext: RuleEngineContext): List { + val valueMap = RuleVariableValueMapBuilder.target(target) + .ruleVariables(executionContext.ruleVariables) + .ruleEnrollment(ruleEnrollment) + .triggerEnvironment(TriggerEnvironment.SERVER) + .ruleEvents(ruleEvents) + .constantValueMap(executionContext.constantsValues) + .build() + return RuleConditionEvaluator().getRuleEffects( + TrackerObjectType.EVENT, target.event, valueMap, + executionContext.supplementaryData, executionContext.rules + ) + } + + override fun evaluate(target: RuleEnrollment, ruleEvents: List, executionContext: RuleEngineContext): List { + val valueMap = RuleVariableValueMapBuilder.target(target) + .ruleVariables(executionContext.ruleVariables) + .triggerEnvironment(TriggerEnvironment.SERVER) + .ruleEvents(ruleEvents) + .constantValueMap(executionContext.constantsValues) + .build() + return RuleConditionEvaluator().getRuleEffects( + TrackerObjectType.ENROLLMENT, target.enrollment, valueMap, + executionContext.supplementaryData, executionContext.rules + ) + } + + override fun evaluateAll(enrollmentTarget: RuleEnrollment?, eventsTarget: List, executionContext: RuleEngineContext): List { + val valueMap = RuleVariableValueMapBuilder.target() + .ruleVariables(executionContext.ruleVariables) + .ruleEnrollment(enrollmentTarget) + .triggerEnvironment(TriggerEnvironment.SERVER) + .ruleEvents(eventsTarget) + .constantValueMap(executionContext.constantsValues) + .multipleBuild() + return RuleEngineMultipleExecution().execute(executionContext.rules, valueMap, + executionContext.supplementaryData) + } + + override fun validate(expression: String, dataItemStore: Map): RuleValidationResult { + // Rule condition expression should be evaluated against Boolean + return getExpressionDescription(expression, Expression.Mode.RULE_ENGINE_CONDITION, dataItemStore) + } + + override fun validateDataFieldExpression(expression: String, dataItemStore: Map): 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) + } + + private fun getExpressionDescription(expression: String, mode: Expression.Mode, dataItemStore: Map): RuleValidationResult { + return try { + val validationMap: Map = dataItemStore.mapValues { e -> e.value.valueType.toValueType() } + Expression(expression, mode, false).validate(validationMap) + val displayNames: Map = dataItemStore.mapValues { e -> e.value.displayName } + val description = Expression(expression, mode, false).describe(displayNames) + RuleValidationResult(valid = true, description = description) + } catch (ex: IllegalExpressionException) { + RuleValidationResult( + valid = false, + exception = RuleEngineValidationException(ex), + errorMessage = ex.message + ) + } catch (ex: ParseException) { + RuleValidationResult( + valid = false, + exception = RuleEngineValidationException(ex), + errorMessage = ex.message + ) + } + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleConditionEvaluator.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleConditionEvaluator.kt new file mode 100644 index 00000000..4b6eaf4f --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleConditionEvaluator.kt @@ -0,0 +1,203 @@ +package org.hisp.dhis.rules.engine + +import org.hisp.dhis.lib.expression.Expression +import org.hisp.dhis.lib.expression.spi.ExpressionData +import org.hisp.dhis.lib.expression.spi.IllegalExpressionException +import org.hisp.dhis.rules.createLogger +import org.hisp.dhis.rules.models.Rule +import org.hisp.dhis.rules.models.* +import org.hisp.dhis.rules.engine.RuleEvaluationResult.Companion.errorRule +import org.hisp.dhis.rules.engine.RuleEvaluationResult.Companion.evaluatedResult +import org.hisp.dhis.rules.engine.RuleEvaluationResult.Companion.notEvaluatedResult +import org.hisp.dhis.rules.utils.unwrapVariableName + +internal class RuleConditionEvaluator { + fun getEvaluatedAndErrorRuleEffects( + targetType: TrackerObjectType, + targetUid: String, + valueMap: MutableMap, + supplementaryData: Map>, + rules: List + ): List { + val ruleEvaluationResults = getRuleEvaluationResults(targetType, targetUid, valueMap, supplementaryData, rules) + return ruleEvaluationResults + .flatMap { result -> result.ruleEffects } + } + + fun getRuleEffects( + targetType: TrackerObjectType, + targetUid: String, + valueMap: MutableMap, + supplementaryData: Map>, + rules: List + ): List { + val ruleEvaluationResults = getRuleEvaluationResults( + targetType, + targetUid, + valueMap, + supplementaryData, + rules + ) + return ruleEvaluationResults + .filter { result -> !result.error } + .flatMap { result -> result.ruleEffects } + } + + fun getRuleEvaluationResults( + targetType: TrackerObjectType, targetUid: String, + valueMap: MutableMap, + supplementaryData: Map>, + rules: List + ): List { + val ruleEvaluationResults: MutableList = ArrayList() + for (rule in rules.sorted()) { + log.fine("Evaluating programrule: " + rule.name) + try { + val ruleEffects: MutableList = ArrayList() + if (process( + rule.condition, + valueMap, + supplementaryData, + Expression.Mode.RULE_ENGINE_CONDITION + ).toBoolean() + ) { + for (action in rule.actions) { + try { + //Check if action is assigning value to calculated variable + if (isAssignToCalculatedValue(action)) { + updateValueMap( + unwrapVariableName(action.content()!!), + RuleVariableValue( + RuleValueType.TEXT, + process(action.data, valueMap, supplementaryData, Expression.Mode.RULE_ENGINE_ACTION), + listOf(), + null + ), + valueMap + ) + } else { + ruleEffects.add(create(rule, action, valueMap, supplementaryData)) + } + } catch (e: Exception) { + addRuleErrorResult(rule, action, e, targetType, targetUid, ruleEvaluationResults) + } + } + ruleEvaluationResults.add(evaluatedResult(rule, ruleEffects)) + } else { + ruleEvaluationResults.add(notEvaluatedResult(rule)) + } + } catch (e: Exception) { + addRuleErrorResult(rule, null, e, targetType, targetUid, ruleEvaluationResults) + } + } + for ((rule, _, evaluatedAs) in ruleEvaluationResults) { + log.fine( + "Rule " + rule.name + " with id " + rule.uid + + " executed for " + targetType.name + "(" + targetUid + ")" + + " with condition (" + rule.condition + ")" + + " was evaluated " + evaluatedAs + ) + } + return ruleEvaluationResults + } + + private fun addRuleErrorResult( + rule: Rule, ruleAction: RuleAction?, e: Exception, targetType: TrackerObjectType, + targetUid: String, ruleEvaluationResults: MutableList + ) { + val errorMessage: String + errorMessage = if (ruleAction != null && e is IllegalExpressionException) { + "Action " + ruleAction::class.simpleName + + " from rule " + rule.name + " with id " + rule.uid + + " executed for " + targetType.name + "(" + targetUid + ")" + + " with condition (" + rule.condition + ")" + + " raised an error: " + e.message + } else if (ruleAction != null) { + "Action " + ruleAction::class.simpleName + + " from rule " + rule.name + " with id " + rule.uid + + " executed for " + targetType.name + "(" + targetUid + ")" + + " with condition (" + rule.condition + ")" + + " raised an unexpected exception: " + e.message + } else if (e is IllegalExpressionException) { + "Rule " + rule.name + " with id " + rule.uid + + " executed for " + targetType.name + "(" + targetUid + ")" + + " with condition (" + rule.condition + ")" + + " raised an error: " + e.message + } else { + "Rule " + rule.name + " with id " + rule.uid + + " executed for " + targetType.name + "(" + targetUid + ")" + + " with condition (" + rule.condition + ")" + + " raised an unexpected exception: " + e.message + } + log.severe(errorMessage) + ruleEvaluationResults.add(errorRule(rule, errorMessage)) + } + + private fun process( + condition: String?, valueMap: Map, + supplementaryData: Map>, mode: Expression.Mode + ): String { + if (condition==null || condition.isEmpty()) { + return "" + } + val expression = Expression(condition, mode, false) + + val build = ExpressionData( + valueMap.mapValues { v -> v.value.toVariableValue() }, + emptyMap(), + supplementaryData, + emptyMap(), + emptyMap() + ) + return convertInteger(expression.evaluate({ name: String -> + throw UnsupportedOperationException( + "function not supported: $name" + ) + }, build)).toString() + } + + private fun convertInteger(result: Any?): Any? { + return if (result is Double && result % 1 == 0.0) { + result.toInt() + } else result + } + + private fun isAssignToCalculatedValue(ruleAction: RuleAction): Boolean { + return ruleAction.type == "ASSIGN" && ruleAction.field() == null + } + + private fun updateValueMap( + variable: String, + value: RuleVariableValue, + valueMap: MutableMap + ) { + valueMap[variable] = value + } + + private fun create( + rule: Rule, + ruleAction: RuleAction, + valueMap: MutableMap, + supplementaryData: Map> + ): RuleEffect { + if (ruleAction.type == "ASSIGN") { + val data = process(ruleAction.data, valueMap, supplementaryData, Expression.Mode.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 "" + return RuleEffect( + rule.uid, + ruleAction, + data + ) + } + + companion object { + private val log = createLogger(RuleConditionEvaluator::class.simpleName!!) + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleEngineMultipleExecution.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleEngineMultipleExecution.kt new file mode 100644 index 00000000..efcfbf03 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleEngineMultipleExecution.kt @@ -0,0 +1,76 @@ +package org.hisp.dhis.rules.engine + +import org.hisp.dhis.rules.models.Rule +import org.hisp.dhis.rules.models.* + +internal class RuleEngineMultipleExecution { + fun execute(rules: List, ruleVariableValueMap: RuleVariableValueMap, + supplementaryData: Map>): List { + val ruleEffects: MutableList = ArrayList() + for ((enrollment, value) in ruleVariableValueMap.enrollmentMap) { + val enrollmentRuleEffects = RuleConditionEvaluator() + .getEvaluatedAndErrorRuleEffects( + TrackerObjectType.ENROLLMENT, enrollment.enrollment, value, + supplementaryData, filterRules(rules) + ) + ruleEffects.add( + RuleEffects( + TrackerObjectType.ENROLLMENT, enrollment.enrollment, + enrollmentRuleEffects + ) + ) + } + for ((event, value) in ruleVariableValueMap.eventMap) { + ruleEffects.add( + RuleEffects( + TrackerObjectType.EVENT, event.event, + RuleConditionEvaluator().getEvaluatedAndErrorRuleEffects( + TrackerObjectType.EVENT, event.event, value, + supplementaryData, filterRules(rules, event) + ) + ) + ) + } + return ruleEffects + } + + private fun filterRules(rules: List): List { + val filteredRules: MutableList = mutableListOf() + for (rule in rules) { + val programStage: String? = rule.programStage + if (programStage.isNullOrEmpty()) { + val ruleActions = filterActionRules( + rule.actions, + AttributeType.TRACKED_ENTITY_ATTRIBUTE + ) + filteredRules.add(rule.copy(actions = ruleActions)) + } + } + return filteredRules + } + + private fun filterRules(rules: List, ruleEvent: RuleEvent): List { + val filteredRules: MutableList = mutableListOf() + for (rule in rules) { + val programStage: String? = rule.programStage + if (programStage.isNullOrEmpty() || programStage == ruleEvent.programStage) { + val ruleActions = filterActionRules( + rule.actions, + AttributeType.DATA_ELEMENT + ) + filteredRules.add(rule.copy(actions = ruleActions)) + } + } + return filteredRules + } + + private fun filterActionRules(ruleActions: List, attributeType: AttributeType): List { + val filteredRuleActions: MutableList = mutableListOf() + for (ruleAction in ruleActions) { + if (ruleAction.attributeType() == null || ruleAction.attributeType() == attributeType.name || ruleAction.attributeType() == AttributeType.UNKNOWN.name) { + filteredRuleActions.add(ruleAction) + } + } + return filteredRuleActions + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleEvaluationResult.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleEvaluationResult.kt new file mode 100644 index 00000000..b20385a0 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleEvaluationResult.kt @@ -0,0 +1,33 @@ +package org.hisp.dhis.rules.engine + +import org.hisp.dhis.rules.models.Rule +import org.hisp.dhis.rules.models.RuleAction +import org.hisp.dhis.rules.models.RuleEffect + +internal data class RuleEvaluationResult( + val rule: Rule, + val ruleEffects: List, + val evaluatedAs: Boolean, + val error: Boolean +) { + companion object { + fun evaluatedResult(rule: Rule, ruleEffects: List): RuleEvaluationResult { + return RuleEvaluationResult(rule, ruleEffects, true, false) + } + + fun notEvaluatedResult(rule: Rule): RuleEvaluationResult { + return RuleEvaluationResult(rule, emptyList(), false, false) + } + + fun errorRule(rule: Rule, errorMessage: String): RuleEvaluationResult { + val effects = listOf( + RuleEffect( + rule.uid, + RuleAction(errorMessage, "ERROR"), + errorMessage + ) + ) + return RuleEvaluationResult(rule, effects, false, true) + } + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValue.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValue.kt new file mode 100644 index 00000000..ee6c7c2e --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValue.kt @@ -0,0 +1,25 @@ +package org.hisp.dhis.rules.engine + +import org.hisp.dhis.lib.expression.spi.ValueType +import org.hisp.dhis.lib.expression.spi.VariableValue +import org.hisp.dhis.rules.models.RuleValueType + +data class RuleVariableValue( + val type: RuleValueType, + val value: String? = null, + val candidates: List = listOf(), + val eventDate: String? = null +) { + fun toVariableValue(): VariableValue { + return VariableValue(valueType(), value, candidates, eventDate) + } + + private fun valueType(): ValueType { + return when (type) { + RuleValueType.DATE -> ValueType.DATE + RuleValueType.NUMERIC -> ValueType.NUMBER + RuleValueType.BOOLEAN -> ValueType.BOOLEAN + else -> ValueType.STRING + } + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValueMap.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValueMap.kt new file mode 100644 index 00000000..e83fe523 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValueMap.kt @@ -0,0 +1,8 @@ +package org.hisp.dhis.rules.engine + +import org.hisp.dhis.rules.models.RuleEvent + +internal data class RuleVariableValueMap( + val enrollmentMap: Map>, + val eventMap: Map> +) \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValueMapBuilder.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValueMapBuilder.kt new file mode 100644 index 00000000..636d75da --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/engine/RuleVariableValueMapBuilder.kt @@ -0,0 +1,310 @@ +package org.hisp.dhis.rules.engine + +import kotlinx.datetime.LocalDate +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime +import org.hisp.dhis.rules.models.* +import org.hisp.dhis.rules.utils.RuleEngineUtils +import org.hisp.dhis.rules.utils.currentDate +import kotlin.collections.ArrayList +import kotlin.collections.HashMap + +internal class RuleVariableValueMapBuilder private constructor() { + val allConstantValues: MutableMap = HashMap() + val ruleVariables: MutableList = ArrayList() + val ruleEvents: MutableList = ArrayList() + var ruleEnrollment: RuleEnrollment? = null + var ruleEvent: RuleEvent? = null + private var triggerEnvironment: TriggerEnvironment? = null + + private constructor(ruleEnrollment: RuleEnrollment) : this() { + + // enrollment is the target + this.ruleEnrollment = ruleEnrollment + } + + private constructor(ruleEvent: RuleEvent) : this() { + + // event is the target + this.ruleEvent = ruleEvent + } + + fun ruleVariables(ruleVariables: List): RuleVariableValueMapBuilder { + this.ruleVariables.addAll(ruleVariables) + return this + } + + fun ruleEnrollment(ruleEnrollment: RuleEnrollment?): RuleVariableValueMapBuilder { + check(this.ruleEnrollment == null) { + "It seems that enrollment has been set as target " + + "already. It can't be used as a part of execution context." + } + this.ruleEnrollment = ruleEnrollment + return this + } + + fun triggerEnvironment(triggerEnvironment: TriggerEnvironment?): RuleVariableValueMapBuilder { + check(this.triggerEnvironment == null) { "triggerEnvironment == null" } + this.triggerEnvironment = triggerEnvironment + return this + } + + fun ruleEvents(ruleEvents: List): RuleVariableValueMapBuilder { + check(!isEventInList(ruleEvents, ruleEvent)) { + "ruleEvent %s is already set " + + "as a target, but also present in the context: events list ${ruleEvent!!.event}" + } + this.ruleEvents.addAll(ruleEvents) + return this + } + + fun constantValueMap(constantValues: Map): RuleVariableValueMapBuilder { + allConstantValues.putAll(constantValues) + return this + } + + fun build(): MutableMap { + val valueMap: MutableMap = HashMap() + + // set environment variables + valueMap.putAll(buildEnvironmentVariables()) + + // set metadata variables + valueMap.putAll(buildRuleVariableValues()) + + // set constants value map + valueMap.putAll(buildConstantsValues()) + + return valueMap + } + + fun multipleBuild(): RuleVariableValueMap { + val enrollmentMap: MutableMap> = HashMap() + if (ruleEnrollment != null) { + enrollmentMap[ruleEnrollment!!] = build() + } + val eventMap: MutableMap> = HashMap() + for (event in ruleEvents) { + ruleEvent = event + eventMap[event] = build() + } + return RuleVariableValueMap(enrollmentMap, eventMap) + } + + private fun isEventInList( + ruleEvents: List, + ruleEvent: RuleEvent? + ): Boolean { + if (ruleEvent != null) { + for ((event1) in ruleEvents) { + if (event1 == ruleEvent.event) { + return true + } + } + } + return false + } + + private fun buildCurrentEventValues(): Map { + val currentEventValues: MutableMap = HashMap() + if (ruleEvent != null) { + for (index in ruleEvent!!.dataValues.indices) { + val ruleDataValue = ruleEvent!!.dataValues[index] + currentEventValues[ruleDataValue.dataElement] = ruleDataValue + } + } + return currentEventValues + } + + private fun buildCurrentEnrollmentValues(): Map { + val currentEnrollmentValues: MutableMap = HashMap() + if (ruleEnrollment != null) { + val ruleAttributeValues = ruleEnrollment!!.attributeValues + for (attributeValue in ruleAttributeValues) { + currentEnrollmentValues[attributeValue.trackedEntityAttribute] = attributeValue + } + } + return currentEnrollmentValues + } + + private fun buildAllEventValues(): Map> { + val allEventsValues: MutableMap> = HashMap() + val events: MutableList = ArrayList(ruleEvents) + if (ruleEvent != null) { + // target event should be among the list of all + // events in order to achieve correct behavior + events.add(ruleEvent!!) + } + + // sort list of events by eventDate: + events.sortByDescending { e -> e.eventDate } + + // aggregating values by data element uid + for (i in events.indices) { + val (_, _, _, _, _, _, _, _, _, dataValues) = events[i] + for (j in dataValues.indices) { + val ruleDataValue = dataValues[j] + + // push new list if it is not there for the given data element + if (!allEventsValues.containsKey(ruleDataValue.dataElement)) { + allEventsValues[ruleDataValue.dataElement] = ArrayList(events.size) + } + + // append data value to the list + allEventsValues[ruleDataValue.dataElement]?.add(ruleDataValue) + } + } + return allEventsValues + } + + private fun buildConstantsValues(): Map { + val valueMap: MutableMap = HashMap() + for ((key, value) in allConstantValues) { + valueMap[key] = RuleVariableValue(RuleValueType.NUMERIC, value) + } + return valueMap + } + + private fun buildEnvironmentVariables(): Map { + val valueMap: MutableMap = HashMap() + val currentDate = LocalDate.Companion.currentDate() + valueMap[RuleEngineUtils.ENV_VAR_CURRENT_DATE] = RuleVariableValue( + RuleValueType.TEXT, + currentDate.toString(), + listOf(currentDate.toString()), + currentDate.toString() + ) + if (triggerEnvironment != null) { + val environment = triggerEnvironment!!.clientName + valueMap[RuleEngineUtils.ENV_VAR_ENVIRONMENT] = RuleVariableValue( + RuleValueType.TEXT, + environment, + listOf(environment), + currentDate.toString() + ) + } + if (ruleEvents.isNotEmpty()) { + valueMap[RuleEngineUtils.ENV_VAR_EVENT_COUNT] = RuleVariableValue( + RuleValueType.NUMERIC, ruleEvents.size.toString(), + listOf(ruleEvents.size.toString()), currentDate.toString() + ) + } + if (ruleEnrollment != null) { + valueMap[RuleEngineUtils.ENV_VAR_ENROLLMENT_ID] = RuleVariableValue( + RuleValueType.TEXT, ruleEnrollment!!.enrollment, + listOf(ruleEnrollment!!.enrollment), currentDate.toString() + ) + valueMap[RuleEngineUtils.ENV_VAR_ENROLLMENT_COUNT] = RuleVariableValue( + RuleValueType.NUMERIC, "1", + listOf("1"), currentDate.toString() + ) + valueMap[RuleEngineUtils.ENV_VAR_TEI_COUNT] = RuleVariableValue( + RuleValueType.NUMERIC, "1", + listOf("1"), currentDate.toString() + ) + val enrollmentDate = ruleEnrollment!!.enrollmentDate + valueMap[RuleEngineUtils.ENV_VAR_ENROLLMENT_DATE] = RuleVariableValue( + RuleValueType.TEXT, enrollmentDate.toString(), + listOf(enrollmentDate.toString()), currentDate.toString() + ) + val incidentDate = ruleEnrollment!!.incidentDate + valueMap[RuleEngineUtils.ENV_VAR_INCIDENT_DATE] = RuleVariableValue( + RuleValueType.TEXT, incidentDate.toString(), + listOf(incidentDate.toString()), currentDate.toString() + ) + val status = ruleEnrollment!!.status.toString() + valueMap[RuleEngineUtils.ENV_VAR_ENROLLMENT_STATUS] = RuleVariableValue( + RuleValueType.TEXT, status, + listOf(status), currentDate.toString() + ) + val organisationUnit = ruleEnrollment!!.organisationUnit + valueMap[RuleEngineUtils.ENV_VAR_OU] = RuleVariableValue(RuleValueType.TEXT, organisationUnit) + val programName = ruleEnrollment!!.programName + valueMap[RuleEngineUtils.ENV_VAR_PROGRAM_NAME] = RuleVariableValue(RuleValueType.TEXT, programName) + val organisationUnitCode = ruleEnrollment!!.organisationUnitCode + valueMap[RuleEngineUtils.ENV_VAR_OU_CODE] = + RuleVariableValue(RuleValueType.TEXT, organisationUnitCode) + } + if (ruleEvent != null) { + val eventDate = ruleEvent!!.eventDate.toLocalDateTime(TimeZone.currentSystemDefault()).date.toString() + valueMap[RuleEngineUtils.ENV_VAR_EVENT_DATE] = RuleVariableValue( + RuleValueType.TEXT, eventDate, listOf(eventDate), currentDate.toString() + ) + if (ruleEvent!!.dueDate != null) { + val dueDate = ruleEvent!!.dueDate + valueMap[RuleEngineUtils.ENV_VAR_DUE_DATE] = RuleVariableValue( + RuleValueType.TEXT, dueDate.toString(), + listOf(dueDate.toString()), currentDate.toString() + ) + } + if (ruleEvent!!.completedDate != null) { + val completedDate = ruleEvent!!.completedDate + valueMap[RuleEngineUtils.ENV_VAR_COMPLETED_DATE] = RuleVariableValue( + RuleValueType.TEXT, completedDate.toString(), + listOf(completedDate.toString()), currentDate.toString() + ) + } + + // override value of event count + var eventCount = (ruleEvents.size + 1).toString() + if (ruleEvents.contains(ruleEvent)) { + eventCount = ruleEvents.size.toString() + } + valueMap[RuleEngineUtils.ENV_VAR_EVENT_COUNT] = RuleVariableValue( + RuleValueType.NUMERIC, eventCount, + listOf(eventCount), currentDate.toString() + ) + valueMap[RuleEngineUtils.ENV_VAR_EVENT_ID] = RuleVariableValue( + RuleValueType.TEXT, ruleEvent!!.event, + listOf(ruleEvent!!.event), currentDate.toString() + ) + val status = ruleEvent!!.status.toString() + valueMap[RuleEngineUtils.ENV_VAR_EVENT_STATUS] = RuleVariableValue( + RuleValueType.TEXT, status, listOf(status), currentDate.toString() + ) + val organisationUnit = ruleEvent!!.organisationUnit + valueMap[RuleEngineUtils.ENV_VAR_OU] = RuleVariableValue(RuleValueType.TEXT, organisationUnit) + val programStageId = ruleEvent!!.programStage + valueMap[RuleEngineUtils.ENV_VAR_PROGRAM_STAGE_ID] = RuleVariableValue(RuleValueType.TEXT, programStageId ) + val programStageName = ruleEvent!!.programStageName + valueMap[RuleEngineUtils.ENV_VAR_PROGRAM_STAGE_NAME] = RuleVariableValue(RuleValueType.TEXT, programStageName) + val organisationUnitCode = ruleEvent!!.organisationUnitCode + valueMap[RuleEngineUtils.ENV_VAR_OU_CODE] = + RuleVariableValue(RuleValueType.TEXT, organisationUnitCode) + } + return valueMap + } + + private fun buildRuleVariableValues(): Map { + val valueMap: MutableMap = HashMap() + + // map data values within all events to data elements + val allEventValues = buildAllEventValues() + + // map tracked entity attributes to values from enrollment + val currentEnrollmentValues = buildCurrentEnrollmentValues() + + // build a map of current event values + val currentEventValues = buildCurrentEventValues() + for (ruleVariable in ruleVariables) { + valueMap.putAll( + ruleVariable.createValues(ruleEvent, allEventValues, currentEnrollmentValues, currentEventValues) + ) + } + return valueMap + } + + companion object { + fun target(ruleEnrollment: RuleEnrollment): RuleVariableValueMapBuilder { + return RuleVariableValueMapBuilder(ruleEnrollment) + } + + fun target(ruleEvent: RuleEvent): RuleVariableValueMapBuilder { + return RuleVariableValueMapBuilder(ruleEvent) + } + + fun target(): RuleVariableValueMapBuilder { + return RuleVariableValueMapBuilder() + } + } +} diff --git a/src/main/java/org/hisp/dhis/rules/models/AttributeType.java b/src/commonMain/kotlin/org/hisp/dhis/rules/models/AttributeType.kt similarity index 63% rename from src/main/java/org/hisp/dhis/rules/models/AttributeType.java rename to src/commonMain/kotlin/org/hisp/dhis/rules/models/AttributeType.kt index 6ff7716d..e5c47be2 100644 --- a/src/main/java/org/hisp/dhis/rules/models/AttributeType.java +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/AttributeType.kt @@ -1,10 +1,10 @@ -package org.hisp.dhis.rules.models; +package org.hisp.dhis.rules.models /** * This Enum specify the type of the id saved in the field of a RuleAction. */ -public enum AttributeType { +enum class AttributeType { DATA_ELEMENT, TRACKED_ENTITY_ATTRIBUTE, - UNKNOWN; + UNKNOWN } diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/Option.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/Option.kt new file mode 100644 index 00000000..51eae686 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/Option.kt @@ -0,0 +1,6 @@ +package org.hisp.dhis.rules.models + +/** + * @author rajazubair + */ +data class Option(val name: String, val code: String) diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/Rule.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/Rule.kt new file mode 100644 index 00000000..e55042c2 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/Rule.kt @@ -0,0 +1,22 @@ +package org.hisp.dhis.rules.models + +data class Rule( + val condition: String, + val actions: List, + val uid: String = "", + val name: String? = null, + val programStage: String? = null, + val priority: Int? = 0 +):Comparable{ + override fun compareTo(other: Rule): Int { + return if (this.priority != null && other.priority != null) { + this.priority.compareTo(other.priority) + } else if (this.priority != null) { + -1 + } else if (other.priority != null) { + 1 + } else { + 0 + } + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleAction.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleAction.kt new file mode 100644 index 00000000..f85a01f4 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleAction.kt @@ -0,0 +1,19 @@ +package org.hisp.dhis.rules.models + +data class RuleAction( + val data: String?, + val type: String, + val values: Map = emptyMap() +){ + fun content():String? { + return values["content"] + } + + fun field():String? { + return values["field"] + } + + fun attributeType():String? { + return values["attributeType"] + } +} \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleAttributeValue.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleAttributeValue.kt new file mode 100644 index 00000000..404b3542 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleAttributeValue.kt @@ -0,0 +1,6 @@ +package org.hisp.dhis.rules.models + +data class RuleAttributeValue( + val trackedEntityAttribute: String, + val value: String +) diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleDataValue.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleDataValue.kt new file mode 100644 index 00000000..d5969f77 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleDataValue.kt @@ -0,0 +1,10 @@ +package org.hisp.dhis.rules.models + +import kotlinx.datetime.Instant + +data class RuleDataValue( + val eventDate: Instant, + val programStage: String, + val dataElement: String, + val value: String +) diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEffect.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEffect.kt new file mode 100644 index 00000000..66f3ca22 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEffect.kt @@ -0,0 +1,7 @@ +package org.hisp.dhis.rules.models + +data class RuleEffect( + val ruleId: String, + val ruleAction: RuleAction, + val data: String? = "" +) \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEffects.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEffects.kt new file mode 100644 index 00000000..f7676281 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEffects.kt @@ -0,0 +1,11 @@ +package org.hisp.dhis.rules.models +data class RuleEffects( + val trackerObjectType: TrackerObjectType, + val trackerObjectUid: String, + val ruleEffects: List +) { + val isEnrollment: Boolean + get() = trackerObjectType == TrackerObjectType.ENROLLMENT + val isEvent: Boolean + get() = trackerObjectType == TrackerObjectType.EVENT +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEngineValidationException.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEngineValidationException.kt new file mode 100644 index 00000000..7a7df64d --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEngineValidationException.kt @@ -0,0 +1,3 @@ +package org.hisp.dhis.rules.models + +class RuleEngineValidationException(cause: IllegalArgumentException) : IllegalArgumentException(cause.message) \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEnrollment.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEnrollment.kt new file mode 100644 index 00000000..6976a637 --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEnrollment.kt @@ -0,0 +1,20 @@ +package org.hisp.dhis.rules.models + +import kotlinx.datetime.LocalDate + +data class RuleEnrollment( + val enrollment: String, + val programName: String, + val incidentDate: LocalDate, + val enrollmentDate: LocalDate, + val status: Status, + val organisationUnit: String, + val organisationUnitCode: String, + val attributeValues: List +) { + enum class Status { + ACTIVE, + COMPLETED, + CANCELLED + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEvent.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEvent.kt new file mode 100644 index 00000000..3dfc934a --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleEvent.kt @@ -0,0 +1,26 @@ +package org.hisp.dhis.rules.models + +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDate + +data class RuleEvent( + val event: String, + val programStage: String, + val programStageName: String, + val status: Status, + val eventDate: Instant, + val dueDate: LocalDate?, + val completedDate: LocalDate?, + val organisationUnit: String, + val organisationUnitCode: String?, + val dataValues: List +) { + enum class Status { + ACTIVE, + COMPLETED, + SCHEDULE, + SKIPPED, + VISITED, + OVERDUE + } +} diff --git a/src/main/java/org/hisp/dhis/rules/RuleEngineIntent.java b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleValidationResult.kt similarity index 87% rename from src/main/java/org/hisp/dhis/rules/RuleEngineIntent.java rename to src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleValidationResult.kt index c11c874c..65d9e9c6 100644 --- a/src/main/java/org/hisp/dhis/rules/RuleEngineIntent.java +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleValidationResult.kt @@ -1,4 +1,4 @@ -package org.hisp.dhis.rules; +package org.hisp.dhis.rules.models /* * Copyright (c) 2004-2020, University of Oslo @@ -26,13 +26,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** + */ /** * @author Zubair Asghar */ -public enum RuleEngineIntent -{ - EVALUATION, - DESCRIPTION -} +data class RuleValidationResult( + val valid: Boolean, + val errorMessage: String? = null, + val exception: Throwable? = null, + val description: String? = null +) \ No newline at end of file diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleValueType.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleValueType.kt new file mode 100644 index 00000000..758fa5ee --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleValueType.kt @@ -0,0 +1,12 @@ +package org.hisp.dhis.rules.models + +enum class RuleValueType(private val defaultValue: Any) { + TEXT(""), + NUMERIC(0.0), + BOOLEAN(false), + DATE("2020-01-01"); + + fun defaultValue(): Any { + return defaultValue + } +} diff --git a/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleVariable.kt b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleVariable.kt new file mode 100644 index 00000000..9013a37c --- /dev/null +++ b/src/commonMain/kotlin/org/hisp/dhis/rules/models/RuleVariable.kt @@ -0,0 +1,22 @@ +package org.hisp.dhis.rules.models + +import org.hisp.dhis.rules.engine.RuleVariableValue +import org.hisp.dhis.rules.engine.RuleVariableValueMapBuilder + +interface RuleVariable { + val options: List