From cc99e59374bdc7b5fc3b6d96dc777de4747cca5b Mon Sep 17 00:00:00 2001 From: AWSHurneyt Date: Thu, 8 Feb 2024 23:21:11 -0800 Subject: [PATCH 1/6] Adding dev logs. Signed-off-by: AWSHurneyt --- .../alerting/model/AggregationResultBucket.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/AggregationResultBucket.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/AggregationResultBucket.kt index 2d36a51c..b048c025 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/AggregationResultBucket.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/AggregationResultBucket.kt @@ -1,5 +1,6 @@ package org.opensearch.commons.alerting.model +import org.apache.logging.log4j.LogManager import org.opensearch.core.common.ParsingException import org.opensearch.core.common.io.stream.StreamInput import org.opensearch.core.common.io.stream.StreamOutput @@ -13,6 +14,8 @@ import org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken import java.io.IOException import java.util.Locale +private val logger = LogManager.getLogger(AggregationResultBucket::class.java) + data class AggregationResultBucket( val parentBucketPath: String?, val bucketKeys: List, @@ -35,6 +38,9 @@ data class AggregationResultBucket( } fun innerXContent(builder: XContentBuilder): XContentBuilder { + logger.info("hurneyt innerXContent::PARENTS_BUCKET_PATH = {}", parentBucketPath) + logger.info("hurneyt innerXContent::BUCKET_KEYS = {}", bucketKeys.toTypedArray()) + logger.info("hurneyt innerXContent::BUCKET = {}", bucket) builder.startObject(CONFIG_NAME) .field(PARENTS_BUCKET_PATH, parentBucketPath) .field(BUCKET_KEYS, bucketKeys.toTypedArray()) @@ -43,6 +49,20 @@ data class AggregationResultBucket( return builder } + fun asTemplateArg(): Map { + logger.info("hurneyt asTemplateArg START") + logger.info("hurneyt asTemplateArg::PARENTS_BUCKET_PATH = {}", parentBucketPath) + logger.info("hurneyt asTemplateArg::BUCKET_KEYS = {}", bucketKeys.toTypedArray()) + logger.info("hurneyt asTemplateArg::BUCKET = {}", bucket) + val output = mapOf( + PARENTS_BUCKET_PATH to parentBucketPath, + BUCKET_KEYS to bucketKeys, + BUCKET to bucket + ) + logger.info("hurneyt asTemplateArg END") + return output + } + companion object { const val CONFIG_NAME = "agg_alert_content" const val PARENTS_BUCKET_PATH = "parent_bucket_path" From 5d25a244e855eb5ad15b78408a992da9ff39aedd Mon Sep 17 00:00:00 2001 From: AWSHurneyt Date: Tue, 5 Mar 2024 08:39:10 -0800 Subject: [PATCH 2/6] Added support for printing sample documents for each bucket in bucket level monitor notification messages. Added support for printing query/rule info in doc level monitor notification messages. Signed-off-by: AWSHurneyt --- .../alerting/model/AggregationResultBucket.kt | 14 +-- .../commons/alerting/model/Alert.kt | 101 +++++++++++++++++- .../commons/alerting/model/AlertContext.kt | 75 +++++++++++++ .../commons/alerting/TestHelpers.kt | 20 ++++ .../alerting/model/AlertContextTests.kt | 45 ++++++++ 5 files changed, 240 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt create mode 100644 src/test/kotlin/org/opensearch/commons/alerting/model/AlertContextTests.kt diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/AggregationResultBucket.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/AggregationResultBucket.kt index b048c025..fc0d3231 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/AggregationResultBucket.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/AggregationResultBucket.kt @@ -1,6 +1,5 @@ package org.opensearch.commons.alerting.model -import org.apache.logging.log4j.LogManager import org.opensearch.core.common.ParsingException import org.opensearch.core.common.io.stream.StreamInput import org.opensearch.core.common.io.stream.StreamOutput @@ -14,8 +13,6 @@ import org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken import java.io.IOException import java.util.Locale -private val logger = LogManager.getLogger(AggregationResultBucket::class.java) - data class AggregationResultBucket( val parentBucketPath: String?, val bucketKeys: List, @@ -38,9 +35,6 @@ data class AggregationResultBucket( } fun innerXContent(builder: XContentBuilder): XContentBuilder { - logger.info("hurneyt innerXContent::PARENTS_BUCKET_PATH = {}", parentBucketPath) - logger.info("hurneyt innerXContent::BUCKET_KEYS = {}", bucketKeys.toTypedArray()) - logger.info("hurneyt innerXContent::BUCKET = {}", bucket) builder.startObject(CONFIG_NAME) .field(PARENTS_BUCKET_PATH, parentBucketPath) .field(BUCKET_KEYS, bucketKeys.toTypedArray()) @@ -50,17 +44,11 @@ data class AggregationResultBucket( } fun asTemplateArg(): Map { - logger.info("hurneyt asTemplateArg START") - logger.info("hurneyt asTemplateArg::PARENTS_BUCKET_PATH = {}", parentBucketPath) - logger.info("hurneyt asTemplateArg::BUCKET_KEYS = {}", bucketKeys.toTypedArray()) - logger.info("hurneyt asTemplateArg::BUCKET = {}", bucket) - val output = mapOf( + return mapOf( PARENTS_BUCKET_PATH to parentBucketPath, BUCKET_KEYS to bucketKeys, BUCKET to bucket ) - logger.info("hurneyt asTemplateArg END") - return output } companion object { diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt index e435c866..4e6f4762 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt @@ -17,7 +17,7 @@ import org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken import java.io.IOException import java.time.Instant -data class Alert( +open class Alert( val id: String = NO_ID, val version: Long = NO_VERSION, val schemaVersion: Int = NO_SCHEMA_VERSION, @@ -591,7 +591,7 @@ data class Alert( return builder } - fun asTemplateArg(): Map { + open fun asTemplateArg(): Map { return mapOf( ACKNOWLEDGED_TIME_FIELD to acknowledgedTime?.toEpochMilli(), ALERT_ID_FIELD to id, @@ -614,4 +614,101 @@ data class Alert( CLUSTERS_FIELD to clusters?.joinToString(",") ) } + + /** + * Creates a copy of an [Alert] with optionally modified properties + */ + fun copy( + id: String = this.id, + version: Long = this.version, + schemaVersion: Int = this.schemaVersion, + monitorId: String = this.monitorId, + workflowId: String = this.workflowId, + workflowName: String = this.workflowName, + monitorName: String = this.monitorName, + monitorVersion: Long = this.monitorVersion, + monitorUser: User? = this.monitorUser, + triggerId: String = this.triggerId, + triggerName: String = this.triggerName, + findingIds: List = this.findingIds, + relatedDocIds: List = this.relatedDocIds, + state: State = this.state, + startTime: Instant = this.startTime, + endTime: Instant? = this.endTime, + lastNotificationTime: Instant? = this.lastNotificationTime, + acknowledgedTime: Instant? = this.acknowledgedTime, + errorMessage: String? = this.errorMessage, + errorHistory: List = this.errorHistory, + severity: String = this.severity, + actionExecutionResults: List = this.actionExecutionResults, + aggregationResultBucket: AggregationResultBucket? = this.aggregationResultBucket, + executionId: String? = this.executionId, + associatedAlertIds: List = this.associatedAlertIds, + clusters: List? = this.clusters + ): Alert { + return Alert( + id = id, + version = version, + schemaVersion = schemaVersion, + monitorId = monitorId, + workflowId = workflowId, + workflowName = workflowName, + monitorName = monitorName, + monitorVersion = monitorVersion, + monitorUser = monitorUser, + triggerId = triggerId, + triggerName = triggerName, + findingIds = findingIds, + relatedDocIds = relatedDocIds, + state = state, + startTime = startTime, + endTime = endTime, + lastNotificationTime = lastNotificationTime, + acknowledgedTime = acknowledgedTime, + errorMessage = errorMessage, + errorHistory = errorHistory, + severity = severity, + actionExecutionResults = actionExecutionResults, + aggregationResultBucket = aggregationResultBucket, + executionId = executionId, + associatedAlertIds = associatedAlertIds, + clusters = clusters + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Alert + + if (id != other.id) return false + if (version != other.version) return false + if (schemaVersion != other.schemaVersion) return false + if (monitorId != other.monitorId) return false + if (workflowId != other.workflowId) return false + if (workflowName != other.workflowName) return false + if (monitorName != other.monitorName) return false + if (monitorVersion != other.monitorVersion) return false + if (monitorUser != other.monitorUser) return false + if (triggerId != other.triggerId) return false + if (triggerName != other.triggerName) return false + if (findingIds != other.findingIds) return false + if (relatedDocIds != other.relatedDocIds) return false + if (state != other.state) return false + if (startTime != other.startTime) return false + if (endTime != other.endTime) return false + if (lastNotificationTime != other.lastNotificationTime) return false + if (acknowledgedTime != other.acknowledgedTime) return false + if (errorMessage != other.errorMessage) return false + if (errorHistory != other.errorHistory) return false + if (severity != other.severity) return false + if (actionExecutionResults != other.actionExecutionResults) return false + if (aggregationResultBucket != other.aggregationResultBucket) return false + if (executionId != other.executionId) return false + if (associatedAlertIds != other.associatedAlertIds) return false + if (clusters != other.clusters) return false + + return true + } } diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt new file mode 100644 index 00000000..dcb6c157 --- /dev/null +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt @@ -0,0 +1,75 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.commons.alerting.model + +/** + * This model is a wrapper for [Alert] that should only be used to create a more + * informative alert object to enrich mustache template notification messages. + */ +data class AlertContext( + val alert: Alert, + val associatedQueries: List? = null, + val sampleDocs: List>? = null +) : Alert( + id = alert.id, + version = alert.version, + schemaVersion = alert.schemaVersion, + monitorId = alert.monitorId, + monitorName = alert.monitorName, + monitorVersion = alert.monitorVersion, + monitorUser = alert.monitorUser, + triggerId = alert.triggerId, + triggerName = alert.triggerName, + state = alert.state, + startTime = alert.startTime, + endTime = alert.endTime, + lastNotificationTime = alert.lastNotificationTime, + acknowledgedTime = alert.acknowledgedTime, + errorMessage = alert.errorMessage, + errorHistory = alert.errorHistory, + severity = alert.severity, + actionExecutionResults = alert.actionExecutionResults, + aggregationResultBucket = alert.aggregationResultBucket, + findingIds = alert.findingIds, + relatedDocIds = alert.relatedDocIds, + executionId = alert.executionId, + workflowId = alert.workflowId, + workflowName = alert.workflowName, + associatedAlertIds = alert.associatedAlertIds, + clusters = alert.clusters +) { + + override fun asTemplateArg(): Map { + val queriesContext = associatedQueries?.map { + mapOf( + DocLevelQuery.QUERY_ID_FIELD to it.id, + DocLevelQuery.NAME_FIELD to it.name, + DocLevelQuery.TAGS_FIELD to it.tags + ) + } + + // Compile the custom context fields. + val customContextFields = mapOf( + ASSOCIATED_QUERIES_FIELD to queriesContext, + SAMPLE_DOCS_FIELD to sampleDocs + ) + + // Get the alert template args + val templateArgs = super.asTemplateArg().toMutableMap() + + // Add the non-null custom context fields to the alert templateArgs. + customContextFields.forEach { (key, value) -> + if (value !== null) templateArgs[key] = value + } + return templateArgs + } + + companion object { + const val ASSOCIATED_QUERIES_FIELD = "associated_queries" + const val SAMPLE_DOCS_FIELD = "sample_documents" + + } +} diff --git a/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt b/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt index 8596a426..ad999af6 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt @@ -20,6 +20,7 @@ import org.opensearch.commons.alerting.aggregation.bucketselectorext.BucketSelec import org.opensearch.commons.alerting.model.ActionExecutionResult import org.opensearch.commons.alerting.model.AggregationResultBucket import org.opensearch.commons.alerting.model.Alert +import org.opensearch.commons.alerting.model.AlertContext import org.opensearch.commons.alerting.model.BucketLevelTrigger import org.opensearch.commons.alerting.model.ChainedAlertTrigger import org.opensearch.commons.alerting.model.ChainedMonitorFindings @@ -601,3 +602,22 @@ fun randomFinding( timestamp = timestamp ) } + +fun randomAlertContext( + alert: Alert = randomAlert(), + associatedQueries: List? = (-1..2).random().takeIf { it != -1 }?.let { + (0..it).map { randomDocLevelQuery() } + }, + sampleDocs: List>? = (-1..2).random().takeIf { it != -1 }?.let { + (0..it).map { + // Using 'randomFinding' to mimic documents in an index. + randomFinding().asTemplateArg() + } + } +): AlertContext { + return AlertContext( + alert = alert, + associatedQueries = associatedQueries, + sampleDocs = sampleDocs + ) +} diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/AlertContextTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/AlertContextTests.kt new file mode 100644 index 00000000..438948ca --- /dev/null +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/AlertContextTests.kt @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.commons.alerting.model + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.opensearch.commons.alerting.randomAlertContext + +class AlertContextTests { + private var alertContext: AlertContext = randomAlertContext() + + @BeforeEach + fun generateRandomData() { + alertContext = randomAlertContext() + } + + @Test + fun `test AlertContext asTemplateArg`() { + val templateArgs = alertContext.asTemplateArg() + + assertEquals(templateArgs[Alert.ALERT_ID_FIELD], alertContext.id, "Template args id does not match") + assertEquals(templateArgs[Alert.ALERT_VERSION_FIELD], alertContext.version, "Template args version does not match") + assertEquals(templateArgs[Alert.STATE_FIELD], alertContext.state.toString(), "Template args state does not match") + assertEquals(templateArgs[Alert.ERROR_MESSAGE_FIELD], alertContext.errorMessage, "Template args error message does not match") + assertEquals(templateArgs[Alert.ACKNOWLEDGED_TIME_FIELD], null, "Template args acknowledged time does not match") + assertEquals(templateArgs[Alert.END_TIME_FIELD], alertContext.endTime?.toEpochMilli(), "Template args end time does not") + assertEquals(templateArgs[Alert.START_TIME_FIELD], alertContext.startTime.toEpochMilli(), "Template args start time does not") + assertEquals(templateArgs[Alert.LAST_NOTIFICATION_TIME_FIELD], null, "Template args last notification time does not match") + assertEquals(templateArgs[Alert.SEVERITY_FIELD], alertContext.severity, "Template args severity does not match") + assertEquals(templateArgs[Alert.CLUSTERS_FIELD], alertContext.clusters?.joinToString(","), "Template args clusters does not match") + val formattedQueries = alertContext.associatedQueries?.map { + mapOf( + DocLevelQuery.QUERY_ID_FIELD to it.id, + DocLevelQuery.NAME_FIELD to it.name, + DocLevelQuery.TAGS_FIELD to it.tags + ) + } + assertEquals(templateArgs[AlertContext.ASSOCIATED_QUERIES_FIELD], formattedQueries, "Template associated queries do not match") + assertEquals(templateArgs[AlertContext.SAMPLE_DOCS_FIELD], alertContext.sampleDocs, "Template args sample docs do not match") + } +} From 71707f8ecb783cb411b78b3cc1e4181851d618ab Mon Sep 17 00:00:00 2001 From: AWSHurneyt Date: Tue, 5 Mar 2024 08:49:13 -0800 Subject: [PATCH 3/6] Fixed ktlint error. Signed-off-by: AWSHurneyt --- .../kotlin/org/opensearch/commons/alerting/model/AlertContext.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt index dcb6c157..09238d66 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt @@ -70,6 +70,5 @@ data class AlertContext( companion object { const val ASSOCIATED_QUERIES_FIELD = "associated_queries" const val SAMPLE_DOCS_FIELD = "sample_documents" - } } From 6b0be08063b5e8e654ac3ba1358126ce823ef2c9 Mon Sep 17 00:00:00 2001 From: AWSHurneyt Date: Tue, 5 Mar 2024 10:41:25 -0800 Subject: [PATCH 4/6] Refactored asTemplateArg based on PR feedback. Signed-off-by: AWSHurneyt --- .../org/opensearch/commons/alerting/model/AlertContext.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt index 09238d66..a9431cfc 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt @@ -62,7 +62,7 @@ data class AlertContext( // Add the non-null custom context fields to the alert templateArgs. customContextFields.forEach { (key, value) -> - if (value !== null) templateArgs[key] = value + value?.let { templateArgs[key] = it } } return templateArgs } From fd4ebe1dce83f4592f68131296304474e94c74ff Mon Sep 17 00:00:00 2001 From: AWSHurneyt Date: Tue, 5 Mar 2024 11:21:08 -0800 Subject: [PATCH 5/6] Added additional unit tests. Signed-off-by: AWSHurneyt --- .../opensearch/commons/alerting/AlertTests.kt | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/test/kotlin/org/opensearch/commons/alerting/AlertTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/AlertTests.kt index 4a5f2346..1d350f2f 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/AlertTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/AlertTests.kt @@ -2,6 +2,8 @@ package org.opensearch.commons.alerting import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.opensearch.commons.alerting.model.Alert import java.time.Instant @@ -84,4 +86,128 @@ class AlertTests { assertEquals(alert.id, "") assertEquals(workflow.id, alert.workflowId) } + + @Test + fun `test alert copy`() { + val alert = randomAlert() + + val copiedAlert = alert.copy() + + assertEquals(alert.id, copiedAlert.id) + assertEquals(alert.version, copiedAlert.version) + assertEquals(alert.schemaVersion, copiedAlert.schemaVersion) + assertEquals(alert.monitorId, copiedAlert.monitorId) + assertEquals(alert.workflowId, copiedAlert.workflowId) + assertEquals(alert.workflowName, copiedAlert.workflowName) + assertEquals(alert.monitorName, copiedAlert.monitorName) + assertEquals(alert.monitorVersion, copiedAlert.monitorVersion) + assertEquals(alert.monitorUser, copiedAlert.monitorUser) + assertEquals(alert.triggerId, copiedAlert.triggerId) + assertEquals(alert.triggerName, copiedAlert.triggerName) + assertEquals(alert.findingIds, copiedAlert.findingIds) + assertEquals(alert.relatedDocIds, copiedAlert.relatedDocIds) + assertEquals(alert.state, copiedAlert.state) + assertEquals(alert.startTime, copiedAlert.startTime) + assertEquals(alert.endTime, copiedAlert.endTime) + assertEquals(alert.lastNotificationTime, copiedAlert.lastNotificationTime) + assertEquals(alert.acknowledgedTime, copiedAlert.acknowledgedTime) + assertEquals(alert.errorMessage, copiedAlert.errorMessage) + assertEquals(alert.errorHistory, copiedAlert.errorHistory) + assertEquals(alert.severity, copiedAlert.severity) + assertEquals(alert.actionExecutionResults, copiedAlert.actionExecutionResults) + assertEquals(alert.aggregationResultBucket, copiedAlert.aggregationResultBucket) + assertEquals(alert.executionId, copiedAlert.executionId) + assertEquals(alert.associatedAlertIds, copiedAlert.associatedAlertIds) + assertEquals(alert.clusters, copiedAlert.clusters) + } + + @Test + fun `test alert copy with modified properties`() { + val alert = randomAlert() + val newAlertValues = randomAlert() + + val alertCopy = alert.copy( + id = newAlertValues.id, + triggerId = newAlertValues.triggerId, + triggerName = newAlertValues.triggerName, + actionExecutionResults = newAlertValues.actionExecutionResults, + clusters = newAlertValues.clusters + ) + + // Modified properties; compare to newAlertValues + assertEquals(newAlertValues.id, alertCopy.id) + assertEquals(newAlertValues.triggerId, alertCopy.triggerId) + assertEquals(newAlertValues.triggerName, alertCopy.triggerName) + assertEquals(newAlertValues.actionExecutionResults, alertCopy.actionExecutionResults) + assertEquals(newAlertValues.clusters, alertCopy.clusters) + + // Retained values; compare to original alert + assertEquals(alert.version, alertCopy.version) + assertEquals(alert.schemaVersion, alertCopy.schemaVersion) + assertEquals(alert.monitorId, alertCopy.monitorId) + assertEquals(alert.workflowId, alertCopy.workflowId) + assertEquals(alert.workflowName, alertCopy.workflowName) + assertEquals(alert.monitorName, alertCopy.monitorName) + assertEquals(alert.monitorVersion, alertCopy.monitorVersion) + assertEquals(alert.monitorUser, alertCopy.monitorUser) + assertEquals(alert.findingIds, alertCopy.findingIds) + assertEquals(alert.relatedDocIds, alertCopy.relatedDocIds) + assertEquals(alert.state, alertCopy.state) + assertEquals(alert.startTime, alertCopy.startTime) + assertEquals(alert.endTime, alertCopy.endTime) + assertEquals(alert.lastNotificationTime, alertCopy.lastNotificationTime) + assertEquals(alert.acknowledgedTime, alertCopy.acknowledgedTime) + assertEquals(alert.errorMessage, alertCopy.errorMessage) + assertEquals(alert.errorHistory, alertCopy.errorHistory) + assertEquals(alert.severity, alertCopy.severity) + assertEquals(alert.aggregationResultBucket, alertCopy.aggregationResultBucket) + assertEquals(alert.executionId, alertCopy.executionId) + assertEquals(alert.associatedAlertIds, alertCopy.associatedAlertIds) + } + + @Test + fun `test alert equals with duplicate alerts`() { + val alert = randomAlert() + val alertCopy = alert.copy() + + val alertsMatch = alert.equals(alertCopy) + + assertTrue(alertsMatch) + } + + @Test + fun `test alert equals with different alerts`() { + val alert = randomAlert() + val newAlertValues = randomAlert() + val alertCopy = alert.copy( + id = newAlertValues.id, + triggerId = newAlertValues.triggerId, + triggerName = newAlertValues.triggerName, + actionExecutionResults = newAlertValues.actionExecutionResults, + clusters = newAlertValues.clusters + ) + + val alertsMatch = alert.equals(alertCopy) + + assertFalse(alertsMatch) + } + + @Test + fun `test alert equals with null alert`() { + val alert = randomAlert() + + val alertsMatch = alert.equals(null) + + assertFalse(alertsMatch) + } + + @Test + fun `test alert equals with alertContext`() { + val alert = randomAlert() + val alertContext = randomAlertContext(alert = alert) + + val alertsMatch = alert.equals(alertContext) + + assertFalse(alertsMatch) + } } From 08d6fd7d4ba037955cfb81a4ec13dee768b87c84 Mon Sep 17 00:00:00 2001 From: AWSHurneyt Date: Mon, 11 Mar 2024 13:26:20 -0700 Subject: [PATCH 6/6] Made AlertContext a separate class from Alert instead of inheriting/extending it. Signed-off-by: AWSHurneyt --- .../commons/alerting/model/Alert.kt | 101 +------------- .../commons/alerting/model/AlertContext.kt | 32 +---- .../opensearch/commons/alerting/AlertTests.kt | 126 ------------------ .../alerting/model/AlertContextTests.kt | 16 +-- 4 files changed, 12 insertions(+), 263 deletions(-) diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt index 4e6f4762..e435c866 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt @@ -17,7 +17,7 @@ import org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken import java.io.IOException import java.time.Instant -open class Alert( +data class Alert( val id: String = NO_ID, val version: Long = NO_VERSION, val schemaVersion: Int = NO_SCHEMA_VERSION, @@ -591,7 +591,7 @@ open class Alert( return builder } - open fun asTemplateArg(): Map { + fun asTemplateArg(): Map { return mapOf( ACKNOWLEDGED_TIME_FIELD to acknowledgedTime?.toEpochMilli(), ALERT_ID_FIELD to id, @@ -614,101 +614,4 @@ open class Alert( CLUSTERS_FIELD to clusters?.joinToString(",") ) } - - /** - * Creates a copy of an [Alert] with optionally modified properties - */ - fun copy( - id: String = this.id, - version: Long = this.version, - schemaVersion: Int = this.schemaVersion, - monitorId: String = this.monitorId, - workflowId: String = this.workflowId, - workflowName: String = this.workflowName, - monitorName: String = this.monitorName, - monitorVersion: Long = this.monitorVersion, - monitorUser: User? = this.monitorUser, - triggerId: String = this.triggerId, - triggerName: String = this.triggerName, - findingIds: List = this.findingIds, - relatedDocIds: List = this.relatedDocIds, - state: State = this.state, - startTime: Instant = this.startTime, - endTime: Instant? = this.endTime, - lastNotificationTime: Instant? = this.lastNotificationTime, - acknowledgedTime: Instant? = this.acknowledgedTime, - errorMessage: String? = this.errorMessage, - errorHistory: List = this.errorHistory, - severity: String = this.severity, - actionExecutionResults: List = this.actionExecutionResults, - aggregationResultBucket: AggregationResultBucket? = this.aggregationResultBucket, - executionId: String? = this.executionId, - associatedAlertIds: List = this.associatedAlertIds, - clusters: List? = this.clusters - ): Alert { - return Alert( - id = id, - version = version, - schemaVersion = schemaVersion, - monitorId = monitorId, - workflowId = workflowId, - workflowName = workflowName, - monitorName = monitorName, - monitorVersion = monitorVersion, - monitorUser = monitorUser, - triggerId = triggerId, - triggerName = triggerName, - findingIds = findingIds, - relatedDocIds = relatedDocIds, - state = state, - startTime = startTime, - endTime = endTime, - lastNotificationTime = lastNotificationTime, - acknowledgedTime = acknowledgedTime, - errorMessage = errorMessage, - errorHistory = errorHistory, - severity = severity, - actionExecutionResults = actionExecutionResults, - aggregationResultBucket = aggregationResultBucket, - executionId = executionId, - associatedAlertIds = associatedAlertIds, - clusters = clusters - ) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Alert - - if (id != other.id) return false - if (version != other.version) return false - if (schemaVersion != other.schemaVersion) return false - if (monitorId != other.monitorId) return false - if (workflowId != other.workflowId) return false - if (workflowName != other.workflowName) return false - if (monitorName != other.monitorName) return false - if (monitorVersion != other.monitorVersion) return false - if (monitorUser != other.monitorUser) return false - if (triggerId != other.triggerId) return false - if (triggerName != other.triggerName) return false - if (findingIds != other.findingIds) return false - if (relatedDocIds != other.relatedDocIds) return false - if (state != other.state) return false - if (startTime != other.startTime) return false - if (endTime != other.endTime) return false - if (lastNotificationTime != other.lastNotificationTime) return false - if (acknowledgedTime != other.acknowledgedTime) return false - if (errorMessage != other.errorMessage) return false - if (errorHistory != other.errorHistory) return false - if (severity != other.severity) return false - if (actionExecutionResults != other.actionExecutionResults) return false - if (aggregationResultBucket != other.aggregationResultBucket) return false - if (executionId != other.executionId) return false - if (associatedAlertIds != other.associatedAlertIds) return false - if (clusters != other.clusters) return false - - return true - } } diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt index a9431cfc..67be4b8b 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/AlertContext.kt @@ -13,36 +13,8 @@ data class AlertContext( val alert: Alert, val associatedQueries: List? = null, val sampleDocs: List>? = null -) : Alert( - id = alert.id, - version = alert.version, - schemaVersion = alert.schemaVersion, - monitorId = alert.monitorId, - monitorName = alert.monitorName, - monitorVersion = alert.monitorVersion, - monitorUser = alert.monitorUser, - triggerId = alert.triggerId, - triggerName = alert.triggerName, - state = alert.state, - startTime = alert.startTime, - endTime = alert.endTime, - lastNotificationTime = alert.lastNotificationTime, - acknowledgedTime = alert.acknowledgedTime, - errorMessage = alert.errorMessage, - errorHistory = alert.errorHistory, - severity = alert.severity, - actionExecutionResults = alert.actionExecutionResults, - aggregationResultBucket = alert.aggregationResultBucket, - findingIds = alert.findingIds, - relatedDocIds = alert.relatedDocIds, - executionId = alert.executionId, - workflowId = alert.workflowId, - workflowName = alert.workflowName, - associatedAlertIds = alert.associatedAlertIds, - clusters = alert.clusters ) { - - override fun asTemplateArg(): Map { + fun asTemplateArg(): Map { val queriesContext = associatedQueries?.map { mapOf( DocLevelQuery.QUERY_ID_FIELD to it.id, @@ -58,7 +30,7 @@ data class AlertContext( ) // Get the alert template args - val templateArgs = super.asTemplateArg().toMutableMap() + val templateArgs = alert.asTemplateArg().toMutableMap() // Add the non-null custom context fields to the alert templateArgs. customContextFields.forEach { (key, value) -> diff --git a/src/test/kotlin/org/opensearch/commons/alerting/AlertTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/AlertTests.kt index 1d350f2f..4a5f2346 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/AlertTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/AlertTests.kt @@ -2,8 +2,6 @@ package org.opensearch.commons.alerting import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.opensearch.commons.alerting.model.Alert import java.time.Instant @@ -86,128 +84,4 @@ class AlertTests { assertEquals(alert.id, "") assertEquals(workflow.id, alert.workflowId) } - - @Test - fun `test alert copy`() { - val alert = randomAlert() - - val copiedAlert = alert.copy() - - assertEquals(alert.id, copiedAlert.id) - assertEquals(alert.version, copiedAlert.version) - assertEquals(alert.schemaVersion, copiedAlert.schemaVersion) - assertEquals(alert.monitorId, copiedAlert.monitorId) - assertEquals(alert.workflowId, copiedAlert.workflowId) - assertEquals(alert.workflowName, copiedAlert.workflowName) - assertEquals(alert.monitorName, copiedAlert.monitorName) - assertEquals(alert.monitorVersion, copiedAlert.monitorVersion) - assertEquals(alert.monitorUser, copiedAlert.monitorUser) - assertEquals(alert.triggerId, copiedAlert.triggerId) - assertEquals(alert.triggerName, copiedAlert.triggerName) - assertEquals(alert.findingIds, copiedAlert.findingIds) - assertEquals(alert.relatedDocIds, copiedAlert.relatedDocIds) - assertEquals(alert.state, copiedAlert.state) - assertEquals(alert.startTime, copiedAlert.startTime) - assertEquals(alert.endTime, copiedAlert.endTime) - assertEquals(alert.lastNotificationTime, copiedAlert.lastNotificationTime) - assertEquals(alert.acknowledgedTime, copiedAlert.acknowledgedTime) - assertEquals(alert.errorMessage, copiedAlert.errorMessage) - assertEquals(alert.errorHistory, copiedAlert.errorHistory) - assertEquals(alert.severity, copiedAlert.severity) - assertEquals(alert.actionExecutionResults, copiedAlert.actionExecutionResults) - assertEquals(alert.aggregationResultBucket, copiedAlert.aggregationResultBucket) - assertEquals(alert.executionId, copiedAlert.executionId) - assertEquals(alert.associatedAlertIds, copiedAlert.associatedAlertIds) - assertEquals(alert.clusters, copiedAlert.clusters) - } - - @Test - fun `test alert copy with modified properties`() { - val alert = randomAlert() - val newAlertValues = randomAlert() - - val alertCopy = alert.copy( - id = newAlertValues.id, - triggerId = newAlertValues.triggerId, - triggerName = newAlertValues.triggerName, - actionExecutionResults = newAlertValues.actionExecutionResults, - clusters = newAlertValues.clusters - ) - - // Modified properties; compare to newAlertValues - assertEquals(newAlertValues.id, alertCopy.id) - assertEquals(newAlertValues.triggerId, alertCopy.triggerId) - assertEquals(newAlertValues.triggerName, alertCopy.triggerName) - assertEquals(newAlertValues.actionExecutionResults, alertCopy.actionExecutionResults) - assertEquals(newAlertValues.clusters, alertCopy.clusters) - - // Retained values; compare to original alert - assertEquals(alert.version, alertCopy.version) - assertEquals(alert.schemaVersion, alertCopy.schemaVersion) - assertEquals(alert.monitorId, alertCopy.monitorId) - assertEquals(alert.workflowId, alertCopy.workflowId) - assertEquals(alert.workflowName, alertCopy.workflowName) - assertEquals(alert.monitorName, alertCopy.monitorName) - assertEquals(alert.monitorVersion, alertCopy.monitorVersion) - assertEquals(alert.monitorUser, alertCopy.monitorUser) - assertEquals(alert.findingIds, alertCopy.findingIds) - assertEquals(alert.relatedDocIds, alertCopy.relatedDocIds) - assertEquals(alert.state, alertCopy.state) - assertEquals(alert.startTime, alertCopy.startTime) - assertEquals(alert.endTime, alertCopy.endTime) - assertEquals(alert.lastNotificationTime, alertCopy.lastNotificationTime) - assertEquals(alert.acknowledgedTime, alertCopy.acknowledgedTime) - assertEquals(alert.errorMessage, alertCopy.errorMessage) - assertEquals(alert.errorHistory, alertCopy.errorHistory) - assertEquals(alert.severity, alertCopy.severity) - assertEquals(alert.aggregationResultBucket, alertCopy.aggregationResultBucket) - assertEquals(alert.executionId, alertCopy.executionId) - assertEquals(alert.associatedAlertIds, alertCopy.associatedAlertIds) - } - - @Test - fun `test alert equals with duplicate alerts`() { - val alert = randomAlert() - val alertCopy = alert.copy() - - val alertsMatch = alert.equals(alertCopy) - - assertTrue(alertsMatch) - } - - @Test - fun `test alert equals with different alerts`() { - val alert = randomAlert() - val newAlertValues = randomAlert() - val alertCopy = alert.copy( - id = newAlertValues.id, - triggerId = newAlertValues.triggerId, - triggerName = newAlertValues.triggerName, - actionExecutionResults = newAlertValues.actionExecutionResults, - clusters = newAlertValues.clusters - ) - - val alertsMatch = alert.equals(alertCopy) - - assertFalse(alertsMatch) - } - - @Test - fun `test alert equals with null alert`() { - val alert = randomAlert() - - val alertsMatch = alert.equals(null) - - assertFalse(alertsMatch) - } - - @Test - fun `test alert equals with alertContext`() { - val alert = randomAlert() - val alertContext = randomAlertContext(alert = alert) - - val alertsMatch = alert.equals(alertContext) - - assertFalse(alertsMatch) - } } diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/AlertContextTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/AlertContextTests.kt index 438948ca..91430aee 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/model/AlertContextTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/AlertContextTests.kt @@ -22,16 +22,16 @@ class AlertContextTests { fun `test AlertContext asTemplateArg`() { val templateArgs = alertContext.asTemplateArg() - assertEquals(templateArgs[Alert.ALERT_ID_FIELD], alertContext.id, "Template args id does not match") - assertEquals(templateArgs[Alert.ALERT_VERSION_FIELD], alertContext.version, "Template args version does not match") - assertEquals(templateArgs[Alert.STATE_FIELD], alertContext.state.toString(), "Template args state does not match") - assertEquals(templateArgs[Alert.ERROR_MESSAGE_FIELD], alertContext.errorMessage, "Template args error message does not match") + assertEquals(templateArgs[Alert.ALERT_ID_FIELD], alertContext.alert.id, "Template args id does not match") + assertEquals(templateArgs[Alert.ALERT_VERSION_FIELD], alertContext.alert.version, "Template args version does not match") + assertEquals(templateArgs[Alert.STATE_FIELD], alertContext.alert.state.toString(), "Template args state does not match") + assertEquals(templateArgs[Alert.ERROR_MESSAGE_FIELD], alertContext.alert.errorMessage, "Template args error message does not match") assertEquals(templateArgs[Alert.ACKNOWLEDGED_TIME_FIELD], null, "Template args acknowledged time does not match") - assertEquals(templateArgs[Alert.END_TIME_FIELD], alertContext.endTime?.toEpochMilli(), "Template args end time does not") - assertEquals(templateArgs[Alert.START_TIME_FIELD], alertContext.startTime.toEpochMilli(), "Template args start time does not") + assertEquals(templateArgs[Alert.END_TIME_FIELD], alertContext.alert.endTime?.toEpochMilli(), "Template args end time does not") + assertEquals(templateArgs[Alert.START_TIME_FIELD], alertContext.alert.startTime.toEpochMilli(), "Template args start time does not") assertEquals(templateArgs[Alert.LAST_NOTIFICATION_TIME_FIELD], null, "Template args last notification time does not match") - assertEquals(templateArgs[Alert.SEVERITY_FIELD], alertContext.severity, "Template args severity does not match") - assertEquals(templateArgs[Alert.CLUSTERS_FIELD], alertContext.clusters?.joinToString(","), "Template args clusters does not match") + assertEquals(templateArgs[Alert.SEVERITY_FIELD], alertContext.alert.severity, "Template args severity does not match") + assertEquals(templateArgs[Alert.CLUSTERS_FIELD], alertContext.alert.clusters?.joinToString(","), "Template args clusters does not match") val formattedQueries = alertContext.associatedQueries?.map { mapOf( DocLevelQuery.QUERY_ID_FIELD to it.id,