From b7ff35db2da5e47a8399b4b4b9f179a375988e0c Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Tue, 17 Dec 2019 16:25:15 -0500 Subject: [PATCH] [Uptime] Remove legacy es client (#51403) * Move a REST endpoint and the GQL endpoint to NP routing. * Delete obsolete REST endpoint. * Update remaining REST routes to work with NP router. * Remove obsolete code, update some unit tests. * Simplify route creation. * Remove tests of API decommissioned API endpoint. * Rename domain check. * Make return shape of index pattern endpoint correspond to required NP resp body. * Move validate to appropriate level of route definition object for monitor details endpoint. * Update snapshot count route. * Fix broken lint rule. * Move a REST endpoint and the GQL endpoint to NP routing. * Update remaining REST routes to work with NP router. * Update remaining REST routes to work with NP router. * Refactor query functions to accept new es client from request contexts. * WIP updating framework adapter. * Refactor remaining routes/resolvers to remove usage of legacy Elasticsearch client. * Fix broken unit tests. * Fix incorrect user usage for a REST endpoint. * Fix some broken imports and types. * Port monitor details REST endpoint to NP. * Remove some merge errors. * Update adapters to take a single options parameter. * Update broken test files. * Resolve typescript warnings. * Update resolver types. * Change GraphQL interface name for es client. * Delete unused code and fix incorrect type. * Rename type for REST endpoint creators. * Nest message values in body object for invalid response messages. * Reorganize a file and clean up some types. * Add wrapper function to reduce boilerplate route code. --- x-pack/legacy/plugins/uptime/index.ts | 3 +- .../graphql/monitor_states/resolvers.ts | 23 +- .../server/graphql/monitors/resolvers.ts | 54 ++-- .../uptime/server/graphql/pings/resolvers.ts | 16 +- .../plugins/uptime/server/graphql/types.ts | 12 +- .../lib/adapters/database/adapter_types.ts | 35 --- .../server/lib/adapters/database/index.ts | 7 - .../database/kibana_database_adapter.ts | 32 --- .../lib/adapters/framework/adapter_types.ts | 29 +- .../framework/kibana_framework_adapter.ts | 49 ++-- .../uptime/server/lib/adapters/index.ts | 1 - .../adapters/monitor_states/adapter_types.ts | 38 +-- .../elasticsearch_monitor_states_adapter.ts | 61 ++-- .../lib/adapters/monitor_states/index.ts | 2 +- .../search/__tests__/test_helpers.ts | 4 +- .../search/enrich_monitor_groups.ts | 4 +- .../search/find_potential_matches.ts | 2 +- .../search/refine_potential_matches.ts | 4 +- ...lasticsearch_monitors_adapter.test.ts.snap | 4 +- .../elasticsearch_monitors_adapter.test.ts | 60 ++-- .../lib/adapters/monitors/adapter_types.ts | 67 +++-- .../elasticsearch_monitors_adapter.ts | 97 ++----- .../server/lib/adapters/monitors/index.ts | 2 +- .../elasticsearch_pings_adapter.test.ts | 271 +++++++++--------- .../lib/adapters/pings/adapter_types.ts | 106 +++++-- .../pings/elasticsearch_pings_adapter.ts | 113 +++----- .../uptime/server/lib/adapters/pings/index.ts | 2 +- .../lib/adapters/saved_objects/index.ts | 2 +- .../kibana_saved_objects_adapter.ts | 21 +- .../lib/adapters/saved_objects/types.ts | 4 +- .../uptime/server/lib/compose/kibana.ts | 22 +- .../__snapshots__/license.test.ts.snap | 1 + .../lib/domains/__tests__/license.test.ts | 2 +- .../uptime/server/lib/domains/license.ts | 7 +- .../legacy/plugins/uptime/server/lib/lib.ts | 2 - .../server/rest_api/create_route_with_auth.ts | 32 ++- .../plugins/uptime/server/rest_api/index.ts | 5 +- .../index_pattern/get_index_pattern.ts | 8 +- .../rest_api/monitors/monitor_locations.ts | 13 +- .../rest_api/monitors/monitors_details.ts | 10 +- .../uptime/server/rest_api/pings/get_all.ts | 16 +- .../rest_api/snapshot/get_snapshot_count.ts | 14 +- .../rest_api/telemetry/log_monitor_page.ts | 6 +- .../rest_api/telemetry/log_overview_page.ts | 6 +- .../plugins/uptime/server/rest_api/types.ts | 82 +++++- .../server/rest_api/uptime_route_wrapper.ts | 18 ++ .../plugins/uptime/server/uptime_server.ts | 6 +- 47 files changed, 687 insertions(+), 688 deletions(-) delete mode 100644 x-pack/legacy/plugins/uptime/server/lib/adapters/database/adapter_types.ts delete mode 100644 x-pack/legacy/plugins/uptime/server/lib/adapters/database/index.ts delete mode 100644 x-pack/legacy/plugins/uptime/server/lib/adapters/database/kibana_database_adapter.ts create mode 100644 x-pack/legacy/plugins/uptime/server/rest_api/uptime_route_wrapper.ts diff --git a/x-pack/legacy/plugins/uptime/index.ts b/x-pack/legacy/plugins/uptime/index.ts index c8de623cb0a13..e090a2c85e136 100644 --- a/x-pack/legacy/plugins/uptime/index.ts +++ b/x-pack/legacy/plugins/uptime/index.ts @@ -36,7 +36,7 @@ export const uptime = (kibana: any) => init(server: KibanaServer) { const initializerContext = {} as PluginInitializerContext; const { savedObjects } = server; - const { elasticsearch, xpack_main } = server.plugins; + const { xpack_main } = server.plugins; const { usageCollection } = server.newPlatform.setup.plugins; plugin(initializerContext).setup( @@ -44,7 +44,6 @@ export const uptime = (kibana: any) => route: server.newPlatform.setup.core.http.createRouter(), }, { - elasticsearch, savedObjects, usageCollection, xpack: xpack_main, diff --git a/x-pack/legacy/plugins/uptime/server/graphql/monitor_states/resolvers.ts b/x-pack/legacy/plugins/uptime/server/graphql/monitor_states/resolvers.ts index 8fbf3529dbe24..8ddb07b3093d0 100644 --- a/x-pack/legacy/plugins/uptime/server/graphql/monitor_states/resolvers.ts +++ b/x-pack/legacy/plugins/uptime/server/graphql/monitor_states/resolvers.ts @@ -39,9 +39,9 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( return { Query: { async getMonitorStates( - resolver, + _resolver, { dateRangeStart, dateRangeEnd, filters, pagination, statusFilter }, - { req } + { APICaller } ): Promise { const decodedPagination = pagination ? JSON.parse(decodeURIComponent(pagination)) @@ -50,15 +50,18 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( totalSummaryCount, { summaries, nextPagePagination, prevPagePagination }, ] = await Promise.all([ - libs.pings.getDocCount(req), - libs.monitorStates.getMonitorStates( - req, + libs.pings.getDocCount({ callES: APICaller }), + libs.monitorStates.getMonitorStates({ + callES: APICaller, dateRangeStart, dateRangeEnd, - decodedPagination, + pagination: decodedPagination, filters, - statusFilter - ), + // this is added to make typescript happy, + // this sort of reassignment used to be further downstream but I've moved it here + // because this code is going to be decomissioned soon + statusFilter: statusFilter || undefined, + }), ]); return { summaries, @@ -67,8 +70,8 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( totalSummaryCount, }; }, - async getStatesIndexStatus(resolver, {}, { req }): Promise { - return await libs.monitorStates.statesIndexExists(req); + async getStatesIndexStatus(_resolver, {}, { APICaller }): Promise { + return await libs.monitorStates.statesIndexExists({ callES: APICaller }); }, }, }; diff --git a/x-pack/legacy/plugins/uptime/server/graphql/monitors/resolvers.ts b/x-pack/legacy/plugins/uptime/server/graphql/monitors/resolvers.ts index 415afc87e201e..8b685d8e08a2b 100644 --- a/x-pack/legacy/plugins/uptime/server/graphql/monitors/resolvers.ts +++ b/x-pack/legacy/plugins/uptime/server/graphql/monitors/resolvers.ts @@ -71,54 +71,62 @@ export const createMonitorsResolvers: CreateUMGraphQLResolvers = ( } => ({ Query: { async getSnapshotHistogram( - resolver, + _resolver, { dateRangeStart, dateRangeEnd, filters, monitorId, statusFilter }, - { req } + { APICaller } ): Promise { - return await libs.pings.getPingHistogram( - req, + return await libs.pings.getPingHistogram({ + callES: APICaller, dateRangeStart, dateRangeEnd, filters, monitorId, - statusFilter - ); + statusFilter, + }); }, async getMonitorChartsData( - resolver, + _resolver, { monitorId, dateRangeStart, dateRangeEnd, location }, - { req } + { APICaller } ): Promise { - return await libs.monitors.getMonitorChartsData( - req, + return await libs.monitors.getMonitorChartsData({ + callES: APICaller, monitorId, dateRangeStart, dateRangeEnd, - location - ); + location, + }); }, async getLatestMonitors( - resolver, + _resolver, { dateRangeStart, dateRangeEnd, monitorId, location }, - { req } + { APICaller } ): Promise { - return await libs.pings.getLatestMonitorDocs( - req, + return await libs.pings.getLatestMonitorDocs({ + callES: APICaller, dateRangeStart, dateRangeEnd, monitorId, - location - ); + location, + }); }, - async getFilterBar(resolver, { dateRangeStart, dateRangeEnd }, { req }): Promise { - return await libs.monitors.getFilterBar(req, dateRangeStart, dateRangeEnd); + async getFilterBar( + _resolver, + { dateRangeStart, dateRangeEnd }, + { APICaller } + ): Promise { + return await libs.monitors.getFilterBar({ + callES: APICaller, + dateRangeStart, + dateRangeEnd, + }); }, async getMonitorPageTitle( - resolver: any, + _resolver: any, { monitorId }, - { req } + { APICaller } ): Promise { - return await libs.monitors.getMonitorPageTitle(req, monitorId); + return await libs.monitors.getMonitorPageTitle({ callES: APICaller, monitorId }); }, }, }); diff --git a/x-pack/legacy/plugins/uptime/server/graphql/pings/resolvers.ts b/x-pack/legacy/plugins/uptime/server/graphql/pings/resolvers.ts index 066080cec353f..373e1467433a2 100644 --- a/x-pack/legacy/plugins/uptime/server/graphql/pings/resolvers.ts +++ b/x-pack/legacy/plugins/uptime/server/graphql/pings/resolvers.ts @@ -34,23 +34,23 @@ export const createPingsResolvers: CreateUMGraphQLResolvers = ( } => ({ Query: { async allPings( - resolver, + _resolver, { monitorId, sort, size, status, dateRangeStart, dateRangeEnd, location }, - { req } + { APICaller } ): Promise { - return await libs.pings.getAll( - req, + return await libs.pings.getAll({ + callES: APICaller, dateRangeStart, dateRangeEnd, monitorId, status, sort, size, - location - ); + location, + }); }, - async getDocCount(resolver, args, { req }): Promise { - return libs.pings.getDocCount(req); + async getDocCount(_resolver, _args, { APICaller }): Promise { + return libs.pings.getDocCount({ callES: APICaller }); }, }, }); diff --git a/x-pack/legacy/plugins/uptime/server/graphql/types.ts b/x-pack/legacy/plugins/uptime/server/graphql/types.ts index cb7634ff42b4c..529ab41a62b3b 100644 --- a/x-pack/legacy/plugins/uptime/server/graphql/types.ts +++ b/x-pack/legacy/plugins/uptime/server/graphql/types.ts @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Request } from 'hapi'; +import { RequestHandlerContext, CallAPIOptions } from 'kibana/server'; import { UMServerLibs } from '../lib/lib'; -export interface UMContext { - req: Request; -} +export type UMContext = RequestHandlerContext & { + APICaller: ( + endpoint: string, + clientParams?: Record, + options?: CallAPIOptions | undefined + ) => Promise; +}; export interface UMGraphQLResolver { Query?: any; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/database/adapter_types.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/database/adapter_types.ts deleted file mode 100644 index ace86cf946043..0000000000000 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/database/adapter_types.ts +++ /dev/null @@ -1,35 +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 { CountParams, CountResponse } from 'elasticsearch'; - -export interface HistogramQueryResult { - key: number; - doc_count: number; - bucket_total: { - value: number; - }; - down: { - bucket_count: { - value: number; - }; - }; -} - -export interface UMESBucket { - key: number; -} - -export interface UMESHistogramBucket { - x: number; - x0: number; -} - -export interface DatabaseAdapter { - count(request: any, params: CountParams): Promise; - search(request: any, params: any): Promise; - head(request: any, params: any): Promise; -} diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/database/index.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/database/index.ts deleted file mode 100644 index 4e09b5d0e9e2d..0000000000000 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/database/index.ts +++ /dev/null @@ -1,7 +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. - */ - -export * from './adapter_types'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/database/kibana_database_adapter.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/database/kibana_database_adapter.ts deleted file mode 100644 index 4b2523503fc8f..0000000000000 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/database/kibana_database_adapter.ts +++ /dev/null @@ -1,32 +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 { DatabaseAdapter } from './adapter_types'; - -interface KibanaElasticsearchPlugin { - getCluster: (clusterName: 'admin' | 'data') => any; - callWithRequest: (request: any, action: string, params: any) => any; -} - -export class UMKibanaDatabaseAdapter implements DatabaseAdapter { - private elasticsearch: KibanaElasticsearchPlugin; - - constructor(kbnElasticsearch: KibanaElasticsearchPlugin) { - this.elasticsearch = kbnElasticsearch.getCluster('data'); - } - - public async search(request: any, params: any): Promise { - return this.elasticsearch.callWithRequest(request, 'search', params); - } - - public async count(request: any, params: any): Promise { - return this.elasticsearch.callWithRequest(request, 'count', params); - } - - public async head(request: any, params: { index: string }): Promise { - return this.elasticsearch.callWithRequest(request, 'indices.exists', params); - } -} diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/framework/adapter_types.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/framework/adapter_types.ts index e67f4fa59531c..b490bf17e292c 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/framework/adapter_types.ts @@ -5,10 +5,16 @@ */ import { GraphQLSchema } from 'graphql'; -import { SavedObjectsLegacyService, RequestHandler, IRouter } from 'src/core/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { + SavedObjectsLegacyService, + RequestHandler, + IRouter, + CallAPIOptions, + SavedObjectsClientContract, +} from 'src/core/server'; import { ObjectType } from '@kbn/config-schema'; -import { UMRouteDefinition } from '../../../rest_api'; +import { UMKibanaRoute } from '../../../rest_api'; export interface UMFrameworkRouteOptions< P extends ObjectType, @@ -22,19 +28,32 @@ export interface UMFrameworkRouteOptions< validate: any; } +type APICaller = ( + endpoint: string, + clientParams: Record, + options?: CallAPIOptions +) => Promise; + +export type UMElasticsearchQueryFn = ( + params: { callES: APICaller } & P +) => Promise | R; + +export type UMSavedObjectsQueryFn = ( + client: SavedObjectsClientContract, + params: P +) => Promise | T; + export interface UptimeCoreSetup { route: IRouter; } export interface UptimeCorePlugins { - elasticsearch: any; savedObjects: SavedObjectsLegacyService; usageCollection: UsageCollectionSetup; xpack: any; } export interface UMBackendFrameworkAdapter { - registerRoute(route: UMRouteDefinition): void; + registerRoute(route: UMKibanaRoute): void; registerGraphQLEndpoint(routePath: string, schema: GraphQLSchema): void; - getSavedObjectsClient(): any; } diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/framework/kibana_framework_adapter.ts index a46a7e11bd738..7ac3db9d0f3d7 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -7,20 +7,16 @@ import { GraphQLSchema } from 'graphql'; import { schema as kbnSchema } from '@kbn/config-schema'; import { runHttpQuery } from 'apollo-server-core'; -import { UptimeCorePlugins, UptimeCoreSetup } from './adapter_types'; +import { UptimeCoreSetup } from './adapter_types'; import { UMBackendFrameworkAdapter } from './adapter_types'; -import { UMRouteDefinition } from '../../../rest_api'; +import { UMKibanaRoute } from '../../../rest_api'; export class UMKibanaBackendFrameworkAdapter implements UMBackendFrameworkAdapter { - constructor( - private readonly server: UptimeCoreSetup, - private readonly plugins: UptimeCorePlugins - ) { + constructor(private readonly server: UptimeCoreSetup) { this.server = server; - this.plugins = plugins; } - public registerRoute({ handler, method, options, path, validate }: UMRouteDefinition) { + public registerRoute({ handler, method, options, path, validate }: UMKibanaRoute) { const routeDefinition = { path, validate, @@ -39,16 +35,6 @@ export class UMKibanaBackendFrameworkAdapter implements UMBackendFrameworkAdapte } public registerGraphQLEndpoint(routePath: string, schema: GraphQLSchema): void { - const options = { - graphQLOptions: (req: any) => ({ - context: { req }, - schema, - }), - path: routePath, - route: { - tags: ['access:uptime'], - }, - }; this.server.route.post( { path: routePath, @@ -64,6 +50,25 @@ export class UMKibanaBackendFrameworkAdapter implements UMBackendFrameworkAdapte }, }, async (context, request, resp): Promise => { + const { + core: { + elasticsearch: { + dataClient: { callAsCurrentUser }, + }, + }, + } = context; + const options = { + graphQLOptions: (_req: any) => { + return { + context: { ...context, APICaller: callAsCurrentUser }, + schema, + }; + }, + path: routePath, + route: { + tags: ['access:uptime'], + }, + }; try { const query = request.body as Record; @@ -91,12 +96,4 @@ export class UMKibanaBackendFrameworkAdapter implements UMBackendFrameworkAdapte } ); } - - public getSavedObjectsClient() { - const { elasticsearch, savedObjects } = this.plugins; - const { SavedObjectsClient, getSavedObjectsRepository } = savedObjects; - const { callWithInternalUser } = elasticsearch.getCluster('admin'); - const internalRepository = getSavedObjectsRepository(callWithInternalUser); - return new SavedObjectsClient(internalRepository); - } } diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/index.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/index.ts index 2a88df91adc29..f5ff3b8c62ba9 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/index.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './database'; export * from './framework'; export * from './monitor_states'; export * from './monitors'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/adapter_types.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/adapter_types.ts index 57b1744f5d324..4104a9287a28d 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/adapter_types.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/adapter_types.ts @@ -6,28 +6,32 @@ import { MonitorSummary, - StatesIndexStatus, CursorDirection, SortOrder, + StatesIndexStatus, } from '../../../../common/graphql/types'; +import { UMElasticsearchQueryFn } from '../framework'; +import { Snapshot } from '../../../../common/runtime_types'; + +export interface MonitorStatesParams { + dateRangeStart: string; + dateRangeEnd: string; + pagination?: CursorPagination; + filters?: string | null; + statusFilter?: string; +} + +export interface GetSnapshotCountParams { + dateRangeStart: string; + dateRangeEnd: string; + filters?: string | null; + statusFilter?: string; +} export interface UMMonitorStatesAdapter { - getMonitorStates( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - pagination?: CursorPagination, - filters?: string | null, - statusFilter?: string | null - ): Promise; - statesIndexExists(request: any): Promise; - getSnapshotCount( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - filters?: string, - statusFilter?: string - ): Promise; + getMonitorStates: UMElasticsearchQueryFn; + getSnapshotCount: UMElasticsearchQueryFn; + statesIndexExists: UMElasticsearchQueryFn<{}, StatesIndexStatus>; } export interface CursorPagination { diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/elasticsearch_monitor_states_adapter.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/elasticsearch_monitor_states_adapter.ts index c3593854fa53f..d264da2e7ec0c 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/elasticsearch_monitor_states_adapter.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/elasticsearch_monitor_states_adapter.ts @@ -4,18 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DatabaseAdapter } from '../database'; -import { UMMonitorStatesAdapter, GetMonitorStatesResult, CursorPagination } from './adapter_types'; -import { StatesIndexStatus } from '../../../../common/graphql/types'; +import { UMMonitorStatesAdapter, CursorPagination } from './adapter_types'; import { INDEX_NAMES, CONTEXT_DEFAULTS } from '../../../../common/constants'; import { fetchPage } from './search'; import { MonitorGroupIterator } from './search/monitor_group_iterator'; -import { Snapshot } from '../../../../common/runtime_types'; import { getSnapshotCountHelper } from './get_snapshot_helper'; export interface QueryContext { - database: any; - request: any; + count: (query: Record) => Promise; + search: (query: Record) => Promise; dateRangeStart: string; dateRangeEnd: string; pagination: CursorPagination; @@ -24,25 +21,23 @@ export interface QueryContext { statusFilter?: string; } -export class ElasticsearchMonitorStatesAdapter implements UMMonitorStatesAdapter { - constructor(private readonly database: DatabaseAdapter) { - this.database = database; - } - +export const elasticsearchMonitorStatesAdapter: UMMonitorStatesAdapter = { // Gets a page of monitor states. - public async getMonitorStates( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - pagination: CursorPagination = CONTEXT_DEFAULTS.CURSOR_PAGINATION, - filters?: string | null, - statusFilter?: string - ): Promise { + getMonitorStates: async ({ + callES, + dateRangeStart, + dateRangeEnd, + pagination, + filters, + statusFilter, + }) => { + pagination = pagination || CONTEXT_DEFAULTS.CURSOR_PAGINATION; + statusFilter = statusFilter === null ? undefined : statusFilter; const size = 10; const queryContext: QueryContext = { - database: this.database, - request, + count: (query: Record): Promise => callES('count', query), + search: (query: Record): Promise => callES('search', query), dateRangeStart, dateRangeEnd, pagination, @@ -58,18 +53,12 @@ export class ElasticsearchMonitorStatesAdapter implements UMMonitorStatesAdapter nextPagePagination: jsonifyPagination(page.nextPagePagination), prevPagePagination: jsonifyPagination(page.prevPagePagination), }; - } + }, - public async getSnapshotCount( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - filters?: string, - statusFilter?: string - ): Promise { + getSnapshotCount: async ({ callES, dateRangeStart, dateRangeEnd, filters, statusFilter }) => { const context: QueryContext = { - database: this.database, - request, + count: query => callES('count', query), + search: query => callES('search', query), dateRangeStart, dateRangeEnd, pagination: CONTEXT_DEFAULTS.CURSOR_PAGINATION, @@ -78,22 +67,22 @@ export class ElasticsearchMonitorStatesAdapter implements UMMonitorStatesAdapter statusFilter, }; return getSnapshotCountHelper(new MonitorGroupIterator(context)); - } + }, - public async statesIndexExists(request: any): Promise { + statesIndexExists: async ({ callES }) => { // TODO: adapt this to the states index in future release const { _shards: { total }, count, - } = await this.database.count(request, { index: INDEX_NAMES.HEARTBEAT }); + } = await callES('count', { index: INDEX_NAMES.HEARTBEAT }); return { indexExists: total > 0, docCount: { count, }, }; - } -} + }, +}; // To simplify the handling of the group of pagination vars they're passed back to the client as a string const jsonifyPagination = (p: any): string | null => { diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/index.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/index.ts index f23cacce7bbbe..a3fc71ad48cb3 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/index.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/index.ts @@ -5,4 +5,4 @@ */ export * from './adapter_types'; -export { ElasticsearchMonitorStatesAdapter } from './elasticsearch_monitor_states_adapter'; +export { elasticsearchMonitorStatesAdapter } from './elasticsearch_monitor_states_adapter'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/__tests__/test_helpers.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/__tests__/test_helpers.ts index 0c3572604879e..d6fe5f82e735d 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/__tests__/test_helpers.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/__tests__/test_helpers.ts @@ -24,12 +24,12 @@ export const nextPagination = (key: any): CursorPagination => { }; export const simpleQueryContext = (): QueryContext => { return { - database: undefined, + count: _query => new Promise(r => ({})), + search: _query => new Promise(r => ({})), dateRangeEnd: '', dateRangeStart: '', filterClause: undefined, pagination: nextPagination('something'), - request: undefined, size: 0, statusFilter: '', }; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/enrich_monitor_groups.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/enrich_monitor_groups.ts index 6b594d8b49118..093e105635c2c 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/enrich_monitor_groups.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/enrich_monitor_groups.ts @@ -243,7 +243,7 @@ export const enrichMonitorGroups: MonitorEnricher = async ( }, }; - const items = await queryContext.database.search(queryContext.request, params); + const items = await queryContext.search(params); const monitorBuckets = get(items, 'aggregations.monitors.buckets', []); @@ -345,7 +345,7 @@ const getHistogramForMonitors = async ( }, }, }; - const result = await queryContext.database.search(queryContext.request, params); + const result = await queryContext.search(params); const buckets: any[] = get(result, 'aggregations.by_id.buckets', []); return buckets.reduce((map: { [key: string]: any }, item: any) => { diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/find_potential_matches.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/find_potential_matches.ts index 002dc2862fa1b..8f5e26b75f56c 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/find_potential_matches.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/find_potential_matches.ts @@ -62,7 +62,7 @@ const query = async (queryContext: QueryContext, searchAfter: any, size: number) body, }; - return await queryContext.database.search(queryContext.request, params); + return await queryContext.search(params); }; const queryBody = (queryContext: QueryContext, searchAfter: any, size: number) => { diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/refine_potential_matches.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/refine_potential_matches.ts index 3bdd6fe616f18..b0060cbee17bb 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/refine_potential_matches.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/refine_potential_matches.ts @@ -95,7 +95,7 @@ const fullyMatchingIds = async ( export const mostRecentCheckGroups = async ( queryContext: QueryContext, potentialMatchMonitorIDs: string[] -) => { +): Promise => { const params = { index: INDEX_NAMES.HEARTBEAT, body: { @@ -134,5 +134,5 @@ export const mostRecentCheckGroups = async ( }, }; - return await queryContext.database.search(queryContext.request, params); + return await queryContext.search(params); }; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/__tests__/__snapshots__/elasticsearch_monitors_adapter.test.ts.snap b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/__tests__/__snapshots__/elasticsearch_monitors_adapter.test.ts.snap index 99349f42d5750..7f0eb86dae751 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/__tests__/__snapshots__/elasticsearch_monitors_adapter.test.ts.snap +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/__tests__/__snapshots__/elasticsearch_monitors_adapter.test.ts.snap @@ -2,7 +2,7 @@ exports[`ElasticsearchMonitorsAdapter getMonitorChartsData will provide expected filters when a location is specified 1`] = ` Array [ - Object {}, + "search", Object { "body": Object { "aggs": Object { @@ -74,7 +74,7 @@ Array [ exports[`ElasticsearchMonitorsAdapter getMonitorChartsData will run expected parameters when no location is specified 1`] = ` Array [ - Object {}, + "search", Object { "body": Object { "aggs": Object { diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/__tests__/elasticsearch_monitors_adapter.test.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/__tests__/elasticsearch_monitors_adapter.test.ts index b21fb982bfb3b..e3e81fe360718 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/__tests__/elasticsearch_monitors_adapter.test.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/__tests__/elasticsearch_monitors_adapter.test.ts @@ -5,38 +5,22 @@ */ import { get, set } from 'lodash'; -import { ElasticsearchMonitorsAdapter } from '../elasticsearch_monitors_adapter'; -import { CountParams, CountResponse } from 'elasticsearch'; +import { elasticsearchMonitorsAdapter as adapter } from '../elasticsearch_monitors_adapter'; import mockChartsData from './monitor_charts_mock.json'; import { assertCloseTo } from '../../../helper'; // FIXME: there are many untested functions in this adapter. They should be tested. describe('ElasticsearchMonitorsAdapter', () => { - let defaultCountResponse: CountResponse; - - beforeEach(() => { - defaultCountResponse = { - count: 0, - _shards: { - total: 0, - successful: 0, - failed: 0, - skipped: 0, - }, - }; - }); - it('getMonitorChartsData will run expected parameters when no location is specified', async () => { expect.assertions(3); const searchMock = jest.fn(); const search = searchMock.bind({}); - const database = { - search, - count: async (request: any, params: CountParams) => defaultCountResponse, - head: async (request: any, params: any) => null, - }; - const adapter = new ElasticsearchMonitorsAdapter(database); - await adapter.getMonitorChartsData({}, 'fooID', 'now-15m', 'now'); + await adapter.getMonitorChartsData({ + callES: search, + monitorId: 'fooID', + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + }); expect(searchMock).toHaveBeenCalledTimes(1); // protect against possible rounding errors polluting the snapshot comparison const fixedInterval = parseInt( @@ -66,13 +50,13 @@ describe('ElasticsearchMonitorsAdapter', () => { expect.assertions(3); const searchMock = jest.fn(); const search = searchMock.bind({}); - const database = { - search, - count: async (request: any, params: CountParams) => defaultCountResponse, - head: async (request: any, params: any) => null, - }; - const adapter = new ElasticsearchMonitorsAdapter(database); - await adapter.getMonitorChartsData({}, 'fooID', 'now-15m', 'now', 'Philadelphia'); + await adapter.getMonitorChartsData({ + callES: search, + monitorId: 'fooID', + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + location: 'Philadelphia', + }); expect(searchMock).toHaveBeenCalledTimes(1); // protect against possible rounding errors polluting the snapshot comparison const fixedInterval = parseInt( @@ -101,12 +85,14 @@ describe('ElasticsearchMonitorsAdapter', () => { it('inserts empty buckets for missing data', async () => { const searchMock = jest.fn(); searchMock.mockReturnValue(mockChartsData); - const database = { - search: searchMock, - count: jest.fn(), - head: jest.fn(), - }; - const adapter = new ElasticsearchMonitorsAdapter(database); - expect(await adapter.getMonitorChartsData({}, 'id', 'now-15m', 'now')).toMatchSnapshot(); + const search = searchMock.bind({}); + expect( + await adapter.getMonitorChartsData({ + callES: search, + monitorId: 'id', + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + }) + ).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/adapter_types.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/adapter_types.ts index 1191eb813a214..b3d8cb855d55a 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/adapter_types.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/adapter_types.ts @@ -5,22 +5,57 @@ */ import { MonitorChart, MonitorPageTitle } from '../../../../common/graphql/types'; +import { UMElasticsearchQueryFn } from '../framework'; +import { MonitorDetails, MonitorLocations } from '../../../../common/runtime_types'; + +export interface GetMonitorChartsDataParams { + /** @member monitorId ID value for the selected monitor */ + monitorId: string; + /** @member dateRangeStart timestamp bounds */ + dateRangeStart: string; + /** @member dateRangeEnd timestamp bounds */ + dateRangeEnd: string; + /** @member location optional location value for use in filtering*/ + location?: string | null; +} + +export interface GetFilterBarParams { + dateRangeStart: string; + /** @member dateRangeEnd timestamp bounds */ + dateRangeEnd: string; +} + +export interface GetMonitorDetailsParams { + monitorId: string; +} + +export interface GetMonitorPageTitleParams { + /** @member monitorId the ID to query */ + monitorId: string; +} + +/** + * Fetch data for the monitor page title. + */ +export interface GetMonitorLocationsParams { + /** + * @member monitorId the ID to query + */ + monitorId: string; + dateStart: string; + dateEnd: string; +} export interface UMMonitorsAdapter { - getMonitorChartsData( - request: any, - monitorId: string, - dateRangeStart: string, - dateRangeEnd: string, - location?: string | null - ): Promise; - getFilterBar(request: any, dateRangeStart: string, dateRangeEnd: string): Promise; - getMonitorPageTitle(request: any, monitorId: string): Promise; - getMonitorDetails(request: any, monitorId: string): Promise; - getMonitorLocations( - request: any, - monitorId: string, - dateStart: string, - dateEnd: string - ): Promise; + /** + * Fetches data used to populate monitor charts + */ + getMonitorChartsData: UMElasticsearchQueryFn; + getFilterBar: UMElasticsearchQueryFn; + /** + * Fetch data for the monitor page title. + */ + getMonitorPageTitle: UMElasticsearchQueryFn<{ monitorId: string }, MonitorPageTitle | null>; + getMonitorDetails: UMElasticsearchQueryFn; + getMonitorLocations: UMElasticsearchQueryFn; } diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts index 4009a4b1331fa..b335205458965 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts @@ -6,22 +6,10 @@ import { get } from 'lodash'; import { INDEX_NAMES } from '../../../../common/constants'; -import { - FilterBar, - MonitorChart, - MonitorPageTitle, - Ping, - LocationDurationLine, -} from '../../../../common/graphql/types'; +import { MonitorChart, Ping, LocationDurationLine } from '../../../../common/graphql/types'; import { getHistogramIntervalFormatted } from '../../helper'; -import { DatabaseAdapter } from '../database'; +import { MonitorError, MonitorLocation } from '../../../../common/runtime_types'; import { UMMonitorsAdapter } from './adapter_types'; -import { - MonitorDetails, - MonitorError, - MonitorLocations, - MonitorLocation, -} from '../../../../common/runtime_types'; const formatStatusBuckets = (time: any, buckets: any, docCount: any) => { let up = null; @@ -43,25 +31,8 @@ const formatStatusBuckets = (time: any, buckets: any, docCount: any) => { }; }; -export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { - constructor(private readonly database: DatabaseAdapter) { - this.database = database; - } - - /** - * Fetches data used to populate monitor charts - * @param request Kibana request - * @param monitorId ID value for the selected monitor - * @param dateRangeStart timestamp bounds - * @param dateRangeEnd timestamp bounds - */ - public async getMonitorChartsData( - request: any, - monitorId: string, - dateRangeStart: string, - dateRangeEnd: string, - location?: string | null - ): Promise { +export const elasticsearchMonitorsAdapter: UMMonitorsAdapter = { + getMonitorChartsData: async ({ callES, dateRangeStart, dateRangeEnd, monitorId, location }) => { const params = { index: INDEX_NAMES.HEARTBEAT, body: { @@ -101,7 +72,7 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { }, }; - const result = await this.database.search(request, params); + const result = await callES('search', params); const dateHistogramBuckets = get(result, 'aggregations.timeseries.buckets', []); /** @@ -187,19 +158,9 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { }); return monitorChartsData; - } + }, - /** - * Fetch options for the filter bar. - * @param request Kibana request object - * @param dateRangeStart timestamp bounds - * @param dateRangeEnd timestamp bounds - */ - public async getFilterBar( - request: any, - dateRangeStart: string, - dateRangeEnd: string - ): Promise { + getFilterBar: async ({ callES, dateRangeStart, dateRangeEnd }) => { const fields: { [key: string]: string } = { ids: 'monitor.id', schemes: 'monitor.type', @@ -225,24 +186,16 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { }, {}), }, }; - const { aggregations } = await this.database.search(request, params); + const { aggregations } = await callES('search', params); return Object.keys(fields).reduce((acc: { [key: string]: any[] }, field) => { const bucketName = fields[field]; acc[field] = aggregations[bucketName].buckets.map((b: { key: string | number }) => b.key); return acc; }, {}); - } + }, - /** - * Fetch data for the monitor page title. - * @param request Kibana server request - * @param monitorId the ID to query - */ - public async getMonitorPageTitle( - request: any, - monitorId: string - ): Promise { + getMonitorPageTitle: async ({ callES, monitorId }) => { const params = { index: INDEX_NAMES.HEARTBEAT, body: { @@ -266,7 +219,7 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { }, }; - const result = await this.database.search(request, params); + const result = await callES('search', params); const pageTitle: Ping | null = get(result, 'hits.hits[0]._source', null); if (pageTitle === null) { return null; @@ -276,14 +229,9 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { url: get(pageTitle, 'url.full', null), name: get(pageTitle, 'monitor.name', null), }; - } + }, - /** - * Fetch data for the monitor page title. - * @param request Kibana server request - * @param monitorId the ID to query - */ - public async getMonitorDetails(request: any, monitorId: string): Promise { + getMonitorDetails: async ({ callES, monitorId }) => { const params = { index: INDEX_NAMES.HEARTBEAT, body: { @@ -317,7 +265,7 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { }, }; - const result = await this.database.search(request, params); + const result = await callES('search', params); const data = result.hits.hits[0]?._source; @@ -329,19 +277,14 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { error: monitorError, timestamp: errorTimeStamp, }; - } + }, /** * Fetch data for the monitor page title. * @param request Kibana server request - * @param monitorId the ID to query + * */ - public async getMonitorLocations( - request: any, - monitorId: string, - dateStart: string, - dateEnd: string - ): Promise { + getMonitorLocations: async ({ callES, monitorId, dateStart, dateEnd }) => { const params = { index: INDEX_NAMES.HEARTBEAT, body: { @@ -394,7 +337,7 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { }, }; - const result = await this.database.search(request, params); + const result = await callES('search', params); const locations = result?.aggregations?.location?.buckets ?? []; const getGeo = (locGeo: any) => { @@ -425,5 +368,5 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter { monitorId, locations: monLocs, }; - } -} + }, +}; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/index.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/index.ts index e1cb06aa95dce..d02068a11e8d8 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/index.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitors/index.ts @@ -5,4 +5,4 @@ */ export * from './adapter_types'; -export { ElasticsearchMonitorsAdapter } from './elasticsearch_monitors_adapter'; +export { elasticsearchMonitorsAdapter } from './elasticsearch_monitors_adapter'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/__tests__/elasticsearch_pings_adapter.test.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/__tests__/elasticsearch_pings_adapter.test.ts index 7b3c72f535401..bd1c182e938a3 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/__tests__/elasticsearch_pings_adapter.test.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/__tests__/elasticsearch_pings_adapter.test.ts @@ -5,14 +5,10 @@ */ import { set } from 'lodash'; -import { DatabaseAdapter } from '../../database'; -import { ElasticsearchPingsAdapter } from '../elasticsearch_pings_adapter'; +import { elasticsearchPingsAdapter as adapter } from '../elasticsearch_pings_adapter'; import { assertCloseTo } from '../../../helper'; describe('ElasticsearchPingsAdapter class', () => { - let database: DatabaseAdapter; - let adapter: ElasticsearchPingsAdapter; - let serverRequest: any; let mockHits: any[]; let mockEsSearchResult: any; let mockEsCountResult: any; @@ -77,22 +73,13 @@ describe('ElasticsearchPingsAdapter class', () => { mockEsCountResult = { count: mockHits.length, }; - database = { - search: async (request: any, params: any) => mockEsSearchResult, - count: async (request: any, params: any) => mockEsCountResult, - head: async (request: any, params: any) => null, - }; - adapter = new ElasticsearchPingsAdapter(database); - serverRequest = { - requestArgs: 'hello', - }; }); describe('getPingHistogram', () => { it('returns a single bucket if array has 1', async () => { expect.assertions(2); - const search = jest.fn(); - search.mockReturnValue({ + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue({ aggregations: { timeseries: { buckets: [ @@ -109,43 +96,41 @@ describe('ElasticsearchPingsAdapter class', () => { }, }, }); - const pingDatabase = { - search, - count: jest.fn(), - head: async (request: any, params: any) => null, - }; - const pingAdapter = new ElasticsearchPingsAdapter(pingDatabase); - const result = await pingAdapter.getPingHistogram(serverRequest, 'now-15m', 'now', null); + const result = await adapter.getPingHistogram({ + callES: mockEsClient, + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + filters: null, + }); assertCloseTo(result.interval, 36000, 100); result.interval = 36000; - expect(pingDatabase.search).toHaveBeenCalledTimes(1); + expect(mockEsClient).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); it('returns expected result for no status filter', async () => { expect.assertions(2); - const search = jest.fn(); + const mockEsClient = jest.fn(); - search.mockReturnValue(standardMockResponse); + mockEsClient.mockReturnValue(standardMockResponse); - const pingDatabase = { - search, - count: jest.fn(), - head: async (request: any, params: any) => null, - }; - const pingAdapter = new ElasticsearchPingsAdapter(pingDatabase); - const result = await pingAdapter.getPingHistogram(serverRequest, 'now-15m', 'now', null); + const result = await adapter.getPingHistogram({ + callES: mockEsClient, + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + filters: null, + }); assertCloseTo(result.interval, 36000, 100); result.interval = 36000; - expect(pingDatabase.search).toHaveBeenCalledTimes(1); + expect(mockEsClient).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); it('handles status + additional user queries', async () => { expect.assertions(2); - const search = jest.fn(); - search.mockReturnValue({ + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue({ aggregations: { timeseries: { buckets: [ @@ -188,31 +173,25 @@ describe('ElasticsearchPingsAdapter class', () => { ], }, }; - const pingDatabase = { - search, - count: jest.fn(), - head: async (request: any, params: any) => null, - }; - const pingAdapter = new ElasticsearchPingsAdapter(pingDatabase); - const result = await pingAdapter.getPingHistogram( - serverRequest, - '1234', - '5678', - JSON.stringify(searchFilter), - undefined, - 'down' - ); + const result = await adapter.getPingHistogram({ + callES: mockEsClient, + dateRangeStart: '1234', + dateRangeEnd: '5678', + filters: JSON.stringify(searchFilter), + monitorId: undefined, + statusFilter: 'down', + }); assertCloseTo(result.interval, 5609564928000, 1000); result.interval = 5609564928000; - expect(pingDatabase.search).toHaveBeenCalledTimes(1); + expect(mockEsClient).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); it('handles simple_text_query without issues', async () => { expect.assertions(2); - const search = jest.fn(); - search.mockReturnValue({ + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue({ aggregations: { timeseries: { buckets: [ @@ -247,89 +226,71 @@ describe('ElasticsearchPingsAdapter class', () => { }, }, }); - const searchFilter = `{"bool":{"must":[{"simple_query_string":{"query":"http"}}]}}`; - const pingDatabase = { - search, - count: jest.fn(), - head: async (request: any, params: any) => null, - }; - const pingAdapter = new ElasticsearchPingsAdapter(pingDatabase); - const result = await pingAdapter.getPingHistogram( - serverRequest, - 'now-15m', - 'now', - searchFilter - ); + const filters = `{"bool":{"must":[{"simple_query_string":{"query":"http"}}]}}`; + const result = await adapter.getPingHistogram({ + callES: mockEsClient, + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + filters, + }); assertCloseTo(result.interval, 36000, 100); result.interval = 36000; - expect(pingDatabase.search).toHaveBeenCalledTimes(1); + expect(mockEsClient).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); it('returns a down-filtered array for when filtered by down status', async () => { expect.assertions(2); - const search = jest.fn(); - search.mockReturnValue(standardMockResponse); - const pingDatabase = { - search, - count: jest.fn(), - head: async (request: any, params: any) => null, - }; - const pingAdapter = new ElasticsearchPingsAdapter(pingDatabase); - const result = await pingAdapter.getPingHistogram( - serverRequest, - '1234', - '5678', - '', - undefined, - 'down' - ); + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue(standardMockResponse); + const result = await adapter.getPingHistogram({ + callES: mockEsClient, + dateRangeStart: '1234', + dateRangeEnd: '5678', + filters: '', + monitorId: undefined, + statusFilter: 'down', + }); assertCloseTo(result.interval, 5609564928000, 1000); result.interval = 5609564928000; - expect(pingDatabase.search).toHaveBeenCalledTimes(1); + expect(mockEsClient).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); it('returns a down-filtered array for when filtered by up status', async () => { expect.assertions(2); - const search = jest.fn(); + const mockEsClient = jest.fn(); - search.mockReturnValue(standardMockResponse); + mockEsClient.mockReturnValue(standardMockResponse); - const pingDatabase = { - search, - count: jest.fn(), - head: async (request: any, params: any) => null, - }; - const pingAdapter = new ElasticsearchPingsAdapter(pingDatabase); - const result = await pingAdapter.getPingHistogram( - serverRequest, - '1234', - '5678', - '', - undefined, - 'up' - ); - - expect(pingDatabase.search).toHaveBeenCalledTimes(1); + const result = await adapter.getPingHistogram({ + callES: mockEsClient, + dateRangeStart: '1234', + dateRangeEnd: '5678', + filters: '', + monitorId: undefined, + statusFilter: 'up', + }); + + expect(mockEsClient).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); }); describe('getDocCount', () => { it('returns data in appropriate shape', async () => { - const { count } = await adapter.getDocCount(serverRequest); + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue(mockEsCountResult); + const { count } = await adapter.getDocCount({ callES: mockEsClient }); expect(count).toEqual(3); }); }); describe('getAll', () => { - let getAllSearchMock: (request: any, params: any) => Promise; let expectedGetAllParams: any; beforeEach(() => { - getAllSearchMock = jest.fn(async (request: any, params: any) => mockEsSearchResult); expectedGetAllParams = { index: 'heartbeat-8*', body: { @@ -354,15 +315,15 @@ describe('ElasticsearchPingsAdapter class', () => { }); it('returns data in the appropriate shape', async () => { - const result = await adapter.getAll( - serverRequest, - 'now-1h', - 'now', - undefined, - undefined, - 'asc', - 12 - ); + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue(mockEsSearchResult); + const result = await adapter.getAll({ + callES: mockEsClient, + dateRangeStart: 'now-1h', + dateRangeEnd: 'now', + sort: 'asc', + size: 12, + }); const count = 3; expect(result.total).toBe(count); @@ -372,57 +333,87 @@ describe('ElasticsearchPingsAdapter class', () => { expect(pings[0].timestamp).toBe('2018-10-30T18:51:59.792Z'); expect(pings[1].timestamp).toBe('2018-10-30T18:53:59.792Z'); expect(pings[2].timestamp).toBe('2018-10-30T18:55:59.792Z'); + expect(mockEsClient).toHaveBeenCalledTimes(1); }); it('creates appropriate sort and size parameters', async () => { - database.search = getAllSearchMock; - await adapter.getAll(serverRequest, 'now-1h', 'now', undefined, undefined, 'asc', 12); + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue(mockEsSearchResult); + await adapter.getAll({ + callES: mockEsClient, + dateRangeStart: 'now-1h', + dateRangeEnd: 'now', + sort: 'asc', + size: 12, + }); set(expectedGetAllParams, 'body.sort[0]', { '@timestamp': { order: 'asc' } }); - expect(database.search).toHaveBeenCalledTimes(1); - expect(database.search).toHaveBeenCalledWith(serverRequest, expectedGetAllParams); + expect(mockEsClient).toHaveBeenCalledTimes(1); + expect(mockEsClient).toHaveBeenCalledWith('search', expectedGetAllParams); }); it('omits the sort param when no sort passed', async () => { - database.search = getAllSearchMock; - await adapter.getAll(serverRequest, 'now-1h', 'now', undefined, undefined, undefined, 12); + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue(mockEsSearchResult); + await adapter.getAll({ + callES: mockEsClient, + dateRangeStart: 'now-1h', + dateRangeEnd: 'now', + size: 12, + }); - expect(database.search).toHaveBeenCalledWith(serverRequest, expectedGetAllParams); + expect(mockEsClient).toHaveBeenCalledWith('search', expectedGetAllParams); }); it('omits the size param when no size passed', async () => { - database.search = getAllSearchMock; - await adapter.getAll(serverRequest, 'now-1h', 'now', undefined, undefined, 'desc'); + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue(mockEsSearchResult); + await adapter.getAll({ + callES: mockEsClient, + dateRangeStart: 'now-1h', + dateRangeEnd: 'now', + sort: 'desc', + }); delete expectedGetAllParams.body.size; set(expectedGetAllParams, 'body.sort[0].@timestamp.order', 'desc'); - expect(database.search).toHaveBeenCalledWith(serverRequest, expectedGetAllParams); + expect(mockEsClient).toHaveBeenCalledWith('search', expectedGetAllParams); }); it('adds a filter for monitor ID', async () => { - database.search = getAllSearchMock; - await adapter.getAll(serverRequest, 'now-1h', 'now', 'testmonitorid'); + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue(mockEsSearchResult); + await adapter.getAll({ + callES: mockEsClient, + dateRangeStart: 'now-1h', + dateRangeEnd: 'now', + monitorId: 'testmonitorid', + }); delete expectedGetAllParams.body.size; expectedGetAllParams.body.query.bool.filter.push({ term: { 'monitor.id': 'testmonitorid' } }); - expect(database.search).toHaveBeenCalledWith(serverRequest, expectedGetAllParams); + expect(mockEsClient).toHaveBeenCalledWith('search', expectedGetAllParams); }); it('adds a filter for monitor status', async () => { - database.search = getAllSearchMock; - await adapter.getAll(serverRequest, 'now-1h', 'now', undefined, 'down'); + const mockEsClient = jest.fn(); + mockEsClient.mockReturnValue(mockEsSearchResult); + await adapter.getAll({ + callES: mockEsClient, + dateRangeStart: 'now-1h', + dateRangeEnd: 'now', + status: 'down', + }); delete expectedGetAllParams.body.size; expectedGetAllParams.body.query.bool.filter.push({ term: { 'monitor.status': 'down' } }); - expect(database.search).toHaveBeenCalledWith(serverRequest, expectedGetAllParams); + expect(mockEsClient).toHaveBeenCalledWith('search', expectedGetAllParams); }); }); describe('getLatestMonitorDocs', () => { - let getLatestSearchMock: (request: any, params: any) => Promise; let expectedGetLatestSearchParams: any; beforeEach(() => { - getLatestSearchMock = jest.fn(async (request: any, params: any) => mockEsSearchResult); expectedGetLatestSearchParams = { index: 'heartbeat-8*', body: { @@ -491,19 +482,19 @@ describe('ElasticsearchPingsAdapter class', () => { }); it('returns data in expected shape', async () => { - database.search = getLatestSearchMock; - const result = await adapter.getLatestMonitorDocs( - serverRequest, - 'now-1h', - 'now', - 'testmonitor' - ); + const mockEsClient = jest.fn(async (_request: any, _params: any) => mockEsSearchResult); + const result = await adapter.getLatestMonitorDocs({ + callES: mockEsClient, + dateRangeStart: 'now-1h', + dateRangeEnd: 'now', + monitorId: 'testmonitor', + }); expect(result).toHaveLength(1); expect(result[0].timestamp).toBe(123456); expect(result[0].monitor).not.toBeFalsy(); // @ts-ignore monitor will be defined expect(result[0].monitor.id).toBe('testmonitor'); - expect(database.search).toHaveBeenCalledWith(serverRequest, expectedGetLatestSearchParams); + expect(mockEsClient).toHaveBeenCalledWith('search', expectedGetLatestSearchParams); }); }); }); diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/adapter_types.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/adapter_types.ts index 1e0cf7ec40646..81df1c7c0f631 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/adapter_types.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/adapter_types.ts @@ -6,35 +6,83 @@ import { DocCount, Ping, PingResults } from '../../../../common/graphql/types'; import { HistogramResult } from '../../../../common/domain_types'; +import { UMElasticsearchQueryFn } from '../framework'; +export interface GetAllParams { + /** @member dateRangeStart timestamp bounds */ + dateRangeStart: string; + + /** @member dateRangeEnd timestamp bounds */ + dateRangeEnd: string; + + /** @member monitorId optional limit by monitorId */ + monitorId?: string | null; + + /** @member status optional limit by check statuses */ + status?: string | null; + + /** @member sort optional sort by timestamp */ + sort?: string | null; + + /** @member size optional limit query size */ + size?: number | null; + + /** @member location optional location value for use in filtering*/ + location?: string | null; +} + +export interface GetLatestMonitorDocsParams { + /** @member dateRangeStart timestamp bounds */ + dateRangeStart: string; + + /** @member dateRangeEnd timestamp bounds */ + dateRangeEnd: string; + + /** @member monitorId optional limit to monitorId */ + monitorId?: string | null; + + /** @member location optional location value for use in filtering*/ + location?: string | null; +} + +export interface GetPingHistogramParams { + /** @member dateRangeStart timestamp bounds */ + dateRangeStart: string; + /** @member dateRangeEnd timestamp bounds */ + dateRangeEnd: string; + /** @member filters user-defined filters */ + filters?: string | null; + /** @member monitorId optional limit to monitorId */ + monitorId?: string | null; + /** @member statusFilter special filter targeting the latest status of each monitor */ + statusFilter?: string | null; +} + +/** + * Count the number of documents in heartbeat indices + */ export interface UMPingsAdapter { - getAll( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - monitorId?: string | null, - status?: string | null, - sort?: string | null, - size?: number | null, - location?: string | null - ): Promise; - - getLatestMonitorDocs( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - monitorId?: string | null, - location?: string | null - ): Promise; - - getPingHistogram( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - filters?: string | null, - monitorId?: string | null, - statusFilter?: string | null - ): Promise; - - getDocCount(request: any): Promise; + getAll: UMElasticsearchQueryFn; + + getLatestMonitorDocs: UMElasticsearchQueryFn; + + getPingHistogram: UMElasticsearchQueryFn; + + /** + * Gets data used for a composite histogram for the currently-running monitors. + */ + getDocCount: UMElasticsearchQueryFn<{}, DocCount>; +} + +export interface HistogramQueryResult { + key: number; + doc_count: number; + bucket_total: { + value: number; + }; + down: { + bucket_count: { + value: number; + }; + }; } diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/elasticsearch_pings_adapter.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/elasticsearch_pings_adapter.ts index 6c71d91794003..6862bed8d2bdd 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/elasticsearch_pings_adapter.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/elasticsearch_pings_adapter.ts @@ -6,41 +6,23 @@ import { get } from 'lodash'; import { INDEX_NAMES } from '../../../../common/constants'; -import { DocCount, HttpBody, Ping, PingResults } from '../../../../common/graphql/types'; +import { HttpBody, Ping, PingResults } from '../../../../common/graphql/types'; import { parseFilterQuery, getFilterClause, getHistogramIntervalFormatted } from '../../helper'; -import { DatabaseAdapter, HistogramQueryResult } from '../database'; -import { UMPingsAdapter } from './adapter_types'; +import { UMPingsAdapter, HistogramQueryResult } from './adapter_types'; import { getHistogramInterval } from '../../helper/get_histogram_interval'; -import { HistogramResult } from '../../../../common/domain_types'; -export class ElasticsearchPingsAdapter implements UMPingsAdapter { - private database: DatabaseAdapter; - - constructor(database: DatabaseAdapter) { - this.database = database; - } - - /** - * Fetches ping documents from ES - * @param request Kibana server request - * @param dateRangeStart timestamp bounds - * @param dateRangeEnd timestamp bounds - * @param monitorId optional limit by monitorId - * @param status optional limit by check statuses - * @param sort optional sort by timestamp - * @param size optional limit query size - */ - public async getAll( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - monitorId?: string | null, - status?: string | null, - sort: string | null = 'desc', - size?: number | null, - location?: string | null - ): Promise { - const sortParam = { sort: [{ '@timestamp': { order: sort } }] }; +export const elasticsearchPingsAdapter: UMPingsAdapter = { + getAll: async ({ + callES, + dateRangeStart, + dateRangeEnd, + monitorId, + status, + sort, + size, + location, + }) => { + const sortParam = { sort: [{ '@timestamp': { order: sort ?? 'desc' } }] }; const sizeParam = size ? { size } : undefined; const filter: any[] = [{ range: { '@timestamp': { gte: dateRangeStart, lte: dateRangeEnd } } }]; if (monitorId) { @@ -79,7 +61,7 @@ export class ElasticsearchPingsAdapter implements UMPingsAdapter { const { hits: { hits, total }, aggregations: aggs, - } = await this.database.search(request, params); + } = await callES('search', params); const locations = get(aggs, 'locations', { buckets: [{ key: 'N/A', doc_count: 0 }] }); @@ -104,23 +86,9 @@ export class ElasticsearchPingsAdapter implements UMPingsAdapter { }; return results; - } + }, - /** - * Fetch data to populate monitor status bar. - * @param request Kibana server request - * @param dateRangeStart timestamp bounds - * @param dateRangeEnd timestamp bounds - * @param monitorId optional limit to monitorId - */ - public async getLatestMonitorDocs( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - monitorId?: string | null, - location?: string | null - ): Promise { - // TODO: Write tests for this function + getLatestMonitorDocs: async ({ callES, dateRangeStart, dateRangeEnd, monitorId, location }) => { const params = { index: INDEX_NAMES.HEARTBEAT, body: { @@ -162,10 +130,9 @@ export class ElasticsearchPingsAdapter implements UMPingsAdapter { }, }; - const result = await this.database.search(request, params); + const result = await callES('search', params); const buckets: any[] = get(result, 'aggregations.by_id.buckets', []); - // @ts-ignore TODO fix destructuring implicit any return buckets.map( ({ latest: { @@ -179,24 +146,16 @@ export class ElasticsearchPingsAdapter implements UMPingsAdapter { }; } ); - } - - /** - * Gets data used for a composite histogram for the currently-running monitors. - * @param request Kibana server request - * @param dateRangeStart timestamp bounds - * @param dateRangeEnd timestamp bounds - * @param filters user-defined filters - * @param statusFilter special filter targeting the latest status of each monitor - */ - public async getPingHistogram( - request: any, - dateRangeStart: string, - dateRangeEnd: string, - filters?: string | null, - monitorId?: string | null, - statusFilter?: string | null - ): Promise { + }, + + getPingHistogram: async ({ + callES, + dateRangeStart, + dateRangeEnd, + filters, + monitorId, + statusFilter, + }) => { const boolFilters = parseFilterQuery(filters); const additionaFilters = []; if (monitorId) { @@ -245,7 +204,7 @@ export class ElasticsearchPingsAdapter implements UMPingsAdapter { }, }; - const result = await this.database.search(request, params); + const result = await callES('search', params); const buckets: HistogramQueryResult[] = get(result, 'aggregations.timeseries.buckets', []); const histogram = buckets.map(bucket => { const x: number = get(bucket, 'key'); @@ -262,15 +221,11 @@ export class ElasticsearchPingsAdapter implements UMPingsAdapter { histogram, interval, }; - } + }, - /** - * Count the number of documents in heartbeat indices - * @param request Kibana server request - */ - public async getDocCount(request: any): Promise { - const { count } = await this.database.count(request, { index: INDEX_NAMES.HEARTBEAT }); + getDocCount: async ({ callES }) => { + const { count } = await callES('count', { index: INDEX_NAMES.HEARTBEAT }); return { count }; - } -} + }, +}; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/index.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/index.ts index eee6aa279dd39..6d93785e01527 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/index.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/index.ts @@ -5,4 +5,4 @@ */ export * from './adapter_types'; -export { ElasticsearchPingsAdapter } from './elasticsearch_pings_adapter'; +export { elasticsearchPingsAdapter } from './elasticsearch_pings_adapter'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/index.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/index.ts index 86154d0ab80a4..bd86daba1bcb6 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/index.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/index.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { UMKibanaSavedObjectsAdapter } from './kibana_saved_objects_adapter'; +export { savedObjectsAdapter } from './kibana_saved_objects_adapter'; export { UMSavedObjectsAdapter } from './types'; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/kibana_saved_objects_adapter.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/kibana_saved_objects_adapter.ts index 1f10c2f7aeed1..7628c5bac0660 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/kibana_saved_objects_adapter.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/kibana_saved_objects_adapter.ts @@ -4,24 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsLegacyService } from 'src/core/server'; import { UMSavedObjectsAdapter } from './types'; import uptimeIndexPattern from './heartbeat_index_pattern.json'; -export class UMKibanaSavedObjectsAdapter implements UMSavedObjectsAdapter { - private readonly savedObjectsClient: any; - constructor(savedObjects: SavedObjectsLegacyService, elasticsearch: any) { - const { SavedObjectsClient, getSavedObjectsRepository } = savedObjects; - const { callWithInternalUser } = elasticsearch.getCluster('admin'); - const internalRepository = getSavedObjectsRepository(callWithInternalUser); - this.savedObjectsClient = new SavedObjectsClient(internalRepository); - } - - public async getUptimeIndexPattern(): Promise { +export const savedObjectsAdapter: UMSavedObjectsAdapter = { + getUptimeIndexPattern: async client => { try { - return await this.savedObjectsClient.get('index-pattern', uptimeIndexPattern.id); + return await client.get('index-pattern', uptimeIndexPattern.id); } catch (error) { - return await this.savedObjectsClient.create( + return await client.create( 'index-pattern', { ...uptimeIndexPattern.attributes, @@ -30,5 +21,5 @@ export class UMKibanaSavedObjectsAdapter implements UMSavedObjectsAdapter { { id: uptimeIndexPattern.id, overwrite: false } ); } - } -} + }, +}; diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/types.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/types.ts index 4d11bd9f6053a..0fef1e1428e97 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/types.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/saved_objects/types.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { UMSavedObjectsQueryFn } from '../framework'; + export interface UMSavedObjectsAdapter { - getUptimeIndexPattern: () => Promise; + getUptimeIndexPattern: UMSavedObjectsQueryFn; } diff --git a/x-pack/legacy/plugins/uptime/server/lib/compose/kibana.ts b/x-pack/legacy/plugins/uptime/server/lib/compose/kibana.ts index a7c370e03490b..cc11bf90da5f3 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/compose/kibana.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/compose/kibana.ts @@ -4,32 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UMKibanaDatabaseAdapter } from '../adapters/database/kibana_database_adapter'; import { UMKibanaBackendFrameworkAdapter } from '../adapters/framework'; -import { ElasticsearchMonitorsAdapter } from '../adapters/monitors'; -import { ElasticsearchPingsAdapter } from '../adapters/pings'; +import { elasticsearchMonitorsAdapter } from '../adapters/monitors'; +import { elasticsearchPingsAdapter } from '../adapters/pings'; import { licenseCheck } from '../domains'; import { UMDomainLibs, UMServerLibs } from '../lib'; -import { ElasticsearchMonitorStatesAdapter } from '../adapters/monitor_states'; -import { UMKibanaSavedObjectsAdapter } from '../adapters/saved_objects/kibana_saved_objects_adapter'; +import { elasticsearchMonitorStatesAdapter } from '../adapters/monitor_states'; +import { savedObjectsAdapter } from '../adapters/saved_objects'; import { UptimeCorePlugins, UptimeCoreSetup } from '../adapters/framework'; export function compose(server: UptimeCoreSetup, plugins: UptimeCorePlugins): UMServerLibs { - const { elasticsearch, savedObjects } = plugins; - const framework = new UMKibanaBackendFrameworkAdapter(server, plugins); - const database = new UMKibanaDatabaseAdapter(elasticsearch); + const framework = new UMKibanaBackendFrameworkAdapter(server); const domainLibs: UMDomainLibs = { license: licenseCheck, - monitors: new ElasticsearchMonitorsAdapter(database), - monitorStates: new ElasticsearchMonitorStatesAdapter(database), - pings: new ElasticsearchPingsAdapter(database), - savedObjects: new UMKibanaSavedObjectsAdapter(savedObjects, elasticsearch), + monitors: elasticsearchMonitorsAdapter, + monitorStates: elasticsearchMonitorStatesAdapter, + pings: elasticsearchPingsAdapter, + savedObjects: savedObjectsAdapter, }; return { framework, - database, ...domainLibs, }; } diff --git a/x-pack/legacy/plugins/uptime/server/lib/domains/__tests__/__snapshots__/license.test.ts.snap b/x-pack/legacy/plugins/uptime/server/lib/domains/__tests__/__snapshots__/license.test.ts.snap index ab791cbd243f8..d03516d49f089 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/domains/__tests__/__snapshots__/license.test.ts.snap +++ b/x-pack/legacy/plugins/uptime/server/lib/domains/__tests__/__snapshots__/license.test.ts.snap @@ -2,6 +2,7 @@ exports[`license check returns result for a valid license 1`] = ` Object { + "message": "License is valid and active", "statusCode": 200, } `; diff --git a/x-pack/legacy/plugins/uptime/server/lib/domains/__tests__/license.test.ts b/x-pack/legacy/plugins/uptime/server/lib/domains/__tests__/license.test.ts index b26cb99cf9b6b..8c340f5525a4c 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/domains/__tests__/license.test.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/domains/__tests__/license.test.ts @@ -11,7 +11,7 @@ describe('license check', () => { let mockLicense: Pick; it('throws for null license', () => { - expect(licenseCheck(null)).toMatchSnapshot(); + expect(licenseCheck(undefined)).toMatchSnapshot(); }); it('throws for unsupported license type', () => { diff --git a/x-pack/legacy/plugins/uptime/server/lib/domains/license.ts b/x-pack/legacy/plugins/uptime/server/lib/domains/license.ts index 947bad75075ef..24ce0205414fa 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/domains/license.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/domains/license.ts @@ -8,14 +8,14 @@ import { ILicense } from '../../../../../../plugins/licensing/server'; export interface UMLicenseStatusResponse { statusCode: number; - message?: string; + message: string; } export type UMLicenseCheck = ( - license: Pick | null + license?: Pick ) => UMLicenseStatusResponse; export const licenseCheck: UMLicenseCheck = license => { - if (license === null) { + if (license === undefined) { return { message: 'Missing license information', statusCode: 400, @@ -34,6 +34,7 @@ export const licenseCheck: UMLicenseCheck = license => { }; } return { + message: 'License is valid and active', statusCode: 200, }; }; diff --git a/x-pack/legacy/plugins/uptime/server/lib/lib.ts b/x-pack/legacy/plugins/uptime/server/lib/lib.ts index e68a6dd18ef5f..da87c3ebfe301 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/lib.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/lib.ts @@ -5,7 +5,6 @@ */ import { - DatabaseAdapter, UMBackendFrameworkAdapter, UMMonitorsAdapter, UMMonitorStatesAdapter, @@ -24,5 +23,4 @@ export interface UMDomainLibs { export interface UMServerLibs extends UMDomainLibs { framework: UMBackendFrameworkAdapter; - database?: DatabaseAdapter; } diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/create_route_with_auth.ts b/x-pack/legacy/plugins/uptime/server/rest_api/create_route_with_auth.ts index a1976ef046e8e..41527d76432cb 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/create_route_with_auth.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/create_route_with_auth.ts @@ -4,32 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandler } from 'kibana/server'; -import { ObjectType } from '@kbn/config-schema'; import { UMServerLibs } from '../lib/lib'; -import { UMRestApiRouteCreator, UMRouteDefinition } from './types'; +import { UptimeRoute, UMRestApiRouteFactory, UMRouteHandler } from './types'; export const createRouteWithAuth = ( libs: UMServerLibs, - routeCreator: UMRestApiRouteCreator -): UMRouteDefinition => { + routeCreator: UMRestApiRouteFactory +): UptimeRoute => { const restRoute = routeCreator(libs); const { handler, method, path, options, ...rest } = restRoute; - const authHandler: RequestHandler = async ( - context, - request, - response - ) => { - if (libs.license(context.licensing.license)) { - return await handler(context, request, response); + const licenseCheckHandler: UMRouteHandler = async (customParams, context, request, response) => { + const { statusCode, message } = libs.license(context.licensing.license); + if (statusCode === 200) { + return await handler(customParams, context, request, response); + } + switch (statusCode) { + case 400: + return response.badRequest({ body: { message } }); + case 401: + return response.unauthorized({ body: { message } }); + case 403: + return response.forbidden({ body: { message } }); + default: + return response.internalError(); } - return response.badRequest(); }; return { method, path, options, - handler: authHandler, + handler: licenseCheckHandler, ...rest, }; }; diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/index.ts b/x-pack/legacy/plugins/uptime/server/rest_api/index.ts index f18b9e8e44c36..4ab225076eff2 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/index.ts @@ -8,12 +8,13 @@ import { createGetAllRoute } from './pings'; import { createGetIndexPatternRoute } from './index_pattern'; import { createLogMonitorPageRoute, createLogOverviewPageRoute } from './telemetry'; import { createGetSnapshotCount } from './snapshot'; -import { UMRestApiRouteCreator } from './types'; +import { UMRestApiRouteFactory } from './types'; import { createGetMonitorDetailsRoute, createGetMonitorLocationsRoute } from './monitors'; export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; -export const restApiRoutes: UMRestApiRouteCreator[] = [ +export { uptimeRouteWrapper } from './uptime_route_wrapper'; +export const restApiRoutes: UMRestApiRouteFactory[] = [ createGetAllRoute, createGetIndexPatternRoute, createGetMonitorDetailsRoute, diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts b/x-pack/legacy/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts index 55f0af57ed847..f061307807a42 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts @@ -5,20 +5,20 @@ */ import { UMServerLibs } from '../../lib/lib'; -import { UMRestApiRouteCreator } from '../types'; +import { UMRestApiRouteFactory } from '../types'; -export const createGetIndexPatternRoute: UMRestApiRouteCreator = (libs: UMServerLibs) => ({ +export const createGetIndexPatternRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', path: '/api/uptime/index_pattern', validate: false, options: { tags: ['access:uptime'], }, - handler: async (_context, _request, response): Promise => { + handler: async ({ savedObjectsClient: client }, _context, _request, response): Promise => { try { return response.ok({ body: { - ...(await libs.savedObjects.getUptimeIndexPattern()), + ...(await libs.savedObjects.getUptimeIndexPattern(client, undefined)), }, }); } catch (e) { diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitor_locations.ts b/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitor_locations.ts index 4a91255bd19f2..b2356ae0a88bf 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitor_locations.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitor_locations.ts @@ -6,9 +6,9 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; -import { UMRestApiRouteCreator } from '../types'; +import { UMRestApiRouteFactory } from '../types'; -export const createGetMonitorLocationsRoute: UMRestApiRouteCreator = (libs: UMServerLibs) => ({ +export const createGetMonitorLocationsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', path: '/api/uptime/monitor/locations', validate: { @@ -21,12 +21,17 @@ export const createGetMonitorLocationsRoute: UMRestApiRouteCreator = (libs: UMSe options: { tags: ['access:uptime'], }, - handler: async (_context, request, response): Promise => { + handler: async ({ callES }, _context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; return response.ok({ body: { - ...(await libs.monitors.getMonitorLocations(request, monitorId, dateStart, dateEnd)), + ...(await libs.monitors.getMonitorLocations({ + callES, + monitorId, + dateStart, + dateEnd, + })), }, }); }, diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitors_details.ts b/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitors_details.ts index 00860248ff153..a57e5ec469c59 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitors_details.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/monitors/monitors_details.ts @@ -6,9 +6,9 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; -import { UMRestApiRouteCreator } from '../types'; +import { UMRestApiRouteFactory } from '../types'; -export const createGetMonitorDetailsRoute: UMRestApiRouteCreator = (libs: UMServerLibs) => ({ +export const createGetMonitorDetailsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', path: '/api/uptime/monitor/details', validate: { @@ -19,11 +19,13 @@ export const createGetMonitorDetailsRoute: UMRestApiRouteCreator = (libs: UMServ options: { tags: ['access:uptime'], }, - handler: async (_context, request, response): Promise => { + handler: async ({ callES }, _context, request, response): Promise => { const { monitorId } = request.query; return response.ok({ - body: { ...(await libs.monitors.getMonitorDetails(request, monitorId)) }, + body: { + ...(await libs.monitors.getMonitorDetails({ callES, monitorId })), + }, }); }, }); diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/pings/get_all.ts b/x-pack/legacy/plugins/uptime/server/rest_api/pings/get_all.ts index e2bdbca9fbfa0..824035954ea28 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/pings/get_all.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/pings/get_all.ts @@ -6,9 +6,9 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; -import { UMRestApiRouteCreator } from '../types'; +import { UMRestApiRouteFactory } from '../types'; -export const createGetAllRoute: UMRestApiRouteCreator = (libs: UMServerLibs) => ({ +export const createGetAllRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', path: '/api/uptime/pings', validate: { @@ -25,19 +25,19 @@ export const createGetAllRoute: UMRestApiRouteCreator = (libs: UMServerLibs) => options: { tags: ['access:uptime'], }, - handler: async (_context, request, response): Promise => { - const { size, sort, dateRangeStart, dateRangeEnd, location, monitorId, status } = request.query; + handler: async ({ callES }, _context, request, response): Promise => { + const { dateRangeStart, dateRangeEnd, location, monitorId, size, sort, status } = request.query; - const result = await libs.pings.getAll( - request, + const result = await libs.pings.getAll({ + callES, dateRangeStart, dateRangeEnd, monitorId, status, sort, size, - location - ); + location, + }); return response.ok({ body: { diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts b/x-pack/legacy/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts index 9b2b26f52699e..986ac797d63b6 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts @@ -6,9 +6,9 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; -import { UMRestApiRouteCreator } from '../types'; +import { UMRestApiRouteFactory } from '../types'; -export const createGetSnapshotCount: UMRestApiRouteCreator = (libs: UMServerLibs) => ({ +export const createGetSnapshotCount: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', path: '/api/uptime/snapshot/count', validate: { @@ -22,15 +22,15 @@ export const createGetSnapshotCount: UMRestApiRouteCreator = (libs: UMServerLibs options: { tags: ['access:uptime'], }, - handler: async (_context, request, response): Promise => { + handler: async ({ callES }, _context, request, response): Promise => { const { dateRangeStart, dateRangeEnd, filters, statusFilter } = request.query; - const result = await libs.monitorStates.getSnapshotCount( - request, + const result = await libs.monitorStates.getSnapshotCount({ + callES, dateRangeStart, dateRangeEnd, filters, - statusFilter - ); + statusFilter, + }); return response.ok({ body: { ...result, diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/telemetry/log_monitor_page.ts b/x-pack/legacy/plugins/uptime/server/rest_api/telemetry/log_monitor_page.ts index f3e926493143b..fca1e6c8d5d46 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/telemetry/log_monitor_page.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/telemetry/log_monitor_page.ts @@ -5,13 +5,13 @@ */ import { KibanaTelemetryAdapter } from '../../lib/adapters/telemetry'; -import { UMRestApiRouteCreator } from '../types'; +import { UMRestApiRouteFactory } from '../types'; -export const createLogMonitorPageRoute: UMRestApiRouteCreator = () => ({ +export const createLogMonitorPageRoute: UMRestApiRouteFactory = () => ({ method: 'POST', path: '/api/uptime/logMonitor', validate: false, - handler: async (_context, _request, response): Promise => { + handler: async (_customParams, _context, _request, response): Promise => { await KibanaTelemetryAdapter.countMonitor(); return response.ok(); }, diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/telemetry/log_overview_page.ts b/x-pack/legacy/plugins/uptime/server/rest_api/telemetry/log_overview_page.ts index 277ef2235fb69..37ed2e5ff5c2c 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/telemetry/log_overview_page.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/telemetry/log_overview_page.ts @@ -5,13 +5,13 @@ */ import { KibanaTelemetryAdapter } from '../../lib/adapters/telemetry'; -import { UMRestApiRouteCreator } from '../types'; +import { UMRestApiRouteFactory } from '../types'; -export const createLogOverviewPageRoute: UMRestApiRouteCreator = () => ({ +export const createLogOverviewPageRoute: UMRestApiRouteFactory = () => ({ method: 'POST', path: '/api/uptime/logOverview', validate: false, - handler: async (_context, _request, response): Promise => { + handler: async (_customParams, _context, _request, response): Promise => { await KibanaTelemetryAdapter.countOverview(); return response.ok(); }, diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/types.ts b/x-pack/legacy/plugins/uptime/server/rest_api/types.ts index b4d7e438f51be..e0c8ba4a286cf 100644 --- a/x-pack/legacy/plugins/uptime/server/rest_api/types.ts +++ b/x-pack/legacy/plugins/uptime/server/rest_api/types.ts @@ -5,15 +5,87 @@ */ import { ObjectType } from '@kbn/config-schema'; -import { RequestHandler, RouteConfig, RouteMethod } from 'kibana/server'; +import { + RequestHandler, + RouteConfig, + RouteMethod, + CallAPIOptions, + SavedObjectsClient, + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, +} from 'kibana/server'; import { UMServerLibs } from '../lib/lib'; -export interface UMServerRoute { +/** + * Defines the basic properties employed by Uptime routes. + */ +export interface UMServerRoute { method: string; - handler: RequestHandler; + handler: T; } -export type UMRouteDefinition = UMServerRoute & +/** + * Merges basic uptime route properties with the route config type + * provided by Kibana core. + */ +export type UMRouteDefinition = UMServerRoute & RouteConfig; -export type UMRestApiRouteCreator = (libs: UMServerLibs) => UMRouteDefinition; +/** + * This type represents an Uptime route definition that corresponds to the contract + * provided by the Kibana platform. Route objects must conform to this type in order + * to successfully interact with the Kibana platform. + */ +export type UMKibanaRoute = UMRouteDefinition>; + +/** + * This is an abstraction over the default Kibana route type. This allows us to use custom + * arguments in our route handlers and impelement custom middleware. + */ +export type UptimeRoute = UMRouteDefinition; + +/** + * Functions of this type accept custom lib functions and outputs a route object. + */ +export type UMRestApiRouteFactory = (libs: UMServerLibs) => UptimeRoute; + +/** + * Functions of this type accept our internal route format and output a route + * object that the Kibana platform can consume. + */ +export type UMKibanaRouteWrapper = (uptimeRoute: UptimeRoute) => UMKibanaRoute; + +/** + * This type can store custom parameters used by the internal Uptime route handlers. + */ +export interface UMRouteParams { + callES: ( + endpoint: string, + clientParams?: Record, + options?: CallAPIOptions | undefined + ) => Promise; + savedObjectsClient: Pick< + SavedObjectsClient, + | 'errors' + | 'create' + | 'bulkCreate' + | 'delete' + | 'find' + | 'bulkGet' + | 'get' + | 'update' + | 'bulkUpdate' + >; +} + +/** + * This is the contract we specify internally for route handling. + */ +export type UMRouteHandler = ( + params: UMRouteParams, + context: RequestHandlerContext, + request: KibanaRequest, Record, Record>, + response: KibanaResponseFactory +) => IKibanaResponse | Promise>; diff --git a/x-pack/legacy/plugins/uptime/server/rest_api/uptime_route_wrapper.ts b/x-pack/legacy/plugins/uptime/server/rest_api/uptime_route_wrapper.ts new file mode 100644 index 0000000000000..fb874edebee60 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/server/rest_api/uptime_route_wrapper.ts @@ -0,0 +1,18 @@ +/* + * 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 { UMKibanaRouteWrapper } from './types'; + +export const uptimeRouteWrapper: UMKibanaRouteWrapper = uptimeRoute => { + return { + ...uptimeRoute, + handler: async (context, request, response) => { + const { callAsCurrentUser: callES } = context.core.elasticsearch.dataClient; + const { client: savedObjectsClient } = context.core.savedObjects; + return await uptimeRoute.handler({ callES, savedObjectsClient }, context, request, response); + }, + }; +}; diff --git a/x-pack/legacy/plugins/uptime/server/uptime_server.ts b/x-pack/legacy/plugins/uptime/server/uptime_server.ts index a8058453f2166..4dfa1373db8d9 100644 --- a/x-pack/legacy/plugins/uptime/server/uptime_server.ts +++ b/x-pack/legacy/plugins/uptime/server/uptime_server.ts @@ -7,10 +7,12 @@ import { makeExecutableSchema } from 'graphql-tools'; import { DEFAULT_GRAPHQL_PATH, resolvers, typeDefs } from './graphql'; import { UMServerLibs } from './lib/lib'; -import { createRouteWithAuth, restApiRoutes } from './rest_api'; +import { createRouteWithAuth, restApiRoutes, uptimeRouteWrapper } from './rest_api'; export const initUptimeServer = (libs: UMServerLibs) => { - restApiRoutes.forEach(route => libs.framework.registerRoute(createRouteWithAuth(libs, route))); + restApiRoutes.forEach(route => + libs.framework.registerRoute(uptimeRouteWrapper(createRouteWithAuth(libs, route))) + ); const graphQLSchema = makeExecutableSchema({ resolvers: resolvers.map(createResolversFn => createResolversFn(libs)),