@@ -54,7 +54,9 @@ import org.opensearch.core.xcontent.XContentBuilder
5454import org.opensearch.index.query.QueryBuilders
5555import org.opensearch.rest.RestStatus
5656import org.opensearch.script.Script
57+ import org.opensearch.search.aggregations.AggregationBuilders
5758import org.opensearch.search.builder.SearchSourceBuilder
59+ import org.opensearch.search.sort.SortOrder
5860import org.opensearch.test.OpenSearchTestCase
5961import org.opensearch.test.junit.annotations.TestLogging
6062import org.opensearch.test.rest.OpenSearchRestTestCase
@@ -1264,6 +1266,110 @@ class MonitorRestApiIT : AlertingRestTestCase() {
12641266 }
12651267 }
12661268
1269+ /* *
1270+ * This use case is needed by the frontend plugin for displaying alert counts on the Monitors list page.
1271+ * https://github.com/opensearch-project/alerting-dashboards-plugin/blob/main/server/services/MonitorService.js#L235
1272+ */
1273+ fun `test get acknowledged, active, error, and ignored alerts counts` () {
1274+ putAlertMappings()
1275+ val monitorAlertCounts = hashMapOf<String , HashMap <String , Int >>()
1276+ val numMonitors = randomIntBetween(1 , 10 )
1277+ repeat(numMonitors) {
1278+ val monitor = createRandomMonitor(refresh = true )
1279+
1280+ val numAcknowledgedAlerts = randomIntBetween(1 , 10 )
1281+ val numActiveAlerts = randomIntBetween(1 , 10 )
1282+ var numCompletedAlerts = randomIntBetween(1 , 10 )
1283+ val numErrorAlerts = randomIntBetween(1 , 10 )
1284+ val numIgnoredAlerts = randomIntBetween(1 , numCompletedAlerts)
1285+ numCompletedAlerts - = numIgnoredAlerts
1286+
1287+ val alertCounts = hashMapOf(
1288+ Alert .State .ACKNOWLEDGED .name to numAcknowledgedAlerts,
1289+ Alert .State .ACTIVE .name to numActiveAlerts,
1290+ Alert .State .COMPLETED .name to numCompletedAlerts,
1291+ Alert .State .ERROR .name to numErrorAlerts,
1292+ " IGNORED" to numIgnoredAlerts
1293+ )
1294+ monitorAlertCounts[monitor.id] = alertCounts
1295+
1296+ repeat(numAcknowledgedAlerts) {
1297+ createAlert(randomAlert(monitor).copy(acknowledgedTime = Instant .now(),state = Alert .State .ACKNOWLEDGED ))
1298+ }
1299+ repeat(numActiveAlerts) {
1300+ createAlert(randomAlert(monitor).copy(state = Alert .State .ACTIVE ))
1301+ }
1302+ repeat(numCompletedAlerts) {
1303+ createAlert(randomAlert(monitor).copy(acknowledgedTime = Instant .now(), state = Alert .State .COMPLETED ))
1304+ }
1305+ repeat(numErrorAlerts) {
1306+ createAlert(randomAlert(monitor).copy(state = Alert .State .ERROR ))
1307+ }
1308+ repeat(numIgnoredAlerts) {
1309+ createAlert(randomAlert(monitor).copy(acknowledgedTime = null , state = Alert .State .COMPLETED ))
1310+ }
1311+ }
1312+
1313+ val sourceBuilder = SearchSourceBuilder ()
1314+ .size(0 )
1315+ .query(QueryBuilders .termsQuery(" monitor_id" , monitorAlertCounts.keys))
1316+ .aggregation(
1317+ AggregationBuilders
1318+ .terms(" uniq_monitor_ids" ).field(" monitor_id" )
1319+ .subAggregation(AggregationBuilders .filter(" active" , QueryBuilders .termQuery(" state" , " ACTIVE" )))
1320+ .subAggregation(AggregationBuilders .filter(" acknowledged" , QueryBuilders .termQuery(" state" , " ACKNOWLEDGED" )))
1321+ .subAggregation(AggregationBuilders .filter(" errors" , QueryBuilders .termQuery(" state" , " ERROR" )))
1322+ .subAggregation(
1323+ AggregationBuilders .filter(
1324+ " ignored" ,
1325+ QueryBuilders .boolQuery()
1326+ .filter(QueryBuilders .termQuery(" state" , " COMPLETED" ))
1327+ .mustNot(QueryBuilders .existsQuery(" acknowledged_time" ))
1328+ )
1329+ )
1330+ .subAggregation(AggregationBuilders .max(" last_notification_time" ).field(" last_notification_time" ))
1331+ .subAggregation(
1332+ AggregationBuilders .topHits(" latest_alert" )
1333+ .size(1 )
1334+ .sort(" start_time" , SortOrder .DESC )
1335+ .fetchSource(arrayOf(" last_notification_time" , " trigger_name" ), null )
1336+ )
1337+ )
1338+
1339+ val searchResponse = client().makeRequest(
1340+ " GET" ,
1341+ " $ALERTING_BASE_URI /_search" ,
1342+ hashMapOf(" index" to AlertIndices .ALL_ALERT_INDEX_PATTERN ),
1343+ NStringEntity (sourceBuilder.toString(), ContentType .APPLICATION_JSON )
1344+ )
1345+ val xcp = createParser(XContentType .JSON .xContent(), searchResponse.entity.content).map()
1346+ val aggregations = (xcp[" aggregations" ]!! as Map <String , Map <String , Any >>)
1347+ val uniqMonitorIds = aggregations[" uniq_monitor_ids" ]!!
1348+ val buckets = uniqMonitorIds[" buckets" ]!! as ArrayList <Map <String , Any >>
1349+
1350+ assertEquals(" Incorrect number of monitors returned" , monitorAlertCounts.keys.size, buckets.size)
1351+ buckets.forEach { bucket ->
1352+ val id = bucket[" key" ]!!
1353+ val monitorCounts = monitorAlertCounts[id]!!
1354+
1355+ val acknowledged = (bucket[" acknowledged" ]!! as Map <String , Int >)[" doc_count" ]!!
1356+ assertEquals(" Incorrect ${Alert .State .ACKNOWLEDGED } count returned for monitor $id " ,
1357+ monitorCounts[Alert .State .ACKNOWLEDGED .name], acknowledged)
1358+
1359+ val active = (bucket[" active" ]!! as Map <String , Int >)[" doc_count" ]!!
1360+ assertEquals(" Incorrect ${Alert .State .ACTIVE } count returned for monitor $id " ,
1361+ monitorCounts[Alert .State .ACTIVE .name], active)
1362+
1363+ val errors = (bucket[" errors" ]!! as Map <String , Int >)[" doc_count" ]!!
1364+ assertEquals(" Incorrect ${Alert .State .ERROR } count returned for monitor $id " ,
1365+ monitorCounts[Alert .State .ERROR .name], errors)
1366+
1367+ val ignored = (bucket[" ignored" ]!! as Map <String , Int >)[" doc_count" ]!!
1368+ assertEquals(" Incorrect IGNORED count returned for monitor $id " ,
1369+ monitorCounts[" IGNORED" ], ignored)
1370+ }
1371+ }
1372+
12671373 private fun validateAlertingStatsNodeResponse (nodesResponse : Map <String , Int >) {
12681374 assertEquals(" Incorrect number of nodes" , numberOfNodes, nodesResponse[" total" ])
12691375 assertEquals(" Failed nodes found during monitor stats call" , 0 , nodesResponse[" failed" ])
0 commit comments