From eac0f9ac92aa8dd6a76eb43aae0260cedd19c549 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Mon, 3 Aug 2020 15:14:09 -0700 Subject: [PATCH] [Search] Fix telemetry collection to not send extra requests (#73382) (#74156) * [Search] Fix telemetry collection to not send extra requests * Update tracker to collect duration from ES response * Fix type check * Update docs and types * Fix typescript Co-authored-by: Liza K Co-authored-by: Liza K --- ...-plugins-data-server.isearchsetup.usage.md | 2 +- .../kibana-plugin-plugins-data-server.md | 2 + ...-plugin-plugins-data-server.searchusage.md | 19 +++++++ ...gins-data-server.searchusage.trackerror.md | 15 ++++++ ...ns-data-server.searchusage.tracksuccess.md | 22 ++++++++ ...lugin-plugins-data-server.usageprovider.md | 22 ++++++++ .../collectors/create_usage_collector.test.ts | 14 ------ .../collectors/create_usage_collector.ts | 16 ------ .../data/public/search/collectors/types.ts | 2 - .../data/public/search/search_interceptor.ts | 9 +--- src/plugins/data/server/index.ts | 2 + .../data/server/search/collectors/index.ts | 20 ++++++++ .../data/server/search/collectors/routes.ts | 50 ------------------- .../data/server/search/collectors/usage.ts | 23 ++++----- .../search/es_search/es_search_strategy.ts | 27 ++++++---- src/plugins/data/server/search/index.ts | 2 + .../data/server/search/search_service.ts | 12 +++-- src/plugins/data/server/search/types.ts | 2 +- src/plugins/data/server/server.api.md | 33 ++++++++---- x-pack/plugins/data_enhanced/kibana.json | 2 +- .../public/search/search_interceptor.test.ts | 3 -- .../public/search/search_interceptor.ts | 3 -- x-pack/plugins/data_enhanced/server/plugin.ts | 15 ++++-- .../server/search/es_search_strategy.ts | 39 +++++++++++++-- 24 files changed, 214 insertions(+), 142 deletions(-) create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.trackerror.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.tracksuccess.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.usageprovider.md create mode 100644 src/plugins/data/server/search/collectors/index.ts delete mode 100644 src/plugins/data/server/search/collectors/routes.ts diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.usage.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.usage.md index 85abd9d9dba980..1a94a709cc214a 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.usage.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.usage.md @@ -9,5 +9,5 @@ Used internally for telemetry Signature: ```typescript -usage: SearchUsage; +usage?: SearchUsage; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 6bf481841f3347..1bcd575803f880 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -27,6 +27,7 @@ | [parseInterval(interval)](./kibana-plugin-plugins-data-server.parseinterval.md) | | | [plugin(initializerContext)](./kibana-plugin-plugins-data-server.plugin.md) | Static code to be shared externally | | [shouldReadFieldFromDocValues(aggregatable, esType)](./kibana-plugin-plugins-data-server.shouldreadfieldfromdocvalues.md) | | +| [usageProvider(core)](./kibana-plugin-plugins-data-server.usageprovider.md) | | ## Interfaces @@ -49,6 +50,7 @@ | [PluginStart](./kibana-plugin-plugins-data-server.pluginstart.md) | | | [Query](./kibana-plugin-plugins-data-server.query.md) | | | [RefreshInterval](./kibana-plugin-plugins-data-server.refreshinterval.md) | | +| [SearchUsage](./kibana-plugin-plugins-data-server.searchusage.md) | | | [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) | | ## Variables diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.md new file mode 100644 index 00000000000000..d867509e915b6a --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchUsage](./kibana-plugin-plugins-data-server.searchusage.md) + +## SearchUsage interface + +Signature: + +```typescript +export interface SearchUsage +``` + +## Methods + +| Method | Description | +| --- | --- | +| [trackError()](./kibana-plugin-plugins-data-server.searchusage.trackerror.md) | | +| [trackSuccess(duration)](./kibana-plugin-plugins-data-server.searchusage.tracksuccess.md) | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.trackerror.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.trackerror.md new file mode 100644 index 00000000000000..212133588f62d5 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.trackerror.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchUsage](./kibana-plugin-plugins-data-server.searchusage.md) > [trackError](./kibana-plugin-plugins-data-server.searchusage.trackerror.md) + +## SearchUsage.trackError() method + +Signature: + +```typescript +trackError(): Promise; +``` +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.tracksuccess.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.tracksuccess.md new file mode 100644 index 00000000000000..b58f440c7dccf6 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusage.tracksuccess.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchUsage](./kibana-plugin-plugins-data-server.searchusage.md) > [trackSuccess](./kibana-plugin-plugins-data-server.searchusage.tracksuccess.md) + +## SearchUsage.trackSuccess() method + +Signature: + +```typescript +trackSuccess(duration: number): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| duration | number | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.usageprovider.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.usageprovider.md new file mode 100644 index 00000000000000..ad5c61b5c85a1c --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.usageprovider.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [usageProvider](./kibana-plugin-plugins-data-server.usageprovider.md) + +## usageProvider() function + +Signature: + +```typescript +export declare function usageProvider(core: CoreSetup): SearchUsage; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| core | CoreSetup | | + +Returns: + +`SearchUsage` + diff --git a/src/plugins/data/public/search/collectors/create_usage_collector.test.ts b/src/plugins/data/public/search/collectors/create_usage_collector.test.ts index a9ca9efb8b7e1a..aaaac5ae6ff7c4 100644 --- a/src/plugins/data/public/search/collectors/create_usage_collector.test.ts +++ b/src/plugins/data/public/search/collectors/create_usage_collector.test.ts @@ -90,18 +90,4 @@ describe('Search Usage Collector', () => { SEARCH_EVENT_TYPE.LONG_QUERY_RUN_BEYOND_TIMEOUT ); }); - - test('tracks response errors', async () => { - const duration = 10; - await usageCollector.trackError(duration); - expect(mockCoreSetup.http.post).toBeCalled(); - expect(mockCoreSetup.http.post.mock.calls[0][0]).toBe('/api/search/usage'); - }); - - test('tracks response duration', async () => { - const duration = 5; - await usageCollector.trackSuccess(duration); - expect(mockCoreSetup.http.post).toBeCalled(); - expect(mockCoreSetup.http.post.mock.calls[0][0]).toBe('/api/search/usage'); - }); }); diff --git a/src/plugins/data/public/search/collectors/create_usage_collector.ts b/src/plugins/data/public/search/collectors/create_usage_collector.ts index cb1b2b65c17c84..7adb0c3caa6759 100644 --- a/src/plugins/data/public/search/collectors/create_usage_collector.ts +++ b/src/plugins/data/public/search/collectors/create_usage_collector.ts @@ -72,21 +72,5 @@ export const createUsageCollector = ( SEARCH_EVENT_TYPE.LONG_QUERY_RUN_BEYOND_TIMEOUT ); }, - trackError: async (duration: number) => { - return core.http.post('/api/search/usage', { - body: JSON.stringify({ - eventType: 'error', - duration, - }), - }); - }, - trackSuccess: async (duration: number) => { - return core.http.post('/api/search/usage', { - body: JSON.stringify({ - eventType: 'success', - duration, - }), - }); - }, }; }; diff --git a/src/plugins/data/public/search/collectors/types.ts b/src/plugins/data/public/search/collectors/types.ts index bb85532fd3ab59..3e98f901eb0c39 100644 --- a/src/plugins/data/public/search/collectors/types.ts +++ b/src/plugins/data/public/search/collectors/types.ts @@ -31,6 +31,4 @@ export interface SearchUsageCollector { trackLongQueryPopupShown: () => Promise; trackLongQueryDialogDismissed: () => Promise; trackLongQueryRunBeyondTimeout: () => Promise; - trackError: (duration: number) => Promise; - trackSuccess: (duration: number) => Promise; } diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 84e24114a9e6c4..21586374d1e512 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -18,7 +18,7 @@ */ import { BehaviorSubject, throwError, timer, Subscription, defer, from, Observable } from 'rxjs'; -import { finalize, filter, tap } from 'rxjs/operators'; +import { finalize, filter } from 'rxjs/operators'; import { ApplicationStart, Toast, ToastsStart, CoreStart } from 'kibana/public'; import { getCombinedSignal, AbortError } from '../../common/utils'; import { IEsSearchRequest, IEsSearchResponse } from '../../common/search'; @@ -123,13 +123,6 @@ export class SearchInterceptor { this.pendingCount$.next(++this.pendingCount); return this.runSearch(request, combinedSignal).pipe( - tap({ - next: (e) => { - if (this.deps.usageCollector) { - this.deps.usageCollector.trackSuccess(e.rawResponse.took); - } - }, - }), finalize(() => { this.pendingCount$.next(--this.pendingCount); cleanup(); diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 461b21e1cc980c..1f3d7fbcb9f0f0 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -170,6 +170,8 @@ export { ISearchStart, getDefaultSearchParams, getTotalLoaded, + usageProvider, + SearchUsage, } from './search'; // Search namespace diff --git a/src/plugins/data/server/search/collectors/index.ts b/src/plugins/data/server/search/collectors/index.ts new file mode 100644 index 00000000000000..417dc1c2012d37 --- /dev/null +++ b/src/plugins/data/server/search/collectors/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { usageProvider, SearchUsage } from './usage'; diff --git a/src/plugins/data/server/search/collectors/routes.ts b/src/plugins/data/server/search/collectors/routes.ts deleted file mode 100644 index 38fb517e3c3f66..00000000000000 --- a/src/plugins/data/server/search/collectors/routes.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { schema } from '@kbn/config-schema'; -import { CoreSetup } from '../../../../../core/server'; -import { DataPluginStart } from '../../plugin'; -import { SearchUsage } from './usage'; - -export function registerSearchUsageRoute( - core: CoreSetup, - usage: SearchUsage -): void { - const router = core.http.createRouter(); - - router.post( - { - path: '/api/search/usage', - validate: { - body: schema.object({ - eventType: schema.string(), - duration: schema.number(), - }), - }, - }, - async (context, request, res) => { - const { eventType, duration } = request.body; - - if (eventType === 'success') usage.trackSuccess(duration); - if (eventType === 'error') usage.trackError(duration); - - return res.ok(); - } - ); -} diff --git a/src/plugins/data/server/search/collectors/usage.ts b/src/plugins/data/server/search/collectors/usage.ts index c43c572c2edbb9..e1be92aa13c37f 100644 --- a/src/plugins/data/server/search/collectors/usage.ts +++ b/src/plugins/data/server/search/collectors/usage.ts @@ -18,19 +18,18 @@ */ import { CoreSetup } from 'kibana/server'; -import { DataPluginStart } from '../../plugin'; import { Usage } from './register'; const SAVED_OBJECT_ID = 'search-telemetry'; export interface SearchUsage { - trackError(duration: number): Promise; + trackError(): Promise; trackSuccess(duration: number): Promise; } -export function usageProvider(core: CoreSetup): SearchUsage { +export function usageProvider(core: CoreSetup): SearchUsage { const getTracker = (eventType: keyof Usage) => { - return async (duration: number) => { + return async (duration?: number) => { const repository = await core .getStartServices() .then(([coreStart]) => coreStart.savedObjects.createInternalRepository()); @@ -52,17 +51,17 @@ export function usageProvider(core: CoreSetup): SearchU attributes[eventType]++; - const averageDuration = - (duration + (attributes.averageDuration ?? 0)) / - ((attributes.errorCount ?? 0) + (attributes.successCount ?? 0)); - - const newAttributes = { ...attributes, averageDuration }; + // Only track the average duration for successful requests + if (eventType === 'successCount') { + attributes.averageDuration = + ((duration ?? 0) + (attributes.averageDuration ?? 0)) / (attributes.successCount ?? 1); + } try { if (doesSavedObjectExist) { - await repository.update(SAVED_OBJECT_ID, SAVED_OBJECT_ID, newAttributes); + await repository.update(SAVED_OBJECT_ID, SAVED_OBJECT_ID, attributes); } else { - await repository.create(SAVED_OBJECT_ID, newAttributes, { id: SAVED_OBJECT_ID }); + await repository.create(SAVED_OBJECT_ID, attributes, { id: SAVED_OBJECT_ID }); } } catch (e) { // Version conflict error, swallow @@ -71,7 +70,7 @@ export function usageProvider(core: CoreSetup): SearchU }; return { - trackError: getTracker('errorCount'), + trackError: () => getTracker('errorCount')(), trackSuccess: getTracker('successCount'), }; } diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts index b8010f735c3274..78ead6df1a44e6 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts @@ -20,11 +20,13 @@ import { first } from 'rxjs/operators'; import { SharedGlobalConfig, Logger } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; import { Observable } from 'rxjs'; +import { SearchUsage } from '../collectors/usage'; import { ISearchStrategy, getDefaultSearchParams, getTotalLoaded } from '..'; export const esSearchStrategyProvider = ( config$: Observable, - logger: Logger + logger: Logger, + usage?: SearchUsage ): ISearchStrategy => { return { search: async (context, request, options) => { @@ -43,15 +45,22 @@ export const esSearchStrategyProvider = ( ...request.params, }; - const rawResponse = (await context.core.elasticsearch.legacy.client.callAsCurrentUser( - 'search', - params, - options - )) as SearchResponse; + try { + const rawResponse = (await context.core.elasticsearch.legacy.client.callAsCurrentUser( + 'search', + params, + options + )) as SearchResponse; - // The above query will either complete or timeout and throw an error. - // There is no progress indication on this api. - return { rawResponse, ...getTotalLoaded(rawResponse._shards) }; + if (usage) usage.trackSuccess(rawResponse.took); + + // The above query will either complete or timeout and throw an error. + // There is no progress indication on this api. + return { rawResponse, ...getTotalLoaded(rawResponse._shards) }; + } catch (e) { + if (usage) usage.trackError(); + throw e; + } }, }; }; diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index 67789fcbf56b47..cea2714671f0b4 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -20,3 +20,5 @@ export { ISearchStrategy, ISearchOptions, ISearchSetup, ISearchStart } from './types'; export { getDefaultSearchParams, getTotalLoaded } from './es_search'; + +export { usageProvider, SearchUsage } from './collectors'; diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index bbd0671754749f..9dc47369567af9 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -32,7 +32,6 @@ import { UsageCollectionSetup } from '../../../usage_collection/server'; import { registerUsageCollector } from './collectors/register'; import { usageProvider } from './collectors/usage'; import { searchTelemetry } from '../saved_objects'; -import { registerSearchUsageRoute } from './collectors/routes'; import { IEsSearchRequest } from '../../common'; interface StrategyMap { @@ -51,9 +50,15 @@ export class SearchService implements Plugin { core: CoreSetup, { usageCollection }: { usageCollection?: UsageCollectionSetup } ): ISearchSetup { + const usage = usageCollection ? usageProvider(core) : undefined; + this.registerSearchStrategy( ES_SEARCH_STRATEGY, - esSearchStrategyProvider(this.initializerContext.config.legacy.globalConfig$, this.logger) + esSearchStrategyProvider( + this.initializerContext.config.legacy.globalConfig$, + this.logger, + usage + ) ); core.savedObjects.registerType(searchTelemetry); @@ -61,10 +66,7 @@ export class SearchService implements Plugin { registerUsageCollector(usageCollection, this.initializerContext); } - const usage = usageProvider(core); - registerSearchRoute(core); - registerSearchUsageRoute(core, usage); return { registerSearchStrategy: this.registerSearchStrategy, usage }; } diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index 25dc890e0257db..76afd7e8c951c3 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -40,7 +40,7 @@ export interface ISearchSetup { /** * Used internally for telemetry */ - usage: SearchUsage; + usage?: SearchUsage; } export interface ISearchStart { diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index d77d42e3e20913..e1ce877ef21ea9 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -32,6 +32,7 @@ import { ClusterRerouteParams } from 'elasticsearch'; import { ClusterStateParams } from 'elasticsearch'; import { ClusterStatsParams } from 'elasticsearch'; import { ConfigOptions } from 'elasticsearch'; +import { CoreSetup as CoreSetup_2 } from 'kibana/server'; import { CountParams } from 'elasticsearch'; import { CreateDocumentParams } from 'elasticsearch'; import { DeleteDocumentByQueryParams } from 'elasticsearch'; @@ -539,8 +540,7 @@ export interface ISearchOptions { // @public (undocumented) export interface ISearchSetup { registerSearchStrategy: (name: string, strategy: ISearchStrategy) => void; - // Warning: (ae-forgotten-export) The symbol "SearchUsage" needs to be exported by the entry point index.d.ts - usage: SearchUsage; + usage?: SearchUsage; } // Warning: (ae-missing-release-tag) "ISearchStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -729,6 +729,16 @@ export const search: { }; }; +// Warning: (ae-missing-release-tag) "SearchUsage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SearchUsage { + // (undocumented) + trackError(): Promise; + // (undocumented) + trackSuccess(duration: number): Promise; +} + // Warning: (ae-missing-release-tag) "shouldReadFieldFromDocValues" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -780,6 +790,11 @@ export const UI_SETTINGS: { readonly FILTERS_EDITOR_SUGGEST_VALUES: "filterEditor:suggestValues"; }; +// Warning: (ae-missing-release-tag) "usageProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function usageProvider(core: CoreSetup_2): SearchUsage; + // Warnings were encountered during analysis: // @@ -804,13 +819,13 @@ export const UI_SETTINGS: { // src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:178:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:179:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:180:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:181:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:182:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:183:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:186:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:180:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:181:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:182:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:183:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:184:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/x-pack/plugins/data_enhanced/kibana.json b/x-pack/plugins/data_enhanced/kibana.json index f0baa84afca322..637af39339e277 100644 --- a/x-pack/plugins/data_enhanced/kibana.json +++ b/x-pack/plugins/data_enhanced/kibana.json @@ -8,7 +8,7 @@ "requiredPlugins": [ "data" ], - "optionalPlugins": ["kibanaReact", "kibanaUtils"], + "optionalPlugins": ["kibanaReact", "kibanaUtils", "usageCollection"], "server": true, "ui": true, "requiredBundles": ["kibanaReact", "kibanaUtils"] diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts index 9bd1ffddeaca8b..639f56d0cafca7 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts @@ -52,8 +52,6 @@ describe('EnhancedSearchInterceptor', () => { trackLongQueryPopupShown: jest.fn(), trackLongQueryDialogDismissed: jest.fn(), trackLongQueryRunBeyondTimeout: jest.fn(), - trackError: jest.fn(), - trackSuccess: jest.fn(), }; searchInterceptor = new EnhancedSearchInterceptor( @@ -458,7 +456,6 @@ describe('EnhancedSearchInterceptor', () => { expect(next.mock.calls[1][0]).toStrictEqual(timedResponses[1].value); expect(error).not.toHaveBeenCalled(); expect(mockUsageCollector.trackLongQueryRunBeyondTimeout).toBeCalledTimes(1); - expect(mockUsageCollector.trackSuccess).toBeCalledTimes(1); }); }); }); diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index d1ed410065248b..927dc91f365b72 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -89,9 +89,6 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { // If the response indicates it is complete, stop polling and complete the observable if (!response.is_running) { - if (this.deps.usageCollector && response.rawResponse) { - this.deps.usageCollector.trackSuccess(response.rawResponse.took); - } return EMPTY; } diff --git a/x-pack/plugins/data_enhanced/server/plugin.ts b/x-pack/plugins/data_enhanced/server/plugin.ts index 9c3a0edf7e733e..0e9731a4141195 100644 --- a/x-pack/plugins/data_enhanced/server/plugin.ts +++ b/x-pack/plugins/data_enhanced/server/plugin.ts @@ -12,11 +12,17 @@ import { Logger, } from '../../../../src/core/server'; import { ES_SEARCH_STRATEGY } from '../../../../src/plugins/data/common'; -import { PluginSetup as DataPluginSetup } from '../../../../src/plugins/data/server'; +import { + PluginSetup as DataPluginSetup, + PluginStart as DataPluginStart, + usageProvider, +} from '../../../../src/plugins/data/server'; import { enhancedEsSearchStrategyProvider } from './search'; +import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; interface SetupDependencies { data: DataPluginSetup; + usageCollection?: UsageCollectionSetup; } export class EnhancedDataServerPlugin implements Plugin { @@ -26,12 +32,15 @@ export class EnhancedDataServerPlugin implements Plugin, deps: SetupDependencies) { + const usage = deps.usageCollection ? usageProvider(core) : undefined; + deps.data.search.registerSearchStrategy( ES_SEARCH_STRATEGY, enhancedEsSearchStrategyProvider( this.initializerContext.config.legacy.globalConfig$, - this.logger + this.logger, + usage ) ); } diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 1f7b6a5f9aacea..d2a8384b1f8826 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -19,20 +19,34 @@ import { getDefaultSearchParams, getTotalLoaded, ISearchStrategy, + SearchUsage, } from '../../../../../src/plugins/data/server'; import { IEnhancedEsSearchRequest } from '../../common'; import { shimHitsTotal } from './shim_hits_total'; +import { IEsSearchResponse } from '../../../../../src/plugins/data/common/search/es_search'; -export interface AsyncSearchResponse { +interface AsyncSearchResponse { id: string; is_partial: boolean; is_running: boolean; response: SearchResponse; } +interface EnhancedEsSearchResponse extends IEsSearchResponse { + is_partial: boolean; + is_running: boolean; +} + +function isEnhancedEsSearchResponse( + response: IEsSearchResponse +): response is EnhancedEsSearchResponse { + return response.hasOwnProperty('is_partial') && response.hasOwnProperty('is_running'); +} + export const enhancedEsSearchStrategyProvider = ( config$: Observable, - logger: Logger + logger: Logger, + usage?: SearchUsage ): ISearchStrategy => { const search = async ( context: RequestHandlerContext, @@ -45,9 +59,24 @@ export const enhancedEsSearchStrategyProvider = ( const defaultParams = getDefaultSearchParams(config); const params = { ...defaultParams, ...request.params }; - return request.indexType === 'rollup' - ? rollupSearch(caller, { ...request, params }, options) - : asyncSearch(caller, { ...request, params }, options); + try { + const response = + request.indexType === 'rollup' + ? await rollupSearch(caller, { ...request, params }, options) + : await asyncSearch(caller, { ...request, params }, options); + + if ( + usage && + (!isEnhancedEsSearchResponse(response) || (!response.is_partial && !response.is_running)) + ) { + usage.trackSuccess(response.rawResponse.took); + } + + return response; + } catch (e) { + if (usage) usage.trackError(); + throw e; + } }; const cancel = async (context: RequestHandlerContext, id: string) => {