diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_availability.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_availability.test.ts index 94bbfaa5a540d..8f9e6889c1d7c 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_availability.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_availability.test.ts @@ -203,28 +203,32 @@ describe('monitor availability', () => { }, }, }, - ], - "minimum_should_match": 1, - "should": Array [ Object { "bool": Object { "minimum_should_match": 1, "should": Array [ Object { - "match_phrase": Object { - "monitor.id": "apm-dev", + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "monitor.id": "apm-dev", + }, + }, + ], }, }, - ], - }, - }, - Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ Object { - "match_phrase": Object { - "monitor.id": "auto-http-0X8D6082B94BBE3B8A", + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "monitor.id": "auto-http-0X8D6082B94BBE3B8A", + }, + }, + ], }, }, ], @@ -797,6 +801,133 @@ describe('monitor availability', () => { ] `); }); + + it('does not overwrite filters', async () => { + const [callES, esMock] = setupMockEsCompositeQuery< + AvailabilityKey, + GetMonitorAvailabilityResult, + AvailabilityDoc + >( + [ + { + bucketCriteria: [], + }, + ], + genBucketItem + ); + await getMonitorAvailability({ + callES, + dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + range: 3, + rangeUnit: 's', + threshold: '99', + filters: JSON.stringify({ bool: { filter: [{ term: { 'monitor.id': 'foo' } }] } }), + }); + const [, params] = esMock.callAsCurrentUser.mock.calls[0]; + expect(params).toMatchInlineSnapshot(` + Object { + "body": Object { + "aggs": Object { + "monitors": Object { + "aggs": Object { + "down_sum": Object { + "sum": Object { + "field": "summary.down", + "missing": 0, + }, + }, + "fields": Object { + "top_hits": Object { + "size": 1, + "sort": Array [ + Object { + "@timestamp": Object { + "order": "desc", + }, + }, + ], + }, + }, + "filtered": Object { + "bucket_selector": Object { + "buckets_path": Object { + "threshold": "ratio.value", + }, + "script": "params.threshold < 0.99", + }, + }, + "ratio": Object { + "bucket_script": Object { + "buckets_path": Object { + "downTotal": "down_sum", + "upTotal": "up_sum", + }, + "script": " + if (params.upTotal + params.downTotal > 0) { + return params.upTotal / (params.upTotal + params.downTotal); + } return null;", + }, + }, + "up_sum": Object { + "sum": Object { + "field": "summary.up", + "missing": 0, + }, + }, + }, + "composite": Object { + "size": 2000, + "sources": Array [ + Object { + "monitorId": Object { + "terms": Object { + "field": "monitor.id", + }, + }, + }, + Object { + "location": Object { + "terms": Object { + "field": "observer.geo.name", + "missing_bucket": true, + }, + }, + }, + ], + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "@timestamp": Object { + "gte": "now-3s", + "lte": "now", + }, + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.id": "foo", + }, + }, + ], + }, + }, + ], + }, + }, + "size": 0, + }, + "index": "heartbeat-7*", + } + `); + }); }); describe('formatBuckets', () => { diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_status.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_status.test.ts index 50fd6e42752df..362b1ed9d876d 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_status.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_status.test.ts @@ -148,28 +148,32 @@ describe('getMonitorStatus', () => { }, }, }, - ], - "minimum_should_match": 1, - "should": Array [ Object { "bool": Object { "minimum_should_match": 1, "should": Array [ Object { - "match_phrase": Object { - "monitor.id": "apm-dev", + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "monitor.id": "apm-dev", + }, + }, + ], }, }, - ], - }, - }, - Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ Object { - "match_phrase": Object { - "monitor.id": "auto-http-0X8D6082B94BBE3B8A", + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "monitor.id": "auto-http-0X8D6082B94BBE3B8A", + }, + }, + ], }, }, ], @@ -286,6 +290,296 @@ describe('getMonitorStatus', () => { `); }); + it('properly assigns filters for complex kuery filters', async () => { + const [callES, esMock] = setupMockEsCompositeQuery( + [{ bucketCriteria: [] }], + genBucketItem + ); + const clientParameters = { + timerange: { + from: 'now-15m', + to: 'now', + }, + numTimes: 5, + locations: [], + filters: { + bool: { + filter: [ + { + bool: { + should: [ + { + match_phrase: { + tags: 'org:google', + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + bool: { + should: [ + { + match_phrase: { + 'monitor.type': 'http', + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + match_phrase: { + 'monitor.type': 'tcp', + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }, + }; + await getMonitorStatus({ + callES, + dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + ...clientParameters, + }); + expect(esMock.callAsCurrentUser).toHaveBeenCalledTimes(1); + const [, params] = esMock.callAsCurrentUser.mock.calls[0]; + expect(params).toMatchInlineSnapshot(` + Object { + "body": Object { + "aggs": Object { + "monitors": Object { + "aggs": Object { + "fields": Object { + "top_hits": Object { + "size": 1, + }, + }, + }, + "composite": Object { + "size": 2000, + "sources": Array [ + Object { + "monitorId": Object { + "terms": Object { + "field": "monitor.id", + }, + }, + }, + Object { + "status": Object { + "terms": Object { + "field": "monitor.status", + }, + }, + }, + Object { + "location": Object { + "terms": Object { + "field": "observer.geo.name", + "missing_bucket": true, + }, + }, + }, + ], + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.status": "down", + }, + }, + Object { + "range": Object { + "@timestamp": Object { + "gte": "now-15m", + "lte": "now", + }, + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "tags": "org:google", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "monitor.type": "http", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "monitor.type": "tcp", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + "size": 0, + }, + "index": "heartbeat-7*", + } + `); + }); + + it('properly assigns filters for complex kuery filters object', async () => { + const [callES, esMock] = setupMockEsCompositeQuery( + [{ bucketCriteria: [] }], + genBucketItem + ); + const clientParameters = { + timerange: { + from: 'now-15m', + to: 'now', + }, + numTimes: 5, + locations: [], + filters: { + bool: { + filter: { + exists: { + field: 'monitor.status', + }, + }, + }, + }, + }; + await getMonitorStatus({ + callES, + dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + ...clientParameters, + }); + expect(esMock.callAsCurrentUser).toHaveBeenCalledTimes(1); + const [, params] = esMock.callAsCurrentUser.mock.calls[0]; + expect(params).toMatchInlineSnapshot(` + Object { + "body": Object { + "aggs": Object { + "monitors": Object { + "aggs": Object { + "fields": Object { + "top_hits": Object { + "size": 1, + }, + }, + }, + "composite": Object { + "size": 2000, + "sources": Array [ + Object { + "monitorId": Object { + "terms": Object { + "field": "monitor.id", + }, + }, + }, + Object { + "status": Object { + "terms": Object { + "field": "monitor.status", + }, + }, + }, + Object { + "location": Object { + "terms": Object { + "field": "observer.geo.name", + "missing_bucket": true, + }, + }, + }, + ], + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.status": "down", + }, + }, + Object { + "range": Object { + "@timestamp": Object { + "gte": "now-15m", + "lte": "now", + }, + }, + }, + Object { + "bool": Object { + "filter": Object { + "exists": Object { + "field": "monitor.status", + }, + }, + }, + }, + ], + }, + }, + "size": 0, + }, + "index": "heartbeat-7*", + } + `); + }); + it('fetches single page of results', async () => { const [callES, esMock] = setupMockEsCompositeQuery( [ diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts index 0801fc5769363..f78048100b998 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts @@ -47,6 +47,11 @@ export const getMonitorAvailability: UMElasticsearchQueryFn< const gte = `now-${range}${rangeUnit}`; + let parsedFilters: any; + if (filters) { + parsedFilters = JSON.parse(filters); + } + do { const esParams: any = { index: dynamicSettings.heartbeatIndices, @@ -62,6 +67,8 @@ export const getMonitorAvailability: UMElasticsearchQueryFn< }, }, }, + // append user filters, if defined + ...(parsedFilters?.bool ? [parsedFilters] : []), ], }, }, @@ -139,11 +146,6 @@ export const getMonitorAvailability: UMElasticsearchQueryFn< }, }; - if (filters) { - const parsedFilter = JSON.parse(filters); - esParams.body.query.bool = { ...esParams.body.query.bool, ...parsedFilter.bool }; - } - if (afterKey) { esParams.body.aggs.monitors.composite.after = afterKey; } diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts index 0788880994200..7cb35c6a5ea23 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts @@ -71,6 +71,8 @@ export const getMonitorStatus: UMElasticsearchQueryFn< }, }, }, + // append user filters, if defined + ...(filters?.bool ? [filters] : []), ], }, }, @@ -116,10 +118,6 @@ export const getMonitorStatus: UMElasticsearchQueryFn< }, }; - if (filters?.bool) { - esParams.body.query.bool = Object.assign({}, esParams.body.query.bool, filters.bool); - } - /** * Perform a logical `and` against the selected location filters. */