diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor/details.ts b/x-pack/plugins/uptime/common/runtime_types/monitor/details.ts index c622d4f19bade..89eea46edb112 100644 --- a/x-pack/plugins/uptime/common/runtime_types/monitor/details.ts +++ b/x-pack/plugins/uptime/common/runtime_types/monitor/details.ts @@ -7,17 +7,24 @@ import * as t from 'io-ts'; // IO type for validation -export const MonitorErrorType = t.partial({ - code: t.number, - message: t.string, - type: t.string, -}); +export const PingErrorType = t.intersection([ + t.partial({ + code: t.string, + id: t.string, + stack_trace: t.string, + type: t.string, + }), + t.type({ + // this is _always_ on the error field + message: t.string, + }), +]); // Typescript type for type checking -export type MonitorError = t.TypeOf; +export type PingError = t.TypeOf; export const MonitorDetailsType = t.intersection([ t.type({ monitorId: t.string }), - t.partial({ error: MonitorErrorType, timestamp: t.string, alerts: t.unknown }), + t.partial({ error: PingErrorType, timestamp: t.string, alerts: t.unknown }), ]); export type MonitorDetails = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index 315b8f543b800..9e5cd7641b65d 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -6,6 +6,7 @@ import * as t from 'io-ts'; import { DateRangeType } from '../common'; +import { PingErrorType } from '../monitor'; export const HttpResponseBodyType = t.partial({ bytes: t.number, @@ -116,18 +117,7 @@ export const PingType = t.intersection([ ecs: t.partial({ version: t.string, }), - error: t.intersection([ - t.partial({ - code: t.string, - id: t.string, - stack_trace: t.string, - type: t.string, - }), - t.type({ - // this is _always_ on the error field - message: t.string, - }), - ]), + error: PingErrorType, http: t.partial({ request: t.partial({ body: t.partial({ diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap index f6cc130b39fc1..89433f8bc57c4 100644 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap @@ -1823,7 +1823,9 @@ exports[`EmptyState component renders error message when an error occurs 1`] = `
-

+

There was an error fetching your data.

diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx index 165b123d8884d..f2d4de9f8be6e 100644 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx +++ b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx @@ -47,7 +47,9 @@ export const EmptyStateError = ({ errors }: EmptyStateErrorProps) => { {!unauthorized && errors.map((error: IHttpFetchError) => ( -

{error.body.message || error.message}

+

+ {error.body.message || error.message} +

))}
} diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx index e0b4c0dbd78f7..8dd5ad48bc9d4 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx @@ -9,11 +9,11 @@ import React from 'react'; import moment from 'moment'; import { BrowserRouter as Router } from 'react-router-dom'; import { MostRecentError } from '../most_recent_error'; -import { MonitorDetails, MonitorError } from '../../../../../../common/runtime_types'; +import { MonitorDetails, PingError } from '../../../../../../common/runtime_types'; describe('MostRecentError component', () => { let monitorDetails: MonitorDetails; - let monitorError: MonitorError; + let monitorError: PingError; beforeAll(() => { moment.prototype.fromNow = jest.fn(() => '5 days ago'); diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx index 62f2f811aad98..e7d9885680340 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx @@ -10,13 +10,13 @@ import { i18n } from '@kbn/i18n'; import { MonitorPageLink } from '../../../common/monitor_page_link'; import { useGetUrlParams } from '../../../../hooks'; import { stringifyUrlParams } from '../../../../lib/helper/stringify_url_params'; -import { MonitorError } from '../../../../../common/runtime_types'; +import { PingError } from '../../../../../common/runtime_types'; interface MostRecentErrorProps { /** * error returned from API for monitor details */ - error: MonitorError | undefined; + error: PingError | undefined; /** * monitorId to be used for link to detail page diff --git a/x-pack/plugins/uptime/public/state/actions/monitor.ts b/x-pack/plugins/uptime/public/state/actions/monitor.ts index a48b68db8126d..ff3f3cca33d09 100644 --- a/x-pack/plugins/uptime/public/state/actions/monitor.ts +++ b/x-pack/plugins/uptime/public/state/actions/monitor.ts @@ -6,7 +6,7 @@ import { createAction } from 'redux-actions'; import { MonitorDetailsActionPayload } from './types'; -import { MonitorError } from '../../../common/runtime_types'; +import { PingError } from '../../../common/runtime_types'; import { MonitorLocations } from '../../../common/runtime_types'; import { QueryParams } from './types'; import { createAsyncAction } from './utils'; @@ -17,7 +17,7 @@ export interface MonitorLocationsPayload extends QueryParams { export interface MonitorDetailsState { monitorId: string; - error: MonitorError; + error: PingError; } export const getMonitorDetailsAction = createAsyncAction< diff --git a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts index cd98ba1600d34..92965515f0876 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts @@ -10,18 +10,16 @@ import { SavedObjectsClientContract, ISavedObjectsRepository, IScopedClusterClient, - ElasticsearchClient, } from 'src/core/server'; import { UMKibanaRoute } from '../../../rest_api'; import { PluginSetupContract } from '../../../../../features/server'; -import { DynamicSettings } from '../../../../common/runtime_types'; import { MlPluginSetup as MlSetup } from '../../../../../ml/server'; +import { UptimeESClient } from '../../lib'; export type UMElasticsearchQueryFn = ( params: { - callES: ElasticsearchClient; + uptimeEsClient: UptimeESClient; esClient?: IScopedClusterClient; - dynamicSettings: DynamicSettings; } & P ) => Promise; diff --git a/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts b/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts index 2126b484b1cfd..3f6c3da2d6af0 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts @@ -14,6 +14,7 @@ import { import { CollectorFetchContext, UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { PageViewParams, UptimeTelemetry, Usage } from './types'; import { savedObjectsAdapter } from '../../saved_objects'; +import { UptimeESClient } from '../../lib'; interface UptimeTelemetryCollector { [key: number]: UptimeTelemetry; @@ -131,7 +132,7 @@ export class KibanaTelemetryAdapter { } public static async countNoOfUniqueMonitorAndLocations( - callCluster: ILegacyScopedClusterClient['callAsCurrentUser'] | ElasticsearchClient, + callCluster: ILegacyScopedClusterClient['callAsCurrentUser'] | UptimeESClient, savedObjectsClient: ISavedObjectsRepository | SavedObjectsClientContract ) { const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts index 4f795e2aaf29e..4f9fefa4188e5 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts @@ -96,13 +96,6 @@ describe('status check alert', () => { expect(mockGetter.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": [MockFunction], - "dynamicSettings": Object { - "certAgeThreshold": 730, - "certExpirationThreshold": 30, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - }, "filters": undefined, "locations": Array [], "numTimes": 5, @@ -110,6 +103,12 @@ describe('status check alert', () => { "from": "now-15m", "to": "now", }, + "uptimeEsClient": Object { + "baseESClient": [MockFunction], + "count": [Function], + "getSavedObjectsClient": [Function], + "search": [Function], + }, }, ] `); @@ -152,13 +151,6 @@ describe('status check alert', () => { expect(mockGetter.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": [MockFunction], - "dynamicSettings": Object { - "certAgeThreshold": 730, - "certExpirationThreshold": 30, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - }, "filters": undefined, "locations": Array [], "numTimes": 5, @@ -166,6 +158,12 @@ describe('status check alert', () => { "from": "now-15m", "to": "now", }, + "uptimeEsClient": Object { + "baseESClient": [MockFunction], + "count": [Function], + "getSavedObjectsClient": [Function], + "search": [Function], + }, }, ] `); @@ -333,13 +331,6 @@ describe('status check alert', () => { expect(mockGetter.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": [MockFunction], - "dynamicSettings": Object { - "certAgeThreshold": 730, - "certExpirationThreshold": 30, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - }, "filters": Object { "bool": Object { "filter": Array [ @@ -506,6 +497,12 @@ describe('status check alert', () => { "from": "now-15m", "to": "now", }, + "uptimeEsClient": Object { + "baseESClient": [MockFunction], + "count": [Function], + "getSavedObjectsClient": [Function], + "search": [Function], + }, }, ] `); @@ -571,13 +568,6 @@ describe('status check alert', () => { expect(mockGetter.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": [MockFunction], - "dynamicSettings": Object { - "certAgeThreshold": 730, - "certExpirationThreshold": 30, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - }, "filters": Object { "bool": Object { "filter": Array [ @@ -614,6 +604,12 @@ describe('status check alert', () => { "from": "now-30h", "to": "now", }, + "uptimeEsClient": Object { + "baseESClient": [MockFunction], + "count": [Function], + "getSavedObjectsClient": [Function], + "search": [Function], + }, }, ] `); @@ -758,17 +754,16 @@ describe('status check alert', () => { expect(mockAvailability.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": [MockFunction], - "dynamicSettings": Object { - "certAgeThreshold": 730, - "certExpirationThreshold": 30, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - }, "filters": "{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"url.port\\":12349}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"url.port\\":5601}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"url.port\\":443}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"observer.geo.name\\":\\"harrisburg\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"monitor.type\\":\\"http\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"tags\\":\\"unsecured\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"tags\\":\\"containers\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"tags\\":\\"org:google\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}]}}]}}]}}", "range": 35, "rangeUnit": "d", "threshold": "99.34", + "uptimeEsClient": Object { + "baseESClient": [MockFunction], + "count": [Function], + "getSavedObjectsClient": [Function], + "search": [Function], + }, }, ] `); @@ -813,17 +808,16 @@ describe('status check alert', () => { expect(mockAvailability.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": [MockFunction], - "dynamicSettings": Object { - "certAgeThreshold": 730, - "certExpirationThreshold": 30, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - }, "filters": "{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\":\\"ur.port\\"}}],\\"minimum_should_match\\":1}}", "range": 23, "rangeUnit": "w", "threshold": "90", + "uptimeEsClient": Object { + "baseESClient": [MockFunction], + "count": [Function], + "getSavedObjectsClient": [Function], + "search": [Function], + }, }, ] `); @@ -857,17 +851,16 @@ describe('status check alert', () => { expect(mockAvailability.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": [MockFunction], - "dynamicSettings": Object { - "certAgeThreshold": 730, - "certExpirationThreshold": 30, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - }, "filters": undefined, "range": 23, "rangeUnit": "w", "threshold": "90", + "uptimeEsClient": Object { + "baseESClient": [MockFunction], + "count": [Function], + "getSavedObjectsClient": [Function], + "search": [Function], + }, }, ] `); diff --git a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts index d4c26fe83b5fc..022ec48bad1d9 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts @@ -82,7 +82,7 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory = (_server, _li context: [], state: [...durationAnomalyTranslations.actionVariables, ...commonStateTranslations], }, - async executor({ options, esClient, savedObjectsClient, dynamicSettings }) { + async executor({ options, uptimeEsClient, savedObjectsClient, dynamicSettings }) { const { services: { alertInstanceFactory }, state, @@ -96,8 +96,7 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory = (_server, _li if (foundAnomalies) { const monitorInfo = await getLatestMonitor({ - dynamicSettings, - callES: esClient, + uptimeEsClient, dateStart: 'now-15m', dateEnd: 'now', monitorId: params.monitorId, diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 577262c231977..3e45ce302bf87 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -7,13 +7,11 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import Mustache from 'mustache'; -import { ElasticsearchClient } from 'kibana/server'; import { UptimeAlertTypeFactory } from './types'; import { esKuery } from '../../../../../../src/plugins/data/server'; import { JsonObject } from '../../../../../../src/plugins/kibana_utils/common'; import { StatusCheckFilters, - DynamicSettings, Ping, GetMonitorAvailabilityParams, } from '../../../common/runtime_types'; @@ -27,7 +25,7 @@ import { UNNAMED_LOCATION } from '../../../common/constants'; import { uptimeAlertWrapper } from './uptime_alert_wrapper'; import { MonitorStatusTranslations } from '../../../common/translations'; import { getUptimeIndexPattern, IndexPatternTitleAndFields } from '../requests/get_index_pattern'; -import { UMServerLibs } from '../lib'; +import { UMServerLibs, UptimeESClient } from '../lib'; const { MONITOR_STATUS } = ACTION_GROUP_DEFINITIONS; @@ -89,8 +87,7 @@ export const generateFilterDSL = async ( }; export const formatFilterString = async ( - dynamicSettings: DynamicSettings, - esClient: ElasticsearchClient, + uptimeEsClient: UptimeESClient, filters: StatusCheckFilters, search: string, libs?: UMServerLibs @@ -98,10 +95,9 @@ export const formatFilterString = async ( await generateFilterDSL( () => libs?.requests?.getIndexPattern - ? libs?.requests?.getIndexPattern({ esClient, dynamicSettings }) + ? libs?.requests?.getIndexPattern({ uptimeEsClient }) : getUptimeIndexPattern({ - esClient, - dynamicSettings, + uptimeEsClient, }), filters, search @@ -265,8 +261,8 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) = state, services: { alertInstanceFactory }, }, - esClient, dynamicSettings, + uptimeEsClient, }) { const { filters, @@ -281,13 +277,7 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) = timerange: oldVersionTimeRange, } = rawParams; - const filterString = await formatFilterString( - dynamicSettings, - esClient, - filters, - search, - libs - ); + const filterString = await formatFilterString(uptimeEsClient, filters, search, libs); const timerange = oldVersionTimeRange || { from: isAutoGenerated @@ -302,8 +292,7 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) = // after that shouldCheckStatus should be explicitly false if (!(!oldVersionTimeRange && shouldCheckStatus === false)) { downMonitorsByLocation = await libs.requests.getMonitorStatus({ - callES: esClient, - dynamicSettings, + uptimeEsClient, timerange, numTimes, locations: [], @@ -337,8 +326,7 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) = let availabilityResults: GetMonitorAvailabilityResult[] = []; if (shouldCheckAvailability) { availabilityResults = await libs.requests.getMonitorAvailability({ - callES: esClient, - dynamicSettings, + uptimeEsClient, ...availability, filters: JSON.stringify(filterString) || undefined, }); diff --git a/x-pack/plugins/uptime/server/lib/alerts/tls.ts b/x-pack/plugins/uptime/server/lib/alerts/tls.ts index 11f602d10bf51..41a5101716122 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/tls.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/tls.ts @@ -100,15 +100,14 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, libs) => context: [], state: [...tlsTranslations.actionVariables, ...commonStateTranslations], }, - async executor({ options, dynamicSettings, esClient }) { + async executor({ options, dynamicSettings, uptimeEsClient }) { const { services: { alertInstanceFactory }, state, } = options; const { certs, total }: CertResult = await libs.requests.getCerts({ - callES: esClient, - dynamicSettings, + uptimeEsClient, from: DEFAULT_FROM, to: DEFAULT_TO, index: 0, diff --git a/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts b/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts index 0961eb6557891..965287ffbde8e 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts @@ -4,19 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'kibana/server'; import { AlertExecutorOptions, AlertType, AlertTypeState } from '../../../../alerts/server'; import { savedObjectsAdapter } from '../saved_objects'; import { DynamicSettings } from '../../../common/runtime_types'; +import { createUptimeESClient, UptimeESClient } from '../lib'; export interface UptimeAlertType extends Omit { executor: ({ options, - esClient, + uptimeEsClient, dynamicSettings, }: { options: AlertExecutorOptions; - esClient: ElasticsearchClient; + uptimeEsClient: UptimeESClient; dynamicSettings: DynamicSettings; savedObjectsClient: SavedObjectsClientContract; }) => Promise; @@ -34,6 +35,8 @@ export const uptimeAlertWrapper = (uptimeAlert: UptimeAlertType) => ({ options.services.savedObjectsClient ); - return uptimeAlert.executor({ options, esClient, dynamicSettings, savedObjectsClient }); + const uptimeEsClient = createUptimeESClient({ esClient, savedObjectsClient }); + + return uptimeAlert.executor({ options, dynamicSettings, uptimeEsClient, savedObjectsClient }); }, }); diff --git a/x-pack/plugins/uptime/server/lib/lib.ts b/x-pack/plugins/uptime/server/lib/lib.ts index a7121eaec6679..39dd868462525 100644 --- a/x-pack/plugins/uptime/server/lib/lib.ts +++ b/x-pack/plugins/uptime/server/lib/lib.ts @@ -3,10 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; import { UMBackendFrameworkAdapter } from './adapters'; import { UMLicenseCheck } from './domains'; import { UptimeRequests } from './requests'; +import { savedObjectsAdapter } from './saved_objects'; +import { ESSearchResponse } from '../../../../typings/elasticsearch'; export interface UMDomainLibs { requests: UptimeRequests; @@ -16,3 +18,58 @@ export interface UMDomainLibs { export interface UMServerLibs extends UMDomainLibs { framework: UMBackendFrameworkAdapter; } + +export type UptimeESClient = ReturnType; + +export function createUptimeESClient({ + esClient, + savedObjectsClient, +}: { + esClient: ElasticsearchClient; + savedObjectsClient: SavedObjectsClientContract; +}) { + return { + baseESClient: esClient, + async search(params: TParams): Promise<{ body: ESSearchResponse }> { + const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings( + savedObjectsClient! + ); + + let res: any; + try { + res = await esClient.search({ index: dynamicSettings!.heartbeatIndices, ...params }); + } catch (e) { + throw e; + } + return res; + }, + async count( + params: TParams + ): Promise<{ + body: { + count: number; + _shards: { + total: number; + successful: number; + skipped: number; + failed: number; + }; + }; + }> { + const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings( + savedObjectsClient! + ); + + let res: any; + try { + res = await esClient.count({ index: dynamicSettings!.heartbeatIndices, ...params }); + } catch (e) { + throw e; + } + return res; + }, + getSavedObjectsClient() { + return savedObjectsClient; + }, + }; +} diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/__snapshots__/extract_filter_aggs_results.test.ts.snap b/x-pack/plugins/uptime/server/lib/requests/__tests__/__snapshots__/extract_filter_aggs_results.test.ts.snap deleted file mode 100644 index 2f6d6e06f93e1..0000000000000 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/__snapshots__/extract_filter_aggs_results.test.ts.snap +++ /dev/null @@ -1,27 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`extractFilterAggsResults extracts the bucket values of the expected filter fields 1`] = ` -Object { - "locations": Array [ - "us-east-2", - "fairbanks", - ], - "ports": Array [ - 12349, - 80, - 5601, - 8200, - 9200, - 9292, - ], - "schemes": Array [ - "http", - "tcp", - "icmp", - ], - "tags": Array [ - "api", - "dev", - ], -} -`; diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/extract_filter_aggs_results.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/extract_filter_aggs_results.test.ts deleted file mode 100644 index 19fd0fda8d83e..0000000000000 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/extract_filter_aggs_results.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { extractFilterAggsResults } from '../get_filter_bar'; - -describe('extractFilterAggsResults', () => { - it('extracts the bucket values of the expected filter fields', () => { - expect( - extractFilterAggsResults( - { - locations: { - doc_count: 8098, - term: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 'us-east-2', doc_count: 4050 }, - { key: 'fairbanks', doc_count: 4048 }, - ], - }, - }, - schemes: { - doc_count: 8098, - term: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 'http', doc_count: 5055 }, - { key: 'tcp', doc_count: 2685 }, - { key: 'icmp', doc_count: 358 }, - ], - }, - }, - ports: { - doc_count: 8098, - term: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 12349, doc_count: 3571 }, - { key: 80, doc_count: 2985 }, - { key: 5601, doc_count: 358 }, - { key: 8200, doc_count: 358 }, - { key: 9200, doc_count: 358 }, - { key: 9292, doc_count: 110 }, - ], - }, - }, - tags: { - doc_count: 8098, - term: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { key: 'api', doc_count: 8098 }, - { key: 'dev', doc_count: 8098 }, - ], - }, - }, - }, - ['locations', 'ports', 'schemes', 'tags'] - ) - ).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts index c0b94b19b7582..cb37438619397 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts @@ -5,8 +5,7 @@ */ import { getCerts } from '../get_certs'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; -import { elasticsearchServiceMock } from '../../../../../../../src/core/server/mocks'; +import { getUptimeESMockClient } from './helper'; describe('getCerts', () => { let mockHits: any; @@ -82,8 +81,9 @@ describe('getCerts', () => { }); it('parses query result and returns expected values', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); - mockEsClient.search.mockResolvedValueOnce({ + const { esClient, uptimeEsClient } = getUptimeESMockClient(); + + esClient.search.mockResolvedValueOnce({ body: { hits: { hits: mockHits, @@ -92,13 +92,7 @@ describe('getCerts', () => { } as any); const result = await getCerts({ - callES: mockEsClient, - dynamicSettings: { - heartbeatIndices: 'heartbeat*', - certAgeThreshold: DYNAMIC_SETTINGS_DEFAULTS.certAgeThreshold, - certExpirationThreshold: DYNAMIC_SETTINGS_DEFAULTS.certExpirationThreshold, - defaultConnectors: [], - }, + uptimeEsClient, index: 1, from: 'now-2d', to: 'now+1h', @@ -129,7 +123,7 @@ describe('getCerts', () => { "total": 0, } `); - expect(mockEsClient.search.mock.calls).toMatchInlineSnapshot(` + expect(esClient.search.mock.calls).toMatchInlineSnapshot(` Array [ Array [ Object { @@ -217,7 +211,7 @@ describe('getCerts', () => { }, ], }, - "index": "heartbeat*", + "index": "heartbeat-8*", }, ], ] diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts index 9503174ed104c..3e3a6878b18b9 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts @@ -6,7 +6,7 @@ import { getLatestMonitor } from '../get_latest_monitor'; import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; -import { elasticsearchServiceMock } from '../../../../../../../src/core/server/mocks'; +import { getUptimeESMockClient } from './helper'; describe('getLatestMonitor', () => { let expectedGetLatestSearchParams: any; @@ -69,12 +69,12 @@ describe('getLatestMonitor', () => { }); it('returns data in expected shape', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + mockEsClient.search.mockResolvedValueOnce(mockEsSearchResult); const result = await getLatestMonitor({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, dateStart: 'now-1h', dateEnd: 'now', monitorId: 'testMonitor', 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 e8df65d410167..82256f31067c3 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 @@ -10,9 +10,9 @@ import { AvailabilityKey, getMonitorAvailability, } from '../get_monitor_availability'; -import { setupMockEsCompositeQuery } from './helper'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; +import { getUptimeESMockClient, setupMockEsCompositeQuery } from './helper'; import { GetMonitorAvailabilityParams, makePing, Ping } from '../../../../common/runtime_types'; + interface AvailabilityTopHit { _source: Ping; } @@ -108,9 +108,11 @@ describe('monitor availability', () => { "minimum_should_match": 1 } }`; + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + await getMonitorAvailability({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, filters: exampleFilter, range: 2, rangeUnit: 'w', @@ -286,9 +288,11 @@ describe('monitor availability', () => { rangeUnit: 'd', threshold: '69', }; + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + const result = await getMonitorAvailability({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, ...clientParameters, }); expect(esMock.search).toHaveBeenCalledTimes(1); @@ -509,9 +513,10 @@ describe('monitor availability', () => { ], genBucketItem ); + const { uptimeEsClient } = getUptimeESMockClient(esMock); + const result = await getMonitorAvailability({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, range: 3, rangeUnit: 'M', threshold: '98', @@ -812,9 +817,11 @@ describe('monitor availability', () => { ], genBucketItem ); + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + await getMonitorAvailability({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, range: 3, rangeUnit: 's', threshold: '99', diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_charts.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_charts.test.ts index 9edd3e2e160d2..428f990352dfc 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_charts.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_charts.test.ts @@ -7,16 +7,16 @@ import { set } from '@elastic/safer-lodash-set'; import mockChartsData from './monitor_charts_mock.json'; import { getMonitorDurationChart } from '../get_monitor_duration'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; -import { elasticsearchServiceMock } from '../../../../../../../src/core/server/mocks'; +import { getUptimeESMockClient } from './helper'; describe('ElasticsearchMonitorsAdapter', () => { it('getMonitorChartsData will provide expected filters', async () => { expect.assertions(2); - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + await getMonitorDurationChart({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, monitorId: 'fooID', dateStart: 'now-15m', dateEnd: 'now', @@ -33,13 +33,13 @@ describe('ElasticsearchMonitorsAdapter', () => { }); it('inserts empty buckets for missing data', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + mockEsClient.search.mockResolvedValueOnce(mockChartsData as any); expect( await getMonitorDurationChart({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, monitorId: 'id', dateStart: 'now-15m', dateEnd: 'now', 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 949bc39f07259..4978fbd6bcdbb 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 @@ -5,8 +5,7 @@ */ import { getMonitorStatus } from '../get_monitor_status'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; -import { setupMockEsCompositeQuery } from './helper'; +import { getUptimeESMockClient, setupMockEsCompositeQuery } from './helper'; export interface BucketItemCriteria { monitorId: string; @@ -77,9 +76,11 @@ describe('getMonitorStatus', () => { minimum_should_match: 1, }, }; + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + await getMonitorStatus({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, filters: exampleFilter, locations: [], numTimes: 5, @@ -193,9 +194,11 @@ describe('getMonitorStatus', () => { [], genBucketItem ); + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + await getMonitorStatus({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, locations: ['fairbanks', 'harrisburg'], numTimes: 1, timerange: { @@ -350,9 +353,11 @@ describe('getMonitorStatus', () => { }, }, }; + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + await getMonitorStatus({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, ...clientParameters, }); expect(esMock.search).toHaveBeenCalledTimes(1); @@ -495,9 +500,11 @@ describe('getMonitorStatus', () => { }, }, }; + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + await getMonitorStatus({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, ...clientParameters, }); expect(esMock.search).toHaveBeenCalledTimes(1); @@ -615,9 +622,11 @@ describe('getMonitorStatus', () => { to: 'now-2m', }, }; + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + const result = await getMonitorStatus({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, ...clientParameters, }); expect(esMock.search).toHaveBeenCalledTimes(1); @@ -793,9 +802,11 @@ describe('getMonitorStatus', () => { criteria, genBucketItem ); + + const { uptimeEsClient } = getUptimeESMockClient(esMock); + const result = await getMonitorStatus({ - callES: esMock, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, locations: [], numTimes: 5, timerange: { diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_ping_histogram.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_ping_histogram.test.ts index 427061b6c16d4..3b26adb18ae7f 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_ping_histogram.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_ping_histogram.test.ts @@ -5,9 +5,8 @@ */ import { getPingHistogram } from '../get_ping_histogram'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; -import { elasticsearchServiceMock } from '../../../../../../../src/core/server/mocks'; import * as intervalHelper from '../../helper/get_histogram_interval'; +import { getUptimeESMockClient } from './helper'; describe('getPingHistogram', () => { beforeEach(() => { @@ -43,7 +42,7 @@ describe('getPingHistogram', () => { it('returns a single bucket if array has 1', async () => { expect.assertions(2); - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); mockEsClient.search.mockResolvedValueOnce({ body: { @@ -67,8 +66,7 @@ describe('getPingHistogram', () => { } as any); const result = await getPingHistogram({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, from: 'now-15m', to: 'now', }); @@ -80,7 +78,7 @@ describe('getPingHistogram', () => { it('returns expected result for no status filter', async () => { expect.assertions(2); - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); standardMockResponse.aggregations.timeseries.interval = '1m'; @@ -89,8 +87,7 @@ describe('getPingHistogram', () => { } as any); const result = await getPingHistogram({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, from: 'now-15m', to: 'now', filters: '', @@ -103,7 +100,7 @@ describe('getPingHistogram', () => { it('handles status + additional user queries', async () => { expect.assertions(2); - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); mockEsClient.search.mockResolvedValueOnce({ body: { @@ -154,8 +151,7 @@ describe('getPingHistogram', () => { }; const result = await getPingHistogram({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, from: 'now-15m', to: 'now', filters: JSON.stringify(searchFilter), @@ -168,7 +164,7 @@ describe('getPingHistogram', () => { it('handles simple_text_query without issues', async () => { expect.assertions(2); - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); mockEsClient.search.mockResolvedValueOnce({ body: { @@ -211,8 +207,7 @@ describe('getPingHistogram', () => { const filters = `{"bool":{"must":[{"simple_query_string":{"query":"http"}}]}}`; const result = await getPingHistogram({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, from: 'now-15m', to: 'now', filters, diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts index f313cce9f758b..9b28d58c7e8c2 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts @@ -7,7 +7,7 @@ import { getPings } from '../get_pings'; import { set } from '@elastic/safer-lodash-set'; import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; -import { elasticsearchServiceMock } from '../../../../../../../src/core/server/mocks'; +import { getUptimeESMockClient } from './helper'; describe('getAll', () => { let mockEsSearchResult: any; @@ -87,12 +87,12 @@ describe('getAll', () => { }); it('returns data in the appropriate shape', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); mockEsClient.search.mockResolvedValueOnce(mockEsSearchResult); + const result = await getPings({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, dateRange: { from: 'now-1h', to: 'now' }, sort: 'asc', size: 12, @@ -110,11 +110,12 @@ describe('getAll', () => { }); it('creates appropriate sort and size parameters', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + mockEsClient.search.mockResolvedValueOnce(mockEsSearchResult); + await getPings({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, dateRange: { from: 'now-1h', to: 'now' }, sort: 'asc', size: 12, @@ -126,7 +127,7 @@ describe('getAll', () => { Array [ Object { "body": Object { - "aggregations": Object { + "aggs": Object { "locations": Object { "terms": Object { "field": "observer.geo.name", @@ -189,11 +190,12 @@ describe('getAll', () => { }); it('omits the sort param when no sort passed', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + mockEsClient.search.mockResolvedValueOnce(mockEsSearchResult); + await getPings({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, dateRange: { from: 'now-1h', to: 'now' }, size: 12, }); @@ -203,7 +205,7 @@ describe('getAll', () => { Array [ Object { "body": Object { - "aggregations": Object { + "aggs": Object { "locations": Object { "terms": Object { "field": "observer.geo.name", @@ -266,11 +268,12 @@ describe('getAll', () => { }); it('omits the size param when no size passed', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + mockEsClient.search.mockResolvedValueOnce(mockEsSearchResult); + await getPings({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, dateRange: { from: 'now-1h', to: 'now' }, sort: 'desc', }); @@ -280,7 +283,7 @@ describe('getAll', () => { Array [ Object { "body": Object { - "aggregations": Object { + "aggs": Object { "locations": Object { "terms": Object { "field": "observer.geo.name", @@ -343,11 +346,12 @@ describe('getAll', () => { }); it('adds a filter for monitor ID', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + mockEsClient.search.mockResolvedValueOnce(mockEsSearchResult); + await getPings({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, dateRange: { from: 'now-1h', to: 'now' }, monitorId: 'testmonitorid', }); @@ -357,7 +361,7 @@ describe('getAll', () => { Array [ Object { "body": Object { - "aggregations": Object { + "aggs": Object { "locations": Object { "terms": Object { "field": "observer.geo.name", @@ -425,11 +429,12 @@ describe('getAll', () => { }); it('adds a filter for monitor status', async () => { - const mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient(); + mockEsClient.search.mockResolvedValueOnce(mockEsSearchResult); + await getPings({ - callES: mockEsClient, - dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS, + uptimeEsClient, dateRange: { from: 'now-1h', to: 'now' }, status: 'down', }); @@ -439,7 +444,7 @@ describe('getAll', () => { Array [ Object { "body": Object { - "aggregations": Object { + "aggs": Object { "locations": Object { "terms": Object { "field": "observer.geo.name", diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/helper.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/helper.ts index 4ebc9b2da7855..37f7583312867 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/helper.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/helper.ts @@ -4,10 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { elasticsearchServiceMock } from '../../../../../../../src/core/server/mocks'; +import { + elasticsearchServiceMock, + savedObjectsClientMock, +} from '../../../../../../../src/core/server/mocks'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ElasticsearchClientMock } from '../../../../../../../src/core/server/elasticsearch/client/mocks'; +import { createUptimeESClient } from '../../lib'; export interface MultiPageCriteria { after_key?: K; @@ -54,3 +58,17 @@ export const setupMockEsCompositeQuery = ( return esMock; }; + +export const getUptimeESMockClient = (esClientMock?: ElasticsearchClientMock) => { + const esClient = elasticsearchServiceMock.createElasticsearchClient(); + + const savedObjectsClient = savedObjectsClientMock.create(); + + return { + esClient: esClientMock || esClient, + uptimeEsClient: createUptimeESClient({ + esClient: esClientMock || esClient, + savedObjectsClient, + }), + }; +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts index 0836cb039b215..d8b8f3733b94b 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts @@ -5,7 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { CertResult, GetCertsParams } from '../../../common/runtime_types'; +import { CertResult, GetCertsParams, Ping } from '../../../common/runtime_types'; enum SortFields { 'issuer' = 'tls.server.x509.issuer.common_name', @@ -15,8 +15,7 @@ enum SortFields { } export const getCerts: UMElasticsearchQueryFn = async ({ - callES, - dynamicSettings, + uptimeEsClient, index, from, to, @@ -29,92 +28,86 @@ export const getCerts: UMElasticsearchQueryFn = asyn }) => { const sort = SortFields[sortBy as keyof typeof SortFields]; - const params: any = { - index: dynamicSettings.heartbeatIndices, - body: { - from: index * size, - size, - sort: [ - { - [sort]: { - order: direction, - }, + const searchBody = { + from: index * size, + size, + sort: [ + { + [sort]: { + order: direction as 'asc' | 'desc', }, - ], - query: { - bool: { - filter: [ - { - exists: { - field: 'tls.server', - }, - }, - { - range: { - 'monitor.timespan': { - gte: from, - lte: to, + }, + ], + query: { + bool: { + ...(search + ? { + minimum_should_match: 1, + should: [ + { + multi_match: { + query: escape(search), + type: 'phrase_prefix', + fields: [ + 'monitor.id.text', + 'monitor.name.text', + 'url.full.text', + 'tls.server.x509.subject.common_name.text', + 'tls.server.x509.issuer.common_name.text', + ], + }, }, - }, + ], + } + : {}), + filter: [ + { + exists: { + field: 'tls.server', }, - ], - }, - }, - _source: [ - 'monitor.id', - 'monitor.name', - 'tls.server.x509.issuer.common_name', - 'tls.server.x509.subject.common_name', - 'tls.server.hash.sha1', - 'tls.server.hash.sha256', - 'tls.server.x509.not_after', - 'tls.server.x509.not_before', - ], - collapse: { - field: 'tls.server.hash.sha256', - inner_hits: { - _source: { - includes: ['monitor.id', 'monitor.name', 'url.full'], }, - collapse: { - field: 'monitor.id', + { + range: { + 'monitor.timespan': { + gte: from, + lte: to, + }, + }, }, - name: 'monitors', - sort: [{ 'monitor.id': 'asc' }], - }, + ], }, - aggs: { - total: { - cardinality: { - field: 'tls.server.hash.sha256', - }, + }, + _source: [ + 'monitor.id', + 'monitor.name', + 'tls.server.x509.issuer.common_name', + 'tls.server.x509.subject.common_name', + 'tls.server.hash.sha1', + 'tls.server.hash.sha256', + 'tls.server.x509.not_after', + 'tls.server.x509.not_before', + ], + collapse: { + field: 'tls.server.hash.sha256', + inner_hits: { + _source: { + includes: ['monitor.id', 'monitor.name', 'url.full'], + }, + collapse: { + field: 'monitor.id', }, + name: 'monitors', + sort: [{ 'monitor.id': 'asc' }], }, }, - }; - - if (!params.body.query.bool.should) { - params.body.query.bool.should = []; - } - - if (search) { - params.body.query.bool.minimum_should_match = 1; - params.body.query.bool.should = [ - { - multi_match: { - query: escape(search), - type: 'phrase_prefix', - fields: [ - 'monitor.id.text', - 'monitor.name.text', - 'url.full.text', - 'tls.server.x509.subject.common_name.text', - 'tls.server.x509.issuer.common_name.text', - ], + aggs: { + total: { + cardinality: { + field: 'tls.server.hash.sha256', }, }, - ]; - } + }, + }; if (notValidBefore || notValidAfter) { const validityFilters: any = { @@ -141,18 +134,17 @@ export const getCerts: UMElasticsearchQueryFn = asyn }); } - params.body.query.bool.filter.push(validityFilters); + searchBody.query.bool.filter.push(validityFilters); } // console.log(JSON.stringify(params, null, 2)); - const { body: result } = await callES.search(params); + const { body: result } = await uptimeEsClient.search({ + body: searchBody, + }); - const certs = (result?.hits?.hits ?? []).map((hit: any) => { - const { - _source: { - tls: { server }, - }, - } = hit; + const certs = (result?.hits?.hits ?? []).map((hit) => { + const ping = hit._source as Ping; + const server = ping.tls?.server!; const notAfter = server?.x509?.not_after; const notBefore = server?.x509?.not_before; @@ -171,7 +163,7 @@ export const getCerts: UMElasticsearchQueryFn = asyn monitors, issuer, sha1, - sha256, + sha256: sha256 as string, not_after: notAfter, not_before: notBefore, common_name: commonName, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts b/x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts index c3295d6dd9c8f..026ce184649cd 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts @@ -45,28 +45,8 @@ export const combineRangeWithFilters = ( return filters; }; -type SupportedFields = 'locations' | 'ports' | 'schemes' | 'tags'; - -export const extractFilterAggsResults = ( - responseAggregations: Record, - keys: SupportedFields[] -): OverviewFilters => { - const values: OverviewFilters = { - locations: [], - ports: [], - schemes: [], - tags: [], - }; - keys.forEach((key) => { - const buckets = responseAggregations?.[key]?.term?.buckets ?? []; - values[key] = buckets.map((item: { key: string | number }) => item.key); - }); - return values; -}; - export const getFilterBar: UMElasticsearchQueryFn = async ({ - callES, - dynamicSettings, + uptimeEsClient, dateRangeStart, dateRangeEnd, search, @@ -82,19 +62,24 @@ export const getFilterBar: UMElasticsearchQueryFn item.key as string), + ports: ports?.term?.buckets.map((item) => item.key as number), + schemes: schemes?.term?.buckets.map((item) => item.key as string), + tags: tags?.term?.buckets.map((item) => item.key as string), + }; }; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_index_pattern.ts b/x-pack/plugins/uptime/server/lib/requests/get_index_pattern.ts index 98d32b16b2884..ab0b9043d14e2 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_index_pattern.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_index_pattern.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ElasticsearchClient } from 'kibana/server'; import { FieldDescriptor, IndexPatternsFetcher } from '../../../../../../src/plugins/data/server'; -import { DynamicSettings } from '../../../common/runtime_types'; +import { UptimeESClient } from '../lib'; +import { savedObjectsAdapter } from '../saved_objects'; export interface IndexPatternTitleAndFields { title: string; @@ -14,14 +14,15 @@ export interface IndexPatternTitleAndFields { } export const getUptimeIndexPattern = async ({ - esClient, - dynamicSettings, + uptimeEsClient, }: { - esClient: ElasticsearchClient; - dynamicSettings: DynamicSettings; + uptimeEsClient: UptimeESClient; }): Promise => { - const indexPatternsFetcher = new IndexPatternsFetcher(esClient); + const indexPatternsFetcher = new IndexPatternsFetcher(uptimeEsClient.baseESClient); + const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings( + uptimeEsClient.getSavedObjectsClient()! + ); // Since `getDynamicIndexPattern` is called in setup_request (and thus by every endpoint) // and since `getFieldsForWildcard` will throw if the specified indices don't exist, // we have to catch errors here to avoid all endpoints returning 500 for users without APM data diff --git a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts index 061d002b010de..e2baf39905bfd 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts @@ -8,15 +8,14 @@ import { UMElasticsearchQueryFn } from '../adapters'; import { StatesIndexStatus } from '../../../common/runtime_types'; export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = async ({ - callES, - dynamicSettings, + uptimeEsClient, }) => { const { body: { _shards: { total }, count, }, - } = await callES.count({ index: dynamicSettings.heartbeatIndices }); + } = await uptimeEsClient.count({}); return { indexExists: total > 0, docCount: count, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts index bff3aaf1176df..dacdcaff7dfd5 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts @@ -5,6 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters/framework'; +import { ESSearchBody } from '../../../../../typings/elasticsearch'; interface GetJourneyScreenshotParams { checkGroup: string; @@ -14,35 +15,32 @@ interface GetJourneyScreenshotParams { export const getJourneyScreenshot: UMElasticsearchQueryFn< GetJourneyScreenshotParams, any -> = async ({ callES, dynamicSettings, checkGroup, stepIndex }) => { - const params: any = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - bool: { - filter: [ - { - term: { - 'monitor.check_group': checkGroup, - }, +> = async ({ uptimeEsClient, checkGroup, stepIndex }) => { + const params: ESSearchBody = { + query: { + bool: { + filter: [ + { + term: { + 'monitor.check_group': checkGroup, }, - { - term: { - 'synthetics.type': 'step/screenshot', - }, + }, + { + term: { + 'synthetics.type': 'step/screenshot', }, - { - term: { - 'synthetics.step.index': stepIndex, - }, + }, + { + term: { + 'synthetics.step.index': stepIndex, }, - ], - }, + }, + ], }, - _source: ['synthetics.blob'], }, + _source: ['synthetics.blob'], }; - const { body: result } = await callES.search(params); + const { body: result } = await uptimeEsClient.search({ body: params }); if (!Array.isArray(result?.hits?.hits) || result.hits.hits.length < 1) { return null; } diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts index f36815a747db3..c330e1b66fe93 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts @@ -12,51 +12,50 @@ interface GetJourneyStepsParams { } export const getJourneySteps: UMElasticsearchQueryFn = async ({ - callES, - dynamicSettings, + uptimeEsClient, checkGroup, }) => { - const params: any = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - bool: { - filter: [ - { - terms: { - 'synthetics.type': ['step/end', 'stderr', 'cmd/status', 'step/screenshot'], - }, + const params = { + query: { + bool: { + filter: [ + { + terms: { + 'synthetics.type': ['step/end', 'stderr', 'cmd/status', 'step/screenshot'], }, - { - term: { - 'monitor.check_group': checkGroup, - }, + }, + { + term: { + 'monitor.check_group': checkGroup, }, - ], - }, - }, - sort: [{ 'synthetics.step.index': { order: 'asc' } }, { '@timestamp': { order: 'asc' } }], - _source: { - excludes: ['synthetics.blob'], + }, + ], }, }, + sort: [{ 'synthetics.step.index': { order: 'asc' } }, { '@timestamp': { order: 'asc' } }], + _source: { + excludes: ['synthetics.blob'], + }, size: 500, }; - const { body: result } = await callES.search(params); + const { body: result } = await uptimeEsClient.search({ body: params }); + const screenshotIndexes: number[] = result.hits.hits - .filter((h: any) => h?._source?.synthetics?.type === 'step/screenshot') - .map((h: any) => h?._source?.synthetics?.step?.index); - return result.hits.hits - .filter((h: any) => h?._source?.synthetics?.type !== 'step/screenshot') - .map( - ({ _id, _source, _source: { synthetics } }: any): Ping => ({ - ..._source, - timestamp: _source['@timestamp'], - docId: _id, + .filter((h) => (h?._source as Ping).synthetics?.type === 'step/screenshot') + .map((h) => (h?._source as Ping).synthetics?.step?.index as number); + + return (result.hits.hits + .filter((h) => (h?._source as Ping).synthetics?.type !== 'step/screenshot') + .map((h) => { + const source = h._source as Ping & { '@timestamp': string }; + return { + ...source, + timestamp: source['@timestamp'], + docId: h._id, synthetics: { - ...synthetics, - screenshotExists: screenshotIndexes.some((i) => i === synthetics?.step?.index), + ...source.synthetics, + screenshotExists: screenshotIndexes.some((i) => i === source.synthetics?.step?.index), }, - }) - ); + }; + }) as unknown) as Ping; }; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts index f6562eaa42e90..1e323b57b30dc 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts @@ -22,45 +22,42 @@ export interface GetLatestMonitorParams { // Get The monitor latest state sorted by timestamp with date range export const getLatestMonitor: UMElasticsearchQueryFn = async ({ - callES, - dynamicSettings, + uptimeEsClient, dateStart, dateEnd, monitorId, observerLocation, }) => { const params = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - bool: { - filter: [ - { exists: { field: 'summary' } }, - { - range: { - '@timestamp': { - gte: dateStart, - lte: dateEnd, - }, + query: { + bool: { + filter: [ + { exists: { field: 'summary' } }, + { + range: { + '@timestamp': { + gte: dateStart, + lte: dateEnd, }, }, - ...(monitorId ? [{ term: { 'monitor.id': monitorId } }] : []), - ...(observerLocation ? [{ term: { 'observer.geo.name': observerLocation } }] : []), - ], - }, - }, - size: 1, - _source: ['url', 'monitor', 'observer', '@timestamp', 'tls.*', 'http', 'error'], - sort: { - '@timestamp': { order: 'desc' }, + }, + ...(monitorId ? [{ term: { 'monitor.id': monitorId } }] : []), + ...(observerLocation ? [{ term: { 'observer.geo.name': observerLocation } }] : []), + ], }, }, + size: 1, + _source: ['url', 'monitor', 'observer', '@timestamp', 'tls.*', 'http', 'error'], + sort: { + '@timestamp': { order: 'desc' }, + }, }; - const { body: result } = await callES.search(params); + const { body: result } = await uptimeEsClient.search({ body: params }); + const doc = result.hits?.hits?.[0]; const docId = doc?._id ?? ''; - const { tls, ...ping } = doc?._source ?? {}; + const { tls, ...ping } = (doc?._source as Ping & { '@timestamp': string }) ?? {}; return { ...ping, 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 2f1a37095c3bc..04b4eef19d689 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 @@ -6,6 +6,8 @@ import { UMElasticsearchQueryFn } from '../adapters'; import { GetMonitorAvailabilityParams, Ping } from '../../../common/runtime_types'; +import { AfterKey } from './get_monitor_status'; +import { SortOptions } from '../../../../../typings/elasticsearch'; export interface AvailabilityKey { monitorId: string; @@ -34,9 +36,9 @@ export const formatBuckets = async (buckets: any[]): Promise = async ({ callES, dynamicSettings, range, rangeUnit, threshold: thresholdString, filters }) => { +> = async ({ uptimeEsClient, range, rangeUnit, threshold: thresholdString, filters }) => { const queryResults: Array> = []; - let afterKey: AvailabilityKey | undefined; + let afterKey: AfterKey; const threshold = Number(thresholdString) / 100; if (threshold <= 0 || threshold > 1.0) { @@ -53,92 +55,90 @@ export const getMonitorAvailability: UMElasticsearchQueryFn< } do { - const esParams: any = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - bool: { - filter: [ + const esParams = { + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte, + lte: 'now', + }, + }, + }, + // append user filters, if defined + ...(parsedFilters?.bool ? [parsedFilters] : []), + ], + }, + }, + size: 0, + aggs: { + monitors: { + composite: { + size: 2000, + ...(afterKey ? { after: afterKey } : {}), + sources: [ { - range: { - '@timestamp': { - gte, - lte: 'now', + monitorId: { + terms: { + field: 'monitor.id', }, }, }, - // append user filters, if defined - ...(parsedFilters?.bool ? [parsedFilters] : []), - ], - }, - }, - size: 0, - aggs: { - monitors: { - composite: { - size: 2000, - sources: [ - { - monitorId: { - terms: { - field: 'monitor.id', - }, + { + location: { + terms: { + field: 'observer.geo.name', + missing_bucket: true, }, }, - { - location: { - terms: { - field: 'observer.geo.name', - missing_bucket: true, + }, + ], + }, + aggs: { + fields: { + top_hits: { + size: 1, + sort: [ + { + '@timestamp': { + order: 'desc', }, }, - }, - ], + ] as SortOptions, + }, }, - aggs: { - fields: { - top_hits: { - size: 1, - sort: [ - { - '@timestamp': { - order: 'desc', - }, - }, - ], - }, + up_sum: { + sum: { + field: 'summary.up', + missing: 0, }, - up_sum: { - sum: { - field: 'summary.up', - missing: 0, - }, + }, + down_sum: { + sum: { + field: 'summary.down', + missing: 0, }, - down_sum: { - sum: { - field: 'summary.down', - missing: 0, + }, + ratio: { + bucket_script: { + buckets_path: { + upTotal: 'up_sum', + downTotal: 'down_sum', }, - }, - ratio: { - bucket_script: { - buckets_path: { - upTotal: 'up_sum', - downTotal: 'down_sum', - }, - script: ` + script: ` if (params.upTotal + params.downTotal > 0) { return params.upTotal / (params.upTotal + params.downTotal); } return null;`, - }, }, - filtered: { - bucket_selector: { - buckets_path: { - threshold: 'ratio.value', - }, - script: `params.threshold < ${threshold}`, + }, + filtered: { + bucket_selector: { + buckets_path: { + threshold: 'ratio.value', }, + script: `params.threshold < ${threshold}`, }, }, }, @@ -146,12 +146,9 @@ export const getMonitorAvailability: UMElasticsearchQueryFn< }, }; - if (afterKey) { - esParams.body.aggs.monitors.composite.after = afterKey; - } + const { body: result } = await uptimeEsClient.search({ body: esParams }); - const { body: result } = await callES.search(esParams); - afterKey = result?.aggregations?.monitors?.after_key; + afterKey = result?.aggregations?.monitors?.after_key as AfterKey; queryResults.push(formatBuckets(result?.aggregations?.monitors?.buckets || [])); } while (afterKey !== undefined); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts index 998ea3dd9df00..c4d122515d133 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ElasticsearchClient } from 'kibana/server'; import { UMElasticsearchQueryFn } from '../adapters'; -import { MonitorDetails, MonitorError } from '../../../common/runtime_types'; +import { MonitorDetails, Ping } from '../../../common/runtime_types'; import { formatFilterString } from '../alerts/status_check'; +import { UptimeESClient } from '../lib'; +import { ESSearchBody } from '../../../../../typings/elasticsearch'; export interface GetMonitorDetailsParams { monitorId: string; @@ -17,13 +18,11 @@ export interface GetMonitorDetailsParams { } const getMonitorAlerts = async ({ - callES, - dynamicSettings, + uptimeEsClient, alertsClient, monitorId, }: { - callES: ElasticsearchClient; - dynamicSettings: any; + uptimeEsClient: UptimeESClient; alertsClient: any; monitorId: string; }) => { @@ -44,41 +43,37 @@ const getMonitorAlerts = async ({ monitorAlerts.push(currAlert); continue; } - const esParams: any = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - bool: { - filter: [ - { - term: { - 'monitor.id': monitorId, - }, + const esParams: ESSearchBody = { + query: { + bool: { + filter: [ + { + term: { + 'monitor.id': monitorId, }, - ], - }, - }, - size: 0, - aggs: { - monitors: { - terms: { - field: 'monitor.id', - size: 1000, }, + ], + }, + }, + size: 0, + aggs: { + monitors: { + terms: { + field: 'monitor.id', + size: 1000, }, }, }, }; const parsedFilters = await formatFilterString( - dynamicSettings, - callES, + uptimeEsClient, currAlert.params.filters, currAlert.params.search ); - esParams.body.query.bool = Object.assign({}, esParams.body.query.bool, parsedFilters?.bool); + esParams.query.bool = Object.assign({}, esParams.query.bool, parsedFilters?.bool); - const { body: result } = await callES.search(esParams); + const { body: result } = await uptimeEsClient.search({ body: esParams }); if (result.hits.total.value > 0) { monitorAlerts.push(currAlert); @@ -90,7 +85,7 @@ const getMonitorAlerts = async ({ export const getMonitorDetails: UMElasticsearchQueryFn< GetMonitorDetailsParams, MonitorDetails -> = async ({ callES, dynamicSettings, monitorId, dateStart, dateEnd, alertsClient }) => { +> = async ({ uptimeEsClient, monitorId, dateStart, dateEnd, alertsClient }) => { const queryFilters: any = [ { range: { @@ -108,48 +103,43 @@ export const getMonitorDetails: UMElasticsearchQueryFn< ]; const params = { - index: dynamicSettings.heartbeatIndices, - body: { - size: 1, - _source: ['error', '@timestamp'], - query: { - bool: { - must: [ - { - exists: { - field: 'error', - }, + size: 1, + _source: ['error', '@timestamp'], + query: { + bool: { + must: [ + { + exists: { + field: 'error', }, - ], - filter: queryFilters, - }, - }, - sort: [ - { - '@timestamp': { - order: 'desc', }, - }, - ], + ], + filter: queryFilters, + }, }, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], }; - const { body: result } = await callES.search(params); + const { body: result } = await uptimeEsClient.search({ body: params }); - const data = result.hits.hits[0]?._source; + const data = result.hits.hits[0]?._source as Ping & { '@timestamp': string }; - const monitorError: MonitorError | undefined = data?.error; const errorTimestamp: string | undefined = data?.['@timestamp']; const monAlerts = await getMonitorAlerts({ - callES, - dynamicSettings, + uptimeEsClient, alertsClient, monitorId, }); return { monitorId, - error: monitorError, + error: data?.error, timestamp: errorTimestamp, alerts: monAlerts, }; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_duration.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_duration.ts index 77ae7570a96a8..cb1251eb7f9db 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_duration.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_duration.ts @@ -23,35 +23,32 @@ export interface GetMonitorChartsParams { export const getMonitorDurationChart: UMElasticsearchQueryFn< GetMonitorChartsParams, MonitorDurationResult -> = async ({ callES, dynamicSettings, dateStart, dateEnd, monitorId }) => { +> = async ({ uptimeEsClient, dateStart, dateEnd, monitorId }) => { const params = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - bool: { - filter: [ - { range: { '@timestamp': { gte: dateStart, lte: dateEnd } } }, - { term: { 'monitor.id': monitorId } }, - { range: { 'monitor.duration.us': { gt: 0 } } }, - ], - }, + query: { + bool: { + filter: [ + { range: { '@timestamp': { gte: dateStart, lte: dateEnd } } }, + { term: { 'monitor.id': monitorId } }, + { range: { 'monitor.duration.us': { gt: 0 } } }, + ], }, - size: 0, - aggs: { - timeseries: { - auto_date_histogram: { - field: '@timestamp', - buckets: QUERY.DEFAULT_BUCKET_COUNT, - }, - aggs: { - location: { - terms: { - field: 'observer.geo.name', - missing: 'N/A', - }, - aggs: { - duration: { stats: { field: 'monitor.duration.us' } }, - }, + }, + size: 0, + aggs: { + timeseries: { + auto_date_histogram: { + field: '@timestamp', + buckets: QUERY.DEFAULT_BUCKET_COUNT, + }, + aggs: { + location: { + terms: { + field: 'observer.geo.name', + missing: 'N/A', + }, + aggs: { + duration: { stats: { field: 'monitor.duration.us' } }, }, }, }, @@ -59,7 +56,7 @@ export const getMonitorDurationChart: UMElasticsearchQueryFn< }, }; - const { body: result } = await callES.search(params); + const { body: result } = await uptimeEsClient.search({ body: params }); const dateHistogramBuckets: any[] = result?.aggregations?.timeseries?.buckets ?? []; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts index b5183ca9ffb9f..af79126226e26 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts @@ -7,6 +7,7 @@ import { UMElasticsearchQueryFn } from '../adapters'; import { MonitorLocations, MonitorLocation } from '../../../common/runtime_types'; import { UNNAMED_LOCATION } from '../../../common/constants'; +import { SortOptions } from '../../../../../typings/elasticsearch'; /** * Fetch data for the monitor page title. @@ -23,64 +24,65 @@ export interface GetMonitorLocationsParams { export const getMonitorLocations: UMElasticsearchQueryFn< GetMonitorLocationsParams, MonitorLocations -> = async ({ callES, dynamicSettings, monitorId, dateStart, dateEnd }) => { +> = async ({ uptimeEsClient, monitorId, dateStart, dateEnd }) => { + const sortOptions: SortOptions = [ + { + '@timestamp': { + order: 'desc', + }, + }, + ]; + const params = { - index: dynamicSettings.heartbeatIndices, - body: { - size: 0, - query: { - bool: { - filter: [ - { - term: { - 'monitor.id': monitorId, - }, + size: 0, + query: { + bool: { + filter: [ + { + term: { + 'monitor.id': monitorId, }, - { - exists: { - field: 'summary', - }, + }, + { + exists: { + field: 'summary', }, - { - range: { - '@timestamp': { - gte: dateStart, - lte: dateEnd, - }, + }, + { + range: { + '@timestamp': { + gte: dateStart, + lte: dateEnd, }, }, - ], - }, - }, - aggs: { - location: { - terms: { - field: 'observer.geo.name', - missing: '__location_missing__', }, - aggs: { - most_recent: { - top_hits: { - size: 1, - sort: { - '@timestamp': { - order: 'desc', - }, - }, - _source: ['monitor', 'summary', 'observer', '@timestamp'], - }, + ], + }, + }, + aggs: { + location: { + terms: { + field: 'observer.geo.name', + missing: '__location_missing__', + }, + aggs: { + most_recent: { + top_hits: { + size: 1, + sort: sortOptions, + _source: ['monitor', 'summary', 'observer', '@timestamp'], }, - down_history: { - sum: { - field: 'summary.down', - missing: 0, - }, + }, + down_history: { + sum: { + field: 'summary.down', + missing: 0, }, - up_history: { - sum: { - field: 'summary.up', - missing: 0, - }, + }, + up_history: { + sum: { + field: 'summary.up', + missing: 0, }, }, }, @@ -88,7 +90,8 @@ export const getMonitorLocations: UMElasticsearchQueryFn< }, }; - const { body: result } = await callES.search(params); + const { body: result } = await uptimeEsClient.search({ body: params }); + const locations = result?.aggregations?.location?.buckets ?? []; const getGeo = (locGeo: { name: string; location?: string }) => { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_states.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_states.ts index 2ff1043d79e84..16638f0e8cea8 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_states.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_states.ts @@ -40,8 +40,7 @@ export const getMonitorStates: UMElasticsearchQueryFn< GetMonitorStatesParams, MonitorSummariesResult > = async ({ - callES, - dynamicSettings, + uptimeEsClient, dateRangeStart, dateRangeEnd, pagination, @@ -53,8 +52,7 @@ export const getMonitorStates: UMElasticsearchQueryFn< statusFilter = statusFilter === null ? undefined : statusFilter; const queryContext = new QueryContext( - callES, - dynamicSettings.heartbeatIndices, + uptimeEsClient, dateRangeStart, dateRangeEnd, pagination, @@ -98,52 +96,49 @@ export const getHistogramForMonitors = async ( minInterval: number ): Promise<{ [key: string]: Histogram }> => { const params = { - index: queryContext.heartbeatIndices, - body: { - size: 0, - query: { - bool: { - filter: [ - { - range: { - 'summary.down': { gt: 0 }, - }, + size: 0, + query: { + bool: { + filter: [ + { + range: { + 'summary.down': { gt: 0 }, }, - { - terms: { - 'monitor.id': monitorIds, - }, + }, + { + terms: { + 'monitor.id': monitorIds, }, - { - range: { - '@timestamp': { - gte: queryContext.dateRangeStart, - lte: queryContext.dateRangeEnd, - }, + }, + { + range: { + '@timestamp': { + gte: queryContext.dateRangeStart, + lte: queryContext.dateRangeEnd, }, }, - ], - }, - }, - aggs: { - histogram: { - date_histogram: { - field: '@timestamp', - // 12 seems to be a good size for performance given - // long monitor lists of up to 100 on the overview page - fixed_interval: minInterval + 'ms', - missing: 0, }, - aggs: { - by_id: { - terms: { - field: 'monitor.id', - size: Math.max(monitorIds.length, 1), - }, - aggs: { - totalDown: { - sum: { field: 'summary.down' }, - }, + ], + }, + }, + aggs: { + histogram: { + date_histogram: { + field: '@timestamp', + // 12 seems to be a good size for performance given + // long monitor lists of up to 100 on the overview page + fixed_interval: minInterval + 'ms', + missing: 0, + }, + aggs: { + by_id: { + terms: { + field: 'monitor.id', + size: Math.max(monitorIds.length, 1), + }, + aggs: { + totalDown: { + sum: { field: 'summary.down' }, }, }, }, @@ -151,7 +146,7 @@ export const getHistogramForMonitors = async ( }, }, }; - const { body: result } = await queryContext.search(params); + const { body: result } = await queryContext.search({ body: params }); const histoBuckets: any[] = result.aggregations?.histogram.buckets ?? []; const simplified = histoBuckets.map((histoBucket: any): { timestamp: number; byId: any } => { 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 06648d68969c1..a5121f7a7a04f 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 @@ -23,12 +23,6 @@ export interface GetMonitorStatusResult { monitorInfo: Ping; } -interface MonitorStatusKey { - monitor_id: string; - status: string; - location: string; -} - const getLocationClause = (locations: string[]) => ({ bool: { should: [ @@ -41,76 +35,80 @@ const getLocationClause = (locations: string[]) => ({ }, }); +export type AfterKey = Record | undefined; + export const getMonitorStatus: UMElasticsearchQueryFn< GetMonitorStatusParams, GetMonitorStatusResult[] -> = async ({ callES, dynamicSettings, filters, locations, numTimes, timerange: { from, to } }) => { - let afterKey: MonitorStatusKey | undefined; +> = async ({ uptimeEsClient, filters, locations, numTimes, timerange: { from, to } }) => { + let afterKey: AfterKey; const STATUS = 'down'; let monitors: any = []; do { // today this value is hardcoded. In the future we may support // multiple status types for this alert, and this will become a parameter - const esParams: any = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - bool: { - filter: [ - { - term: { - 'monitor.status': STATUS, - }, + const esParams = { + query: { + bool: { + filter: [ + { + term: { + 'monitor.status': STATUS, }, - { - range: { - '@timestamp': { - gte: from, - lte: to, - }, + }, + { + range: { + '@timestamp': { + gte: from, + lte: to, }, }, - // append user filters, if defined - ...(filters?.bool ? [filters] : []), - ], - }, + }, + // append user filters, if defined + ...(filters?.bool ? [filters] : []), + ], }, - size: 0, - aggs: { - monitors: { - composite: { - size: 2000, - sources: [ - { - monitorId: { - terms: { - field: 'monitor.id', - }, + }, + size: 0, + aggs: { + monitors: { + composite: { + size: 2000, + /** + * We "paginate" results by utilizing the `afterKey` field + * to tell Elasticsearch where it should start on subsequent queries. + */ + ...(afterKey ? { after: afterKey } : {}), + sources: [ + { + monitorId: { + terms: { + field: 'monitor.id', }, }, - { - status: { - terms: { - field: 'monitor.status', - }, + }, + { + status: { + terms: { + field: 'monitor.status', }, }, - { - location: { - terms: { - field: 'observer.geo.name', - missing_bucket: true, - }, + }, + { + location: { + terms: { + field: 'observer.geo.name', + missing_bucket: true, }, }, - ], - }, - aggs: { - fields: { - top_hits: { - size: 1, - }, + }, + ], + }, + aggs: { + fields: { + top_hits: { + size: 1, }, }, }, @@ -122,19 +120,14 @@ export const getMonitorStatus: UMElasticsearchQueryFn< * Perform a logical `and` against the selected location filters. */ if (locations.length) { - esParams.body.query.bool.filter.push(getLocationClause(locations)); + esParams.query.bool.filter.push(getLocationClause(locations)); } - /** - * We "paginate" results by utilizing the `afterKey` field - * to tell Elasticsearch where it should start on subsequent queries. - */ - if (afterKey) { - esParams.body.aggs.monitors.composite.after = afterKey; - } + const { body: result } = await uptimeEsClient.search({ + body: esParams, + }); - const { body: result } = await callES.search(esParams); - afterKey = result?.aggregations?.monitors?.after_key; + afterKey = result?.aggregations?.monitors?.after_key as AfterKey; monitors = monitors.concat(result?.aggregations?.monitors?.buckets || []); } while (afterKey !== undefined); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts b/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts index 4eb2d862cb702..325c7b0e2edef 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UMElasticsearchQueryFn } from '../adapters'; import { getFilterClause } from '../helper'; import { HistogramResult, HistogramQueryResult } from '../../../common/runtime_types'; import { QUERY } from '../../../common/constants'; import { getHistogramInterval } from '../helper/get_histogram_interval'; +import { UMElasticsearchQueryFn } from '../adapters/framework'; export interface GetPingHistogramParams { /** @member dateRangeStart timestamp bounds */ @@ -26,7 +26,7 @@ export interface GetPingHistogramParams { export const getPingHistogram: UMElasticsearchQueryFn< GetPingHistogramParams, HistogramResult -> = async ({ callES, dynamicSettings, from, to, filters, monitorId, bucketSize }) => { +> = async ({ uptimeEsClient, from, to, filters, monitorId, bucketSize }) => { const boolFilters = filters ? JSON.parse(filters) : null; const additionalFilters = []; if (monitorId) { @@ -40,34 +40,31 @@ export const getPingHistogram: UMElasticsearchQueryFn< const minInterval = getHistogramInterval(from, to, QUERY.DEFAULT_BUCKET_COUNT); const params = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - bool: { - filter, - }, + query: { + bool: { + filter, }, - size: 0, - aggs: { - timeseries: { - date_histogram: { - field: '@timestamp', - fixed_interval: bucketSize || minInterval + 'ms', - missing: 0, - }, - aggs: { - down: { - filter: { - term: { - 'monitor.status': 'down', - }, + }, + size: 0, + aggs: { + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: bucketSize || minInterval + 'ms', + missing: 0, + }, + aggs: { + down: { + filter: { + term: { + 'monitor.status': 'down', }, }, - up: { - filter: { - term: { - 'monitor.status': 'up', - }, + }, + up: { + filter: { + term: { + 'monitor.status': 'up', }, }, }, @@ -76,7 +73,7 @@ export const getPingHistogram: UMElasticsearchQueryFn< }, }; - const { body: result } = await callES.search(params); + const { body: result } = await uptimeEsClient.search({ body: params }); const buckets: HistogramQueryResult[] = result?.aggregations?.timeseries?.buckets ?? []; const histogram = buckets.map((bucket) => { const x: number = bucket.key; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts index e72b16de3d66f..3b852ad1a7381 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts @@ -52,8 +52,7 @@ const REMOVE_NON_SUMMARY_BROWSER_CHECKS = { }; export const getPings: UMElasticsearchQueryFn = async ({ - callES, - dynamicSettings, + uptimeEsClient, dateRange: { from, to }, index, monitorId, @@ -63,56 +62,39 @@ export const getPings: UMElasticsearchQueryFn = a location, }) => { const size = sizeParam ?? DEFAULT_PAGE_SIZE; - const sortParam = { sort: [{ '@timestamp': { order: sort ?? 'desc' } }] }; - const filter: any[] = [{ range: { '@timestamp': { gte: from, lte: to } } }]; - if (monitorId) { - filter.push({ term: { 'monitor.id': monitorId } }); - } - if (status) { - filter.push({ term: { 'monitor.status': status } }); - } - let postFilterClause = {}; - if (location) { - postFilterClause = { post_filter: { term: { 'observer.geo.name': location } } }; - } - const queryContext = { - bool: { - filter, - ...REMOVE_NON_SUMMARY_BROWSER_CHECKS, - }, - }; - const params: any = { - index: dynamicSettings.heartbeatIndices, - body: { - query: { - ...queryContext, + const searchBody = { + size, + ...(index ? { from: index * size } : {}), + query: { + bool: { + filter: [ + { range: { '@timestamp': { gte: from, lte: to } } }, + ...(monitorId ? [{ term: { 'monitor.id': monitorId } }] : []), + ...(status ? [{ term: { 'monitor.status': status } }] : []), + ], + ...REMOVE_NON_SUMMARY_BROWSER_CHECKS, }, - ...sortParam, - size, - aggregations: { - locations: { - terms: { - field: 'observer.geo.name', - missing: 'N/A', - size: 1000, - }, + }, + sort: [{ '@timestamp': { order: (sort ?? 'desc') as 'asc' | 'desc' } }], + aggs: { + locations: { + terms: { + field: 'observer.geo.name', + missing: 'N/A', + size: 1000, }, }, - ...postFilterClause, }, + ...(location ? { post_filter: { term: { 'observer.geo.name': location } } } : {}), }; - if (index) { - params.body.from = index * size; - } - const { body: { hits: { hits, total }, aggregations: aggs, }, - } = await callES.search(params); + } = await uptimeEsClient.search({ body: searchBody }); const locations = aggs?.locations ?? { buckets: [{ key: 'N/A', doc_count: 0 }] }; @@ -131,7 +113,7 @@ export const getPings: UMElasticsearchQueryFn = a return { total: total.value, - locations: locations.buckets.map((bucket: { key: string }) => bucket.key), + locations: locations.buckets.map((bucket) => bucket.key as string), pings, }; }; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts b/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts index ac36585ff0939..9df20c79a6106 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts @@ -16,15 +16,13 @@ export interface GetSnapshotCountParams { } export const getSnapshotCount: UMElasticsearchQueryFn = async ({ - callES, - dynamicSettings: { heartbeatIndices }, + uptimeEsClient, dateRangeStart, dateRangeEnd, filters, }): Promise => { const context = new QueryContext( - callES, - heartbeatIndices, + uptimeEsClient, dateRangeStart, dateRangeEnd, CONTEXT_DEFAULTS.CURSOR_PAGINATION, @@ -40,7 +38,6 @@ export const getSnapshotCount: UMElasticsearchQueryFn => { const { body: res } = await context.search({ - index: context.heartbeatIndices, body: statusCountBody(await context.dateAndCustomFilters()), }); diff --git a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/query_context.test.ts b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/query_context.test.ts index e53fff429dd8d..b4de286a5b92d 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/query_context.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/query_context.test.ts @@ -19,9 +19,7 @@ describe(QueryContext, () => { }; let qc: QueryContext; - beforeEach( - () => (qc = new QueryContext({}, 'indexName', rangeStart, rangeEnd, pagination, null, 10)) - ); + beforeEach(() => (qc = new QueryContext({}, rangeStart, rangeEnd, pagination, null, 10))); describe('dateRangeFilter()', () => { const expectedRange = { diff --git a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/test_helpers.ts b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/test_helpers.ts index 40775bde1c7f5..205b283d40d6a 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/test_helpers.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/test_helpers.ts @@ -8,13 +8,6 @@ import { CursorPagination } from '../types'; import { CursorDirection, SortOrder } from '../../../../../common/runtime_types'; import { QueryContext } from '../query_context'; -export const prevPagination = (key: any): CursorPagination => { - return { - cursorDirection: CursorDirection.BEFORE, - sortOrder: SortOrder.ASC, - cursorKey: key, - }; -}; export const nextPagination = (key: any): CursorPagination => { return { cursorDirection: CursorDirection.AFTER, @@ -23,14 +16,5 @@ export const nextPagination = (key: any): CursorPagination => { }; }; export const simpleQueryContext = (): QueryContext => { - return new QueryContext( - undefined, - 'indexName', - '', - '', - nextPagination('something'), - undefined, - 0, - '' - ); + return new QueryContext(undefined, '', '', nextPagination('something'), undefined, 0, ''); }; diff --git a/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts b/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts index 38e7dabb19941..2331d991e3af3 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts @@ -36,7 +36,6 @@ const query = async (queryContext: QueryContext, searchAfter: any, size: number) const body = await queryBody(queryContext, searchAfter, size); const params = { - index: queryContext.heartbeatIndices, body, }; diff --git a/x-pack/plugins/uptime/server/lib/requests/search/query_context.ts b/x-pack/plugins/uptime/server/lib/requests/search/query_context.ts index 96df8ea651c44..bcfb3035920fb 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/query_context.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/query_context.ts @@ -12,7 +12,6 @@ import { CursorDirection, SortOrder } from '../../../../common/runtime_types'; export class QueryContext { callES: ElasticsearchClient; - heartbeatIndices: string; dateRangeStart: string; dateRangeEnd: string; pagination: CursorPagination; @@ -23,7 +22,6 @@ export class QueryContext { constructor( database: any, - heartbeatIndices: string, dateRangeStart: string, dateRangeEnd: string, pagination: CursorPagination, @@ -32,7 +30,6 @@ export class QueryContext { statusFilter?: string ) { this.callES = database; - this.heartbeatIndices = heartbeatIndices; this.dateRangeStart = dateRangeStart; this.dateRangeEnd = dateRangeEnd; this.pagination = pagination; @@ -42,12 +39,10 @@ export class QueryContext { } async search(params: any): Promise { - params.index = this.heartbeatIndices; - return this.callES.search(params); + return this.callES.search({ body: params.body }); } async count(params: any): Promise { - params.index = this.heartbeatIndices; return this.callES.count(params); } @@ -138,7 +133,6 @@ export class QueryContext { clone(): QueryContext { return new QueryContext( this.callES, - this.heartbeatIndices, this.dateRangeStart, this.dateRangeEnd, this.pagination, diff --git a/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts b/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts index 6be9f813016f8..dc3af2805d13f 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts @@ -109,7 +109,6 @@ export const query = async ( potentialMatchMonitorIDs: string[] ): Promise => { const params = { - index: queryContext.heartbeatIndices, body: { size: 0, query: { diff --git a/x-pack/plugins/uptime/server/lib/saved_objects.ts b/x-pack/plugins/uptime/server/lib/saved_objects.ts index 2eec9e233f5d2..a9191dde3df4c 100644 --- a/x-pack/plugins/uptime/server/lib/saved_objects.ts +++ b/x-pack/plugins/uptime/server/lib/saved_objects.ts @@ -48,7 +48,7 @@ export const savedObjectsAdapter: UMSavedObjectsAdapter = { getUptimeDynamicSettings: async (client): Promise => { try { const obj = await client.get(umDynamicSettings.name, settingsObjectId); - return obj.attributes; + return obj?.attributes ?? DYNAMIC_SETTINGS_DEFAULTS; } catch (getErr) { if (SavedObjectsErrorHelpers.isNotFoundError(getErr)) { return DYNAMIC_SETTINGS_DEFAULTS; diff --git a/x-pack/plugins/uptime/server/rest_api/certs/certs.ts b/x-pack/plugins/uptime/server/rest_api/certs/certs.ts index fb22d603a2d56..d377095a2a370 100644 --- a/x-pack/plugins/uptime/server/rest_api/certs/certs.ts +++ b/x-pack/plugins/uptime/server/rest_api/certs/certs.ts @@ -30,7 +30,7 @@ export const createGetCertsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = direction: schema.maybe(schema.string()), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, request, response): Promise => { const index = request.query?.index ?? 0; const size = request.query?.size ?? DEFAULT_SIZE; const from = request.query?.from ?? DEFAULT_FROM; @@ -39,8 +39,7 @@ export const createGetCertsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = const direction = request.query?.direction ?? DEFAULT_DIRECTION; const { search } = request.query; const result = await libs.requests.getCerts({ - callES, - dynamicSettings, + uptimeEsClient, index, search, size, diff --git a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts index 98e995173d811..f7be9e10d1004 100644 --- a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts +++ b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts @@ -20,7 +20,9 @@ export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSer method: 'GET', path: '/api/uptime/dynamic_settings', validate: false, - handler: async ({ dynamicSettings }, _context, _request, response): Promise => { + handler: async ({ savedObjectsClient }, _context, _request, response): Promise => { + const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); + return response.ok({ body: dynamicSettings, }); diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts index 418cde9e701d5..5c1be4cdd8143 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts @@ -12,13 +12,12 @@ export const createGetIndexPatternRoute: UMRestApiRouteFactory = (libs: UMServer method: 'GET', path: API_URLS.INDEX_PATTERN, validate: false, - handler: async ({ callES, dynamicSettings }, _context, _request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, _request, response): Promise => { try { return response.ok({ body: { ...(await libs.requests.getIndexPattern({ - esClient: callES, - dynamicSettings, + uptimeEsClient, })), }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts index 9a4280efa98f9..e57643aed7e36 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts @@ -12,11 +12,13 @@ export const createGetIndexStatusRoute: UMRestApiRouteFactory = (libs: UMServerL method: 'GET', path: API_URLS.INDEX_STATUS, validate: false, - handler: async ({ callES, dynamicSettings }, _context, _request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, _request, response): Promise => { try { return response.ok({ body: { - ...(await libs.requests.getIndexStatus({ callES, dynamicSettings })), + ...(await libs.requests.getIndexStatus({ + uptimeEsClient, + })), }, }); } catch (e) { diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts index 7b461060bf4bc..3eac49341b2c2 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts @@ -24,7 +24,7 @@ export const createMonitorListRoute: UMRestApiRouteFactory = (libs) => ({ options: { tags: ['access:uptime-read'], }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, request, response): Promise => { try { const { dateRangeStart, @@ -42,10 +42,9 @@ export const createMonitorListRoute: UMRestApiRouteFactory = (libs) => ({ indexStatus, { summaries, nextPagePagination, prevPagePagination }, ] = await Promise.all([ - libs.requests.getIndexStatus({ callES, dynamicSettings }), + libs.requests.getIndexStatus({ uptimeEsClient }), libs.requests.getMonitorStates({ - callES, - dynamicSettings, + uptimeEsClient, dateRangeStart, dateRangeEnd, pagination: decodedPagination, diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts index a110209043a7e..e2dbf7114fc91 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts @@ -19,14 +19,13 @@ export const createGetMonitorLocationsRoute: UMRestApiRouteFactory = (libs: UMSe dateEnd: schema.string(), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; return response.ok({ body: { ...(await libs.requests.getMonitorLocations({ - callES, - dynamicSettings, + uptimeEsClient, monitorId, dateStart, dateEnd, diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts index bb002f8a8c286..3d47f0eab8640 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts @@ -20,11 +20,10 @@ export const createGetStatusBarRoute: UMRestApiRouteFactory = (libs: UMServerLib dateEnd: schema.string(), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; const result = await libs.requests.getLatestMonitor({ - callES, - dynamicSettings, + uptimeEsClient, monitorId, dateStart, dateEnd, diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts index bb54effc0d57e..0982fc1986604 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts @@ -19,7 +19,7 @@ export const createGetMonitorDetailsRoute: UMRestApiRouteFactory = (libs: UMServ dateEnd: schema.maybe(schema.string()), }), }, - handler: async ({ callES, dynamicSettings }, context, request, response): Promise => { + handler: async ({ uptimeEsClient }, context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; const alertsClient = context.alerting?.getAlertsClient(); @@ -27,8 +27,7 @@ export const createGetMonitorDetailsRoute: UMRestApiRouteFactory = (libs: UMServ return response.ok({ body: { ...(await libs.requests.getMonitorDetails({ - callES, - dynamicSettings, + uptimeEsClient, monitorId, dateStart, dateEnd, diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts index a4e024e795b46..eec3fdf9e7257 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts @@ -20,14 +20,13 @@ export const createGetMonitorDurationRoute: UMRestApiRouteFactory = (libs: UMSer dateEnd: schema.string(), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; return response.ok({ body: { ...(await libs.requests.getMonitorDurationChart({ - callES, - dynamicSettings, + uptimeEsClient, monitorId, dateStart, dateEnd, diff --git a/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts b/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts index 00cbaf0d16723..163fbd4f8dd6e 100644 --- a/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts +++ b/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts @@ -28,7 +28,7 @@ export const createGetOverviewFilters: UMRestApiRouteFactory = (libs: UMServerLi tags: arrayOrStringType, }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response) => { + handler: async ({ uptimeEsClient }, _context, request, response) => { const { dateRangeStart, dateRangeEnd, locations, schemes, search, ports, tags } = request.query; let parsedSearch: Record | undefined; @@ -41,8 +41,7 @@ export const createGetOverviewFilters: UMRestApiRouteFactory = (libs: UMServerLi } const filtersResponse = await libs.requests.getFilterBar({ - callES, - dynamicSettings, + uptimeEsClient, dateRangeStart, dateRangeEnd, search: parsedSearch, diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts index 4ac50d0e78c4c..ba36b171793b7 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts @@ -21,12 +21,11 @@ export const createGetPingHistogramRoute: UMRestApiRouteFactory = (libs: UMServe bucketSize: schema.maybe(schema.string()), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, request, response): Promise => { const { dateStart, dateEnd, monitorId, filters, bucketSize } = request.query; const result = await libs.requests.getPingHistogram({ - callES, - dynamicSettings, + uptimeEsClient, from: dateStart, to: dateEnd, monitorId, diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts index d97195a7fe2b1..d00386d06c74c 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts @@ -27,7 +27,7 @@ export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = status: schema.maybe(schema.string()), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, request, response): Promise => { const { from, to, ...optional } = request.query; const params = GetPingsParamsType.decode({ dateRange: { from, to }, ...optional }); if (isLeft(params)) { @@ -37,8 +37,7 @@ export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = } const result = await libs.requests.getPings({ - callES, - dynamicSettings, + uptimeEsClient, ...params.right, }); diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts index 1fc52dd24f9d0..161d7dd558fdb 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts @@ -17,11 +17,10 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ stepIndex: schema.number(), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response) => { + handler: async ({ uptimeEsClient }, _context, request, response) => { const { checkGroup, stepIndex } = request.params; const result = await libs.requests.getJourneyScreenshot({ - callES, - dynamicSettings, + uptimeEsClient, checkGroup, stepIndex, }); diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts index b6e06850ad3b6..5b8583ea0322f 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts @@ -16,11 +16,10 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => checkGroup: schema.string(), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response) => { + handler: async ({ uptimeEsClient }, _context, request, response) => { const { checkGroup } = request.params; const result = await libs.requests.getJourneySteps({ - callES, - dynamicSettings, + uptimeEsClient, checkGroup, }); diff --git a/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts b/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts index 9502335e4e5a8..224ef87fd90af 100644 --- a/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts +++ b/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts @@ -19,11 +19,10 @@ export const createGetSnapshotCount: UMRestApiRouteFactory = (libs: UMServerLibs filters: schema.maybe(schema.string()), }), }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient }, _context, request, response): Promise => { const { dateRangeStart, dateRangeEnd, filters } = request.query; const result = await libs.requests.getSnapshotCount({ - callES, - dynamicSettings, + uptimeEsClient, dateRangeStart, dateRangeEnd, filters, diff --git a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts index 9881d41f3bf2b..85f274c96cf9a 100644 --- a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts +++ b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts @@ -24,7 +24,7 @@ export const createLogPageViewRoute: UMRestApiRouteFactory = () => ({ }), }, handler: async ( - { savedObjectsClient, callES, dynamicSettings }, + { savedObjectsClient, uptimeEsClient }, _context, request, response @@ -33,7 +33,10 @@ export const createLogPageViewRoute: UMRestApiRouteFactory = () => ({ if (pageView.refreshTelemetryHistory) { KibanaTelemetryAdapter.clearLocalTelemetry(); } - await KibanaTelemetryAdapter.countNoOfUniqueMonitorAndLocations(callES, savedObjectsClient); + await KibanaTelemetryAdapter.countNoOfUniqueMonitorAndLocations( + uptimeEsClient, + savedObjectsClient + ); const pageViewResult = KibanaTelemetryAdapter.countPageView(pageView as PageViewParams); return response.ok({ diff --git a/x-pack/plugins/uptime/server/rest_api/types.ts b/x-pack/plugins/uptime/server/rest_api/types.ts index 5e5f4a2a991cf..df1762a3b318d 100644 --- a/x-pack/plugins/uptime/server/rest_api/types.ts +++ b/x-pack/plugins/uptime/server/rest_api/types.ts @@ -15,10 +15,8 @@ import { KibanaResponseFactory, IKibanaResponse, IScopedClusterClient, - ElasticsearchClient, } from 'kibana/server'; -import { DynamicSettings } from '../../common/runtime_types'; -import { UMServerLibs } from '../lib/lib'; +import { UMServerLibs, UptimeESClient } from '../lib/lib'; /** * Defines the basic properties employed by Uptime routes. @@ -64,9 +62,8 @@ export type UMKibanaRouteWrapper = (uptimeRoute: UptimeRoute) => UMKibanaRoute; * This type can store custom parameters used by the internal Uptime route handlers. */ export interface UMRouteParams { - callES: ElasticsearchClient; + uptimeEsClient: UptimeESClient; esClient: IScopedClusterClient; - dynamicSettings: DynamicSettings; savedObjectsClient: SavedObjectsClientContract; } diff --git a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts index b2f1c7d6424e6..a1cf3c05e2de3 100644 --- a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts +++ b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts @@ -5,7 +5,7 @@ */ import { UMKibanaRouteWrapper } from './types'; -import { savedObjectsAdapter } from '../lib/saved_objects'; +import { createUptimeESClient } from '../lib/lib'; export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute) => ({ ...uptimeRoute, @@ -15,9 +15,14 @@ export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute) => ({ handler: async (context, request, response) => { const { client: esClient } = context.core.elasticsearch; const { client: savedObjectsClient } = context.core.savedObjects; - const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); + + const uptimeEsClient = createUptimeESClient({ + savedObjectsClient, + esClient: esClient.asCurrentUser, + }); + return uptimeRoute.handler( - { callES: esClient.asCurrentUser, esClient, savedObjectsClient, dynamicSettings }, + { uptimeEsClient, esClient, savedObjectsClient }, context, request, response diff --git a/x-pack/typings/elasticsearch/aggregations.d.ts b/x-pack/typings/elasticsearch/aggregations.d.ts index 6b64ad6276f57..c63d85bd82dc0 100644 --- a/x-pack/typings/elasticsearch/aggregations.d.ts +++ b/x-pack/typings/elasticsearch/aggregations.d.ts @@ -172,6 +172,12 @@ export interface AggregationOptionsByType { field?: string; background_filter?: Record; } & AggregationSourceOptions; + bucket_selector: { + buckets_path: { + [x: string]: string; + }; + script: string; + }; } type AggregationType = keyof AggregationOptionsByType; @@ -204,14 +210,14 @@ type SubAggregationResponseOf< interface AggregationResponsePart { terms: { - doc_count_error_upper_bound: number; - sum_other_doc_count: number; buckets: Array< { doc_count: number; key: string | number; } & SubAggregationResponseOf >; + doc_count_error_upper_bound?: number; + sum_other_doc_count?: number; }; histogram: { buckets: Array< @@ -362,6 +368,7 @@ interface AggregationResponsePart; }; bucket_sort: undefined; + bucket_selector: undefined; } // Type for debugging purposes. If you see an error in AggregationResponseMap diff --git a/x-pack/typings/elasticsearch/index.d.ts b/x-pack/typings/elasticsearch/index.d.ts index f5d595edcd71e..f2c51f601a099 100644 --- a/x-pack/typings/elasticsearch/index.d.ts +++ b/x-pack/typings/elasticsearch/index.d.ts @@ -5,7 +5,7 @@ */ import { SearchParams, SearchResponse } from 'elasticsearch'; -import { AggregationResponseMap, AggregationInputMap } from './aggregations'; +import { AggregationResponseMap, AggregationInputMap, SortOptions } from './aggregations'; export { AggregationInputMap, AggregationOptionsByType, @@ -23,7 +23,13 @@ interface CollapseQuery { inner_hits: { name: string; size?: number; - sort?: [{ date: 'asc' | 'desc' }]; + sort?: SortOptions; + _source?: { + includes: string[]; + }; + collapse?: { + field: string; + }; }; max_concurrent_group_searches?: number; } @@ -31,9 +37,11 @@ interface CollapseQuery { export interface ESSearchBody { query?: any; size?: number; + from?: number; aggs?: AggregationInputMap; track_total_hits?: boolean | number; collapse?: CollapseQuery; + _source?: string | string[] | { excludes: string | string[] }; } export type ESSearchRequest = Omit & {