diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61524b5b..1a11fcec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,8 @@ on: pull_request: branches: - "*" +env: + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true jobs: Get-CI-Image-Tag: @@ -57,6 +59,7 @@ jobs: - 11 - 17 - 21 + - 23 name: Build and Test runs-on: windows-latest diff --git a/build.gradle b/build.gradle index 71345208..68524f55 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { opensearch_group = "org.opensearch" - opensearch_version = System.getProperty("opensearch.version", "2.16.0-SNAPSHOT") + opensearch_version = System.getProperty("opensearch.version", "2.18.1-SNAPSHOT") isSnapshot = "true" == System.getProperty("build.snapshot", "true") buildVersionQualifier = System.getProperty("build.version_qualifier", "") kotlin_version = System.getProperty("kotlin.version", "1.8.21") diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c..a4b76b95 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3999f7f3..7c553f64 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae +distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # 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 +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 6689b85b..9b42019c 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ 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. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ 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. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/release-notes/opensearch-common-utils.release-notes-2.16.0.0.md b/release-notes/opensearch-common-utils.release-notes-2.16.0.0.md new file mode 100644 index 00000000..6ae5b6b7 --- /dev/null +++ b/release-notes/opensearch-common-utils.release-notes-2.16.0.0.md @@ -0,0 +1,12 @@ +## Version 2.16.0.0 2024-07-25 + +Compatible with OpenSearch 2.16.0 + +### Maintenance +* Increment version to 2.16.0-SNAPSHOT ([#688](https://github.com/opensearch-project/common-utils/pull/688)) + +### Enhancements +* [Backport 2.x] Add support for remote monitors ([#694](https://github.com/opensearch-project/common-utils/pull/694)) + +### Documentation +* Added 2.16.0.0 release notes. ([#700](https://github.com/opensearch-project/common-utils/pull/700)) \ No newline at end of file diff --git a/release-notes/opensearch-common-utils.release-notes-2.17.0.0.md b/release-notes/opensearch-common-utils.release-notes-2.17.0.0.md new file mode 100644 index 00000000..ff24e040 --- /dev/null +++ b/release-notes/opensearch-common-utils.release-notes-2.17.0.0.md @@ -0,0 +1,16 @@ +## Version 2.17.0.0 2024-09-03 + +Compatible with OpenSearch 2.17.0 + +### Maintenance +* Fixed Common-Utils CIs: ([#703](https://github.com/opensearch-project/common-utils/pull/703)) + +### Bug Fixes +* Added missing ctx variables ([#710](https://github.com/opensearch-project/common-utils/pull/710)) +* Changed the names of security actions for Alerting Comments feature ([#724](https://github.com/opensearch-project/common-utils/pull/724)) + +### Enhancements +* Updated pull request template to include API spec change in checklist ([#696](https://github.com/opensearch-project/common-utils/pull/696)) + +### Documentation +* Added 2.17.0.0 release notes ([#727](https://github.com/opensearch-project/common-utils/pull/727)) diff --git a/release-notes/opensearch-common-utils.release-notes-2.18.0.0.md b/release-notes/opensearch-common-utils.release-notes-2.18.0.0.md new file mode 100644 index 00000000..a39b7b31 --- /dev/null +++ b/release-notes/opensearch-common-utils.release-notes-2.18.0.0.md @@ -0,0 +1,13 @@ +## Version 2.18.0.0 2024-10-28 + +Compatible with OpenSearch 2.18.0 + +### Maintenance +* Increment version to 2.18.0-SNAPSHOT ([#729](https://github.com/opensearch-project/common-utils/pull/729)) +* Update Gradle to 8.10.2 ([#746](https://github.com/opensearch-project/common-utils/pull/746)) + +### Enhancements +* changes to support dynamic deletion of doc-level monitor query indices ([#734](https://github.com/opensearch-project/common-utils/pull/734)) + +### Documentation +* Added 2.18.0.0 release notes. ([#750](https://github.com/opensearch-project/common-utils/pull/750)) \ No newline at end of file diff --git a/src/main/kotlin/org/opensearch/commons/alerting/action/AlertingActions.kt b/src/main/kotlin/org/opensearch/commons/alerting/action/AlertingActions.kt index ba4e33fe..fcf98261 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/action/AlertingActions.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/action/AlertingActions.kt @@ -21,9 +21,9 @@ object AlertingActions { const val SUBSCRIBE_FINDINGS_ACTION_NAME = "cluster:admin/opensearch/alerting/findings/subscribe" const val GET_MONITOR_ACTION_NAME = "cluster:admin/opendistro/alerting/monitor/get" const val SEARCH_MONITORS_ACTION_NAME = "cluster:admin/opendistro/alerting/monitor/search" - const val INDEX_COMMENT_ACTION_NAME = "cluster:admin/opensearch/alerting/alerts/comments/write" - const val SEARCH_COMMENTS_ACTION_NAME = "cluster:admin/opensearch/alerting/alerts/comments/search" - const val DELETE_COMMENT_ACTION_NAME = "cluster:admin/opensearch/alerting/alerts/comments/delete" + const val INDEX_COMMENT_ACTION_NAME = "cluster:admin/opensearch/alerting/comments/write" + const val SEARCH_COMMENTS_ACTION_NAME = "cluster:admin/opensearch/alerting/comments/search" + const val DELETE_COMMENT_ACTION_NAME = "cluster:admin/opensearch/alerting/comments/delete" @JvmField val INDEX_MONITOR_ACTION_TYPE = diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/BucketLevelTrigger.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/BucketLevelTrigger.kt index 59dbae7f..39ea4fbc 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/BucketLevelTrigger.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/BucketLevelTrigger.kt @@ -69,7 +69,13 @@ data class BucketLevelTrigger( NAME_FIELD to name, SEVERITY_FIELD to severity, ACTIONS_FIELD to actions.map { it.asTemplateArg() }, - PARENT_BUCKET_PATH to getParentBucketPath() + PARENT_BUCKET_PATH to getParentBucketPath(), + CONDITION_FIELD to mapOf( + SCRIPT_FIELD to mapOf( + SOURCE_FIELD to bucketSelector.script.idOrCode, + LANG_FIELD to bucketSelector.script.lang + ) + ) ) } @@ -81,6 +87,9 @@ data class BucketLevelTrigger( const val BUCKET_LEVEL_TRIGGER_FIELD = "bucket_level_trigger" const val CONDITION_FIELD = "condition" const val PARENT_BUCKET_PATH = "parentBucketPath" + const val SCRIPT_FIELD = "script" + const val SOURCE_FIELD = "source" + const val LANG_FIELD = "lang" val XCONTENT_REGISTRY = NamedXContentRegistry.Entry( Trigger::class.java, diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/DocLevelMonitorInput.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/DocLevelMonitorInput.kt index 4ed95cdb..3193ee57 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/DocLevelMonitorInput.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/DocLevelMonitorInput.kt @@ -24,7 +24,7 @@ data class DocLevelMonitorInput( sin.readList(::DocLevelQuery) // docLevelQueries ) - fun asTemplateArg(): Map { + override fun asTemplateArg(): Map { return mapOf( DESCRIPTION_FIELD to description, INDICES_FIELD to indices, diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/DocumentLevelTrigger.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/DocumentLevelTrigger.kt index df584234..a1f8b617 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/DocumentLevelTrigger.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/DocumentLevelTrigger.kt @@ -60,7 +60,13 @@ data class DocumentLevelTrigger( ID_FIELD to id, NAME_FIELD to name, SEVERITY_FIELD to severity, - ACTIONS_FIELD to actions.map { it.asTemplateArg() } + ACTIONS_FIELD to actions.map { it.asTemplateArg() }, + CONDITION_FIELD to mapOf( + SCRIPT_FIELD to mapOf( + SOURCE_FIELD to condition.idOrCode, + LANG_FIELD to condition.lang + ) + ) ) } @@ -78,6 +84,8 @@ data class DocumentLevelTrigger( const val CONDITION_FIELD = "condition" const val SCRIPT_FIELD = "script" const val QUERY_IDS_FIELD = "query_ids" + const val SOURCE_FIELD = "source" + const val LANG_FIELD = "lang" val XCONTENT_REGISTRY = NamedXContentRegistry.Entry( Trigger::class.java, diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/Input.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/Input.kt index 7c02420a..3846cea6 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/Input.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/Input.kt @@ -66,4 +66,7 @@ interface Input : BaseModel { } fun name(): String + + /** Returns a representation of the schedule suitable for passing into painless and mustache scripts. */ + fun asTemplateArg(): Map = emptyMap() } diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt index 322e4b30..bccfccfe 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt @@ -42,6 +42,7 @@ data class Monitor( val triggers: List, val uiMetadata: Map, val dataSources: DataSources = DataSources(), + val deleteQueryIndexInEveryRun: Boolean? = false, val owner: String? = "alerting" ) : ScheduledJob { @@ -110,6 +111,7 @@ data class Monitor( } else { DataSources() }, + deleteQueryIndexInEveryRun = sin.readOptionalBoolean(), owner = sin.readOptionalString() ) @@ -127,8 +129,18 @@ data class Monitor( } /** Returns a representation of the monitor suitable for passing into painless and mustache scripts. */ - fun asTemplateArg(): Map { - return mapOf(_ID to id, _VERSION to version, NAME_FIELD to name, ENABLED_FIELD to enabled) + fun asTemplateArg(): Map { + return mapOf( + _ID to id, + _VERSION to version, + NAME_FIELD to name, + ENABLED_FIELD to enabled, + MONITOR_TYPE_FIELD to monitorType.toString(), + ENABLED_TIME_FIELD to enabledTime?.toEpochMilli(), + LAST_UPDATE_TIME_FIELD to lastUpdateTime.toEpochMilli(), + SCHEDULE_FIELD to schedule.asTemplateArg(), + INPUTS_FIELD to inputs.map { it.asTemplateArg() } + ) } fun toXContentWithUser(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { @@ -159,6 +171,7 @@ data class Monitor( .optionalTimeField(LAST_UPDATE_TIME_FIELD, lastUpdateTime) if (uiMetadata.isNotEmpty()) builder.field(UI_METADATA_FIELD, uiMetadata) builder.field(DATA_SOURCES_FIELD, dataSources) + builder.field(DELETE_QUERY_INDEX_IN_EVERY_RUN_FIELD, deleteQueryIndexInEveryRun) builder.field(OWNER_FIELD, owner) if (params.paramAsBoolean("with_type", false)) builder.endObject() return builder.endObject() @@ -210,6 +223,7 @@ data class Monitor( out.writeMap(uiMetadata) out.writeBoolean(dataSources != null) // for backward compatibility with pre-existing monitors which don't have datasources field dataSources.writeTo(out) + out.writeOptionalBoolean(deleteQueryIndexInEveryRun) out.writeOptionalString(owner) } @@ -230,6 +244,7 @@ data class Monitor( const val UI_METADATA_FIELD = "ui_metadata" const val DATA_SOURCES_FIELD = "data_sources" const val ENABLED_TIME_FIELD = "enabled_time" + const val DELETE_QUERY_INDEX_IN_EVERY_RUN_FIELD = "delete_query_index_in_every_run" const val OWNER_FIELD = "owner" val MONITOR_TYPE_PATTERN = Pattern.compile("[a-zA-Z0-9_]{5,25}") @@ -258,6 +273,7 @@ data class Monitor( val triggers: MutableList = mutableListOf() val inputs: MutableList = mutableListOf() var dataSources = DataSources() + var deleteQueryIndexInEveryRun = false var owner = "alerting" XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp) @@ -311,6 +327,11 @@ data class Monitor( } else { DataSources.parse(xcp) } + DELETE_QUERY_INDEX_IN_EVERY_RUN_FIELD -> deleteQueryIndexInEveryRun = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) { + deleteQueryIndexInEveryRun + } else { + xcp.booleanValue() + } OWNER_FIELD -> owner = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) owner else xcp.text() else -> { xcp.skipChildren() @@ -338,6 +359,7 @@ data class Monitor( triggers.toList(), uiMetadata, dataSources, + deleteQueryIndexInEveryRun, owner ) } diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/QueryLevelTrigger.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/QueryLevelTrigger.kt index 0be93671..a88ef9b6 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/QueryLevelTrigger.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/QueryLevelTrigger.kt @@ -60,7 +60,13 @@ data class QueryLevelTrigger( ID_FIELD to id, NAME_FIELD to name, SEVERITY_FIELD to severity, - ACTIONS_FIELD to actions.map { it.asTemplateArg() } + ACTIONS_FIELD to actions.map { it.asTemplateArg() }, + CONDITION_FIELD to mapOf( + SCRIPT_FIELD to mapOf( + SOURCE_FIELD to condition.idOrCode, + LANG_FIELD to condition.lang + ) + ) ) } @@ -77,6 +83,8 @@ data class QueryLevelTrigger( const val QUERY_LEVEL_TRIGGER_FIELD = "query_level_trigger" const val CONDITION_FIELD = "condition" const val SCRIPT_FIELD = "script" + const val SOURCE_FIELD = "source" + const val LANG_FIELD = "lang" val XCONTENT_REGISTRY = NamedXContentRegistry.Entry( Trigger::class.java, diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/Schedule.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/Schedule.kt index d82bc375..3d08c095 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/Schedule.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/Schedule.kt @@ -146,6 +146,9 @@ sealed class Schedule : BaseModel { abstract fun getPeriodEndingAt(endTime: Instant?): Pair abstract fun runningOnTime(lastExecutionTime: Instant?): Boolean + + /** Returns a representation of the schedule suitable for passing into painless and mustache scripts. */ + abstract fun asTemplateArg(): Map } /** @@ -257,6 +260,14 @@ data class CronSchedule( out.writeString(expression) out.writeZoneId(timezone) } + + override fun asTemplateArg(): Map = + mapOf( + CRON_FIELD to mapOf( + EXPRESSION_FIELD to expression, + TIMEZONE_FIELD to timezone.toString() + ) + ) } data class IntervalSchedule( @@ -354,4 +365,12 @@ data class IntervalSchedule( out.writeInt(interval) out.writeEnum(unit) } + + override fun asTemplateArg(): Map = + mapOf( + PERIOD_FIELD to mapOf( + INTERVAL_FIELD to interval, + UNIT_FIELD to unit.toString() + ) + ) } diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/SearchInput.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/SearchInput.kt index 7579a10d..99a5cb8d 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/SearchInput.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/SearchInput.kt @@ -85,4 +85,12 @@ data class SearchInput(val indices: List, val query: SearchSourceBuilder return SearchInput(sin) } } + + override fun asTemplateArg(): Map = + mapOf( + SEARCH_FIELD to mapOf( + INDICES_FIELD to indices, + QUERY_FIELD to query.toString() + ) + ) } diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/action/Action.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/action/Action.kt index 4fa0c514..88d15210 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/action/Action.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/action/Action.kt @@ -65,7 +65,12 @@ data class Action( } fun asTemplateArg(): Map { - return mapOf(NAME_FIELD to name) + return mapOf( + ID_FIELD to id, + NAME_FIELD to name, + DESTINATION_ID_FIELD to destinationId, + THROTTLE_ENABLED_FIELD to throttleEnabled + ) } @Throws(IOException::class) diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/remote/monitors/RemoteDocLevelMonitorInput.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/remote/monitors/RemoteDocLevelMonitorInput.kt index 4d1911df..1e6184f3 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/remote/monitors/RemoteDocLevelMonitorInput.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/remote/monitors/RemoteDocLevelMonitorInput.kt @@ -21,7 +21,7 @@ data class RemoteDocLevelMonitorInput(val input: BytesReference, val docLevelMon DocLevelMonitorInput.readFrom(sin) ) - fun asTemplateArg(): Map { + override fun asTemplateArg(): Map { val bytes = input.toBytesRef().bytes return mapOf( RemoteDocLevelMonitorInput.INPUT_SIZE to bytes.size, diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/remote/monitors/RemoteMonitorInput.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/remote/monitors/RemoteMonitorInput.kt index c2d3867b..29a939ff 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/remote/monitors/RemoteMonitorInput.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/remote/monitors/RemoteMonitorInput.kt @@ -18,7 +18,7 @@ data class RemoteMonitorInput(val input: BytesReference) : Input { sin.readBytesReference() ) - fun asTemplateArg(): Map { + override fun asTemplateArg(): Map { val bytes = input.toBytesRef().bytes return mapOf( INPUT_SIZE to bytes.size, diff --git a/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt b/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt index 7ae132ef..ccba0b47 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt @@ -408,6 +408,13 @@ fun randomDocLevelMonitorInput( return DocLevelMonitorInput(description = description, indices = indices, queries = queries) } +fun randomSearchInput( + indices: List = listOf(1..RandomNumbers.randomIntBetween(Random(), 0, 10)).map { RandomStrings.randomAsciiLettersOfLength(Random(), 10) }, + query: SearchSourceBuilder = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) +): SearchInput { + return SearchInput(indices, query) +} + fun randomClusterMetricsInput( path: String = ClusterMetricsInput.ClusterMetricType.values() .filter { it.defaultPath.isNotBlank() && !it.requiresPathParams } diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/BucketLevelTriggerTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/BucketLevelTriggerTests.kt new file mode 100644 index 00000000..1a9e3cbe --- /dev/null +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/BucketLevelTriggerTests.kt @@ -0,0 +1,54 @@ +package org.opensearch.commons.alerting.model + +import org.junit.jupiter.api.Test +import org.opensearch.commons.alerting.model.BucketLevelTrigger.Companion.CONDITION_FIELD +import org.opensearch.commons.alerting.model.BucketLevelTrigger.Companion.LANG_FIELD +import org.opensearch.commons.alerting.model.BucketLevelTrigger.Companion.PARENT_BUCKET_PATH +import org.opensearch.commons.alerting.model.BucketLevelTrigger.Companion.SCRIPT_FIELD +import org.opensearch.commons.alerting.model.BucketLevelTrigger.Companion.SOURCE_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.ACTIONS_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.ID_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.NAME_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.SEVERITY_FIELD +import org.opensearch.commons.alerting.randomBucketLevelTrigger +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class BucketLevelTriggerTests { + + @Test + fun `test BucketLevelTrigger asTemplateArgs`() { + val trigger = randomBucketLevelTrigger() + + val templateArgs = trigger.asTemplateArg() + + assertEquals(trigger.id, templateArgs[ID_FIELD], "Template arg field 'id' doesn't match") + assertEquals(trigger.name, templateArgs[NAME_FIELD], "Template arg field 'name' doesn't match") + assertEquals(trigger.severity, templateArgs[SEVERITY_FIELD], "Template arg field 'severity' doesn't match") + val actions = templateArgs[ACTIONS_FIELD] as List<*> + assertEquals( + trigger.actions.size, + actions.size, + "Template arg field 'actions' doesn't match" + ) + assertEquals( + trigger.getParentBucketPath(), + templateArgs[PARENT_BUCKET_PATH], + "Template arg field 'parentBucketPath' doesn't match" + ) + val condition = templateArgs[CONDITION_FIELD] as? Map<*, *> + assertNotNull(condition, "Template arg field 'condition' is empty") + val script = condition[SCRIPT_FIELD] as? Map<*, *> + assertNotNull(script, "Template arg field 'condition.script' is empty") + assertEquals( + trigger.bucketSelector.script.idOrCode, + script[SOURCE_FIELD], + "Template arg field 'script.source' doesn't match" + ) + assertEquals( + trigger.bucketSelector.script.lang, + script[LANG_FIELD], + "Template arg field 'script.lang' doesn't match" + ) + } +} diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/DocumentLevelTriggerTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/DocumentLevelTriggerTests.kt new file mode 100644 index 00000000..7375223c --- /dev/null +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/DocumentLevelTriggerTests.kt @@ -0,0 +1,48 @@ +package org.opensearch.commons.alerting.model + +import org.junit.jupiter.api.Test +import org.opensearch.commons.alerting.model.DocumentLevelTrigger.Companion.CONDITION_FIELD +import org.opensearch.commons.alerting.model.DocumentLevelTrigger.Companion.LANG_FIELD +import org.opensearch.commons.alerting.model.DocumentLevelTrigger.Companion.SCRIPT_FIELD +import org.opensearch.commons.alerting.model.DocumentLevelTrigger.Companion.SOURCE_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.ACTIONS_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.ID_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.NAME_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.SEVERITY_FIELD +import org.opensearch.commons.alerting.randomDocumentLevelTrigger +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class DocumentLevelTriggerTests { + + @Test + fun `test DocumentLevelTrigger asTemplateArgs`() { + val trigger = randomDocumentLevelTrigger() + + val templateArgs = trigger.asTemplateArg() + + assertEquals(trigger.id, templateArgs[ID_FIELD], "Template arg field 'id' doesn't match") + assertEquals(trigger.name, templateArgs[NAME_FIELD], "Template arg field 'name' doesn't match") + assertEquals(trigger.severity, templateArgs[SEVERITY_FIELD], "Template arg field 'severity' doesn't match") + val actions = templateArgs[ACTIONS_FIELD] as List<*> + assertEquals( + trigger.actions.size, + actions.size, + "Template arg field 'actions' doesn't match" + ) + val condition = templateArgs[CONDITION_FIELD] as? Map<*, *> + assertNotNull(condition, "Template arg field 'condition' is empty") + val script = condition[SCRIPT_FIELD] as? Map<*, *> + assertNotNull(script, "Template arg field 'condition.script' is empty") + assertEquals( + trigger.condition.idOrCode, + script[SOURCE_FIELD], + "Template arg field 'script.source' doesn't match" + ) + assertEquals( + trigger.condition.lang, + script[LANG_FIELD], + "Template arg field 'script.lang' doesn't match" + ) + } +} diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/MonitorsTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/MonitorsTests.kt new file mode 100644 index 00000000..9529e44d --- /dev/null +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/MonitorsTests.kt @@ -0,0 +1,49 @@ +package org.opensearch.commons.alerting.model + +import org.junit.jupiter.api.Test +import org.opensearch.commons.alerting.randomQueryLevelMonitor +import org.opensearch.commons.alerting.util.IndexUtils +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class MonitorsTests { + + @Test + fun `test monitor asTemplateArgs`() { + val monitor = randomQueryLevelMonitor(enabled = true) + + val templateArgs = monitor.asTemplateArg() + + assertEquals(monitor.id, templateArgs[IndexUtils._ID], "Template arg field 'id' doesn't match") + assertEquals( + monitor.version, + templateArgs[IndexUtils._VERSION], + "Template arg field 'version' doesn't match" + ) + assertEquals(monitor.name, templateArgs[Monitor.NAME_FIELD], "Template arg field 'name' doesn't match") + assertEquals( + monitor.enabled, + templateArgs[Monitor.ENABLED_FIELD], + "Template arg field 'enabled' doesn't match" + ) + assertEquals( + monitor.monitorType.toString(), + templateArgs[Monitor.MONITOR_TYPE_FIELD], + "Template arg field 'monitoryType' doesn't match" + ) + assertEquals( + monitor.enabledTime?.toEpochMilli(), + templateArgs[Monitor.ENABLED_TIME_FIELD], + "Template arg field 'enabledTime' doesn't match" + ) + assertEquals( + monitor.lastUpdateTime.toEpochMilli(), + templateArgs[Monitor.LAST_UPDATE_TIME_FIELD], + "Template arg field 'lastUpdateTime' doesn't match" + ) + assertNotNull(templateArgs[Monitor.SCHEDULE_FIELD], "Template arg field 'schedule' not set") + val inputs = templateArgs[Monitor.INPUTS_FIELD] as? List<*> + assertNotNull(inputs, "Template arg field 'inputs' not set") + assertEquals(1, inputs.size, "Template arg field 'inputs' is not populated") + } +} diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/QueryLevelTriggerTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/QueryLevelTriggerTests.kt new file mode 100644 index 00000000..824e1b1e --- /dev/null +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/QueryLevelTriggerTests.kt @@ -0,0 +1,48 @@ +package org.opensearch.commons.alerting.model + +import org.junit.jupiter.api.Test +import org.opensearch.commons.alerting.model.QueryLevelTrigger.Companion.CONDITION_FIELD +import org.opensearch.commons.alerting.model.QueryLevelTrigger.Companion.LANG_FIELD +import org.opensearch.commons.alerting.model.QueryLevelTrigger.Companion.SCRIPT_FIELD +import org.opensearch.commons.alerting.model.QueryLevelTrigger.Companion.SOURCE_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.ACTIONS_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.ID_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.NAME_FIELD +import org.opensearch.commons.alerting.model.Trigger.Companion.SEVERITY_FIELD +import org.opensearch.commons.alerting.randomQueryLevelTrigger +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class QueryLevelTriggerTests { + + @Test + fun `test QueryLevelTrigger asTemplateArgs`() { + val trigger = randomQueryLevelTrigger() + + val templateArgs = trigger.asTemplateArg() + + assertEquals(trigger.id, templateArgs[ID_FIELD], "Template arg field 'id' doesn't match") + assertEquals(trigger.name, templateArgs[NAME_FIELD], "Template arg field 'name' doesn't match") + assertEquals(trigger.severity, templateArgs[SEVERITY_FIELD], "Template arg field 'severity' doesn't match") + val actions = templateArgs[ACTIONS_FIELD] as List<*> + assertEquals( + trigger.actions.size, + actions.size, + "Template arg field 'actions' doesn't match" + ) + val condition = templateArgs[CONDITION_FIELD] as? Map<*, *> + assertNotNull(condition, "Template arg field 'condition' is empty") + val script = condition[SCRIPT_FIELD] as? Map<*, *> + assertNotNull(script, "Template arg field 'condition.script' is empty") + assertEquals( + trigger.condition.idOrCode, + script[SOURCE_FIELD], + "Template arg field 'script.source' doesn't match" + ) + assertEquals( + trigger.condition.lang, + script[LANG_FIELD], + "Template arg field 'script.lang' doesn't match" + ) + } +} diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/ScheduleTest.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/ScheduleTest.kt index 590dda04..e0423d56 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/model/ScheduleTest.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/ScheduleTest.kt @@ -1,6 +1,12 @@ package org.opensearch.commons.alerting.model import org.junit.jupiter.api.Test +import org.opensearch.commons.alerting.model.Schedule.Companion.CRON_FIELD +import org.opensearch.commons.alerting.model.Schedule.Companion.EXPRESSION_FIELD +import org.opensearch.commons.alerting.model.Schedule.Companion.INTERVAL_FIELD +import org.opensearch.commons.alerting.model.Schedule.Companion.PERIOD_FIELD +import org.opensearch.commons.alerting.model.Schedule.Companion.TIMEZONE_FIELD +import org.opensearch.commons.alerting.model.Schedule.Companion.UNIT_FIELD import org.opensearch.commons.alerting.util.string import org.opensearch.core.xcontent.ToXContent import java.time.Instant @@ -67,7 +73,8 @@ class ScheduleTest : XContentTestBase { val cronSchedule = CronSchedule(cronExpression, ZoneId.of("America/Los_Angeles")) // The nextTimeToExecute should be the minute after the previous execution time instance, not enabledTimeInstance - val nextTimeToExecute = cronSchedule.getExpectedNextExecutionTime(enabledTimeInstance, previousExecutionTimeInstance) + val nextTimeToExecute = + cronSchedule.getExpectedNextExecutionTime(enabledTimeInstance, previousExecutionTimeInstance) assertNotNull(nextTimeToExecute, "There should be next execute time") assertEquals( previousExecutionTimeInstance.plusSeconds(2L), @@ -107,7 +114,8 @@ class ScheduleTest : XContentTestBase { val intervalSchedule = IntervalSchedule(1, ChronoUnit.MINUTES, testInstance) // The nextTimeToExecute should be the minute after the previous execution time instance - val nextTimeToExecute = intervalSchedule.getExpectedNextExecutionTime(enabledTimeInstance, previousExecutionTimeInstance) + val nextTimeToExecute = + intervalSchedule.getExpectedNextExecutionTime(enabledTimeInstance, previousExecutionTimeInstance) assertNotNull(nextTimeToExecute, "There should be next execute time") assertEquals( previousExecutionTimeInstance.plusSeconds(60L), @@ -165,12 +173,19 @@ class ScheduleTest : XContentTestBase { @Test fun `test invalid type`() { val scheduleString = "{\"foobarzzz\":{\"expression\":\"0 * * * *\",\"timezone\":\"+++9\"}}" - assertFailsWith(IllegalArgumentException::class, "Expected IllegalArgumentException") { Schedule.parse(parser(scheduleString)) } + assertFailsWith(IllegalArgumentException::class, "Expected IllegalArgumentException") { + Schedule.parse( + parser( + scheduleString + ) + ) + } } @Test fun `test two types`() { - val scheduleString = "{\"cron\":{\"expression\":\"0 * * * *\",\"timezone\":\"Asia/Tokyo\"}, \"period\":{\"interval\":\"1\",\"unit\":\"Minutes\"}}" + val scheduleString = + "{\"cron\":{\"expression\":\"0 * * * *\",\"timezone\":\"Asia/Tokyo\"}, \"period\":{\"interval\":\"1\",\"unit\":\"Minutes\"}}" assertFailsWith(IllegalArgumentException::class, "Expected IllegalArgumentException") { Schedule.parse(parser(scheduleString)) } @@ -335,4 +350,44 @@ class ScheduleTest : XContentTestBase { IntervalSchedule(-1, ChronoUnit.MINUTES) } } + + @Test + fun `test IntervalSchedule as asTemplateArgs`() { + val schedule = createTestIntervalSchedule() + + val templateArgs = schedule.asTemplateArg() + + val period = templateArgs[PERIOD_FIELD] as? Map<*, *> + assertNotNull(period, "Template arg field 'period' is empty") + assertEquals( + schedule.interval, + period[INTERVAL_FIELD], + "Template arg field 'interval' doesn't match" + ) + assertEquals( + schedule.unit.toString(), + period[UNIT_FIELD], + "Template arg field 'unit' doesn't match" + ) + } + + @Test + fun `test CronSchedule as asTemplateArgs`() { + val schedule = createTestCronSchedule() + + val templateArgs = schedule.asTemplateArg() + + val cron = templateArgs[CRON_FIELD] as? Map<*, *> + assertNotNull(cron, "Template arg field 'cron' is empty") + assertEquals( + schedule.expression, + cron[EXPRESSION_FIELD], + "Template arg field 'expression' doesn't match" + ) + assertEquals( + schedule.timezone.toString(), + cron[TIMEZONE_FIELD], + "Template arg field 'timezone' doesn't match" + ) + } } diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/SearchInputTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/SearchInputTests.kt new file mode 100644 index 00000000..0fc0f656 --- /dev/null +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/SearchInputTests.kt @@ -0,0 +1,32 @@ +package org.opensearch.commons.alerting.model + +import org.junit.jupiter.api.Test +import org.opensearch.commons.alerting.model.SearchInput.Companion.INDICES_FIELD +import org.opensearch.commons.alerting.model.SearchInput.Companion.QUERY_FIELD +import org.opensearch.commons.alerting.model.SearchInput.Companion.SEARCH_FIELD +import org.opensearch.commons.alerting.randomSearchInput +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class SearchInputTests { + + @Test + fun `test SearchInput asTemplateArgs`() { + val searchInput = randomSearchInput() + + val templateArgs = searchInput.asTemplateArg() + + val search = templateArgs[SEARCH_FIELD] as? Map<*, *> + assertNotNull(search, "Template arg field 'search' is empty") + assertEquals( + searchInput.indices, + search[INDICES_FIELD], + "Template arg field 'indices' doesn't match" + ) + assertEquals( + searchInput.query.toString(), + search[QUERY_FIELD], + "Template arg field 'query' doesn't match" + ) + } +} diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/action/ActionTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/action/ActionTests.kt new file mode 100644 index 00000000..dcbf8998 --- /dev/null +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/action/ActionTests.kt @@ -0,0 +1,40 @@ +package org.opensearch.commons.alerting.model.action + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.opensearch.commons.alerting.model.action.Action.Companion.DESTINATION_ID_FIELD +import org.opensearch.commons.alerting.model.action.Action.Companion.ID_FIELD +import org.opensearch.commons.alerting.model.action.Action.Companion.NAME_FIELD +import org.opensearch.commons.alerting.model.action.Action.Companion.THROTTLE_ENABLED_FIELD +import org.opensearch.commons.alerting.randomAction + +class ActionTests { + + @Test + fun `test action asTemplateArgs`() { + val action = randomAction() + + val templateArgs = action.asTemplateArg() + + assertEquals( + action.id, + templateArgs[ID_FIELD], + "Template arg field 'id' doesn't match" + ) + assertEquals( + action.name, + templateArgs[NAME_FIELD], + "Template arg field 'name' doesn't match" + ) + assertEquals( + action.destinationId, + templateArgs[DESTINATION_ID_FIELD], + "Template arg field 'destinationId' doesn't match" + ) + assertEquals( + action.throttleEnabled, + templateArgs[THROTTLE_ENABLED_FIELD], + "Template arg field 'throttleEnabled' doesn't match" + ) + } +}