@@ -1674,6 +1674,353 @@ class DocumentMonitorRunnerIT : AlertingRestTestCase() {
1674
1674
assertEquals(1 , output.objectMap(" trigger_results" ).values.size)
1675
1675
}
1676
1676
1677
+ fun `test execute monitor generates alerts and findings with NOT EQUALS query and EXISTS query` () {
1678
+ val testIndex = createTestIndex()
1679
+ val testTime = DateTimeFormatter .ISO_OFFSET_DATE_TIME .format(ZonedDateTime .now().truncatedTo(MILLIS ))
1680
+ val testDoc = """ {
1681
+ "message" : "This is an error from IAD region",
1682
+ "test_strict_date_time" : "$testTime ",
1683
+ "test_field" : "us-west-2"
1684
+ }"""
1685
+
1686
+ val query = " NOT test_field: \" us-east-1\" AND _exists_: test_field"
1687
+ val docQuery = DocLevelQuery (query = query, name = " 3" , fields = listOf ())
1688
+ val docLevelInput = DocLevelMonitorInput (" description" , listOf (testIndex), listOf (docQuery))
1689
+
1690
+ val trigger = randomDocumentLevelTrigger(condition = ALWAYS_RUN )
1691
+ val monitor = createMonitor(randomDocumentLevelMonitor(inputs = listOf (docLevelInput), triggers = listOf (trigger)))
1692
+ assertNotNull(monitor.id)
1693
+
1694
+ indexDoc(testIndex, " 1" , testDoc)
1695
+ indexDoc(testIndex, " 5" , testDoc)
1696
+
1697
+ val response = executeMonitor(monitor.id)
1698
+
1699
+ val output = entityAsMap(response)
1700
+
1701
+ assertEquals(monitor.name, output[" monitor_name" ])
1702
+ @Suppress(" UNCHECKED_CAST" )
1703
+ val searchResult = (output.objectMap(" input_results" )[" results" ] as List <Map <String , Any >>).first()
1704
+ @Suppress(" UNCHECKED_CAST" )
1705
+ val matchingDocsToQuery = searchResult[docQuery.id] as List <String >
1706
+ assertEquals(" Incorrect search result" , 2 , matchingDocsToQuery.size)
1707
+ assertTrue(" Incorrect search result" , matchingDocsToQuery.containsAll(listOf (" 1|$testIndex " , " 5|$testIndex " )))
1708
+
1709
+ val alerts = searchAlertsWithFilter(monitor)
1710
+ assertEquals(" Alert saved for test monitor" , 2 , alerts.size)
1711
+
1712
+ val findings = searchFindings(monitor)
1713
+ assertEquals(" Findings saved for test monitor" , 2 , findings.size)
1714
+ val findings0 = findings[0 ].relatedDocIds.contains(" 1" ) || findings[0 ].relatedDocIds.contains(" 5" )
1715
+ val findings1 = findings[1 ].relatedDocIds.contains(" 5" ) || findings[1 ].relatedDocIds.contains(" 1" )
1716
+ assertTrue(" Findings saved for test monitor" , findings0)
1717
+ assertTrue(" Findings saved for test monitor" , findings1)
1718
+ }
1719
+
1720
+ fun `test document-level monitor when index alias contain docs that do match a NOT EQUALS query and EXISTS query` () {
1721
+ val aliasName = " test-alias"
1722
+ createIndexAlias(
1723
+ aliasName,
1724
+ """
1725
+ "properties" : {
1726
+ "test_strict_date_time" : { "type" : "date", "format" : "strict_date_time" },
1727
+ "test_field" : { "type" : "keyword" },
1728
+ "number" : { "type" : "keyword" }
1729
+ }
1730
+ """ .trimIndent()
1731
+ )
1732
+
1733
+ val docQuery = DocLevelQuery (query = " NOT test_field:\" us-east-1\" AND _exists_: test_field" , name = " 3" , fields = listOf ())
1734
+ val docLevelInput = DocLevelMonitorInput (" description" , listOf (" $aliasName " ), listOf (docQuery))
1735
+
1736
+ val action = randomAction(template = randomTemplateScript(" Hello {{ctx.monitor.name}}" ), destinationId = createDestination().id)
1737
+ val monitor = createMonitor(
1738
+ randomDocumentLevelMonitor(
1739
+ inputs = listOf (docLevelInput),
1740
+ triggers = listOf (randomDocumentLevelTrigger(condition = ALWAYS_RUN , actions = listOf (action)))
1741
+ )
1742
+ )
1743
+
1744
+ val testTime = DateTimeFormatter .ISO_OFFSET_DATE_TIME .format(ZonedDateTime .now().truncatedTo(MILLIS ))
1745
+ val testDoc = """ {
1746
+ "@timestamp": "$testTime ",
1747
+ "message" : "This is an error from IAD region",
1748
+ "test_strict_date_time" : "$testTime ",
1749
+ "test_field" : "us-west-2"
1750
+ }"""
1751
+ indexDoc(aliasName, " 1" , testDoc)
1752
+ var response = executeMonitor(monitor.id)
1753
+ var output = entityAsMap(response)
1754
+ var searchResult = (output.objectMap(" input_results" )[" results" ] as List <Map <String , Any >>).first()
1755
+ @Suppress(" UNCHECKED_CAST" )
1756
+ var matchingDocsToQuery = searchResult[docQuery.id] as List <String >
1757
+ assertEquals(" Incorrect search result" , 1 , matchingDocsToQuery.size)
1758
+
1759
+ rolloverDatastream(aliasName)
1760
+ indexDoc(aliasName, " 2" , testDoc)
1761
+ response = executeMonitor(monitor.id)
1762
+ output = entityAsMap(response)
1763
+ searchResult = (output.objectMap(" input_results" )[" results" ] as List <Map <String , Any >>).first()
1764
+ @Suppress(" UNCHECKED_CAST" )
1765
+ matchingDocsToQuery = searchResult[docQuery.id] as List <String >
1766
+ assertEquals(" Incorrect search result" , 1 , matchingDocsToQuery.size)
1767
+
1768
+ deleteIndexAlias(aliasName)
1769
+ }
1770
+
1771
+ fun `test execute monitor with wildcard index that generates alerts and findings for NOT EQUALS and EXISTS query operator` () {
1772
+ val testIndexPrefix = " test-index-${randomAlphaOfLength(10 ).lowercase(Locale .ROOT )} "
1773
+ val testQueryName = " wildcard-test-query"
1774
+ val testIndex = createTestIndex(" ${testIndexPrefix} 1" )
1775
+ val testIndex2 = createTestIndex(" ${testIndexPrefix} 2" )
1776
+
1777
+ val testTime = DateTimeFormatter .ISO_OFFSET_DATE_TIME .format(ZonedDateTime .now().truncatedTo(MILLIS ))
1778
+ val testDoc = """ {
1779
+ "message" : "This is an error from IAD region",
1780
+ "test_strict_date_time" : "$testTime ",
1781
+ "test_field" : "us-west-2"
1782
+ }"""
1783
+
1784
+ val query = " NOT test_field:\" us-west-1\" AND _exists_: test_field"
1785
+ val docQuery = DocLevelQuery (query = query, name = testQueryName, fields = listOf ())
1786
+ val docLevelInput = DocLevelMonitorInput (" description" , listOf (" $testIndexPrefix *" ), listOf (docQuery))
1787
+
1788
+ val trigger = randomDocumentLevelTrigger(condition = Script (" query[name=$testQueryName ]" ))
1789
+ val monitor = createMonitor(randomDocumentLevelMonitor(inputs = listOf (docLevelInput), triggers = listOf (trigger)))
1790
+ assertNotNull(monitor.id)
1791
+
1792
+ indexDoc(testIndex, " 1" , testDoc)
1793
+ indexDoc(testIndex2, " 5" , testDoc)
1794
+
1795
+ val response = executeMonitor(monitor.id)
1796
+
1797
+ val output = entityAsMap(response)
1798
+
1799
+ assertEquals(monitor.name, output[" monitor_name" ])
1800
+ @Suppress(" UNCHECKED_CAST" )
1801
+ val searchResult = (output.objectMap(" input_results" )[" results" ] as List <Map <String , Any >>).first()
1802
+ @Suppress(" UNCHECKED_CAST" )
1803
+ val matchingDocsToQuery = searchResult[docQuery.id] as List <String >
1804
+ assertEquals(" Incorrect search result" , 2 , matchingDocsToQuery.size)
1805
+ assertTrue(" Incorrect search result" , matchingDocsToQuery.containsAll(listOf (" 1|$testIndex " , " 5|$testIndex2 " )))
1806
+
1807
+ val alerts = searchAlertsWithFilter(monitor)
1808
+ assertEquals(" Alert saved for test monitor" , 2 , alerts.size)
1809
+
1810
+ val findings = searchFindings(monitor)
1811
+ assertEquals(" Findings saved for test monitor" , 2 , findings.size)
1812
+ val foundFindings = findings.filter { it.relatedDocIds.contains(" 1" ) || it.relatedDocIds.contains(" 5" ) }
1813
+ assertEquals(" Didn't find findings for docs 1 and 5" , 2 , foundFindings.size)
1814
+ }
1815
+
1816
+ fun `test execute monitor with indices having fields with same name different field mappings in multiple indices with NOT EQUALS` () {
1817
+ val testIndex = createTestIndex(
1818
+ " test1" ,
1819
+ """ "properties": {
1820
+ "source": {
1821
+ "properties": {
1822
+ "device": {
1823
+ "properties": {
1824
+ "hwd": {
1825
+ "properties": {
1826
+ "id": {
1827
+ "type":"text",
1828
+ "analyzer":"whitespace"
1829
+ }
1830
+ }
1831
+ }
1832
+ }
1833
+ }
1834
+ }
1835
+ },
1836
+ "test_field" : {
1837
+ "type":"text"
1838
+ }
1839
+ }
1840
+ """ .trimIndent()
1841
+ )
1842
+
1843
+ val testIndex2 = createTestIndex(
1844
+ " test2" ,
1845
+ """ "properties": {
1846
+ "test_field" : {
1847
+ "type":"keyword"
1848
+ }
1849
+ }
1850
+ """ .trimIndent()
1851
+ )
1852
+
1853
+ val testIndex4 = createTestIndex(
1854
+ " test4" ,
1855
+ """ "properties": {
1856
+ "source": {
1857
+ "properties": {
1858
+ "device": {
1859
+ "properties": {
1860
+ "hwd": {
1861
+ "properties": {
1862
+ "id": {
1863
+ "type":"text"
1864
+ }
1865
+ }
1866
+ }
1867
+ }
1868
+ }
1869
+ }
1870
+ },
1871
+ "test_field" : {
1872
+ "type":"text"
1873
+ }
1874
+ }
1875
+ """ .trimIndent()
1876
+ )
1877
+
1878
+ val testDoc1 = """ {
1879
+ "source" : {"device" : {"hwd" : {"id" : "123456"}} },
1880
+ "nested_field": { "test1": "some text" }
1881
+ }"""
1882
+ val testDoc2 = """ {
1883
+ "nested_field": { "test1": "some text" },
1884
+ "test_field": "123456"
1885
+ }"""
1886
+
1887
+ val docQuery1 = DocLevelQuery (
1888
+ query = " NOT test_field:\" 12345\" AND _exists_: test_field" ,
1889
+ name = " 4" ,
1890
+ fields = listOf ()
1891
+ )
1892
+ val docQuery2 = DocLevelQuery (
1893
+ query = " NOT source.device.hwd.id:\" 12345\" AND _exists_: source.device.hwd.id" ,
1894
+ name = " 5" ,
1895
+ fields = listOf ()
1896
+ )
1897
+
1898
+ val docLevelInput = DocLevelMonitorInput (" description" , listOf (" test*" ), listOf (docQuery1, docQuery2))
1899
+
1900
+ val trigger = randomDocumentLevelTrigger(condition = ALWAYS_RUN )
1901
+ val monitor = createMonitor(randomDocumentLevelMonitor(inputs = listOf (docLevelInput), triggers = listOf (trigger)))
1902
+ assertNotNull(monitor.id)
1903
+
1904
+ indexDoc(testIndex4, " 1" , testDoc1)
1905
+ indexDoc(testIndex2, " 1" , testDoc2)
1906
+ indexDoc(testIndex, " 1" , testDoc1)
1907
+ indexDoc(testIndex, " 2" , testDoc2)
1908
+
1909
+ executeMonitor(monitor.id)
1910
+
1911
+ val alerts = searchAlertsWithFilter(monitor)
1912
+ assertEquals(" Alert saved for test monitor" , 4 , alerts.size)
1913
+
1914
+ val findings = searchFindings(monitor)
1915
+ assertEquals(" Findings saved for test monitor" , 4 , findings.size)
1916
+
1917
+ val request = """ {
1918
+ "size": 0,
1919
+ "query": {
1920
+ "match_all": {}
1921
+ }
1922
+ }"""
1923
+ val httpResponse = adminClient().makeRequest(
1924
+ " GET" , " /${monitor.dataSources.queryIndex} /_search" ,
1925
+ StringEntity (request, ContentType .APPLICATION_JSON )
1926
+ )
1927
+ assertEquals(" Search failed" , RestStatus .OK , httpResponse.restStatus())
1928
+
1929
+ val searchResponse = SearchResponse .fromXContent(createParser(JsonXContent .jsonXContent, httpResponse.entity.content))
1930
+ searchResponse.hits.totalHits?.let { assertEquals(5L , it.value) }
1931
+ }
1932
+
1933
+ fun `test execute monitor with indices having fields with same name but different field mappings with NOT EQUALS` () {
1934
+ val testIndex = createTestIndex(
1935
+ " test1" ,
1936
+ """ "properties": {
1937
+ "source": {
1938
+ "properties": {
1939
+ "id": {
1940
+ "type":"text",
1941
+ "analyzer":"whitespace"
1942
+ }
1943
+ }
1944
+ },
1945
+ "test_field" : {
1946
+ "type":"text",
1947
+ "analyzer":"whitespace"
1948
+ }
1949
+ }
1950
+ """ .trimIndent()
1951
+ )
1952
+
1953
+ val testIndex2 = createTestIndex(
1954
+ " test2" ,
1955
+ """ "properties": {
1956
+ "source": {
1957
+ "properties": {
1958
+ "id": {
1959
+ "type":"text"
1960
+ }
1961
+ }
1962
+ },
1963
+ "test_field" : {
1964
+ "type":"text"
1965
+ }
1966
+ }
1967
+ """ .trimIndent()
1968
+ )
1969
+ val testDoc = """ {
1970
+ "source" : {"id" : "12345" },
1971
+ "nested_field": { "test1": "some text" },
1972
+ "test_field": "12345"
1973
+ }"""
1974
+
1975
+ val docQuery = DocLevelQuery (
1976
+ query = " (NOT test_field:\" 123456\" AND _exists_:test_field) AND source.id:\" 12345\" " ,
1977
+ name = " 5" ,
1978
+ fields = listOf ()
1979
+ )
1980
+ val docLevelInput = DocLevelMonitorInput (" description" , listOf (" test*" ), listOf (docQuery))
1981
+
1982
+ val trigger = randomDocumentLevelTrigger(condition = ALWAYS_RUN )
1983
+ val monitor = createMonitor(randomDocumentLevelMonitor(inputs = listOf (docLevelInput), triggers = listOf (trigger)))
1984
+ assertNotNull(monitor.id)
1985
+
1986
+ indexDoc(testIndex, " 1" , testDoc)
1987
+ indexDoc(testIndex2, " 1" , testDoc)
1988
+
1989
+ executeMonitor(monitor.id)
1990
+
1991
+ val alerts = searchAlertsWithFilter(monitor)
1992
+ assertEquals(" Alert saved for test monitor" , 2 , alerts.size)
1993
+
1994
+ val findings = searchFindings(monitor)
1995
+ assertEquals(" Findings saved for test monitor" , 2 , findings.size)
1996
+
1997
+ // as mappings of source.id & test_field are different so, both of them expands
1998
+ val expectedQueries = listOf (
1999
+ " (NOT test_field_test2_${monitor.id} :\" 123456\" AND _exists_:test_field_test2_${monitor.id} ) " +
2000
+ " AND source.id_test2_${monitor.id} :\" 12345\" " ,
2001
+ " (NOT test_field_test1_${monitor.id} :\" 123456\" AND _exists_:test_field_test1_${monitor.id} ) " +
2002
+ " AND source.id_test1_${monitor.id} :\" 12345\" "
2003
+ )
2004
+
2005
+ val request = """ {
2006
+ "size": 10,
2007
+ "query": {
2008
+ "match_all": {}
2009
+ }
2010
+ }"""
2011
+ var httpResponse = adminClient().makeRequest(
2012
+ " GET" , " /${monitor.dataSources.queryIndex} /_search" ,
2013
+ StringEntity (request, ContentType .APPLICATION_JSON )
2014
+ )
2015
+ assertEquals(" Search failed" , RestStatus .OK , httpResponse.restStatus())
2016
+ var searchResponse = SearchResponse .fromXContent(createParser(JsonXContent .jsonXContent, httpResponse.entity.content))
2017
+ searchResponse.hits.forEach { hit ->
2018
+ val query = ((hit.sourceAsMap[" query" ] as Map <String , Any >)[" query_string" ] as Map <String , Any >)[" query" ]
2019
+ assertTrue(expectedQueries.contains(query))
2020
+ }
2021
+ }
2022
+
2023
+ >>>>>>> ba84d04d ( Findings API Enhancements changes and integ tests fix (#1464 ))
1677
2024
@Suppress(" UNCHECKED_CAST" )
1678
2025
/* * helper that returns a field in a json map whose values are all json objects */
1679
2026
private fun Map <String , Any >.objectMap (key : String ): Map <String , Map <String , Any >> {
0 commit comments