From 26c47bdf5ec20b3de79cd96b9ef11267b54d7e04 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Thu, 15 Oct 2020 14:55:13 -0700 Subject: [PATCH 01/21] [Search] Add request context and asScoped pattern --- .../search_examples/server/my_strategy.ts | 10 +- .../server/routes/server_search_route.ts | 7 +- .../data/common/search/aggs/agg_config.ts | 3 +- .../data/common/search/es_search/types.ts | 11 -- .../search_source/search_source.test.ts | 4 +- .../search/search_source/search_source.ts | 21 ++-- src/plugins/data/common/search/types.ts | 28 +++-- src/plugins/data/public/index.ts | 1 - src/plugins/data/public/search/index.ts | 1 - .../data/public/search/search_interceptor.ts | 2 +- .../data/public/search/search_service.ts | 21 +--- src/plugins/data/server/index.ts | 1 + .../es_search/es_search_strategy.test.ts | 24 ++--- .../search/es_search/es_search_strategy.ts | 5 +- src/plugins/data/server/search/index.ts | 6 +- src/plugins/data/server/search/mocks.ts | 5 +- .../data/server/search/routes/search.test.ts | 53 ++++------ .../data/server/search/routes/search.ts | 21 +--- .../data/server/search/search_service.ts | 96 +++++++++-------- src/plugins/data/server/search/types.ts | 29 +++-- .../server/routes/validate_es.ts | 14 +-- .../abstract_search_strategy.test.js | 21 +--- .../strategies/abstract_search_strategy.ts | 8 +- .../server/search/eql_search_strategy.test.ts | 49 ++++----- .../server/search/eql_search_strategy.ts | 9 +- .../server/search/es_search_strategy.test.ts | 46 +++----- .../server/search/es_search_strategy.ts | 100 ++++++++---------- .../search_strategy/index_fields/index.ts | 7 +- .../security_solution/index.ts | 10 +- .../server/search_strategy/timeline/index.ts | 10 +- 30 files changed, 272 insertions(+), 351 deletions(-) diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts index 26e7056cdd787e..957216c7b9713c 100644 --- a/examples/search_examples/server/my_strategy.ts +++ b/examples/search_examples/server/my_strategy.ts @@ -24,18 +24,18 @@ import { IMyStrategyResponse, IMyStrategyRequest } from '../common'; export const mySearchStrategyProvider = ( data: PluginStart ): ISearchStrategy => { - const es = data.search.getSearchStrategy('es'); + const es = data.search.getSearchStrategy(); return { - search: (request, options, context) => - es.search(request, options, context).pipe( + search: (deps, request, options) => + es.search(deps, request, options).pipe( map((esSearchRes) => ({ ...esSearchRes, cool: request.get_cool ? 'YES' : 'NOPE', })) ), - cancel: async (context, id) => { + cancel: async (deps, id) => { if (es.cancel) { - es.cancel(context, id); + es.cancel(deps, id); } }, }; diff --git a/examples/search_examples/server/routes/server_search_route.ts b/examples/search_examples/server/routes/server_search_route.ts index 21ae38b99f3d2d..dae423aeecc8cb 100644 --- a/examples/search_examples/server/routes/server_search_route.ts +++ b/examples/search_examples/server/routes/server_search_route.ts @@ -39,8 +39,8 @@ export function registerServerSearchRoute(router: IRouter, data: DataPluginStart // Run a synchronous search server side, by enforcing a high keepalive and waiting for completion. // If you wish to run the search with polling (in basic+), you'd have to poll on the search API. // Please reach out to the @app-arch-team if you need this to be implemented. - const res = await data.search - .search( + const res = await context + .search!.search( { params: { index, @@ -57,8 +57,7 @@ export function registerServerSearchRoute(router: IRouter, data: DataPluginStart keepAlive: '5m', }, } as IEsSearchRequest, - {}, - context + {} ) .toPromise(); diff --git a/src/plugins/data/common/search/aggs/agg_config.ts b/src/plugins/data/common/search/aggs/agg_config.ts index 910c79f5dd0d74..8ca27755e3dda8 100644 --- a/src/plugins/data/common/search/aggs/agg_config.ts +++ b/src/plugins/data/common/search/aggs/agg_config.ts @@ -21,13 +21,12 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { Assign, Ensure } from '@kbn/utility-types'; -import { ISearchSource } from 'src/plugins/data/public'; +import { ISearchOptions, ISearchSource } from 'src/plugins/data/public'; import { ExpressionAstFunction, ExpressionAstArgument, SerializedFieldFormat, } from 'src/plugins/expressions/common'; -import { ISearchOptions } from '../es_search'; import { IAggType } from './agg_type'; import { writeParams } from './agg_params'; diff --git a/src/plugins/data/common/search/es_search/types.ts b/src/plugins/data/common/search/es_search/types.ts index b1c3e5cdd3960d..7dbbd01d2cdadf 100644 --- a/src/plugins/data/common/search/es_search/types.ts +++ b/src/plugins/data/common/search/es_search/types.ts @@ -22,17 +22,6 @@ import { IKibanaSearchRequest, IKibanaSearchResponse } from '../types'; export const ES_SEARCH_STRATEGY = 'es'; -export interface ISearchOptions { - /** - * An `AbortSignal` that allows the caller of `search` to abort a search request. - */ - abortSignal?: AbortSignal; - /** - * Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. - */ - strategy?: string; -} - export type ISearchRequestParams> = { trackTotalHits?: boolean; } & Search; diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index 00e06663e998e0..98d66310c040e1 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, of } from 'rxjs'; import { IndexPattern } from '../../index_patterns'; import { GetConfigFn } from '../../types'; import { fetchSoon } from './legacy'; @@ -53,7 +53,7 @@ describe('SearchSource', () => { let searchSourceDependencies: SearchSourceDependencies; beforeEach(() => { - mockSearchMethod = jest.fn().mockResolvedValue({ rawResponse: '' }); + mockSearchMethod = jest.fn().mockReturnValue(of({ rawResponse: '' })); searchSourceDependencies = { getConfig: jest.fn(), diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 1a6b770cf2ca8e..418e316860eb9b 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -75,8 +75,7 @@ import { normalizeSortRequest } from './normalize_sort_request'; import { filterDocvalueFields } from './filter_docvalue_fields'; import { fieldWildcardFilter } from '../../../../kibana_utils/common'; import { IIndexPattern } from '../../index_patterns'; -import { IEsSearchRequest, IEsSearchResponse, ISearchOptions } from '../..'; -import { IKibanaSearchRequest, IKibanaSearchResponse } from '../types'; +import { ISearchGeneric, ISearchOptions } from '../..'; import { ISearchSource, SearchSourceOptions, SearchSourceFields } from './types'; import { FetchHandlers, RequestFailure, getSearchParamsFromRequest, SearchRequest } from './fetch'; @@ -102,15 +101,7 @@ export const searchSourceRequiredUiSettings = [ ]; export interface SearchSourceDependencies extends FetchHandlers { - // Types are nearly identical to ISearchGeneric, except we are making - // search options required here and returning a promise instead of observable. - search: < - SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse - >( - request: SearchStrategyRequest, - options: ISearchOptions - ) => Promise; + search: ISearchGeneric; } /** @public **/ @@ -144,7 +135,7 @@ export class SearchSource { } /** - * sets value to a single search source feild + * sets value to a single search source field * @param field: field name * @param value: value for the field */ @@ -319,9 +310,9 @@ export class SearchSource { getConfig, }); - return search({ params, indexType: searchRequest.indexType }, options).then(({ rawResponse }) => - onResponse(searchRequest, rawResponse) - ); + return search({ params, indexType: searchRequest.indexType }, options) + .toPromise() + .then(({ rawResponse }) => onResponse(searchRequest, rawResponse)); } /** diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts index c3943af5c6ff7b..fa93f64da2699d 100644 --- a/src/plugins/data/common/search/types.ts +++ b/src/plugins/data/common/search/types.ts @@ -18,12 +18,7 @@ */ import { Observable } from 'rxjs'; -import { IEsSearchRequest, IEsSearchResponse, ISearchOptions } from '../../common/search'; - -export type ISearch = ( - request: IKibanaSearchRequest, - options?: ISearchOptions -) => Observable; +import { IEsSearchRequest, IEsSearchResponse } from './es_search'; export type ISearchGeneric = < SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, @@ -33,6 +28,13 @@ export type ISearchGeneric = < options?: ISearchOptions ) => Observable; +export type ISearchCancelGeneric = (id: string, options?: ISearchOptions) => Promise; + +export interface ISearchClient { + search: ISearchGeneric; + cancel: ISearchCancelGeneric; +} + export interface IKibanaSearchResponse { /** * Some responses may contain a unique id to identify the request this response came from. @@ -61,6 +63,9 @@ export interface IKibanaSearchResponse { */ isPartial?: boolean; + /** + * The raw response returned by the internal search method (usually the raw ES response) + */ rawResponse: RawResponse; } @@ -72,3 +77,14 @@ export interface IKibanaSearchRequest { params?: Params; } + +export interface ISearchOptions { + /** + * An `AbortSignal` that allows the caller of `search` to abort a search request. + */ + abortSignal?: AbortSignal; + /** + * Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. + */ + strategy?: string; +} diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index c041511745be21..7c5dabed8a0cc2 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -358,7 +358,6 @@ export { IKibanaSearchRequest, IKibanaSearchResponse, injectSearchSourceReferences, - ISearch, ISearchSetup, ISearchStart, ISearchStartSearchSource, diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 86804a819cb0e5..8ab92aed18b0ab 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -31,7 +31,6 @@ export { IKibanaSearchRequest, IKibanaSearchResponse, injectReferences as injectSearchSourceReferences, - ISearch, ISearchGeneric, ISearchSource, parseSearchSourceJSON, diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 2e42635a7f811c..2ebf98ad0fdbe1 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -191,7 +191,7 @@ export class SearchInterceptor { * * @param request * @options - * @returns `Observalbe` emitting the search response or an error. + * @returns `Observable` emitting the search response or an error. */ public search( request: IKibanaSearchRequest, diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 734e88e085661d..c124527f3da2da 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -22,16 +22,7 @@ import { BehaviorSubject } from 'rxjs'; import { ISearchSetup, ISearchStart, SearchEnhancements } from './types'; import { handleResponse } from './fetch'; -import { - IEsSearchRequest, - IEsSearchResponse, - IKibanaSearchRequest, - IKibanaSearchResponse, - ISearchGeneric, - ISearchOptions, - SearchSourceService, - SearchSourceDependencies, -} from '../../common/search'; +import { ISearchGeneric, SearchSourceService, SearchSourceDependencies } from '../../common/search'; import { getCallMsearch } from './legacy'; import { AggsService, AggsStartDependencies } from './aggs'; import { IndexPatternsContract } from '../index_patterns/index_patterns'; @@ -120,15 +111,7 @@ export class SearchService implements Plugin { const searchSourceDependencies: SearchSourceDependencies = { getConfig: uiSettings.get.bind(uiSettings), - search: < - SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse - >( - request: SearchStrategyRequest, - options: ISearchOptions - ) => { - return search(request, options).toPromise(); - }, + search, onResponse: handleResponse, legacy: { callMsearch: getCallMsearch({ http }), diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 11dcbb01bf4a69..8c4fb6cb0a0bcf 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -213,6 +213,7 @@ export { ISearchStrategy, ISearchSetup, ISearchStart, + SearchStrategyDependencies, toSnakeCase, getAsyncOptions, getDefaultSearchParams, diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts index 2dbcc3196aa757..ddac28d9d54f5b 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts @@ -17,9 +17,9 @@ * under the License. */ -import { RequestHandlerContext } from '../../../../../core/server'; import { pluginInitializerContextConfigMock } from '../../../../../core/server/mocks'; import { esSearchStrategyProvider } from './es_search_strategy'; +import { SearchStrategyDependencies } from '../types'; describe('ES search strategy', () => { const mockLogger: any = { @@ -36,16 +36,12 @@ describe('ES search strategy', () => { }, }); - const mockContext = ({ - core: { - uiSettings: { - client: { - get: () => {}, - }, - }, - elasticsearch: { client: { asCurrentUser: { search: mockApiCaller } } }, + const mockDeps = ({ + uiSettingsClient: { + get: () => {}, }, - } as unknown) as RequestHandlerContext; + esClient: { asCurrentUser: { search: mockApiCaller } }, + } as unknown) as SearchStrategyDependencies; const mockConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; @@ -63,7 +59,7 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*' }; await esSearchStrategyProvider(mockConfig$, mockLogger) - .search({ params }, {}, mockContext) + .search(mockDeps, { params }, {}) .subscribe(() => { expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toEqual({ @@ -79,7 +75,7 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*', ignore_unavailable: false, timeout: '1000ms' }; await esSearchStrategyProvider(mockConfig$, mockLogger) - .search({ params }, {}, mockContext) + .search(mockDeps, { params }, {}) .subscribe(() => { expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toEqual({ @@ -93,11 +89,11 @@ describe('ES search strategy', () => { it('has all response parameters', async (done) => await esSearchStrategyProvider(mockConfig$, mockLogger) .search( + mockDeps, { params: { index: 'logstash-*' }, }, - {}, - mockContext + {} ) .subscribe((data) => { expect(data.isRunning).toBe(false); 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 92cc941e14853c..cb66cafb661442 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 @@ -38,12 +38,11 @@ export const esSearchStrategyProvider = ( usage?: SearchUsage ): ISearchStrategy => { return { - search: (request, options, context) => + search: ({ esClient, uiSettingsClient }, request, options) => from( new Promise(async (resolve, reject) => { logger.debug(`search ${request.params?.index}`); const config = await config$.pipe(first()).toPromise(); - const uiSettingsClient = await context.core.uiSettings.client; // Only default index pattern type is supported here. // See data_enhanced for other type support. @@ -64,7 +63,7 @@ export const esSearchStrategyProvider = ( try { const promise = shimAbortSignal( - context.core.elasticsearch.client.asCurrentUser.search(params), + esClient.asCurrentUser.search(params), options?.abortSignal ); const { body: rawResponse } = (await promise) as ApiResponse>; diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index b671ed806510b6..1be641401b29cc 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -17,12 +17,8 @@ * under the License. */ -export { ISearchStrategy, ISearchSetup, ISearchStart, SearchEnhancements } from './types'; - +export * from './types'; export * from './es_search'; - export { usageProvider, SearchUsage } from './collectors'; - export * from './aggs'; - export { shimHitsTotal } from './routes'; diff --git a/src/plugins/data/server/search/mocks.ts b/src/plugins/data/server/search/mocks.ts index 0d4ba0cba24a38..4914726c85ef8b 100644 --- a/src/plugins/data/server/search/mocks.ts +++ b/src/plugins/data/server/search/mocks.ts @@ -33,7 +33,10 @@ export function createSearchStartMock(): jest.Mocked { return { aggs: searchAggsStartMock(), getSearchStrategy: jest.fn(), - search: jest.fn(), + asScoped: jest.fn().mockReturnValue({ + search: jest.fn(), + cancel: jest.fn(), + }), searchSource: searchSourceMock.createStartContract(), }; } diff --git a/src/plugins/data/server/search/routes/search.test.ts b/src/plugins/data/server/search/routes/search.test.ts index 834e5de5c31214..1ad4a4d9dae295 100644 --- a/src/plugins/data/server/search/routes/search.test.ts +++ b/src/plugins/data/server/search/routes/search.test.ts @@ -17,34 +17,18 @@ * under the License. */ -import { Observable, from } from 'rxjs'; - -import { - CoreSetup, - RequestHandlerContext, - SharedGlobalConfig, - StartServicesAccessor, -} from 'src/core/server'; -import { - coreMock, - httpServerMock, - pluginInitializerContextConfigMock, -} from '../../../../../../src/core/server/mocks'; +import { from } from 'rxjs'; + +import { CoreSetup, RequestHandlerContext } from 'src/core/server'; +import { coreMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { registerSearchRoute } from './search'; import { DataPluginStart } from '../../plugin'; -import { dataPluginMock } from '../../mocks'; describe('Search service', () => { - let mockDataStart: MockedKeys; let mockCoreSetup: MockedKeys>; - let getStartServices: jest.Mocked>; - let globalConfig$: Observable; beforeEach(() => { - mockDataStart = dataPluginMock.createStartContract(); - mockCoreSetup = coreMock.createSetup({ pluginStartContract: mockDataStart }); - getStartServices = mockCoreSetup.getStartServices; - globalConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; + mockCoreSetup = coreMock.createSetup(); }); it('handler calls context.search.search with the given request and strategy', async () => { @@ -67,8 +51,12 @@ describe('Search service', () => { }, }; - mockDataStart.search.search.mockReturnValue(from(Promise.resolve(response))); - const mockContext = {}; + const mockContext = { + search: { + search: jest.fn().mockReturnValue(from(Promise.resolve(response))), + }, + }; + const mockBody = { id: undefined, params: {} }; const mockParams = { strategy: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ @@ -77,14 +65,14 @@ describe('Search service', () => { }); const mockResponse = httpServerMock.createResponseFactory(); - registerSearchRoute(mockCoreSetup.http.createRouter(), { getStartServices, globalConfig$ }); + registerSearchRoute(mockCoreSetup.http.createRouter()); const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; const handler = mockRouter.post.mock.calls[0][1]; await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); - expect(mockDataStart.search.search).toBeCalled(); - expect(mockDataStart.search.search.mock.calls[0][0]).toStrictEqual(mockBody); + expect(mockContext.search.search).toBeCalled(); + expect(mockContext.search.search.mock.calls[0][0]).toStrictEqual(mockBody); expect(mockResponse.ok).toBeCalled(); expect(mockResponse.ok.mock.calls[0][0]).toEqual({ body: response, @@ -101,9 +89,12 @@ describe('Search service', () => { }) ); - mockDataStart.search.search.mockReturnValue(rejectedValue); + const mockContext = { + search: { + search: jest.fn().mockReturnValue(rejectedValue), + }, + }; - const mockContext = {}; const mockBody = { id: undefined, params: {} }; const mockParams = { strategy: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ @@ -112,14 +103,14 @@ describe('Search service', () => { }); const mockResponse = httpServerMock.createResponseFactory(); - registerSearchRoute(mockCoreSetup.http.createRouter(), { getStartServices, globalConfig$ }); + registerSearchRoute(mockCoreSetup.http.createRouter()); const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; const handler = mockRouter.post.mock.calls[0][1]; await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); - expect(mockDataStart.search.search).toBeCalled(); - expect(mockDataStart.search.search.mock.calls[0][0]).toStrictEqual(mockBody); + expect(mockContext.search.search).toBeCalled(); + expect(mockContext.search.search.mock.calls[0][0]).toStrictEqual(mockBody); expect(mockResponse.customError).toBeCalled(); const error: any = mockResponse.customError.mock.calls[0][0]; expect(error.body.message).toBe('oh no'); diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index 1e8433d9685e3f..9fb8b0d004f08b 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -20,13 +20,9 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from 'src/core/server'; import { getRequestAbortedSignal } from '../../lib'; -import { SearchRouteDependencies } from '../search_service'; import { shimHitsTotal } from './shim_hits_total'; -export function registerSearchRoute( - router: IRouter, - { getStartServices }: SearchRouteDependencies -): void { +export function registerSearchRoute(router: IRouter): void { router.post( { path: '/internal/search/{strategy}/{id?}', @@ -46,17 +42,14 @@ export function registerSearchRoute( const { strategy, id } = request.params; const abortSignal = getRequestAbortedSignal(request.events.aborted$); - const [, , selfStart] = await getStartServices(); - try { - const response = await selfStart.search - .search( + const response = await context + .search!.search( { ...searchRequest, id }, { abortSignal, strategy, - }, - context + } ) .toPromise(); @@ -97,12 +90,8 @@ export function registerSearchRoute( async (context, request, res) => { const { strategy, id } = request.params; - const [, , selfStart] = await getStartServices(); - const searchStrategy = selfStart.search.getSearchStrategy(strategy); - if (!searchStrategy.cancel) return res.ok(); - try { - await searchStrategy.cancel(context, id); + await context.search!.cancel(id, { strategy }); return res.ok(); } catch (err) { return res.customError({ diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 0130d3aacc91fb..c7ef611e90cb2b 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -26,12 +26,17 @@ import { Logger, Plugin, PluginInitializerContext, - RequestHandlerContext, SharedGlobalConfig, StartServicesAccessor, } from 'src/core/server'; import { first } from 'rxjs/operators'; -import { ISearchSetup, ISearchStart, ISearchStrategy, SearchEnhancements } from './types'; +import { + ISearchSetup, + ISearchStart, + ISearchStrategy, + SearchEnhancements, + SearchStrategyDependencies, +} from './types'; import { AggsService, AggsSetupDependencies } from './aggs'; @@ -53,6 +58,7 @@ import { SearchSourceService, searchSourceRequiredUiSettings, ISearchOptions, + ISearchClient, } from '../../common/search'; import { getShardDelayBucketAgg, @@ -61,6 +67,12 @@ import { import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn'; import { ConfigSchema } from '../../config'; +declare module 'src/core/server' { + interface RequestHandlerContext { + search?: ISearchClient; + } +} + type StrategyMap = Record>; /** @internal */ @@ -103,9 +115,17 @@ export class SearchService implements Plugin { getStartServices: core.getStartServices, globalConfig$: this.initializerContext.config.legacy.globalConfig$, }; - registerSearchRoute(router, routeDependencies); + registerSearchRoute(router); registerMsearchRoute(router, routeDependencies); + core.http.registerRouteHandlerContext('search', (context) => { + return this.getSearchClient({ + savedObjectsClient: context.core.savedObjects.client, + esClient: context.core.elasticsearch.client, + uiSettingsClient: context.core.uiSettings.client, + }); + }); + this.registerSearchStrategy( ES_SEARCH_STRATEGY, esSearchStrategyProvider( @@ -144,14 +164,25 @@ export class SearchService implements Plugin { usage, }; } + public start( { elasticsearch, savedObjects, uiSettings }: CoreStart, { fieldFormats, indexPatterns }: SearchServiceStartDependencies ): ISearchStart { + const getSearchClientAsScoped = (request: KibanaRequest) => { + const savedObjectsClient = savedObjects.getScopedClient(request); + const deps = { + savedObjectsClient, + esClient: elasticsearch.client.asScoped(request), + uiSettingsClient: uiSettings.asScopedToClient(savedObjectsClient), + }; + return this.getSearchClient(deps); + }; + return { aggs: this.aggsService.start({ fieldFormats, uiSettings }), getSearchStrategy: this.getSearchStrategy, - search: this.search.bind(this), + asScoped: getSearchClientAsScoped, searchSource: { asScoped: async (request: KibanaRequest) => { const esClient = elasticsearch.client.asScoped(request); @@ -169,39 +200,7 @@ export class SearchService implements Plugin { const searchSourceDependencies: SearchSourceDependencies = { getConfig: (key: string): T => uiSettingsCache[key], - search: < - SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse - >( - searchStrategyRequest: SearchStrategyRequest, - options: ISearchOptions - ) => { - /** - * Unless we want all SearchSource users to provide both a KibanaRequest - * (needed for index patterns) AND the RequestHandlerContext (needed for - * low-level search), we need to fake the context as it can be derived - * from the request object anyway. This will pose problems for folks who - * are registering custom search strategies as they are only getting a - * subset of the entire context. Ideally low-level search should be - * refactored to only require the needed dependencies: esClient & uiSettings. - */ - const fakeRequestHandlerContext = { - core: { - elasticsearch: { - client: esClient, - }, - uiSettings: { - client: uiSettingsClient, - }, - }, - } as RequestHandlerContext; - - return this.search( - searchStrategyRequest, - options, - fakeRequestHandlerContext - ).toPromise(); - }, + search: getSearchClientAsScoped(request).search, // onResponse isn't used on the server, so we just return the original value onResponse: (req, res) => res, legacy: { @@ -239,22 +238,35 @@ export class SearchService implements Plugin { SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse >( + deps: SearchStrategyDependencies, searchRequest: SearchStrategyRequest, - options: ISearchOptions, - context: RequestHandlerContext + options?: ISearchOptions ) => { const strategy = this.getSearchStrategy( - options.strategy || this.defaultSearchStrategyName + options?.strategy ); - return strategy.search(searchRequest, options, context); + return strategy.search(deps, searchRequest, options); + }; + + private cancel = (deps: SearchStrategyDependencies, id: string, options?: ISearchOptions) => { + const strategy = this.getSearchStrategy(options?.strategy); + + return strategy.cancel ? strategy.cancel(deps, id) : Promise.resolve(); + }; + + private getSearchClient = (deps: SearchStrategyDependencies): ISearchClient => { + return { + search: (searchRequest, options?) => this.search(deps, searchRequest, options), + cancel: (id: string, options?: ISearchOptions) => this.cancel(deps, id, options), + }; }; private getSearchStrategy = < SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse >( - name: string + name: string = this.defaultSearchStrategyName ): ISearchStrategy => { this.logger.debug(`Get strategy ${name}`); const strategy = this.searchStrategies[name]; diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index 9ba06d88dc4b34..622d6856b20f6f 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -18,12 +18,18 @@ */ import { Observable } from 'rxjs'; -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { + IScopedClusterClient, + IUiSettingsClient, + SavedObjectsClientContract, + KibanaRequest, +} from 'src/core/server'; import { ISearchOptions, ISearchStartSearchSource, IKibanaSearchRequest, IKibanaSearchResponse, + ISearchClient, } from '../../common/search'; import { AggsSetup, AggsStart } from './aggs'; import { SearchUsage } from './collectors'; @@ -33,6 +39,12 @@ export interface SearchEnhancements { defaultStrategy: string; } +export interface SearchStrategyDependencies { + savedObjectsClient: SavedObjectsClientContract; + esClient: IScopedClusterClient; + uiSettingsClient: IUiSettingsClient; +} + export interface ISearchSetup { aggs: AggsSetup; /** @@ -67,11 +79,11 @@ export interface ISearchStrategy< SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse > { search: ( + deps: SearchStrategyDependencies, request: SearchStrategyRequest, - options: ISearchOptions, - context: RequestHandlerContext + options?: ISearchOptions ) => Observable; - cancel?: (context: RequestHandlerContext, id: string) => Promise; + cancel?: (deps: SearchStrategyDependencies, id: string) => Promise; } export interface ISearchStart< @@ -80,13 +92,14 @@ export interface ISearchStart< > { aggs: AggsStart; /** - * Get other registered search strategies. For example, if a new strategy needs to use the - * already-registered ES search strategy, it can use this function to accomplish that. + * Get other registered search strategies by name (or, by default, the Elasticsearch strategy). + * For example, if a new strategy needs to use the already-registered ES search strategy, it can + * use this function to accomplish that. */ getSearchStrategy: ( - name: string + name?: string // Name of the search strategy (defaults to the Elasticsearch strategy) ) => ISearchStrategy; - search: ISearchStrategy['search']; + asScoped: (request: KibanaRequest) => ISearchClient; searchSource: { asScoped: (request: KibanaRequest) => Promise; }; diff --git a/src/plugins/vis_type_timelion/server/routes/validate_es.ts b/src/plugins/vis_type_timelion/server/routes/validate_es.ts index 242be515e52bc7..73f169e4bd8596 100644 --- a/src/plugins/vis_type_timelion/server/routes/validate_es.ts +++ b/src/plugins/vis_type_timelion/server/routes/validate_es.ts @@ -20,7 +20,6 @@ import _ from 'lodash'; import { IRouter, CoreSetup } from 'kibana/server'; import { ES_SEARCH_STRATEGY } from '../../../data/server'; -import { TimelionPluginStartDeps } from '../plugin'; export function validateEsRoute(router: IRouter, core: CoreSetup) { router.get( @@ -30,7 +29,6 @@ export function validateEsRoute(router: IRouter, core: CoreSetup) { }, async function (context, request, response) { const uiSettings = await context.core.uiSettings.client.getAll(); - const deps = (await core.getStartServices())[1] as TimelionPluginStartDeps; const timefield = uiSettings['timelion:es.timefield']; @@ -58,14 +56,10 @@ export function validateEsRoute(router: IRouter, core: CoreSetup) { let resp; try { resp = ( - await deps.data.search - .search( - body, - { - strategy: ES_SEARCH_STRATEGY, - }, - context - ) + await context + .search!.search(body, { + strategy: ES_SEARCH_STRATEGY, + }) .toPromise() ).rawResponse; } catch (errResp) { diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js index ceae784cf74a64..ece0ba4c2da2d5 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js @@ -60,22 +60,8 @@ describe('AbstractSearchStrategy', () => { const responses = await abstractSearchStrategy.search( { - requestContext: {}, - framework: { - core: { - getStartServices: jest.fn().mockReturnValue( - Promise.resolve([ - {}, - { - data: { - search: { - search: searchFn, - }, - }, - }, - ]) - ), - }, + requestContext: { + search: { search: searchFn }, }, }, searches @@ -92,8 +78,7 @@ describe('AbstractSearchStrategy', () => { }, { strategy: 'es', - }, - {} + } ); }); }); diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts index 7b62ad310a354e..c670825093608f 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts @@ -56,12 +56,11 @@ export class AbstractSearchStrategy { } async search(req: ReqFacade, bodies: any[], options = {}) { - const [, deps] = await req.framework.core.getStartServices(); const requests: any[] = []; bodies.forEach((body) => { requests.push( - deps.data.search - .search( + req.requestContext + .search!.search( { params: { ...body, @@ -72,8 +71,7 @@ export class AbstractSearchStrategy { { ...options, strategy: this.searchStrategyName, - }, - req.requestContext + } ) .toPromise() ); diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts index 5b634fe4cf26c1..72705f52b2937e 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger, RequestHandlerContext } from 'src/core/server'; +import { Logger } from 'src/core/server'; import { EqlSearchStrategyRequest } from '../../common/search/types'; import { eqlSearchStrategyProvider } from './eql_search_strategy'; +import { SearchStrategyDependencies } from '../../../../../src/plugins/data/server'; const getMockEqlResponse = () => ({ body: { @@ -46,32 +47,26 @@ describe('EQL search strategy', () => { describe('search()', () => { let mockEqlSearch: jest.Mock; let mockEqlGet: jest.Mock; - let mockContext: RequestHandlerContext; + let mockDeps: SearchStrategyDependencies; let params: Required['params']; let options: Required['options']; beforeEach(() => { mockEqlSearch = jest.fn().mockResolvedValueOnce(getMockEqlResponse()); mockEqlGet = jest.fn().mockResolvedValueOnce(getMockEqlResponse()); - mockContext = ({ - core: { - uiSettings: { - client: { - get: jest.fn(), - }, - }, - elasticsearch: { - client: { - asCurrentUser: { - eql: { - get: mockEqlGet, - search: mockEqlSearch, - }, - }, + mockDeps = ({ + uiSettingsClient: { + get: jest.fn(), + }, + esClient: { + asCurrentUser: { + eql: { + get: mockEqlGet, + search: mockEqlSearch, }, }, }, - } as unknown) as RequestHandlerContext; + } as unknown) as SearchStrategyDependencies; params = { index: 'logstash-*', body: { query: 'process where 1 == 1' }, @@ -82,7 +77,7 @@ describe('EQL search strategy', () => { describe('async functionality', () => { it('performs an eql client search with params when no ID is provided', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search({ options, params }, {}, mockContext).toPromise(); + await eqlSearch.search(mockDeps, { options, params }, {}).toPromise(); const [[request, requestOptions]] = mockEqlSearch.mock.calls; expect(request.index).toEqual('logstash-*'); @@ -92,7 +87,7 @@ describe('EQL search strategy', () => { it('retrieves the current request if an id is provided', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search({ id: 'my-search-id' }, {}, mockContext).toPromise(); + await eqlSearch.search(mockDeps, { id: 'my-search-id' }, {}).toPromise(); const [[requestParams]] = mockEqlGet.mock.calls; expect(mockEqlSearch).not.toHaveBeenCalled(); @@ -103,7 +98,7 @@ describe('EQL search strategy', () => { describe('arguments', () => { it('sends along async search options', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search({ options, params }, {}, mockContext).toPromise(); + await eqlSearch.search(mockDeps, { options, params }, {}).toPromise(); const [[request]] = mockEqlSearch.mock.calls; expect(request).toEqual( @@ -116,7 +111,7 @@ describe('EQL search strategy', () => { it('sends along default search parameters', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search({ options, params }, {}, mockContext).toPromise(); + await eqlSearch.search(mockDeps, { options, params }, {}).toPromise(); const [[request]] = mockEqlSearch.mock.calls; expect(request).toEqual( @@ -131,6 +126,7 @@ describe('EQL search strategy', () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); await eqlSearch .search( + mockDeps, { options, params: { @@ -139,8 +135,7 @@ describe('EQL search strategy', () => { keep_on_completion: false, }, }, - {}, - mockContext + {} ) .toPromise(); const [[request]] = mockEqlSearch.mock.calls; @@ -158,12 +153,12 @@ describe('EQL search strategy', () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); await eqlSearch .search( + mockDeps, { options: { ...options, maxRetries: 2, ignore: [300] }, params, }, - {}, - mockContext + {} ) .toPromise(); const [[, requestOptions]] = mockEqlSearch.mock.calls; @@ -179,7 +174,7 @@ describe('EQL search strategy', () => { it('passes transport options for an existing request', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); await eqlSearch - .search({ id: 'my-search-id', options: { ignore: [400] } }, {}, mockContext) + .search(mockDeps, { id: 'my-search-id', options: { ignore: [400] } }, {}) .toPromise(); const [[, requestOptions]] = mockEqlGet.mock.calls; diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts index a7ca999699e237..377b90c9cf7a93 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts @@ -21,19 +21,18 @@ export const eqlSearchStrategyProvider = ( logger: Logger ): ISearchStrategy => { return { - cancel: async (context, id) => { + cancel: async ({ esClient }, id) => { logger.debug(`_eql/delete ${id}`); - await context.core.elasticsearch.client.asCurrentUser.eql.delete({ + await esClient.asCurrentUser.eql.delete({ id, }); }, - search: (request, options, context) => + search: ({ esClient, uiSettingsClient }, request, options) => from( new Promise(async (resolve) => { logger.debug(`_eql/search ${JSON.stringify(request.params) || request.id}`); let promise: TransportRequestPromise; - const eqlClient = context.core.elasticsearch.client.asCurrentUser.eql; - const uiSettingsClient = await context.core.uiSettings.client; + const eqlClient = esClient.asCurrentUser.eql; const asyncOptions = getAsyncOptions(); const searchOptions = toSnakeCase({ ...request.options }); diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts index bab304b6afc9fa..4f6b490972f06c 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from '../../../../../src/core/server'; import { enhancedEsSearchStrategyProvider } from './es_search_strategy'; import { BehaviorSubject } from 'rxjs'; +import { SearchStrategyDependencies } from '../../../../../src/plugins/data/server/search'; const mockAsyncResponse = { body: { @@ -40,26 +40,20 @@ describe('ES search strategy', () => { const mockLogger: any = { debug: () => {}, }; - const mockContext = { - core: { - uiSettings: { - client: { - get: jest.fn(), - }, - }, - elasticsearch: { - client: { - asCurrentUser: { - asyncSearch: { - get: mockGetCaller, - submit: mockSubmitCaller, - }, - transport: { request: mockApiCaller }, - }, + const mockDeps = ({ + uiSettingsClient: { + get: jest.fn(), + }, + esClient: { + asCurrentUser: { + asyncSearch: { + get: mockGetCaller, + submit: mockSubmitCaller, }, + transport: { request: mockApiCaller }, }, }, - }; + } as unknown) as SearchStrategyDependencies; const mockConfig$ = new BehaviorSubject({ elasticsearch: { shardTimeout: { @@ -86,9 +80,7 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*', body: { query: {} } }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch - .search({ params }, {}, (mockContext as unknown) as RequestHandlerContext) - .toPromise(); + await esSearch.search(mockDeps, { params }, {}).toPromise(); expect(mockSubmitCaller).toBeCalled(); const request = mockSubmitCaller.mock.calls[0][0]; @@ -102,9 +94,7 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*', body: { query: {} } }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch - .search({ id: 'foo', params }, {}, (mockContext as unknown) as RequestHandlerContext) - .toPromise(); + await esSearch.search(mockDeps, { id: 'foo', params }, {}).toPromise(); expect(mockGetCaller).toBeCalled(); const request = mockGetCaller.mock.calls[0][0]; @@ -121,12 +111,12 @@ describe('ES search strategy', () => { await esSearch .search( + mockDeps, { indexType: 'rollup', params, }, - {}, - (mockContext as unknown) as RequestHandlerContext + {} ) .toPromise(); @@ -142,9 +132,7 @@ describe('ES search strategy', () => { const params = { index: 'foo-*', body: {} }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch - .search({ params }, {}, (mockContext as unknown) as RequestHandlerContext) - .toPromise(); + await esSearch.search(mockDeps, { params }, {}).toPromise(); expect(mockSubmitCaller).toBeCalled(); const request = mockSubmitCaller.mock.calls[0][0]; 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 9b89fb9fab3cb8..58de427a14b072 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 @@ -9,7 +9,7 @@ import { first } from 'rxjs/operators'; import { SearchResponse } from 'elasticsearch'; import { Observable } from 'rxjs'; import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; -import { SharedGlobalConfig, RequestHandlerContext, Logger } from '../../../../../src/core/server'; +import { SharedGlobalConfig, Logger } from '../../../../../src/core/server'; import { getTotalLoaded, ISearchStrategy, @@ -20,13 +20,14 @@ import { shimHitsTotal, getAsyncOptions, shimAbortSignal, + SearchStrategyDependencies, } from '../../../../../src/plugins/data/server'; import { IEnhancedEsSearchRequest } from '../../common'; import { ISearchOptions, IEsSearchResponse, isCompleteResponse, -} from '../../../../../src/plugins/data/common/search'; +} from '../../../../../src/plugins/data/common'; function isEnhancedEsSearchResponse(response: any): response is IEsSearchResponse { return response.hasOwnProperty('isPartial') && response.hasOwnProperty('isRunning'); @@ -36,55 +37,48 @@ export const enhancedEsSearchStrategyProvider = ( config$: Observable, logger: Logger, usage?: SearchUsage -): ISearchStrategy => { - const search = ( - request: IEnhancedEsSearchRequest, - options: ISearchOptions, - context: RequestHandlerContext - ) => - from( - new Promise(async (resolve, reject) => { - logger.debug(`search ${JSON.stringify(request.params) || request.id}`); - - const isAsync = request.indexType !== 'rollup'; - - try { - const response = isAsync - ? await asyncSearch(request, options, context) - : await rollupSearch(request, options, context); - - if ( - usage && - isAsync && - isEnhancedEsSearchResponse(response) && - isCompleteResponse(response) - ) { - usage.trackSuccess(response.rawResponse.took); +): ISearchStrategy => { + return { + search: (deps, request, options) => + from( + new Promise(async (resolve, reject) => { + logger.debug(`search ${JSON.stringify(request.params) || request.id}`); + + const isAsync = request.indexType !== 'rollup'; + + try { + const response = isAsync + ? await asyncSearch(deps, request, options) + : await rollupSearch(deps, request, options); + + if ( + usage && + isAsync && + isEnhancedEsSearchResponse(response) && + isCompleteResponse(response) + ) { + usage.trackSuccess(response.rawResponse.took); + } + + resolve(response); + } catch (e) { + if (usage) usage.trackError(); + reject(e); } - - resolve(response); - } catch (e) { - if (usage) usage.trackError(); - reject(e); - } - }) - ); - - const cancel = async (context: RequestHandlerContext, id: string) => { - logger.debug(`cancel ${id}`); - await context.core.elasticsearch.client.asCurrentUser.asyncSearch.delete({ - id, - }); + }) + ), + cancel: async ({ esClient }, id) => { + logger.debug(`cancel ${id}`); + await esClient.asCurrentUser.asyncSearch.delete({ id }); + }, }; async function asyncSearch( + { esClient, uiSettingsClient }: SearchStrategyDependencies, request: IEnhancedEsSearchRequest, - options: ISearchOptions, - context: RequestHandlerContext + options?: ISearchOptions ): Promise { let promise: TransportRequestPromise; - const esClient = context.core.elasticsearch.client.asCurrentUser; - const uiSettingsClient = await context.core.uiSettings.client; const asyncOptions = getAsyncOptions(); // If we have an ID, then just poll for that ID, otherwise send the entire request body @@ -96,9 +90,9 @@ export const enhancedEsSearchStrategyProvider = ( ...request.params, }); - promise = esClient.asyncSearch.submit(submitOptions); + promise = esClient.asCurrentUser.asyncSearch.submit(submitOptions); } else { - promise = esClient.asyncSearch.get({ + promise = esClient.asCurrentUser.asyncSearch.get({ id: request.id, ...toSnakeCase(asyncOptions), }); @@ -115,13 +109,11 @@ export const enhancedEsSearchStrategyProvider = ( }; } - const rollupSearch = async function ( + async function rollupSearch( + { esClient, uiSettingsClient }: SearchStrategyDependencies, request: IEnhancedEsSearchRequest, - options: ISearchOptions, - context: RequestHandlerContext + options?: ISearchOptions ): Promise { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const uiSettingsClient = await context.core.uiSettings.client; const config = await config$.pipe(first()).toPromise(); const { body, index, ...params } = request.params!; const method = 'POST'; @@ -132,7 +124,7 @@ export const enhancedEsSearchStrategyProvider = ( ...params, }); - const promise = esClient.transport.request({ + const promise = esClient.asCurrentUser.transport.request({ method, path, body, @@ -146,7 +138,5 @@ export const enhancedEsSearchStrategyProvider = ( rawResponse: response, ...getTotalLoaded(response._shards), }; - }; - - return { search, cancel }; + } }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts index bc461f3885a70b..a0955f5d44f2a9 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts @@ -26,13 +26,10 @@ export const securitySolutionIndexFieldsProvider = (): ISearchStrategy< const beatFields: BeatFields = require('../../utils/beat_schema/fields').fieldsBeat; return { - search: (request, options, context) => + search: ({ esClient }, request, options) => from( new Promise(async (resolve) => { - const { elasticsearch } = context.core; - const indexPatternsFetcher = new IndexPatternsFetcher( - elasticsearch.legacy.client.callAsCurrentUser - ); + const indexPatternsFetcher = new IndexPatternsFetcher(esClient.asCurrentUser); const dedupeIndices = dedupeIndexName(request.indices); const responsesIndexFields = await Promise.all( diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts index 962865880df5f2..2d6937f40b5d19 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts @@ -17,10 +17,10 @@ import { SecuritySolutionFactory } from './factory/types'; export const securitySolutionSearchStrategyProvider = ( data: PluginStart ): ISearchStrategy, StrategyResponseType> => { - const es = data.search.getSearchStrategy('es'); + const es = data.search.getSearchStrategy(); return { - search: (request, options, context) => { + search: (deps, request, options) => { if (request.factoryQueryType == null) { throw new Error('factoryQueryType is required'); } @@ -28,12 +28,12 @@ export const securitySolutionSearchStrategyProvider = queryFactory.parse(request, esSearchRes))); }, - cancel: async (context, id) => { + cancel: async (deps, id) => { if (es.cancel) { - es.cancel(context, id); + es.cancel(deps, id); } }, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts index 165f0f586ebdb4..39f65054d4f710 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts @@ -17,10 +17,10 @@ import { SecuritySolutionTimelineFactory } from './factory/types'; export const securitySolutionTimelineSearchStrategyProvider = ( data: PluginStart ): ISearchStrategy, TimelineStrategyResponseType> => { - const es = data.search.getSearchStrategy('es'); + const es = data.search.getSearchStrategy(); return { - search: (request, options, context) => { + search: (deps, request, options) => { if (request.factoryQueryType == null) { throw new Error('factoryQueryType is required'); } @@ -29,12 +29,12 @@ export const securitySolutionTimelineSearchStrategyProvider = queryFactory.parse(request, esSearchRes))); }, - cancel: async (context, id) => { + cancel: async (deps, id) => { if (es.cancel) { - es.cancel(context, id); + es.cancel(deps, id); } }, }; From 36b8fcbd1c49226ee099a899755a860a2d7ceb74 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Thu, 15 Oct 2020 15:11:48 -0700 Subject: [PATCH 02/21] Update docs --- ...ugins-data-public.ikibanasearchresponse.md | 2 +- ...ublic.ikibanasearchresponse.rawresponse.md | 2 + ...bana-plugin-plugins-data-public.isearch.md | 11 ---- .../kibana-plugin-plugins-data-public.md | 1 - ...ns-data-public.searchinterceptor.search.md | 2 +- ...plugin-plugins-data-public.searchsource.md | 2 +- ...gins-data-public.searchsource.serialize.md | 4 +- ...ugins-data-public.searchsource.setfield.md | 2 +- ...gins-data-server.isearchstart.asscoped.md} | 6 +- ...a-server.isearchstart.getsearchstrategy.md | 4 +- ...plugin-plugins-data-server.isearchstart.md | 4 +- ...gins-data-server.isearchstrategy.cancel.md | 2 +- ...gin-plugins-data-server.isearchstrategy.md | 4 +- ...gins-data-server.isearchstrategy.search.md | 2 +- .../kibana-plugin-plugins-data-server.md | 1 + ...plugin-plugins-data-server.plugin.start.md | 8 +-- ...ver.searchstrategydependencies.esclient.md | 11 ++++ ...-data-server.searchstrategydependencies.md | 20 +++++++ ...strategydependencies.savedobjectsclient.md | 11 ++++ ...chstrategydependencies.uisettingsclient.md | 11 ++++ src/plugins/data/public/public.api.md | 40 ++++++------- src/plugins/data/server/server.api.md | 59 ++++++++++++------- 22 files changed, 131 insertions(+), 78 deletions(-) delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearch.md rename docs/development/plugins/data/server/{kibana-plugin-plugins-data-server.isearchstart.search.md => kibana-plugin-plugins-data-server.isearchstart.asscoped.md} (54%) create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.esclient.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.savedobjectsclient.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.uisettingsclient.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md index 159dc8f4ada180..1d3e0c08dfc18d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md @@ -18,6 +18,6 @@ export interface IKibanaSearchResponse | [isPartial](./kibana-plugin-plugins-data-public.ikibanasearchresponse.ispartial.md) | boolean | Indicates whether the results returned are complete or partial | | [isRunning](./kibana-plugin-plugins-data-public.ikibanasearchresponse.isrunning.md) | boolean | Indicates whether search is still in flight | | [loaded](./kibana-plugin-plugins-data-public.ikibanasearchresponse.loaded.md) | number | If relevant to the search strategy, return a loaded number that represents how progress is indicated. | -| [rawResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md) | RawResponse | | +| [rawResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md) | RawResponse | The raw response returned by the internal search method (usually the raw ES response) | | [total](./kibana-plugin-plugins-data-public.ikibanasearchresponse.total.md) | number | If relevant to the search strategy, return a total number that represents how progress is indicated. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md index 865c7d795801bc..5857911259e128 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md @@ -4,6 +4,8 @@ ## IKibanaSearchResponse.rawResponse property +The raw response returned by the internal search method (usually the raw ES response) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearch.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearch.md deleted file mode 100644 index 79f667a70571a5..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearch.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearch](./kibana-plugin-plugins-data-public.isearch.md) - -## ISearch type - -Signature: - -```typescript -export declare type ISearch = (request: IKibanaSearchRequest, options?: ISearchOptions) => Observable; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 6a3c437305cc83..cb0008e426edf9 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -158,7 +158,6 @@ | [IndexPatternsContract](./kibana-plugin-plugins-data-public.indexpatternscontract.md) | | | [IndexPatternSelectProps](./kibana-plugin-plugins-data-public.indexpatternselectprops.md) | | | [InputTimeRange](./kibana-plugin-plugins-data-public.inputtimerange.md) | | -| [ISearch](./kibana-plugin-plugins-data-public.isearch.md) | | | [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | | [ISearchSource](./kibana-plugin-plugins-data-public.isearchsource.md) | search source interface | | [MatchAllFilter](./kibana-plugin-plugins-data-public.matchallfilter.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md index 672ff5065c456c..61f8eeb973f4c0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md @@ -23,5 +23,5 @@ search(request: IKibanaSearchRequest, options?: ISearchOptions): Observable` -`Observalbe` emitting the search response or an error. +`Observable` emitting the search response or an error. diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md index 87346f81b13e22..548fa66e6e518d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md @@ -42,7 +42,7 @@ export declare class SearchSource | [getSerializedFields()](./kibana-plugin-plugins-data-public.searchsource.getserializedfields.md) | | serializes search source fields (which can later be passed to [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md)) | | [onRequestStart(handler)](./kibana-plugin-plugins-data-public.searchsource.onrequeststart.md) | | Add a handler that will be notified whenever requests start | | [serialize()](./kibana-plugin-plugins-data-public.searchsource.serialize.md) | | Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object.The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named kibanaSavedObjectMeta.searchSourceJSON.index and kibanaSavedObjectMeta.searchSourceJSON.filter[<number>].meta.index.Using createSearchSource, the instance can be re-created. | -| [setField(field, value)](./kibana-plugin-plugins-data-public.searchsource.setfield.md) | | sets value to a single search source feild | +| [setField(field, value)](./kibana-plugin-plugins-data-public.searchsource.setfield.md) | | sets value to a single search source field | | [setFields(newFields)](./kibana-plugin-plugins-data-public.searchsource.setfields.md) | | Internal, do not use. Overrides all search source fields with the new field array. | | [setParent(parent, options)](./kibana-plugin-plugins-data-public.searchsource.setparent.md) | | Set a searchSource that this source should inherit from | | [setPreferredSearchStrategyId(searchStrategyId)](./kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md) | | internal, dont use | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md index 496e1ae9677d80..3bc2a205417772 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md @@ -15,13 +15,13 @@ Using `createSearchSource`, the instance can be re-created. ```typescript serialize(): { searchSourceJSON: string; - references: import("../../../../../core/types").SavedObjectReference[]; + references: import("src/core/server").SavedObjectReference[]; }; ``` Returns: `{ searchSourceJSON: string; - references: import("../../../../../core/types").SavedObjectReference[]; + references: import("src/core/server").SavedObjectReference[]; }` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md index 22619940f1589a..e96a35d8deee95 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md @@ -4,7 +4,7 @@ ## SearchSource.setField() method -sets value to a single search source feild +sets value to a single search source field Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.asscoped.md similarity index 54% rename from docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md rename to docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.asscoped.md index 98ea175aaaea7f..f97cc22a530016 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.asscoped.md @@ -1,11 +1,11 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) > [search](./kibana-plugin-plugins-data-server.isearchstart.search.md) +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) > [asScoped](./kibana-plugin-plugins-data-server.isearchstart.asscoped.md) -## ISearchStart.search property +## ISearchStart.asScoped property Signature: ```typescript -search: ISearchStrategy['search']; +asScoped: (request: KibanaRequest) => ISearchClient; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md index 398ea21641942c..9820e281c3f93f 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md @@ -4,10 +4,10 @@ ## ISearchStart.getSearchStrategy property -Get other registered search strategies. For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. +Get other registered search strategies by name (or, by default, the Elasticsearch strategy). For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. Signature: ```typescript -getSearchStrategy: (name: string) => ISearchStrategy; +getSearchStrategy: (name?: string) => ISearchStrategy; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md index b99c5f0f10a9e8..771b529f238242 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md @@ -15,7 +15,7 @@ export interface ISearchStartAggsStart | | -| [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) | (name: string) => ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse> | Get other registered search strategies. For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. | -| [search](./kibana-plugin-plugins-data-server.isearchstart.search.md) | ISearchStrategy['search'] | | +| [asScoped](./kibana-plugin-plugins-data-server.isearchstart.asscoped.md) | (request: KibanaRequest) => ISearchClient | | +| [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) | (name?: string) => ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse> | Get other registered search strategies by name (or, by default, the Elasticsearch strategy). For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. | | [searchSource](./kibana-plugin-plugins-data-server.isearchstart.searchsource.md) | {
asScoped: (request: KibanaRequest) => Promise<ISearchStartSearchSource>;
} | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md index 34903697090ead..5267290b3b0ee4 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md @@ -7,5 +7,5 @@ Signature: ```typescript -cancel?: (context: RequestHandlerContext, id: string) => Promise; +cancel?: (deps: SearchStrategyDependencies, id: string) => Promise; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md index 6dd95da2be3c1f..39cc51049175e9 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md @@ -16,6 +16,6 @@ export interface ISearchStrategy(context: RequestHandlerContext, id: string) => Promise<void> | | -| [search](./kibana-plugin-plugins-data-server.isearchstrategy.search.md) | (request: SearchStrategyRequest, options: ISearchOptions, context: RequestHandlerContext) => Observable<SearchStrategyResponse> | | +| [cancel](./kibana-plugin-plugins-data-server.isearchstrategy.cancel.md) | (deps: SearchStrategyDependencies, id: string) => Promise<void> | | +| [search](./kibana-plugin-plugins-data-server.isearchstrategy.search.md) | (deps: SearchStrategyDependencies, request: SearchStrategyRequest, options?: ISearchOptions) => Observable<SearchStrategyResponse> | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md index 84b90ae23f9160..d7911cbfdcbacd 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md @@ -7,5 +7,5 @@ Signature: ```typescript -search: (request: SearchStrategyRequest, options: ISearchOptions, context: RequestHandlerContext) => Observable; +search: (deps: SearchStrategyDependencies, request: SearchStrategyRequest, options?: ISearchOptions) => Observable; ``` 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 f1eecd6e49b028..fa3d35e8fe7e46 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 @@ -58,6 +58,7 @@ | [PluginSetup](./kibana-plugin-plugins-data-server.pluginsetup.md) | | | [PluginStart](./kibana-plugin-plugins-data-server.pluginstart.md) | | | [RefreshInterval](./kibana-plugin-plugins-data-server.refreshinterval.md) | | +| [SearchStrategyDependencies](./kibana-plugin-plugins-data-server.searchstrategydependencies.md) | | | [SearchUsage](./kibana-plugin-plugins-data-server.searchusage.md) | | | [TabbedAggColumn](./kibana-plugin-plugins-data-server.tabbedaggcolumn.md) | \* | | [TabbedTable](./kibana-plugin-plugins-data-server.tabbedtable.md) | \* | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md index 215eac9829451f..a1e42c44fa8615 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md @@ -9,10 +9,10 @@ ```typescript start(core: CoreStart): { fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; + fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; }; search: ISearchStart>; }; @@ -28,10 +28,10 @@ start(core: CoreStart): { `{ fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; + fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; }; search: ISearchStart>; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.esclient.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.esclient.md new file mode 100644 index 00000000000000..d205021e109549 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.esclient.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchStrategyDependencies](./kibana-plugin-plugins-data-server.searchstrategydependencies.md) > [esClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.esclient.md) + +## SearchStrategyDependencies.esClient property + +Signature: + +```typescript +esClient: IScopedClusterClient; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.md new file mode 100644 index 00000000000000..be95fb04a2c4fc --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchStrategyDependencies](./kibana-plugin-plugins-data-server.searchstrategydependencies.md) + +## SearchStrategyDependencies interface + +Signature: + +```typescript +export interface SearchStrategyDependencies +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [esClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.esclient.md) | IScopedClusterClient | | +| [savedObjectsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.savedobjectsclient.md) | SavedObjectsClientContract | | +| [uiSettingsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.uisettingsclient.md) | IUiSettingsClient | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.savedobjectsclient.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.savedobjectsclient.md new file mode 100644 index 00000000000000..f159a863312a41 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.savedobjectsclient.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchStrategyDependencies](./kibana-plugin-plugins-data-server.searchstrategydependencies.md) > [savedObjectsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.savedobjectsclient.md) + +## SearchStrategyDependencies.savedObjectsClient property + +Signature: + +```typescript +savedObjectsClient: SavedObjectsClientContract; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.uisettingsclient.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.uisettingsclient.md new file mode 100644 index 00000000000000..38a33e41c396f1 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.uisettingsclient.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchStrategyDependencies](./kibana-plugin-plugins-data-server.searchstrategydependencies.md) > [uiSettingsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.uisettingsclient.md) + +## SearchStrategyDependencies.uiSettingsClient property + +Signature: + +```typescript +uiSettingsClient: IUiSettingsClient; +``` diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 2ed3e440040de4..230b0f0bc0ed99 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -136,7 +136,7 @@ export class AggConfig { // (undocumented) makeLabel(percentageMode?: boolean): any; static nextId(list: IAggConfig[]): number; - onSearchRequestStart(searchSource: ISearchSource_2, options?: ISearchOptions): Promise | Promise; + onSearchRequestStart(searchSource: ISearchSource_2, options?: ISearchOptions_2): Promise | Promise; // (undocumented) params: any; // Warning: (ae-incompatible-release-tags) The symbol "parent" is marked as @public, but its signature references "IAggConfigs" which is marked as @internal @@ -1044,7 +1044,6 @@ export interface IKibanaSearchResponse { isPartial?: boolean; isRunning?: boolean; loaded?: number; - // (undocumented) rawResponse: RawResponse; total?: number; } @@ -1374,11 +1373,6 @@ export type InputTimeRange = TimeRange | { // @public (undocumented) export const isCompleteResponse: (response?: IKibanaSearchResponse | undefined) => boolean | undefined; -// Warning: (ae-missing-release-tag) "ISearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type ISearch = (request: IKibanaSearchRequest, options?: ISearchOptions) => Observable; - // Warning: (ae-missing-release-tag) "ISearchGeneric" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2066,7 +2060,7 @@ export class SearchSource { onRequestStart(handler: (searchSource: SearchSource, options?: ISearchOptions) => Promise): void; serialize(): { searchSourceJSON: string; - references: import("../../../../../core/types").SavedObjectReference[]; + references: import("src/core/server").SavedObjectReference[]; }; setField(field: K, value: SearchSourceFields[K]): this; setFields(newFields: SearchSourceFields): this; @@ -2300,21 +2294,21 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:388:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:388:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:388:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:388:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:415:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:387:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:387:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:387:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:387:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:45:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 0828460830f2c6..942d4df96083a9 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -21,8 +21,10 @@ import { ErrorToastOptions } from 'src/core/public/notifications'; import { ExpressionAstFunction } from 'src/plugins/expressions/common'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { ISavedObjectsRepository } from 'kibana/server'; +import { IScopedClusterClient } from 'src/core/server'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; +import { IUiSettingsClient } from 'src/core/server'; import { KibanaRequest } from 'src/core/server'; import { LegacyAPICaller } from 'kibana/server'; import { Logger } from 'kibana/server'; @@ -38,7 +40,6 @@ import { Plugin as Plugin_3 } from 'kibana/server'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/server'; import { RecursiveReadonly } from '@kbn/utility-types'; import { RequestAdapter } from 'src/plugins/inspector/common'; -import { RequestHandlerContext } from 'src/core/server'; import { RequestStatistics } from 'src/plugins/inspector/common'; import { SavedObject } from 'src/core/server'; import { SavedObjectsClientContract } from 'src/core/server'; @@ -366,7 +367,7 @@ export const getAsyncOptions: () => { // Warning: (ae-missing-release-tag) "getDefaultSearchParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient): Promise<{ +export function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient_2): Promise<{ maxConcurrentShardRequests: number | undefined; ignoreThrottled: boolean; ignoreUnavailable: boolean; @@ -712,9 +713,11 @@ export interface ISearchStart ISearchStrategy; + // Warning: (ae-forgotten-export) The symbol "ISearchClient" needs to be exported by the entry point index.d.ts + // // (undocumented) - search: ISearchStrategy['search']; + asScoped: (request: KibanaRequest) => ISearchClient; + getSearchStrategy: (name?: string) => ISearchStrategy; // (undocumented) searchSource: { asScoped: (request: KibanaRequest) => Promise; @@ -726,9 +729,9 @@ export interface ISearchStart { // (undocumented) - cancel?: (context: RequestHandlerContext, id: string) => Promise; + cancel?: (deps: SearchStrategyDependencies, id: string) => Promise; // (undocumented) - search: (request: SearchStrategyRequest, options: ISearchOptions, context: RequestHandlerContext) => Observable; + search: (deps: SearchStrategyDependencies, request: SearchStrategyRequest, options?: ISearchOptions) => Observable; } // @public (undocumented) @@ -877,10 +880,10 @@ export class Plugin implements Plugin_2 Promise; + fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; }; search: ISearchStart>; }; @@ -978,6 +981,18 @@ export const search: { tabifyGetColumns: typeof tabifyGetColumns; }; +// Warning: (ae-missing-release-tag) "SearchStrategyDependencies" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SearchStrategyDependencies { + // (undocumented) + esClient: IScopedClusterClient; + // (undocumented) + savedObjectsClient: SavedObjectsClientContract; + // (undocumented) + uiSettingsClient: IUiSettingsClient; +} + // 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) @@ -1126,22 +1141,22 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // 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:228:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:228:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:228:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:228:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:230:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:231:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:240:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:241:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:242:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:246:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:247:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:251:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:254:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:229:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:229:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:229:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:229:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:231:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:232:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:241:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:242:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:243:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:247:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:248:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:252:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:255:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index_patterns/index_patterns_service.ts:50:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:88:66 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/search/types.ts:91:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/search/types.ts:104:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) From c7296f0b667f8d25ae961d2d255f1189ca011cf3 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Mon, 19 Oct 2020 14:27:20 -0700 Subject: [PATCH 03/21] Unify interface for getting search client --- .../data/server/search/search_service.ts | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index c7ef611e90cb2b..2b4eee1db18e51 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -118,12 +118,9 @@ export class SearchService implements Plugin { registerSearchRoute(router); registerMsearchRoute(router, routeDependencies); - core.http.registerRouteHandlerContext('search', (context) => { - return this.getSearchClient({ - savedObjectsClient: context.core.savedObjects.client, - esClient: context.core.elasticsearch.client, - uiSettingsClient: context.core.uiSettings.client, - }); + core.http.registerRouteHandlerContext('search', async (context, request) => { + const [coreStart] = await core.getStartServices(); + return this.asScopedProvider(coreStart)(request); }); this.registerSearchStrategy( @@ -166,23 +163,15 @@ export class SearchService implements Plugin { } public start( - { elasticsearch, savedObjects, uiSettings }: CoreStart, + core: CoreStart, { fieldFormats, indexPatterns }: SearchServiceStartDependencies ): ISearchStart { - const getSearchClientAsScoped = (request: KibanaRequest) => { - const savedObjectsClient = savedObjects.getScopedClient(request); - const deps = { - savedObjectsClient, - esClient: elasticsearch.client.asScoped(request), - uiSettingsClient: uiSettings.asScopedToClient(savedObjectsClient), - }; - return this.getSearchClient(deps); - }; - + const { elasticsearch, savedObjects, uiSettings } = core; + const asScoped = this.asScopedProvider(core); return { aggs: this.aggsService.start({ fieldFormats, uiSettings }), getSearchStrategy: this.getSearchStrategy, - asScoped: getSearchClientAsScoped, + asScoped, searchSource: { asScoped: async (request: KibanaRequest) => { const esClient = elasticsearch.client.asScoped(request); @@ -200,7 +189,7 @@ export class SearchService implements Plugin { const searchSourceDependencies: SearchSourceDependencies = { getConfig: (key: string): T => uiSettingsCache[key], - search: getSearchClientAsScoped(request).search, + search: asScoped(request).search, // onResponse isn't used on the server, so we just return the original value onResponse: (req, res) => res, legacy: { @@ -255,13 +244,6 @@ export class SearchService implements Plugin { return strategy.cancel ? strategy.cancel(deps, id) : Promise.resolve(); }; - private getSearchClient = (deps: SearchStrategyDependencies): ISearchClient => { - return { - search: (searchRequest, options?) => this.search(deps, searchRequest, options), - cancel: (id: string, options?: ISearchOptions) => this.cancel(deps, id, options), - }; - }; - private getSearchStrategy = < SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse @@ -275,4 +257,19 @@ export class SearchService implements Plugin { } return strategy; }; + + private asScopedProvider = ({ elasticsearch, savedObjects, uiSettings }: CoreStart) => { + return (request: KibanaRequest): ISearchClient => { + const savedObjectsClient = savedObjects.getScopedClient(request); + const deps = { + savedObjectsClient, + esClient: elasticsearch.client.asScoped(request), + uiSettingsClient: uiSettings.asScopedToClient(savedObjectsClient), + }; + return { + search: (searchRequest, options) => this.search(deps, searchRequest, options), + cancel: (id, options) => this.cancel(deps, id, options), + }; + }; + }; } From 9dadfb22c0ffea82372933d5f5ef48c2335c3d05 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Tue, 20 Oct 2020 10:08:33 -0700 Subject: [PATCH 04/21] Update examples/search_examples/server/my_strategy.ts Co-authored-by: Anton Dosov --- examples/search_examples/server/my_strategy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts index 957216c7b9713c..b537e5b5798334 100644 --- a/examples/search_examples/server/my_strategy.ts +++ b/examples/search_examples/server/my_strategy.ts @@ -35,7 +35,7 @@ export const mySearchStrategyProvider = ( ), cancel: async (deps, id) => { if (es.cancel) { - es.cancel(deps, id); + await es.cancel(deps, id); } }, }; From 2b3d2a0415930ecbad9f996bddc08a1d94ffb60c Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Mon, 26 Oct 2020 12:38:52 -0700 Subject: [PATCH 05/21] Review feedback --- .../search_examples/server/my_strategy.ts | 8 ++++---- .../search/search_source/search_source.ts | 5 +++-- .../es_search/es_search_strategy.test.ts | 8 ++++---- .../search/es_search/es_search_strategy.ts | 4 ++-- .../data/server/search/search_service.ts | 18 ++++++++--------- src/plugins/data/server/search/types.ts | 6 +++--- .../public/search/search_interceptor.ts | 2 +- .../server/search/eql_search_strategy.test.ts | 20 +++++++++---------- .../server/search/eql_search_strategy.ts | 6 +++--- .../server/search/es_search_strategy.test.ts | 10 +++++----- .../server/search/es_search_strategy.ts | 20 +++++++++---------- .../search_strategy/index_fields/index.ts | 2 +- .../security_solution/index.ts | 8 ++++---- .../server/search_strategy/timeline/index.ts | 8 ++++---- 14 files changed, 63 insertions(+), 62 deletions(-) diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts index 957216c7b9713c..cb06f931bd1195 100644 --- a/examples/search_examples/server/my_strategy.ts +++ b/examples/search_examples/server/my_strategy.ts @@ -26,16 +26,16 @@ export const mySearchStrategyProvider = ( ): ISearchStrategy => { const es = data.search.getSearchStrategy(); return { - search: (deps, request, options) => - es.search(deps, request, options).pipe( + search: (request, options, deps) => + es.search(request, options, deps).pipe( map((esSearchRes) => ({ ...esSearchRes, cool: request.get_cool ? 'YES' : 'NOPE', })) ), - cancel: async (deps, id) => { + cancel: async (id, options, deps) => { if (es.cancel) { - es.cancel(deps, id); + return es.cancel(id, options, deps); } }, }; diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 418e316860eb9b..f52b8434f0c8f8 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -71,6 +71,7 @@ import { setWith } from '@elastic/safer-lodash-set'; import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash'; +import { map } from 'rxjs/operators'; import { normalizeSortRequest } from './normalize_sort_request'; import { filterDocvalueFields } from './filter_docvalue_fields'; import { fieldWildcardFilter } from '../../../../kibana_utils/common'; @@ -311,8 +312,8 @@ export class SearchSource { }); return search({ params, indexType: searchRequest.indexType }, options) - .toPromise() - .then(({ rawResponse }) => onResponse(searchRequest, rawResponse)); + .pipe(map(({ rawResponse }) => onResponse(searchRequest, rawResponse))) + .toPromise(); } /** diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts index ddac28d9d54f5b..4556bee94603fa 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts @@ -59,7 +59,7 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*' }; await esSearchStrategyProvider(mockConfig$, mockLogger) - .search(mockDeps, { params }, {}) + .search({ params }, {}, mockDeps) .subscribe(() => { expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toEqual({ @@ -75,7 +75,7 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*', ignore_unavailable: false, timeout: '1000ms' }; await esSearchStrategyProvider(mockConfig$, mockLogger) - .search(mockDeps, { params }, {}) + .search({ params }, {}, mockDeps) .subscribe(() => { expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toEqual({ @@ -89,11 +89,11 @@ describe('ES search strategy', () => { it('has all response parameters', async (done) => await esSearchStrategyProvider(mockConfig$, mockLogger) .search( - mockDeps, { params: { index: 'logstash-*' }, }, - {} + {}, + mockDeps ) .subscribe((data) => { expect(data.isRunning).toBe(false); 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 cb66cafb661442..d20f90ae1cb0bc 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 @@ -38,7 +38,7 @@ export const esSearchStrategyProvider = ( usage?: SearchUsage ): ISearchStrategy => { return { - search: ({ esClient, uiSettingsClient }, request, options) => + search: (request, options, { esClient, uiSettingsClient }) => from( new Promise(async (resolve, reject) => { logger.debug(`search ${request.params?.index}`); @@ -64,7 +64,7 @@ export const esSearchStrategyProvider = ( try { const promise = shimAbortSignal( esClient.asCurrentUser.search(params), - options?.abortSignal + options.abortSignal ); const { body: rawResponse } = (await promise) as ApiResponse>; diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 2b4eee1db18e51..b6d9d7f3e4e6a5 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -227,21 +227,21 @@ export class SearchService implements Plugin { SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse >( - deps: SearchStrategyDependencies, searchRequest: SearchStrategyRequest, - options?: ISearchOptions + options: ISearchOptions, + deps: SearchStrategyDependencies ) => { const strategy = this.getSearchStrategy( - options?.strategy + options.strategy ); - return strategy.search(deps, searchRequest, options); + return strategy.search(searchRequest, options, deps); }; - private cancel = (deps: SearchStrategyDependencies, id: string, options?: ISearchOptions) => { - const strategy = this.getSearchStrategy(options?.strategy); + private cancel = (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => { + const strategy = this.getSearchStrategy(options.strategy); - return strategy.cancel ? strategy.cancel(deps, id) : Promise.resolve(); + return strategy.cancel ? strategy.cancel(id, options, deps) : Promise.resolve(); }; private getSearchStrategy = < @@ -267,8 +267,8 @@ export class SearchService implements Plugin { uiSettingsClient: uiSettings.asScopedToClient(savedObjectsClient), }; return { - search: (searchRequest, options) => this.search(deps, searchRequest, options), - cancel: (id, options) => this.cancel(deps, id, options), + search: (searchRequest, options = {}) => this.search(searchRequest, options, deps), + cancel: (id, options = {}) => this.cancel(id, options, deps), }; }; }; diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index 622d6856b20f6f..ebce02014c2a4e 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -79,11 +79,11 @@ export interface ISearchStrategy< SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse > { search: ( - deps: SearchStrategyDependencies, request: SearchStrategyRequest, - options?: ISearchOptions + options: ISearchOptions, + deps: SearchStrategyDependencies ) => Observable; - cancel?: (deps: SearchStrategyDependencies, id: string) => Promise; + cancel?: (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => Promise; } export interface ISearchStart< 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 aee32a7c62759d..91304e3191ae26 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -65,7 +65,7 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { timeout: this.searchTimeout, }); const aborted$ = from(toPromise(combinedSignal)); - const strategy = options?.strategy || ENHANCED_ES_SEARCH_STRATEGY; + const strategy = options?.strategy ?? ENHANCED_ES_SEARCH_STRATEGY; this.pendingCount$.next(this.pendingCount$.getValue() + 1); diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts index 91bc4ac524b9ed..efa481ec15c405 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts @@ -77,7 +77,7 @@ describe('EQL search strategy', () => { describe('async functionality', () => { it('performs an eql client search with params when no ID is provided', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockDeps, { options, params }, {}).toPromise(); + await eqlSearch.search({ options, params }, {}, mockDeps).toPromise(); const [[request, requestOptions]] = mockEqlSearch.mock.calls; expect(request.index).toEqual('logstash-*'); @@ -87,7 +87,7 @@ describe('EQL search strategy', () => { it('retrieves the current request if an id is provided', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockDeps, { id: 'my-search-id' }, {}).toPromise(); + await eqlSearch.search({ id: 'my-search-id' }, {}, mockDeps).toPromise(); const [[requestParams]] = mockEqlGet.mock.calls; expect(mockEqlSearch).not.toHaveBeenCalled(); @@ -98,7 +98,7 @@ describe('EQL search strategy', () => { expect.assertions(1); mockEqlSearch.mockReset().mockRejectedValueOnce(new Error('client error')); const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - eqlSearch.search({ options, params }, {}, mockContext).subscribe( + eqlSearch.search({ options, params }, {}, mockDeps).subscribe( () => {}, (err) => { expect(err).toEqual(new Error('client error')); @@ -110,7 +110,7 @@ describe('EQL search strategy', () => { describe('arguments', () => { it('sends along async search options', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockDeps, { options, params }, {}).toPromise(); + await eqlSearch.search({ options, params }, {}, mockDeps).toPromise(); const [[request]] = mockEqlSearch.mock.calls; expect(request).toEqual( @@ -123,7 +123,7 @@ describe('EQL search strategy', () => { it('sends along default search parameters', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockDeps, { options, params }, {}).toPromise(); + await eqlSearch.search({ options, params }, {}, mockDeps).toPromise(); const [[request]] = mockEqlSearch.mock.calls; expect(request).toEqual( @@ -138,7 +138,6 @@ describe('EQL search strategy', () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); await eqlSearch .search( - mockDeps, { options, params: { @@ -147,7 +146,8 @@ describe('EQL search strategy', () => { keep_on_completion: false, }, }, - {} + {}, + mockDeps ) .toPromise(); const [[request]] = mockEqlSearch.mock.calls; @@ -165,12 +165,12 @@ describe('EQL search strategy', () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); await eqlSearch .search( - mockDeps, { options: { ...options, maxRetries: 2, ignore: [300] }, params, }, - {} + {}, + mockDeps ) .toPromise(); const [[, requestOptions]] = mockEqlSearch.mock.calls; @@ -186,7 +186,7 @@ describe('EQL search strategy', () => { it('passes transport options for an existing request', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); await eqlSearch - .search(mockDeps, { id: 'my-search-id', options: { ignore: [400] } }, {}) + .search({ id: 'my-search-id', options: { ignore: [400] } }, {}, mockDeps) .toPromise(); const [[, requestOptions]] = mockEqlGet.mock.calls; diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts index d2c3c22dca383a..ad154014866ea3 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts @@ -21,13 +21,13 @@ export const eqlSearchStrategyProvider = ( logger: Logger ): ISearchStrategy => { return { - cancel: async ({ esClient }, id) => { + cancel: async (id, options, { esClient }) => { logger.debug(`_eql/delete ${id}`); await esClient.asCurrentUser.eql.delete({ id, }); }, - search: ({ esClient, uiSettingsClient }, request, options) => + search: (request, options, { esClient, uiSettingsClient }) => from( new Promise(async (resolve, reject) => { logger.debug(`_eql/search ${JSON.stringify(request.params) || request.id}`); @@ -62,7 +62,7 @@ export const eqlSearchStrategyProvider = ( ); } - const rawResponse = await shimAbortSignal(promise, options?.abortSignal); + const rawResponse = await shimAbortSignal(promise, options.abortSignal); const { id, is_partial: isPartial, is_running: isRunning } = rawResponse.body; resolve({ diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts index 4f6b490972f06c..b9b6e25067f2f2 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts @@ -80,7 +80,7 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*', body: { query: {} } }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch.search(mockDeps, { params }, {}).toPromise(); + await esSearch.search({ params }, {}, mockDeps).toPromise(); expect(mockSubmitCaller).toBeCalled(); const request = mockSubmitCaller.mock.calls[0][0]; @@ -94,7 +94,7 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*', body: { query: {} } }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch.search(mockDeps, { id: 'foo', params }, {}).toPromise(); + await esSearch.search({ id: 'foo', params }, {}, mockDeps).toPromise(); expect(mockGetCaller).toBeCalled(); const request = mockGetCaller.mock.calls[0][0]; @@ -111,12 +111,12 @@ describe('ES search strategy', () => { await esSearch .search( - mockDeps, { indexType: 'rollup', params, }, - {} + {}, + mockDeps ) .toPromise(); @@ -132,7 +132,7 @@ describe('ES search strategy', () => { const params = { index: 'foo-*', body: {} }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch.search(mockDeps, { params }, {}).toPromise(); + await esSearch.search({ params }, {}, mockDeps).toPromise(); expect(mockSubmitCaller).toBeCalled(); const request = mockSubmitCaller.mock.calls[0][0]; 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 58de427a14b072..144a0631489534 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 @@ -39,7 +39,7 @@ export const enhancedEsSearchStrategyProvider = ( usage?: SearchUsage ): ISearchStrategy => { return { - search: (deps, request, options) => + search: (request, options, deps) => from( new Promise(async (resolve, reject) => { logger.debug(`search ${JSON.stringify(request.params) || request.id}`); @@ -48,8 +48,8 @@ export const enhancedEsSearchStrategyProvider = ( try { const response = isAsync - ? await asyncSearch(deps, request, options) - : await rollupSearch(deps, request, options); + ? await asyncSearch(request, options, deps) + : await rollupSearch(request, options, deps); if ( usage && @@ -67,16 +67,16 @@ export const enhancedEsSearchStrategyProvider = ( } }) ), - cancel: async ({ esClient }, id) => { + cancel: async (id, options, { esClient }) => { logger.debug(`cancel ${id}`); await esClient.asCurrentUser.asyncSearch.delete({ id }); }, }; async function asyncSearch( - { esClient, uiSettingsClient }: SearchStrategyDependencies, request: IEnhancedEsSearchRequest, - options?: ISearchOptions + options: ISearchOptions, + { esClient, uiSettingsClient }: SearchStrategyDependencies ): Promise { let promise: TransportRequestPromise; const asyncOptions = getAsyncOptions(); @@ -98,7 +98,7 @@ export const enhancedEsSearchStrategyProvider = ( }); } - const esResponse = await shimAbortSignal(promise, options?.abortSignal); + const esResponse = await shimAbortSignal(promise, options.abortSignal); const { id, response, is_partial: isPartial, is_running: isRunning } = esResponse.body; return { id, @@ -110,9 +110,9 @@ export const enhancedEsSearchStrategyProvider = ( } async function rollupSearch( - { esClient, uiSettingsClient }: SearchStrategyDependencies, request: IEnhancedEsSearchRequest, - options?: ISearchOptions + options: ISearchOptions, + { esClient, uiSettingsClient }: SearchStrategyDependencies ): Promise { const config = await config$.pipe(first()).toPromise(); const { body, index, ...params } = request.params!; @@ -131,7 +131,7 @@ export const enhancedEsSearchStrategyProvider = ( querystring, }); - const esResponse = await shimAbortSignal(promise, options?.abortSignal); + const esResponse = await shimAbortSignal(promise, options.abortSignal); const response = esResponse.body as SearchResponse; return { diff --git a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts index a0955f5d44f2a9..81ee4dc7c9ad2d 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts @@ -26,7 +26,7 @@ export const securitySolutionIndexFieldsProvider = (): ISearchStrategy< const beatFields: BeatFields = require('../../utils/beat_schema/fields').fieldsBeat; return { - search: ({ esClient }, request, options) => + search: (request, options, { esClient }) => from( new Promise(async (resolve) => { const indexPatternsFetcher = new IndexPatternsFetcher(esClient.asCurrentUser); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts index 2d6937f40b5d19..91282386312089 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts @@ -20,7 +20,7 @@ export const securitySolutionSearchStrategyProvider = { + search: (request, options, deps) => { if (request.factoryQueryType == null) { throw new Error('factoryQueryType is required'); } @@ -28,12 +28,12 @@ export const securitySolutionSearchStrategyProvider = queryFactory.parse(request, esSearchRes))); }, - cancel: async (deps, id) => { + cancel: async (id, options, deps) => { if (es.cancel) { - es.cancel(deps, id); + return es.cancel(id, options, deps); } }, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts index 39f65054d4f710..6967db17813a81 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts @@ -20,7 +20,7 @@ export const securitySolutionTimelineSearchStrategyProvider = { + search: (request, options, deps) => { if (request.factoryQueryType == null) { throw new Error('factoryQueryType is required'); } @@ -29,12 +29,12 @@ export const securitySolutionTimelineSearchStrategyProvider = queryFactory.parse(request, esSearchRes))); }, - cancel: async (deps, id) => { + cancel: async (id, options, deps) => { if (es.cancel) { - es.cancel(deps, id); + return es.cancel(id, options, deps); } }, }; From 6c9aec5ef110aff5115fa32ec9e9e79d34b6bf00 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Wed, 28 Oct 2020 12:27:42 -0700 Subject: [PATCH 06/21] Fix checks --- examples/search_examples/server/my_strategy.ts | 4 ++-- .../server/series_functions/es/index.js | 14 ++++---------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts index f210fa9a32329d..d5b19f0619bbc7 100644 --- a/examples/search_examples/server/my_strategy.ts +++ b/examples/search_examples/server/my_strategy.ts @@ -33,9 +33,9 @@ export const mySearchStrategyProvider = ( cool: request.get_cool ? 'YES' : 'NOPE', })) ), - cancel: (id, options, deps) => { + cancel: async (id, options, deps) => { if (es.cancel) { - return es.cancel(id, options, deps); + await es.cancel(id, options, deps); } }, }; diff --git a/src/plugins/vis_type_timelion/server/series_functions/es/index.js b/src/plugins/vis_type_timelion/server/series_functions/es/index.js index fc3250f0d47262..c04f044b79717f 100644 --- a/src/plugins/vis_type_timelion/server/series_functions/es/index.js +++ b/src/plugins/vis_type_timelion/server/series_functions/es/index.js @@ -130,16 +130,10 @@ export default new Datasource('es', { const body = buildRequest(config, tlConfig, scriptedFields, esShardTimeout); - const deps = (await tlConfig.getStartServices())[1]; - - const resp = await deps.data.search - .search( - body, - { - strategy: ES_SEARCH_STRATEGY, - }, - tlConfig.context - ) + const resp = await tlConfig.context.search + .search(body, { + strategy: ES_SEARCH_STRATEGY, + }) .toPromise(); if (!resp.rawResponse._shards.total) { From 1ae55efa63b53e7cf18239ab509bda3e1c79423d Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Thu, 29 Oct 2020 09:32:05 -0700 Subject: [PATCH 07/21] Fix CI --- ...n-plugins-data-server.isearchstrategy.cancel.md | 2 +- ...a-plugin-plugins-data-server.isearchstrategy.md | 4 ++-- ...n-plugins-data-server.isearchstrategy.search.md | 2 +- src/plugins/data/server/server.api.md | 4 ++-- .../server/series_functions/es/es.test.js | 9 +-------- .../server/series_functions/fixtures/tl_config.js | 14 +++++--------- 6 files changed, 12 insertions(+), 23 deletions(-) diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md index 5267290b3b0ee4..709d9bb7be9e5d 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md @@ -7,5 +7,5 @@ Signature: ```typescript -cancel?: (deps: SearchStrategyDependencies, id: string) => Promise; +cancel?: (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => Promise; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md index 39cc51049175e9..c9f4c886735a75 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md @@ -16,6 +16,6 @@ export interface ISearchStrategy(deps: SearchStrategyDependencies, id: string) => Promise<void> | | -| [search](./kibana-plugin-plugins-data-server.isearchstrategy.search.md) | (deps: SearchStrategyDependencies, request: SearchStrategyRequest, options?: ISearchOptions) => Observable<SearchStrategyResponse> | | +| [cancel](./kibana-plugin-plugins-data-server.isearchstrategy.cancel.md) | (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => Promise<void> | | +| [search](./kibana-plugin-plugins-data-server.isearchstrategy.search.md) | (request: SearchStrategyRequest, options: ISearchOptions, deps: SearchStrategyDependencies) => Observable<SearchStrategyResponse> | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md index d7911cbfdcbacd..266995f2ec82c5 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md @@ -7,5 +7,5 @@ Signature: ```typescript -search: (deps: SearchStrategyDependencies, request: SearchStrategyRequest, options?: ISearchOptions) => Observable; +search: (request: SearchStrategyRequest, options: ISearchOptions, deps: SearchStrategyDependencies) => Observable; ``` diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 952bdf28235c5b..b10f2d28910676 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -741,9 +741,9 @@ export interface ISearchStart { // (undocumented) - cancel?: (deps: SearchStrategyDependencies, id: string) => Promise; + cancel?: (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => Promise; // (undocumented) - search: (deps: SearchStrategyDependencies, request: SearchStrategyRequest, options?: ISearchOptions) => Observable; + search: (request: SearchStrategyRequest, options: ISearchOptions, deps: SearchStrategyDependencies) => Observable; } // @public (undocumented) diff --git a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js index 8be3cf5171c654..95cc9388574a03 100644 --- a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js +++ b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js @@ -33,14 +33,7 @@ import { UI_SETTINGS } from '../../../../data/server'; function stubRequestAndServer(response, indexPatternSavedObjects = []) { return { - getStartServices: sinon - .stub() - .returns( - Promise.resolve([ - {}, - { data: { search: { search: () => from(Promise.resolve(response)) } } }, - ]) - ), + context: { search: { search: () => from(Promise.resolve(response)) } }, savedObjectsClient: { find: function () { return Promise.resolve({ diff --git a/src/plugins/vis_type_timelion/server/series_functions/fixtures/tl_config.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/tl_config.js index 38d70278fbf003..2f51cf38c0180b 100644 --- a/src/plugins/vis_type_timelion/server/series_functions/fixtures/tl_config.js +++ b/src/plugins/vis_type_timelion/server/series_functions/fixtures/tl_config.js @@ -18,7 +18,7 @@ */ import moment from 'moment'; -import sinon from 'sinon'; +import { of } from 'rxjs'; import timelionDefaults from '../../lib/get_namespaced_settings'; import esResponse from './es_response'; @@ -30,14 +30,6 @@ export default function () { if (!functions[name]) throw new Error('No such function: ' + name); return functions[name]; }, - getStartServices: sinon - .stub() - .returns( - Promise.resolve([ - {}, - { data: { search: { search: () => Promise.resolve({ rawResponse: esResponse }) } } }, - ]) - ), esShardTimeout: moment.duration(30000), allowedGraphiteUrls: ['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'], @@ -54,5 +46,9 @@ export default function () { tlConfig.setTargetSeries(); + tlConfig.context = { + search: { search: () => of({ rawResponse: esResponse }) }, + }; + return tlConfig; } From 4634fb47d486c24487477769d6d3198081505997 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Thu, 29 Oct 2020 15:36:23 -0700 Subject: [PATCH 08/21] Fix security search --- .../server/search_strategy/security_solution/index.ts | 2 +- .../security_solution/server/search_strategy/timeline/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts index 91282386312089..4abec07b3b493f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts @@ -17,7 +17,7 @@ import { SecuritySolutionFactory } from './factory/types'; export const securitySolutionSearchStrategyProvider = ( data: PluginStart ): ISearchStrategy, StrategyResponseType> => { - const es = data.search.getSearchStrategy(); + const es = data.search.getSearchStrategy('es'); return { search: (request, options, deps) => { diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts index 6967db17813a81..0b73eed61765ff 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts @@ -17,7 +17,7 @@ import { SecuritySolutionTimelineFactory } from './factory/types'; export const securitySolutionTimelineSearchStrategyProvider = ( data: PluginStart ): ISearchStrategy, TimelineStrategyResponseType> => { - const es = data.search.getSearchStrategy(); + const es = data.search.getSearchStrategy('es'); return { search: (request, options, deps) => { From 5f7c9abdbdea13345d60b9b327702885b64aa9e0 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Mon, 2 Nov 2020 10:08:48 -0700 Subject: [PATCH 09/21] Fix test --- .../vis_type_timelion/server/series_functions/es/es.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js index e1ce94d0694fe4..2fa18b259de520 100644 --- a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js +++ b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js @@ -32,7 +32,6 @@ import { UI_SETTINGS } from '../../../../data/server'; describe('es', () => { let tlConfig; - let dataSearchStub; function stubRequestAndServer(response, indexPatternSavedObjects = []) { return { @@ -73,7 +72,7 @@ describe('es', () => { await invoke(es, [5], tlConfig); - expect(dataSearchStub.data.search.search.mock.calls[0][1]).toHaveProperty('sessionId', 1); + expect(tlConfig.context.search.search.mock.calls[0][1]).toHaveProperty('sessionId', 1); }); test('returns a seriesList', () => { From f1b515d57fc62ee478eccee3662e6baae7f4c0c2 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Mon, 2 Nov 2020 10:32:03 -0700 Subject: [PATCH 10/21] Fix test for reals --- .../vis_type_timelion/server/series_functions/es/es.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js index 2fa18b259de520..f4ba36e4fdd67f 100644 --- a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js +++ b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import { from } from 'rxjs'; +import { of } from 'rxjs'; import es from './index'; import tlConfigFn from '../fixtures/tl_config'; import * as aggResponse from './lib/agg_response_to_series_list'; @@ -35,7 +35,7 @@ describe('es', () => { function stubRequestAndServer(response, indexPatternSavedObjects = []) { return { - context: { search: { search: () => from(Promise.resolve(response)) } }, + context: { search: { search: jest.fn().mockReturnValue(of(response)) } }, savedObjectsClient: { find: function () { return Promise.resolve({ From 11517ee82a35b21c292f5f031c074ee39ac3ea08 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Tue, 3 Nov 2020 08:50:29 -0700 Subject: [PATCH 11/21] Fix types --- .../data_enhanced/server/search/eql_search_strategy.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts index d33b629a907566..88aaee8eb7da2d 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import type { RequestHandlerContext, Logger } from 'kibana/server'; +import type { Logger } from 'kibana/server'; import { EqlSearchStrategyRequest } from '../../common/search/types'; import { eqlSearchStrategyProvider } from './eql_search_strategy'; import { SearchStrategyDependencies } from '../../../../../src/plugins/data/server'; From b1e06b01d5077e1edf173951d914ad9ddfa3419b Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Tue, 3 Nov 2020 16:44:09 -0700 Subject: [PATCH 12/21] [data.search] Refactor search polling and improve types --- .../search/es_search/es_search_rxjs_utils.ts | 55 --------- .../search/es_search/get_total_loaded.test.ts | 36 ------ .../data/common/search/es_search/index.ts | 5 - .../es_search/shim_abort_signal.test.ts | 64 ---------- .../search/es_search/shim_abort_signal.ts | 48 -------- .../data/common/search/es_search/types.ts | 6 - src/plugins/data/common/search/index.ts | 1 + .../to_snake_case.ts => utils.test.ts} | 12 +- .../common/search/{es_search => }/utils.ts | 2 +- .../public/search/errors/painless_error.tsx | 3 +- .../data/public/search/search_interceptor.ts | 16 +-- src/plugins/data/server/index.ts | 16 +-- .../data/server/search/collectors/index.ts | 3 +- .../data/server/search/collectors/usage.ts | 21 +++- .../search/es_search/es_search_rxjs_utils.ts | 53 --------- .../search/es_search/es_search_strategy.ts | 38 +++--- .../es_search/get_default_search_params.ts | 41 ------- .../data/server/search/es_search/index.ts | 5 +- .../search/es_search/request_utils.test.ts | 70 +++++++++++ .../server/search/es_search/request_utils.ts | 63 ++++++++++ .../search/es_search/response_utils.test.ts | 68 +++++++++++ .../search/es_search/response_utils.ts} | 18 ++- src/plugins/data/server/search/index.ts | 2 +- x-pack/plugins/data_enhanced/common/index.ts | 5 +- .../search/es_search/es_search_rxjs_utils.ts | 41 ------- .../data_enhanced/common/search/index.ts | 2 +- .../common/search/poll_search.ts | 38 ++++++ .../data_enhanced/common/search/types.ts | 14 --- .../public/search/search_interceptor.ts | 43 ++----- .../server/search/eql_search_strategy.ts | 81 ++++++------- .../server/search/es_search_strategy.ts | 111 +++++++----------- .../search/get_default_search_params.ts | 33 ------ .../server/search/request_utils.ts | 59 ++++++++++ .../server/search/response_utils.ts | 21 ++++ .../index.ts => server/search/types.ts} | 9 +- 35 files changed, 497 insertions(+), 606 deletions(-) delete mode 100644 src/plugins/data/common/search/es_search/es_search_rxjs_utils.ts delete mode 100644 src/plugins/data/common/search/es_search/get_total_loaded.test.ts delete mode 100644 src/plugins/data/common/search/es_search/shim_abort_signal.test.ts delete mode 100644 src/plugins/data/common/search/es_search/shim_abort_signal.ts rename src/plugins/data/common/search/{es_search/to_snake_case.ts => utils.test.ts} (76%) rename src/plugins/data/common/search/{es_search => }/utils.ts (96%) delete mode 100644 src/plugins/data/server/search/es_search/es_search_rxjs_utils.ts delete mode 100644 src/plugins/data/server/search/es_search/get_default_search_params.ts create mode 100644 src/plugins/data/server/search/es_search/request_utils.test.ts create mode 100644 src/plugins/data/server/search/es_search/request_utils.ts create mode 100644 src/plugins/data/server/search/es_search/response_utils.test.ts rename src/plugins/data/{common/search/es_search/get_total_loaded.ts => server/search/es_search/response_utils.ts} (69%) delete mode 100644 x-pack/plugins/data_enhanced/common/search/es_search/es_search_rxjs_utils.ts create mode 100644 x-pack/plugins/data_enhanced/common/search/poll_search.ts delete mode 100644 x-pack/plugins/data_enhanced/server/search/get_default_search_params.ts create mode 100644 x-pack/plugins/data_enhanced/server/search/request_utils.ts create mode 100644 x-pack/plugins/data_enhanced/server/search/response_utils.ts rename x-pack/plugins/data_enhanced/{common/search/es_search/index.ts => server/search/types.ts} (55%) diff --git a/src/plugins/data/common/search/es_search/es_search_rxjs_utils.ts b/src/plugins/data/common/search/es_search/es_search_rxjs_utils.ts deleted file mode 100644 index e3238ea62db57c..00000000000000 --- a/src/plugins/data/common/search/es_search/es_search_rxjs_utils.ts +++ /dev/null @@ -1,55 +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 { from } from 'rxjs'; -import { map } from 'rxjs/operators'; - -import type { SearchResponse } from 'elasticsearch'; -import type { ApiResponse } from '@elastic/elasticsearch'; - -import { shimAbortSignal } from './shim_abort_signal'; -import { getTotalLoaded } from './get_total_loaded'; - -import type { IEsRawSearchResponse } from './types'; -import type { IKibanaSearchResponse } from '../types'; - -export const doSearch = ( - searchMethod: () => Promise, - abortSignal?: AbortSignal -) => from(shimAbortSignal(searchMethod(), abortSignal)); - -export const toKibanaSearchResponse = < - SearchResponse extends IEsRawSearchResponse = IEsRawSearchResponse, - KibanaResponse extends IKibanaSearchResponse = IKibanaSearchResponse ->() => - map, KibanaResponse>( - (response) => - ({ - id: response.body.id, - isPartial: response.body.is_partial || false, - isRunning: response.body.is_running || false, - rawResponse: response.body, - } as KibanaResponse) - ); - -export const includeTotalLoaded = () => - map((response: IKibanaSearchResponse>) => ({ - ...response, - ...getTotalLoaded(response.rawResponse._shards), - })); diff --git a/src/plugins/data/common/search/es_search/get_total_loaded.test.ts b/src/plugins/data/common/search/es_search/get_total_loaded.test.ts deleted file mode 100644 index 74e2873ede7628..00000000000000 --- a/src/plugins/data/common/search/es_search/get_total_loaded.test.ts +++ /dev/null @@ -1,36 +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 { getTotalLoaded } from './get_total_loaded'; - -describe('getTotalLoaded', () => { - it('returns the total/loaded, not including skipped', () => { - const result = getTotalLoaded({ - successful: 10, - failed: 5, - skipped: 5, - total: 100, - }); - - expect(result).toEqual({ - total: 100, - loaded: 15, - }); - }); -}); diff --git a/src/plugins/data/common/search/es_search/index.ts b/src/plugins/data/common/search/es_search/index.ts index 555667a9f53009..d8f7b5091eb8f6 100644 --- a/src/plugins/data/common/search/es_search/index.ts +++ b/src/plugins/data/common/search/es_search/index.ts @@ -18,8 +18,3 @@ */ export * from './types'; -export * from './utils'; -export * from './es_search_rxjs_utils'; -export * from './shim_abort_signal'; -export * from './to_snake_case'; -export * from './get_total_loaded'; diff --git a/src/plugins/data/common/search/es_search/shim_abort_signal.test.ts b/src/plugins/data/common/search/es_search/shim_abort_signal.test.ts deleted file mode 100644 index 61af8b4c782ae0..00000000000000 --- a/src/plugins/data/common/search/es_search/shim_abort_signal.test.ts +++ /dev/null @@ -1,64 +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 { shimAbortSignal } from './shim_abort_signal'; - -const createSuccessTransportRequestPromise = ( - body: any, - { statusCode = 200 }: { statusCode?: number } = {} -) => { - const promise = Promise.resolve({ body, statusCode }) as any; - promise.abort = jest.fn(); - - return promise; -}; - -describe('shimAbortSignal', () => { - test('aborts the promise if the signal is aborted', () => { - const promise = createSuccessTransportRequestPromise({ - success: true, - }); - const controller = new AbortController(); - shimAbortSignal(promise, controller.signal); - controller.abort(); - - expect(promise.abort).toHaveBeenCalled(); - }); - - test('returns the original promise', async () => { - const promise = createSuccessTransportRequestPromise({ - success: true, - }); - const controller = new AbortController(); - const response = await shimAbortSignal(promise, controller.signal); - - expect(response).toEqual(expect.objectContaining({ body: { success: true } })); - }); - - test('allows the promise to be aborted manually', () => { - const promise = createSuccessTransportRequestPromise({ - success: true, - }); - const controller = new AbortController(); - const enhancedPromise = shimAbortSignal(promise, controller.signal); - - enhancedPromise.abort(); - expect(promise.abort).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/data/common/search/es_search/shim_abort_signal.ts b/src/plugins/data/common/search/es_search/shim_abort_signal.ts deleted file mode 100644 index 554a24e2688155..00000000000000 --- a/src/plugins/data/common/search/es_search/shim_abort_signal.ts +++ /dev/null @@ -1,48 +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. - */ - -/** - * @internal - * TransportRequestPromise extends base Promise with an "abort" method - */ -export interface TransportRequestPromise extends Promise { - abort?: () => void; -} - -/** - * - * @internal - * NOTE: Temporary workaround until https://github.com/elastic/elasticsearch-js/issues/1297 - * is resolved - * - * @param promise a TransportRequestPromise - * @param signal optional AbortSignal - * - * @returns a TransportRequestPromise that will be aborted if the signal is aborted - */ - -export const shimAbortSignal = >( - promise: T, - signal: AbortSignal | undefined -): T => { - if (signal) { - signal.addEventListener('abort', () => promise.abort && promise.abort()); - } - return promise; -}; diff --git a/src/plugins/data/common/search/es_search/types.ts b/src/plugins/data/common/search/es_search/types.ts index 7d81cf42e18665..7dbbd01d2cdadf 100644 --- a/src/plugins/data/common/search/es_search/types.ts +++ b/src/plugins/data/common/search/es_search/types.ts @@ -30,10 +30,4 @@ export interface IEsSearchRequest extends IKibanaSearchRequest extends SearchResponse { - id?: string; - is_partial?: boolean; - is_running?: boolean; -} - export type IEsSearchResponse = IKibanaSearchResponse>; diff --git a/src/plugins/data/common/search/index.ts b/src/plugins/data/common/search/index.ts index e650cf10db87c0..01944d6e37aaf2 100644 --- a/src/plugins/data/common/search/index.ts +++ b/src/plugins/data/common/search/index.ts @@ -24,3 +24,4 @@ export * from './search_source'; export * from './tabify'; export * from './types'; export * from './session'; +export * from './utils'; diff --git a/src/plugins/data/common/search/es_search/to_snake_case.ts b/src/plugins/data/common/search/utils.test.ts similarity index 76% rename from src/plugins/data/common/search/es_search/to_snake_case.ts rename to src/plugins/data/common/search/utils.test.ts index b222a56fbf6020..cf3278c90d37ad 100644 --- a/src/plugins/data/common/search/es_search/to_snake_case.ts +++ b/src/plugins/data/common/search/utils.test.ts @@ -17,8 +17,12 @@ * under the License. */ -import { mapKeys, snakeCase } from 'lodash'; +import { isErrorResponse, isCompleteResponse, isPartialResponse } from './utils'; -export function toSnakeCase(obj: Record): Record { - return mapKeys(obj, (value, key) => snakeCase(key)); -} +describe('utils', () => { + describe('isErrorResponse', () => {}); + + describe('isCompleteResponse', () => {}); + + describe('isPartialResponse', () => {}); +}); diff --git a/src/plugins/data/common/search/es_search/utils.ts b/src/plugins/data/common/search/utils.ts similarity index 96% rename from src/plugins/data/common/search/es_search/utils.ts rename to src/plugins/data/common/search/utils.ts index 6ed222ab0830cf..0d544a51c2d457 100644 --- a/src/plugins/data/common/search/es_search/utils.ts +++ b/src/plugins/data/common/search/utils.ts @@ -17,7 +17,7 @@ * under the License. */ -import type { IKibanaSearchResponse } from '../types'; +import type { IKibanaSearchResponse } from './types'; /** * @returns true if response had an error while executing in ES diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx index 282a602d358c77..3cfe9f4278ba0c 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -25,11 +25,10 @@ import { ApplicationStart } from 'kibana/public'; import { IEsError, isEsError } from './types'; import { EsError } from './es_error'; import { getRootCause } from './utils'; -import { IKibanaSearchRequest } from '..'; export class PainlessError extends EsError { painlessStack?: string; - constructor(err: IEsError, request: IKibanaSearchRequest) { + constructor(err: IEsError) { super(err); } diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 3584d75ab86bb1..2ed77ce8e9796f 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -94,12 +94,7 @@ export class SearchInterceptor { * @returns `Error` a search service specific error or the original error, if a specific error can't be recognized. * @internal */ - protected handleSearchError( - e: any, - request: IKibanaSearchRequest, - timeoutSignal: AbortSignal, - options?: ISearchOptions - ): Error { + protected handleSearchError(e: any, timeoutSignal: AbortSignal, options?: ISearchOptions): Error { if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { // Handle a client or a server side timeout const err = new SearchTimeoutError(e, this.getTimeoutMode()); @@ -113,7 +108,7 @@ export class SearchInterceptor { return e; } else if (isEsError(e)) { if (isPainlessError(e)) { - return new PainlessError(e, request); + return new PainlessError(e); } else { return new EsError(e); } @@ -126,13 +121,12 @@ export class SearchInterceptor { * @internal */ protected runSearch( - request: IKibanaSearchRequest, + { id, ...request }: IKibanaSearchRequest, signal: AbortSignal, strategy?: string ): Promise { - const { id, ...searchRequest } = request; const path = trimEnd(`/internal/search/${strategy || ES_SEARCH_STRATEGY}/${id || ''}`, '/'); - const body = JSON.stringify(searchRequest); + const body = JSON.stringify(request); return this.deps.http.fetch({ method: 'POST', @@ -236,7 +230,7 @@ export class SearchInterceptor { this.pendingCount$.next(this.pendingCount$.getValue() + 1); return from(this.runSearch(request, combinedSignal, options?.strategy)).pipe( catchError((e: Error) => { - return throwError(this.handleSearchError(e, request, timeoutSignal, options)); + return throwError(this.handleSearchError(e, timeoutSignal, options)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 9a9b8b67730cc8..422d616998e6db 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -224,8 +224,12 @@ export { SearchStrategyDependencies, getDefaultSearchParams, getShardTimeout, + getTotalLoaded, + toKibanaSearchResponse, shimHitsTotal, usageProvider, + searchUsageObserver, + shimAbortSignal, SearchUsage, } from './search'; @@ -233,18 +237,6 @@ import { trackSearchStatus } from './search'; // Search namespace export const search = { - esSearch: { - utils: { - doSearch, - shimAbortSignal, - trackSearchStatus, - includeTotalLoaded, - toKibanaSearchResponse, - // utils: - getTotalLoaded, - toSnakeCase, - }, - }, aggs: { CidrMask, dateHistogramInterval, diff --git a/src/plugins/data/server/search/collectors/index.ts b/src/plugins/data/server/search/collectors/index.ts index 417dc1c2012d37..8ad6501d505eb1 100644 --- a/src/plugins/data/server/search/collectors/index.ts +++ b/src/plugins/data/server/search/collectors/index.ts @@ -17,4 +17,5 @@ * under the License. */ -export { usageProvider, SearchUsage } from './usage'; +export type { SearchUsage } from './usage'; +export { usageProvider, searchUsageObserver } from './usage'; diff --git a/src/plugins/data/server/search/collectors/usage.ts b/src/plugins/data/server/search/collectors/usage.ts index e1be92aa13c37f..948175a41cb6bf 100644 --- a/src/plugins/data/server/search/collectors/usage.ts +++ b/src/plugins/data/server/search/collectors/usage.ts @@ -17,8 +17,9 @@ * under the License. */ -import { CoreSetup } from 'kibana/server'; -import { Usage } from './register'; +import type { CoreSetup, Logger } from 'kibana/server'; +import type { IEsSearchResponse } from '../../../common'; +import type { Usage } from './register'; const SAVED_OBJECT_ID = 'search-telemetry'; @@ -74,3 +75,19 @@ export function usageProvider(core: CoreSetup): SearchUsage { trackSuccess: getTracker('successCount'), }; } + +/** + * Rxjs observer for easily doing `tap(searchUsageObserver(logger, usage))` in an rxjs chain. + */ +export function searchUsageObserver(logger: Logger, usage?: SearchUsage) { + return { + next(response: IEsSearchResponse) { + logger.debug(`trackSearchStatus:next ${response.rawResponse.took}`); + usage?.trackSuccess(response.rawResponse.took); + }, + error() { + logger.debug(`trackSearchStatus:error`); + usage?.trackError(); + }, + }; +} diff --git a/src/plugins/data/server/search/es_search/es_search_rxjs_utils.ts b/src/plugins/data/server/search/es_search/es_search_rxjs_utils.ts deleted file mode 100644 index 3ba2f9c4b26988..00000000000000 --- a/src/plugins/data/server/search/es_search/es_search_rxjs_utils.ts +++ /dev/null @@ -1,53 +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 { pipe } from 'rxjs'; -import { tap } from 'rxjs/operators'; - -import type { Logger, SearchResponse } from 'kibana/server'; -import type { SearchUsage } from '../collectors'; -import type { IEsSearchResponse, IKibanaSearchResponse } from '../../../common/search'; - -/** - * trackSearchStatus is a custom rxjs operator that can be used to track the progress of a search. - * @param Logger - * @param SearchUsage - */ -export const trackSearchStatus = < - KibanaResponse extends IKibanaSearchResponse = IEsSearchResponse> ->( - logger: Logger, - usage?: SearchUsage -) => { - return pipe( - tap( - (response: KibanaResponse) => { - const trackSuccessData = response.rawResponse.took; - - if (trackSuccessData !== undefined) { - logger.debug(`trackSearchStatus:next ${trackSuccessData}`); - usage?.trackSuccess(trackSuccessData); - } - }, - (err: any) => { - logger.debug(`trackSearchStatus:error ${err}`); - usage?.trackError(); - } - ) - ); -}; 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 3e2d415eac16f7..620df9c8edcb06 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 @@ -16,20 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -import { Observable } from 'rxjs'; -import { first } from 'rxjs/operators'; - -import type { Logger } from 'kibana/server'; -import type { ApiResponse } from '@elastic/elasticsearch'; -import type { SharedGlobalConfig } from 'kibana/server'; - -import { doSearch, includeTotalLoaded, toKibanaSearchResponse, toSnakeCase } from '../../../common'; -import { trackSearchStatus } from './es_search_rxjs_utils'; -import { getDefaultSearchParams, getShardTimeout } from '../es_search'; - +import { from, Observable } from 'rxjs'; +import { first, tap } from 'rxjs/operators'; +import type { SearchResponse } from 'elasticsearch'; +import type { Logger, SharedGlobalConfig } from 'kibana/server'; import type { ISearchStrategy } from '../types'; -import type { SearchUsage } from '../collectors/usage'; -import type { IEsRawSearchResponse } from '../../../common'; +import type { SearchUsage } from '../collectors'; +import { getDefaultSearchParams, getShardTimeout, shimAbortSignal } from './request_utils'; +import { toKibanaSearchResponse } from './response_utils'; +import { searchUsageObserver } from '../collectors/usage'; export const esSearchStrategyProvider = ( config$: Observable, @@ -43,19 +38,18 @@ export const esSearchStrategyProvider = ( throw new Error(`Unsupported index pattern type ${request.indexType}`); } - return doSearch>(async () => { + const search = async () => { const config = await config$.pipe(first()).toPromise(); - const params = toSnakeCase({ + const params = { ...(await getDefaultSearchParams(uiSettingsClient)), ...getShardTimeout(config), ...request.params, - }); + }; + const promise = esClient.asCurrentUser.search>(params); + const { body } = await shimAbortSignal(promise, abortSignal); + return toKibanaSearchResponse(body); + }; - return esClient.asCurrentUser.search(params); - }, abortSignal).pipe( - toKibanaSearchResponse(), - trackSearchStatus(logger, usage), - includeTotalLoaded() - ); + return from(search()).pipe(tap(searchUsageObserver(logger, usage))); }, }); diff --git a/src/plugins/data/server/search/es_search/get_default_search_params.ts b/src/plugins/data/server/search/es_search/get_default_search_params.ts deleted file mode 100644 index a01b0885abf3b4..00000000000000 --- a/src/plugins/data/server/search/es_search/get_default_search_params.ts +++ /dev/null @@ -1,41 +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 { UI_SETTINGS } from '../../../common/constants'; -import type { SharedGlobalConfig, IUiSettingsClient } from '../../../../../core/server'; - -export function getShardTimeout(config: SharedGlobalConfig) { - const timeout = config.elasticsearch.shardTimeout.asMilliseconds(); - return timeout - ? { - timeout: `${timeout}ms`, - } - : {}; -} - -export async function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient) { - const maxConcurrentShardRequests = await uiSettingsClient.get( - UI_SETTINGS.COURIER_MAX_CONCURRENT_SHARD_REQUESTS - ); - return { - maxConcurrentShardRequests: - maxConcurrentShardRequests > 0 ? maxConcurrentShardRequests : undefined, - ignoreUnavailable: true, // Don't fail if the index/indices don't exist - trackTotalHits: true, - }; -} diff --git a/src/plugins/data/server/search/es_search/index.ts b/src/plugins/data/server/search/es_search/index.ts index 14e8a4e1b0245c..f6487e3ef84f5a 100644 --- a/src/plugins/data/server/search/es_search/index.ts +++ b/src/plugins/data/server/search/es_search/index.ts @@ -18,7 +18,6 @@ */ export { esSearchStrategyProvider } from './es_search_strategy'; -export * from './get_default_search_params'; -export * from './es_search_rxjs_utils'; - +export * from './request_utils'; +export * from './response_utils'; export { ES_SEARCH_STRATEGY, IEsSearchRequest, IEsSearchResponse } from '../../../common'; diff --git a/src/plugins/data/server/search/es_search/request_utils.test.ts b/src/plugins/data/server/search/es_search/request_utils.test.ts new file mode 100644 index 00000000000000..18fe3e7f55bb60 --- /dev/null +++ b/src/plugins/data/server/search/es_search/request_utils.test.ts @@ -0,0 +1,70 @@ +/* + * 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 { shimAbortSignal } from './shim_abort_signal'; + +const createSuccessTransportRequestPromise = ( + body: any, + { statusCode = 200 }: { statusCode?: number } = {} +) => { + const promise = Promise.resolve({ body, statusCode }) as any; + promise.abort = jest.fn(); + + return promise; +}; + +describe('request utils', () => { + describe('getShardTimeout', () => {}); + + describe('getDefaultSearchParams', () => {}); + + describe('shimAbortSignal', () => { + test('aborts the promise if the signal is aborted', () => { + const promise = createSuccessTransportRequestPromise({ + success: true, + }); + const controller = new AbortController(); + shimAbortSignal(promise, controller.signal); + controller.abort(); + + expect(promise.abort).toHaveBeenCalled(); + }); + + test('returns the original promise', async () => { + const promise = createSuccessTransportRequestPromise({ + success: true, + }); + const controller = new AbortController(); + const response = await shimAbortSignal(promise, controller.signal); + + expect(response).toEqual(expect.objectContaining({ body: { success: true } })); + }); + + test('allows the promise to be aborted manually', () => { + const promise = createSuccessTransportRequestPromise({ + success: true, + }); + const controller = new AbortController(); + const enhancedPromise = shimAbortSignal(promise, controller.signal); + + enhancedPromise.abort(); + expect(promise.abort).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/plugins/data/server/search/es_search/request_utils.ts b/src/plugins/data/server/search/es_search/request_utils.ts new file mode 100644 index 00000000000000..5ea0fc0253a1fe --- /dev/null +++ b/src/plugins/data/server/search/es_search/request_utils.ts @@ -0,0 +1,63 @@ +/* + * 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 type { ApiResponse, TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; +import type { Search } from '@elastic/elasticsearch/api/requestParams'; +import type { SearchResponse } from 'elasticsearch'; +import type { IUiSettingsClient, SharedGlobalConfig } from 'kibana/server'; +import { UI_SETTINGS } from '../../../common'; + +export function getShardTimeout(config: SharedGlobalConfig): Pick { + const timeout = config.elasticsearch.shardTimeout.asMilliseconds(); + return timeout ? { timeout: `${timeout}ms` } : {}; +} + +export async function getDefaultSearchParams( + uiSettingsClient: IUiSettingsClient +): Promise< + Pick +> { + const maxConcurrentShardRequests = await uiSettingsClient.get( + UI_SETTINGS.COURIER_MAX_CONCURRENT_SHARD_REQUESTS + ); + return { + max_concurrent_shard_requests: + maxConcurrentShardRequests > 0 ? maxConcurrentShardRequests : undefined, + ignore_unavailable: true, // Don't fail if the index/indices don't exist + track_total_hits: true, + }; +} + +/** + * Temporary workaround until https://github.com/elastic/elasticsearch-js/issues/1297 is resolved. + * Shims the `AbortSignal` behavior so that, if the given `signal` aborts, the `abort` method on the + * `TransportRequestPromise` is called, actually performing the cancellation. + * @internal + */ +export function shimAbortSignal>>( + promise: TransportRequestPromise, + signal?: AbortSignal +) { + const abortHandler = () => { + promise.abort(); + signal?.removeEventListener('abort', abortHandler); + }; + signal?.addEventListener('abort', abortHandler); + return promise.then().finally(() => signal?.removeEventListener('abort', abortHandler)); +} diff --git a/src/plugins/data/server/search/es_search/response_utils.test.ts b/src/plugins/data/server/search/es_search/response_utils.test.ts new file mode 100644 index 00000000000000..5f08c871002d74 --- /dev/null +++ b/src/plugins/data/server/search/es_search/response_utils.test.ts @@ -0,0 +1,68 @@ +/* + * 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 { getTotalLoaded, toKibanaSearchResponse } from './response_utils'; + +describe('response utils', () => { + describe('getTotalLoaded', () => { + it('returns the total/loaded, not including skipped', () => { + const result = getTotalLoaded({ + _shards: { + successful: 10, + failed: 5, + skipped: 5, + total: 100, + }, + }); + + expect(result).toEqual({ + total: 100, + loaded: 15, + }); + }); + }); + + describe('toKibanaSearchResponse', () => { + it('returns rawResponse, isPartial, isRunning, total, and loaded', () => { + const result = toKibanaSearchResponse({ + _shards: { + successful: 10, + failed: 5, + skipped: 5, + total: 100, + }, + }); + + expect(result).toEqual({ + rawResponse: { + _shards: { + successful: 10, + failed: 5, + skipped: 5, + total: 100, + }, + }, + isRunning: false, + isPartial: false, + total: 100, + loaded: 15, + }); + }); + }); +}); diff --git a/src/plugins/data/common/search/es_search/get_total_loaded.ts b/src/plugins/data/server/search/es_search/response_utils.ts similarity index 69% rename from src/plugins/data/common/search/es_search/get_total_loaded.ts rename to src/plugins/data/server/search/es_search/response_utils.ts index 233bcf81866664..2f502f55057b8f 100644 --- a/src/plugins/data/common/search/es_search/get_total_loaded.ts +++ b/src/plugins/data/server/search/es_search/response_utils.ts @@ -17,14 +17,28 @@ * under the License. */ -import type { ShardsResponse } from 'elasticsearch'; +import { SearchResponse } from 'elasticsearch'; /** * Get the `total`/`loaded` for this response (see `IKibanaSearchResponse`). Note that `skipped` is * not included as it is already included in `successful`. * @internal */ -export function getTotalLoaded({ total, failed, successful }: ShardsResponse) { +export function getTotalLoaded(response: SearchResponse) { + const { total, failed, successful } = response._shards; const loaded = failed + successful; return { total, loaded }; } + +/** + * Get the Kibana representation of this response (see `IKibanaSearchResponse`). + * @internal + */ +export function toKibanaSearchResponse(rawResponse: SearchResponse) { + return { + rawResponse, + isPartial: false, + isRunning: false, + ...getTotalLoaded(rawResponse), + }; +} diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index 1be641401b29cc..3001bbe3c2f381 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -19,6 +19,6 @@ export * from './types'; export * from './es_search'; -export { usageProvider, SearchUsage } from './collectors'; +export { usageProvider, SearchUsage, searchUsageObserver } from './collectors'; export * from './aggs'; export { shimHitsTotal } from './routes'; diff --git a/x-pack/plugins/data_enhanced/common/index.ts b/x-pack/plugins/data_enhanced/common/index.ts index 61767af030803e..dd1a2d39ab5d14 100644 --- a/x-pack/plugins/data_enhanced/common/index.ts +++ b/x-pack/plugins/data_enhanced/common/index.ts @@ -10,9 +10,6 @@ export { EqlRequestParams, EqlSearchStrategyRequest, EqlSearchStrategyResponse, - IAsyncSearchRequest, - IEnhancedEsSearchRequest, IAsyncSearchOptions, - doPartialSearch, - throwOnEsError, + pollSearch, } from './search'; diff --git a/x-pack/plugins/data_enhanced/common/search/es_search/es_search_rxjs_utils.ts b/x-pack/plugins/data_enhanced/common/search/es_search/es_search_rxjs_utils.ts deleted file mode 100644 index 2d5d09fd0205e3..00000000000000 --- a/x-pack/plugins/data_enhanced/common/search/es_search/es_search_rxjs_utils.ts +++ /dev/null @@ -1,41 +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 { of, merge, timer, throwError } from 'rxjs'; -import { takeWhile, switchMap, expand, mergeMap, tap } from 'rxjs/operators'; - -import { - AbortError, - doSearch, - IKibanaSearchResponse, - isErrorResponse, -} from '../../../../../../src/plugins/data/common'; -import type { IKibanaSearchRequest } from '../../../../../../src/plugins/data/common'; -import type { IAsyncSearchOptions } from '../../../common/search/types'; - -const DEFAULT_POLLING_INTERVAL = 1000; - -export const doPartialSearch = ( - searchMethod: () => Promise, - partialSearchMethod: (id: IKibanaSearchRequest['id']) => Promise, - isCompleteResponse: (response: SearchResponse) => boolean, - getId: (response: SearchResponse) => IKibanaSearchRequest['id'], - requestId: IKibanaSearchRequest['id'], - { abortSignal, pollInterval = DEFAULT_POLLING_INTERVAL }: IAsyncSearchOptions -) => - doSearch( - requestId ? () => partialSearchMethod(requestId) : searchMethod, - abortSignal - ).pipe( - tap((response) => (requestId = getId(response))), - expand(() => timer(pollInterval).pipe(switchMap(() => partialSearchMethod(requestId)))), - takeWhile((response) => !isCompleteResponse(response), true) - ); - -export const throwOnEsError = () => - mergeMap((r: IKibanaSearchResponse) => - isErrorResponse(r) ? merge(of(r), throwError(new AbortError())) : of(r) - ); diff --git a/x-pack/plugins/data_enhanced/common/search/index.ts b/x-pack/plugins/data_enhanced/common/search/index.ts index 44f82386e35c3c..34bb21cb91af14 100644 --- a/x-pack/plugins/data_enhanced/common/search/index.ts +++ b/x-pack/plugins/data_enhanced/common/search/index.ts @@ -5,4 +5,4 @@ */ export * from './types'; -export * from './es_search'; +export * from './poll_search'; diff --git a/x-pack/plugins/data_enhanced/common/search/poll_search.ts b/x-pack/plugins/data_enhanced/common/search/poll_search.ts new file mode 100644 index 00000000000000..c70e72b399c3ac --- /dev/null +++ b/x-pack/plugins/data_enhanced/common/search/poll_search.ts @@ -0,0 +1,38 @@ +/* + * 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 { from, NEVER, timer } from 'rxjs'; +import { expand, finalize, switchMap, takeUntil, takeWhile, tap } from 'rxjs/operators'; +import type { + IEsSearchResponse, + IKibanaSearchResponse, +} from '../../../../../src/plugins/data/common'; +import { + AbortError, + isErrorResponse, + isPartialResponse, + toPromise, +} from '../../../../../src/plugins/data/common'; +import type { IAsyncSearchOptions } from './types'; + +export function pollSearch( + search: () => Promise, + { pollInterval = 1000, ...options }: IAsyncSearchOptions = {} +) { + const aborted = options?.abortSignal + ? toPromise(options?.abortSignal) + : { promise: NEVER, cleanup: () => {} }; + + return from(search()).pipe( + expand(() => timer(pollInterval).pipe(switchMap(search))), + tap((response) => { + if (isErrorResponse(response)) throw new AbortError(); + }), + takeWhile(isPartialResponse, true), + takeUntil(aborted.promise), + finalize(aborted.cleanup) + ); +} diff --git a/x-pack/plugins/data_enhanced/common/search/types.ts b/x-pack/plugins/data_enhanced/common/search/types.ts index 4abf8351114f81..c8c79a2decb1ac 100644 --- a/x-pack/plugins/data_enhanced/common/search/types.ts +++ b/x-pack/plugins/data_enhanced/common/search/types.ts @@ -16,20 +16,6 @@ import { export const ENHANCED_ES_SEARCH_STRATEGY = 'ese'; -export interface IAsyncSearchRequest extends IEsSearchRequest { - /** - * The ID received from the response from the initial request - */ - id?: string; -} - -export interface IEnhancedEsSearchRequest extends IEsSearchRequest { - /** - * Used to determine whether to use the _rollups_search or a regular search endpoint. - */ - isRollup?: boolean; -} - export const EQL_SEARCH_STRATEGY = 'eql'; export type EqlRequestParams = EqlSearch>; 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 4cafcdb29ae8dc..74c05f53c3d7cc 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -4,24 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { throwError, from, Subscription } from 'rxjs'; -import { tap, takeUntil, finalize, catchError } from 'rxjs/operators'; +import { throwError, Subscription } from 'rxjs'; +import { tap, finalize, catchError } from 'rxjs/operators'; import { TimeoutErrorMode, - IEsSearchResponse, SearchInterceptor, SearchInterceptorDeps, UI_SETTINGS, + IKibanaSearchRequest, } from '../../../../../src/plugins/data/public'; -import { AbortError, toPromise } from '../../../../../src/plugins/data/common'; +import { AbortError } from '../../../../../src/plugins/data/common'; -import { - IAsyncSearchRequest, - ENHANCED_ES_SEARCH_STRATEGY, - IAsyncSearchOptions, - doPartialSearch, - throwOnEsError, -} from '../../common'; +import { ENHANCED_ES_SEARCH_STRATEGY, IAsyncSearchOptions, pollSearch } from '../../common'; export class EnhancedSearchInterceptor extends SearchInterceptor { private uiSettingsSub: Subscription; @@ -60,45 +54,28 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { if (this.deps.usageCollector) this.deps.usageCollector.trackQueriesCancelled(); }; - public search( - request: IAsyncSearchRequest, - { pollInterval = 1000, ...options }: IAsyncSearchOptions = {} - ) { - let { id } = request; - + public search({ id, ...request }: IKibanaSearchRequest, options: IAsyncSearchOptions = {}) { const { combinedSignal, timeoutSignal, cleanup } = this.setupAbortSignal({ abortSignal: options.abortSignal, timeout: this.searchTimeout, }); - const abortedPromise = toPromise(combinedSignal); const strategy = options?.strategy ?? ENHANCED_ES_SEARCH_STRATEGY; + const search = () => this.runSearch({ id, ...request }, combinedSignal, strategy); this.pendingCount$.next(this.pendingCount$.getValue() + 1); - return doPartialSearch( - () => this.runSearch(request, combinedSignal, strategy), - (requestId) => this.runSearch({ ...request, id: requestId }, combinedSignal, strategy), - (r) => !r.isRunning, - (response) => response.id, - id, - { pollInterval } - ).pipe( - tap((r) => { - id = r.id ?? id; - }), - throwOnEsError(), - takeUntil(from(abortedPromise.promise)), + return pollSearch(search, { ...options, abortSignal: combinedSignal }).pipe( + tap((response) => (id = response.id)), catchError((e: AbortError) => { if (id) { this.deps.http.delete(`/internal/search/${strategy}/${id}`); } - return throwError(this.handleSearchError(e, request, timeoutSignal, options)); + return throwError(this.handleSearchError(e, timeoutSignal, options)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); cleanup(); - abortedPromise.cleanup(); }) ); } diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts index a75f2617a9bf35..9064b18eb127be 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts @@ -4,18 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import type { Logger } from 'kibana/server'; -import type { ApiResponse } from '@elastic/elasticsearch'; - -import { search } from '../../../../../src/plugins/data/server'; -import { doPartialSearch } from '../../common/search/es_search/es_search_rxjs_utils'; -import { getAsyncOptions, getDefaultSearchParams } from './get_default_search_params'; - -import type { ISearchStrategy, IEsRawSearchResponse } from '../../../../../src/plugins/data/server'; +import { tap } from 'rxjs/operators'; +import type { ISearchStrategy } from '../../../../../src/plugins/data/server'; import type { EqlSearchStrategyRequest, EqlSearchStrategyResponse, -} from '../../common/search/types'; + IAsyncSearchOptions, +} from '../../common'; +import { + getDefaultSearchParams, + searchUsageObserver, + shimAbortSignal, +} from '../../../../../src/plugins/data/server'; +import { pollSearch } from '../../common'; +import { getDefaultAsyncGetParams, getIgnoreThrottled } from './request_utils'; export const eqlSearchStrategyProvider = ( logger: Logger @@ -23,48 +25,39 @@ export const eqlSearchStrategyProvider = ( return { cancel: async (id, options, { esClient }) => { logger.debug(`_eql/delete ${id}`); - await esClient.asCurrentUser.eql.delete({ - id, - }); + await esClient.asCurrentUser.eql.delete({ id }); }, - search: (request, options, { esClient, uiSettingsClient }) => { + search: ({ id, ...request }, options: IAsyncSearchOptions, { esClient, uiSettingsClient }) => { logger.debug(`_eql/search ${JSON.stringify(request.params) || request.id}`); - const { utils } = search.esSearch; - const asyncOptions = getAsyncOptions(); - const requestOptions = utils.toSnakeCase({ ...request.options }); const client = esClient.asCurrentUser.eql; - return doPartialSearch>( - async () => { - const { ignoreThrottled, ignoreUnavailable } = await getDefaultSearchParams( - uiSettingsClient - ); - - return client.search( - utils.toSnakeCase({ - ignoreThrottled, - ignoreUnavailable, - ...asyncOptions, + const search = async () => { + const params = id + ? { id, ...getDefaultAsyncGetParams() } + : { + ...(await getIgnoreThrottled(uiSettingsClient)), + ...(await getDefaultSearchParams(uiSettingsClient)), + ...getDefaultAsyncGetParams(), ...request.params, - }) as EqlSearchStrategyRequest['params'], - requestOptions - ); - }, - (id) => - client.get( - { - id: id!, - ...utils.toSnakeCase(asyncOptions), - }, - requestOptions - ), - (response) => !response.body.is_running, - (response) => response.body.id, - request.id, - options - ).pipe(utils.toKibanaSearchResponse()); + }; + const promise = id + ? client.get(params, request.options) + : client.search(params, request.options); + const { body } = await shimAbortSignal(promise, options.abortSignal); + return { + id: body.id, + rawResponse: body, + isPartial: body.is_partial, + isRunning: body.is_running, + }; + }; + + return pollSearch(search, options).pipe( + tap((response) => (id = response.id)), + tap(searchUsageObserver(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 53bcac02cb01d6..8fdebc7a58c2f2 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 @@ -4,85 +4,64 @@ * you may not use this file except in compliance with the Elastic License. */ +import type { Observable } from 'rxjs'; +import type { Logger, SharedGlobalConfig } from 'kibana/server'; +import { first, tap } from 'rxjs/operators'; +import { SearchResponse } from 'elasticsearch'; import { from } from 'rxjs'; -import { first, map } from 'rxjs/operators'; -import { Observable } from 'rxjs'; - -import type { SearchResponse } from 'elasticsearch'; -import type { ApiResponse } from '@elastic/elasticsearch'; - -import { - getShardTimeout, - shimHitsTotal, - search, - SearchStrategyDependencies, -} from '../../../../../src/plugins/data/server'; -import { doPartialSearch } from '../../common/search/es_search/es_search_rxjs_utils'; -import { getDefaultSearchParams, getAsyncOptions } from './get_default_search_params'; - -import type { SharedGlobalConfig, Logger } from '../../../../../src/core/server'; - import type { + IEsSearchRequest, + IEsSearchResponse, + ISearchOptions, ISearchStrategy, + SearchStrategyDependencies, SearchUsage, - IEsRawSearchResponse, - ISearchOptions, - IEsSearchResponse, } from '../../../../../src/plugins/data/server'; - -import type { IEnhancedEsSearchRequest } from '../../common'; - -const { utils } = search.esSearch; - -interface IEsRawAsyncSearchResponse extends IEsRawSearchResponse { - response: SearchResponse; -} +import { + getDefaultSearchParams, + getShardTimeout, + getTotalLoaded, + searchUsageObserver, + shimAbortSignal, +} from '../../../../../src/plugins/data/server'; +import { + getDefaultAsyncGetParams, + getDefaultAsyncSubmitParams, + getIgnoreThrottled, +} from './request_utils'; +import { toKibanaSearchResponse } from './response_utils'; +import { pollSearch } from '../../common/search/poll_search'; +import { IAsyncSearchOptions } from '../../common/search'; export const enhancedEsSearchStrategyProvider = ( config$: Observable, logger: Logger, usage?: SearchUsage -): ISearchStrategy => { +): ISearchStrategy => { function asyncSearch( - request: IEnhancedEsSearchRequest, - options: ISearchOptions, + { id, ...request }: IEsSearchRequest, + options: IAsyncSearchOptions, { esClient, uiSettingsClient }: SearchStrategyDependencies ) { - const asyncOptions = getAsyncOptions(); const client = esClient.asCurrentUser.asyncSearch; - return doPartialSearch>( - async () => - client.submit( - utils.toSnakeCase({ - ...(await getDefaultSearchParams(uiSettingsClient)), - batchedReduceSize: 64, - ...asyncOptions, - ...request.params, - }) - ), - (id) => - client.get({ - id: id!, - ...utils.toSnakeCase({ ...asyncOptions }), - }), - (response) => !response.body.is_running, - (response) => response.body.id, - request.id, - options - ).pipe( - utils.toKibanaSearchResponse(), - map((response) => ({ - ...response, - rawResponse: shimHitsTotal(response.rawResponse.response!), - })), - utils.trackSearchStatus(logger, usage), - utils.includeTotalLoaded() + const search = async () => { + const params = id + ? { ...getDefaultAsyncGetParams(), id } + : { ...(await getDefaultAsyncSubmitParams(uiSettingsClient)), ...request.params }; + const promise = id ? client.get(params) : client.submit(params); + const { body } = await shimAbortSignal(promise, options.abortSignal); + return toKibanaSearchResponse(body); + }; + + return pollSearch(search, options).pipe( + tap((response) => (id = response.id)), + tap(searchUsageObserver(logger, usage)) ); } async function rollupSearch( - request: IEnhancedEsSearchRequest, + request: IEsSearchRequest, options: ISearchOptions, { esClient, uiSettingsClient }: SearchStrategyDependencies ): Promise { @@ -90,11 +69,12 @@ export const enhancedEsSearchStrategyProvider = ( const { body, index, ...params } = request.params!; const method = 'POST'; const path = encodeURI(`/${index}/_rollup_search`); - const querystring = utils.toSnakeCase({ + const querystring = { ...getShardTimeout(config), + ...(await getIgnoreThrottled(uiSettingsClient)), ...(await getDefaultSearchParams(uiSettingsClient)), ...params, - }); + }; const promise = esClient.asCurrentUser.transport.request({ method, @@ -103,17 +83,16 @@ export const enhancedEsSearchStrategyProvider = ( querystring, }); - const esResponse = await utils.shimAbortSignal(promise, options?.abortSignal); - + const esResponse = await shimAbortSignal(promise, options?.abortSignal); const response = esResponse.body as SearchResponse; return { rawResponse: response, - ...utils.getTotalLoaded(response._shards), + ...getTotalLoaded(response), }; } return { - search: (request, options, deps) => { + search: (request, options: IAsyncSearchOptions, deps) => { logger.debug(`search ${JSON.stringify(request.params) || request.id}`); return request.indexType !== 'rollup' diff --git a/x-pack/plugins/data_enhanced/server/search/get_default_search_params.ts b/x-pack/plugins/data_enhanced/server/search/get_default_search_params.ts deleted file mode 100644 index fdda78798808f8..00000000000000 --- a/x-pack/plugins/data_enhanced/server/search/get_default_search_params.ts +++ /dev/null @@ -1,33 +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 { IUiSettingsClient } from 'src/core/server'; -import { UI_SETTINGS } from '../../../../../src/plugins/data/common'; - -import { getDefaultSearchParams as getBaseSearchParams } from '../../../../../src/plugins/data/server'; - -/** - @internal - */ -export async function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient) { - const ignoreThrottled = !(await uiSettingsClient.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN)); - - return { - ignoreThrottled, - ...(await getBaseSearchParams(uiSettingsClient)), - }; -} - -/** - @internal - */ -export const getAsyncOptions = (): { - waitForCompletionTimeout: string; - keepAlive: string; -} => ({ - waitForCompletionTimeout: '100ms', // Wait up to 100ms for the response to return - keepAlive: '1m', // Extend the TTL for this search request by one minute, -}); diff --git a/x-pack/plugins/data_enhanced/server/search/request_utils.ts b/x-pack/plugins/data_enhanced/server/search/request_utils.ts new file mode 100644 index 00000000000000..d4d3b980c63b08 --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/search/request_utils.ts @@ -0,0 +1,59 @@ +/* + * 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 { IUiSettingsClient } from 'kibana/server'; +import { + AsyncSearchGet, + AsyncSearchSubmit, + Search, +} from '@elastic/elasticsearch/api/requestParams'; +import { UI_SETTINGS } from '../../../../../src/plugins/data/common'; +import { getDefaultSearchParams } from '../../../../../src/plugins/data/server'; + +/** + * @internal + */ +export async function getIgnoreThrottled( + uiSettingsClient: IUiSettingsClient +): Pick { + const includeFrozen = await uiSettingsClient.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN); + return { ignore_throttled: !includeFrozen }; +} + +/** + @internal + */ +export async function getDefaultAsyncSubmitParams( + uiSettingsClient: IUiSettingsClient +): Pick< + AsyncSearchSubmit, + | 'batched_reduce_size' + | 'keep_alive' + | 'wait_for_completion_timeout' + | 'ignore_throttled' + | 'max_concurrent_shard_requests' + | 'ignore_unavailable' + | 'track_total_hits' +> { + return { + batched_reduce_size: 64, + ...getDefaultAsyncGetParams(), + ...(await getIgnoreThrottled(uiSettingsClient)), + ...(await getDefaultSearchParams(uiSettingsClient)), + }; +} + +/** + @internal + */ +export function getDefaultAsyncGetParams(): Pick< + AsyncSearchGet, + 'keep_alive' | 'wait_for_completion_timeout' +> { + return { + keep_alive: '1m', // Extend the TTL for this search request by one minute + wait_for_completion_timeout: '100ms', // Wait up to 100ms for the response to return + }; +} diff --git a/x-pack/plugins/data_enhanced/server/search/response_utils.ts b/x-pack/plugins/data_enhanced/server/search/response_utils.ts new file mode 100644 index 00000000000000..60e357de74a9d5 --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/search/response_utils.ts @@ -0,0 +1,21 @@ +/* + * 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 { getTotalLoaded } from '../../../../../src/plugins/data/server'; +import { AsyncSearchResponse } from './types'; + +/** + * Get the Kibana representation of this response (see `IKibanaSearchResponse`). + */ +export function toKibanaSearchResponse(response: AsyncSearchResponse) { + return { + id: response.id, + rawResponse: response.response, + isPartial: response.is_partial, + isRunning: response.is_running, + ...getTotalLoaded(response.response), + }; +} diff --git a/x-pack/plugins/data_enhanced/common/search/es_search/index.ts b/x-pack/plugins/data_enhanced/server/search/types.ts similarity index 55% rename from x-pack/plugins/data_enhanced/common/search/es_search/index.ts rename to x-pack/plugins/data_enhanced/server/search/types.ts index bbf9f14ba63c29..b34915925c9645 100644 --- a/x-pack/plugins/data_enhanced/common/search/es_search/index.ts +++ b/x-pack/plugins/data_enhanced/server/search/types.ts @@ -4,4 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './es_search_rxjs_utils'; +import { SearchResponse } from 'elasticsearch'; + +export interface AsyncSearchResponse { + id?: string; + response: SearchResponse; + is_partial: boolean; + is_running: boolean; +} From cf2b31d9d5d58f9918ad3c9cad81723e07800a50 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Fri, 20 Nov 2020 15:46:59 -0700 Subject: [PATCH 13/21] Fix & update tests & types --- ...data-public.painlesserror._constructor_.md | 3 +- ...lugin-plugins-data-public.painlesserror.md | 2 +- ...lic.searchinterceptor.handlesearcherror.md | 3 +- ...n-plugins-data-public.searchinterceptor.md | 2 +- ...gins-data-server.getdefaultsearchparams.md | 12 +- ...gin-plugins-data-server.getshardtimeout.md | 12 +- ...ins-data-server.iesrawsearchresponse.id.md | 11 -- ...-server.iesrawsearchresponse.is_partial.md | 11 -- ...-server.iesrawsearchresponse.is_running.md | 11 -- ...lugins-data-server.iesrawsearchresponse.md | 20 ---- .../kibana-plugin-plugins-data-server.md | 2 +- ...ibana-plugin-plugins-data-server.search.md | 18 --- ...plugins-data-server.searchusageobserver.md | 31 ++++++ src/plugins/data/common/search/utils.test.ts | 84 +++++++++++++- src/plugins/data/public/public.api.md | 6 +- .../public/search/search_interceptor.test.ts | 21 ++-- src/plugins/data/server/index.ts | 10 -- .../search/es_search/request_utils.test.ts | 84 +++++++++++++- .../server/search/es_search/request_utils.ts | 23 ++-- .../search/es_search/response_utils.test.ts | 9 +- .../data/server/search/routes/call_msearch.ts | 7 +- src/plugins/data/server/server.api.md | 103 ++++++++---------- .../common/search/poll_search.ts | 17 ++- .../public/search/search_interceptor.ts | 3 +- x-pack/plugins/data_enhanced/server/plugin.ts | 2 +- .../server/search/eql_search_strategy.test.ts | 4 +- .../server/search/eql_search_strategy.ts | 25 ++--- .../server/search/es_search_strategy.ts | 7 +- .../server/search/request_utils.ts | 24 ++-- .../server/search/response_utils.ts | 5 +- 30 files changed, 323 insertions(+), 249 deletions(-) delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.id.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.is_partial.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.is_running.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md index 051414eac75856..5f43f8477cb9f1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `PainlessError` class Signature: ```typescript -constructor(err: IEsError, request: IKibanaSearchRequest); +constructor(err: IEsError); ``` ## Parameters @@ -17,5 +17,4 @@ constructor(err: IEsError, request: IKibanaSearchRequest); | Parameter | Type | Description | | --- | --- | --- | | err | IEsError | | -| request | IKibanaSearchRequest | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md index 6ab32f3fb1dfad..c77b8b259136b1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md @@ -14,7 +14,7 @@ export declare class PainlessError extends EsError | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(err, request)](./kibana-plugin-plugins-data-public.painlesserror._constructor_.md) | | Constructs a new instance of the PainlessError class | +| [(constructor)(err)](./kibana-plugin-plugins-data-public.painlesserror._constructor_.md) | | Constructs a new instance of the PainlessError class | ## Properties diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md index 1c8b6eb41a72e3..b5ac4a4e53887e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md @@ -7,7 +7,7 @@ Signature: ```typescript -protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; +protected handleSearchError(e: any, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; ``` ## Parameters @@ -15,7 +15,6 @@ protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal | Parameter | Type | Description | | --- | --- | --- | | e | any | | -| request | IKibanaSearchRequest | | | timeoutSignal | AbortSignal | | | options | ISearchOptions | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index 40c7055e4c0599..5f266e7d8bd8c1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -27,7 +27,7 @@ export declare class SearchInterceptor | Method | Modifiers | Description | | --- | --- | --- | | [getTimeoutMode()](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) | | | -| [handleSearchError(e, request, timeoutSignal, options)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | +| [handleSearchError(e, timeoutSignal, options)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | | [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given search method. Overrides the AbortSignal with one that will abort either when cancelPending is called, when the request times out, or when the original AbortSignal is aborted. Updates pendingCount$ when the request is started/finalized. | | [showError(e)](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) | | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.getdefaultsearchparams.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.getdefaultsearchparams.md index 3d9191196aaf02..19a4bbbbef86cf 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.getdefaultsearchparams.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.getdefaultsearchparams.md @@ -7,11 +7,7 @@ Signature: ```typescript -export declare function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient): Promise<{ - maxConcurrentShardRequests: number | undefined; - ignoreUnavailable: boolean; - trackTotalHits: boolean; -}>; +export declare function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient): Promise>; ``` ## Parameters @@ -22,9 +18,5 @@ export declare function getDefaultSearchParams(uiSettingsClient: IUiSettingsClie Returns: -`Promise<{ - maxConcurrentShardRequests: number | undefined; - ignoreUnavailable: boolean; - trackTotalHits: boolean; -}>` +`Promise>` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.getshardtimeout.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.getshardtimeout.md index d7e2a597ff33d8..87aa32608eb14e 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.getshardtimeout.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.getshardtimeout.md @@ -7,11 +7,7 @@ Signature: ```typescript -export declare function getShardTimeout(config: SharedGlobalConfig): { - timeout: string; -} | { - timeout?: undefined; -}; +export declare function getShardTimeout(config: SharedGlobalConfig): Pick; ``` ## Parameters @@ -22,9 +18,5 @@ export declare function getShardTimeout(config: SharedGlobalConfig): { Returns: -`{ - timeout: string; -} | { - timeout?: undefined; -}` +`Pick` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.id.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.id.md deleted file mode 100644 index 8e1d5d01bb6642..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsRawSearchResponse](./kibana-plugin-plugins-data-server.iesrawsearchresponse.md) > [id](./kibana-plugin-plugins-data-server.iesrawsearchresponse.id.md) - -## IEsRawSearchResponse.id property - -Signature: - -```typescript -id?: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.is_partial.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.is_partial.md deleted file mode 100644 index da2a57a84ab2fd..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.is_partial.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsRawSearchResponse](./kibana-plugin-plugins-data-server.iesrawsearchresponse.md) > [is\_partial](./kibana-plugin-plugins-data-server.iesrawsearchresponse.is_partial.md) - -## IEsRawSearchResponse.is\_partial property - -Signature: - -```typescript -is_partial?: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.is_running.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.is_running.md deleted file mode 100644 index 78b9e07b77890c..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.is_running.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsRawSearchResponse](./kibana-plugin-plugins-data-server.iesrawsearchresponse.md) > [is\_running](./kibana-plugin-plugins-data-server.iesrawsearchresponse.is_running.md) - -## IEsRawSearchResponse.is\_running property - -Signature: - -```typescript -is_running?: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.md deleted file mode 100644 index 306c18dea9b0da..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iesrawsearchresponse.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsRawSearchResponse](./kibana-plugin-plugins-data-server.iesrawsearchresponse.md) - -## IEsRawSearchResponse interface - -Signature: - -```typescript -export interface IEsRawSearchResponse extends SearchResponse -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [id](./kibana-plugin-plugins-data-server.iesrawsearchresponse.id.md) | string | | -| [is\_partial](./kibana-plugin-plugins-data-server.iesrawsearchresponse.is_partial.md) | boolean | | -| [is\_running](./kibana-plugin-plugins-data-server.iesrawsearchresponse.is_running.md) | boolean | | - 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 8957f6d0f06b4c..b5b3a7d1a18280 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 @@ -34,6 +34,7 @@ | [getTime(indexPattern, timeRange, options)](./kibana-plugin-plugins-data-server.gettime.md) | | | [parseInterval(interval)](./kibana-plugin-plugins-data-server.parseinterval.md) | | | [plugin(initializerContext)](./kibana-plugin-plugins-data-server.plugin.md) | Static code to be shared externally | +| [searchUsageObserver(logger, usage)](./kibana-plugin-plugins-data-server.searchusageobserver.md) | Rxjs observer for easily doing tap(searchUsageObserver(logger, usage)) in an rxjs chain. | | [shouldReadFieldFromDocValues(aggregatable, esType)](./kibana-plugin-plugins-data-server.shouldreadfieldfromdocvalues.md) | | | [usageProvider(core)](./kibana-plugin-plugins-data-server.usageprovider.md) | | @@ -45,7 +46,6 @@ | [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | | | [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | | -| [IEsRawSearchResponse](./kibana-plugin-plugins-data-server.iesrawsearchresponse.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) | | | [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md index e2a71a7badd4d6..9e38ce3f64f381 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md @@ -8,24 +8,6 @@ ```typescript search: { - esSearch: { - utils: { - doSearch: (searchMethod: () => Promise, abortSignal?: AbortSignal | undefined) => import("rxjs").Observable; - shimAbortSignal: >(promise: T, signal: AbortSignal | undefined) => T; - trackSearchStatus: = import("./search").IEsSearchResponse>>(logger: import("src/core/server").Logger, usage?: import("./search").SearchUsage | undefined) => import("rxjs").UnaryFunction, import("rxjs").Observable>; - includeTotalLoaded: () => import("rxjs").OperatorFunction>, { - total: number; - loaded: number; - id?: string | undefined; - isRunning?: boolean | undefined; - isPartial?: boolean | undefined; - rawResponse: import("elasticsearch").SearchResponse; - }>; - toKibanaSearchResponse: = import("../common").IEsRawSearchResponse, KibanaResponse_1 extends import("../common").IKibanaSearchResponse = import("../common").IKibanaSearchResponse>() => import("rxjs").OperatorFunction, KibanaResponse_1>; - getTotalLoaded: typeof getTotalLoaded; - toSnakeCase: typeof toSnakeCase; - }; - }; aggs: { CidrMask: typeof CidrMask; dateHistogramInterval: typeof dateHistogramInterval; diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md new file mode 100644 index 00000000000000..5e03bb381527e0 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md @@ -0,0 +1,31 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [searchUsageObserver](./kibana-plugin-plugins-data-server.searchusageobserver.md) + +## searchUsageObserver() function + +Rxjs observer for easily doing `tap(searchUsageObserver(logger, usage))` in an rxjs chain. + +Signature: + +```typescript +export declare function searchUsageObserver(logger: Logger, usage?: SearchUsage): { + next(response: IEsSearchResponse): void; + error(): void; +}; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| logger | Logger | | +| usage | SearchUsage | | + +Returns: + +`{ + next(response: IEsSearchResponse): void; + error(): void; +}` + diff --git a/src/plugins/data/common/search/utils.test.ts b/src/plugins/data/common/search/utils.test.ts index cf3278c90d37ad..94f7b14de4bc3b 100644 --- a/src/plugins/data/common/search/utils.test.ts +++ b/src/plugins/data/common/search/utils.test.ts @@ -20,9 +20,87 @@ import { isErrorResponse, isCompleteResponse, isPartialResponse } from './utils'; describe('utils', () => { - describe('isErrorResponse', () => {}); + describe('isErrorResponse', () => { + it('returns `true` if the response is undefined', () => { + const isError = isErrorResponse(); + expect(isError).toBe(true); + }); - describe('isCompleteResponse', () => {}); + it('returns `true` if the response is not running and partial', () => { + const isError = isErrorResponse({ + isPartial: true, + isRunning: false, + rawResponse: {}, + }); + expect(isError).toBe(true); + }); - describe('isPartialResponse', () => {}); + it('returns `false` if the response is running and partial', () => { + const isError = isErrorResponse({ + isPartial: true, + isRunning: true, + rawResponse: {}, + }); + expect(isError).toBe(false); + }); + + it('returns `false` if the response is complete', () => { + const isError = isErrorResponse({ + isPartial: false, + isRunning: false, + rawResponse: {}, + }); + expect(isError).toBe(false); + }); + }); + + describe('isCompleteResponse', () => { + it('returns `false` if the response is undefined', () => { + const isError = isCompleteResponse(); + expect(isError).toBe(false); + }); + + it('returns `false` if the response is running and partial', () => { + const isError = isCompleteResponse({ + isPartial: true, + isRunning: true, + rawResponse: {}, + }); + expect(isError).toBe(false); + }); + + it('returns `true` if the response is complete', () => { + const isError = isCompleteResponse({ + isPartial: false, + isRunning: false, + rawResponse: {}, + }); + expect(isError).toBe(true); + }); + }); + + describe('isPartialResponse', () => { + it('returns `false` if the response is undefined', () => { + const isError = isPartialResponse(); + expect(isError).toBe(false); + }); + + it('returns `true` if the response is running and partial', () => { + const isError = isPartialResponse({ + isPartial: true, + isRunning: true, + rawResponse: {}, + }); + expect(isError).toBe(true); + }); + + it('returns `false` if the response is complete', () => { + const isError = isPartialResponse({ + isPartial: false, + isRunning: false, + rawResponse: {}, + }); + expect(isError).toBe(false); + }); + }); }); diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 6c4609e5506c23..90da217f8f6d8b 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1678,7 +1678,7 @@ export interface OptionedValueProp { // @public (undocumented) export class PainlessError extends EsError { // Warning: (ae-forgotten-export) The symbol "IEsError" needs to be exported by the entry point index.d.ts - constructor(err: IEsError, request: IKibanaSearchRequest); + constructor(err: IEsError); // (undocumented) getErrorMessage(application: ApplicationStart): JSX.Element; // (undocumented) @@ -2078,11 +2078,11 @@ export class SearchInterceptor { // (undocumented) protected getTimeoutMode(): TimeoutErrorMode; // (undocumented) - protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; + protected handleSearchError(e: any, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) - protected runSearch(request: IKibanaSearchRequest, options?: ISearchOptions): Promise; + protected runSearch({ id, ...request }: IKibanaSearchRequest, options?: ISearchOptions): Promise; search(request: IKibanaSearchRequest, options?: ISearchOptions): Observable; // @internal (undocumented) protected setupAbortSignal({ abortSignal, timeout, }: { diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index 60274261da25f4..6e75f6e5eef9ef 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -65,20 +65,17 @@ describe('SearchInterceptor', () => { test('Renders a PainlessError', async () => { searchInterceptor.showError( - new PainlessError( - { - body: { - attributes: { - error: { - failed_shards: { - reason: 'bananas', - }, + new PainlessError({ + body: { + attributes: { + error: { + failed_shards: { + reason: 'bananas', }, }, - } as any, - }, - {} as any - ) + }, + } as any, + }) ); expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); expect(mockCoreSetup.notifications.toasts.addError).not.toBeCalled(); diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 75d2f8b3464631..ad54e692a4da9c 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -146,7 +146,6 @@ export { IndexPatternAttributes, UI_SETTINGS, IndexPattern, - IEsRawSearchResponse, } from '../common'; /** @@ -179,13 +178,6 @@ import { // tabify tabifyAggResponse, tabifyGetColumns, - // search - toSnakeCase, - shimAbortSignal, - doSearch, - includeTotalLoaded, - toKibanaSearchResponse, - getTotalLoaded, } from '../common'; export { @@ -241,8 +233,6 @@ export { SearchUsage, } from './search'; -import { trackSearchStatus } from './search'; - // Search namespace export const search = { aggs: { diff --git a/src/plugins/data/server/search/es_search/request_utils.test.ts b/src/plugins/data/server/search/es_search/request_utils.test.ts index 18fe3e7f55bb60..b63a6b3ae7e9b2 100644 --- a/src/plugins/data/server/search/es_search/request_utils.test.ts +++ b/src/plugins/data/server/search/es_search/request_utils.test.ts @@ -17,7 +17,8 @@ * under the License. */ -import { shimAbortSignal } from './shim_abort_signal'; +import { getShardTimeout, getDefaultSearchParams, shimAbortSignal } from './request_utils'; +import { IUiSettingsClient, SharedGlobalConfig } from 'kibana/server'; const createSuccessTransportRequestPromise = ( body: any, @@ -30,11 +31,88 @@ const createSuccessTransportRequestPromise = ( }; describe('request utils', () => { - describe('getShardTimeout', () => {}); + describe('getShardTimeout', () => { + test('returns an empty object if the config does not contain a value', () => { + const result = getShardTimeout(({ + elasticsearch: { + shardTimeout: { + asMilliseconds: jest.fn(), + }, + }, + } as unknown) as SharedGlobalConfig); + expect(result).toEqual({}); + }); + + test('returns an empty object if the config contains 0', () => { + const result = getShardTimeout(({ + elasticsearch: { + shardTimeout: { + asMilliseconds: jest.fn().mockReturnValue(0), + }, + }, + } as unknown) as SharedGlobalConfig); + expect(result).toEqual({}); + }); + + test('returns a duration if the config >= 0', () => { + const result = getShardTimeout(({ + elasticsearch: { + shardTimeout: { + asMilliseconds: jest.fn().mockReturnValue(10), + }, + }, + } as unknown) as SharedGlobalConfig); + expect(result).toEqual({ timeout: '10ms' }); + }); + }); + + describe('getDefaultSearchParams', () => { + describe('max_concurrent_shard_requests', () => { + test('returns value if > 0', async () => { + const result = await getDefaultSearchParams(({ + get: jest.fn().mockResolvedValue(1), + } as unknown) as IUiSettingsClient); + expect(result).toHaveProperty('max_concurrent_shard_requests', 1); + }); + + test('returns undefined if === 0', async () => { + const result = await getDefaultSearchParams(({ + get: jest.fn().mockResolvedValue(0), + } as unknown) as IUiSettingsClient); + expect(result.max_concurrent_shard_requests).toBe(undefined); + }); + + test('returns undefined if undefined', async () => { + const result = await getDefaultSearchParams(({ + get: jest.fn(), + } as unknown) as IUiSettingsClient); + expect(result.max_concurrent_shard_requests).toBe(undefined); + }); + }); - describe('getDefaultSearchParams', () => {}); + describe('other defaults', () => { + test('returns ignore_unavailable and track_total_hits', async () => { + const result = await getDefaultSearchParams(({ + get: jest.fn(), + } as unknown) as IUiSettingsClient); + expect(result).toHaveProperty('ignore_unavailable', true); + expect(result).toHaveProperty('track_total_hits', true); + }); + }); + }); describe('shimAbortSignal', () => { + test('aborts the promise if the signal is already aborted', async () => { + const promise = createSuccessTransportRequestPromise({ + success: true, + }); + const controller = new AbortController(); + controller.abort(); + shimAbortSignal(promise, controller.signal); + + expect(promise.abort).toHaveBeenCalled(); + }); + test('aborts the promise if the signal is aborted', () => { const promise = createSuccessTransportRequestPromise({ success: true, diff --git a/src/plugins/data/server/search/es_search/request_utils.ts b/src/plugins/data/server/search/es_search/request_utils.ts index 5ea0fc0253a1fe..03b7db7da8ffeb 100644 --- a/src/plugins/data/server/search/es_search/request_utils.ts +++ b/src/plugins/data/server/search/es_search/request_utils.ts @@ -17,9 +17,8 @@ * under the License. */ -import type { ApiResponse, TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; +import type { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; import type { Search } from '@elastic/elasticsearch/api/requestParams'; -import type { SearchResponse } from 'elasticsearch'; import type { IUiSettingsClient, SharedGlobalConfig } from 'kibana/server'; import { UI_SETTINGS } from '../../../common'; @@ -50,14 +49,18 @@ export async function getDefaultSearchParams( * `TransportRequestPromise` is called, actually performing the cancellation. * @internal */ -export function shimAbortSignal>>( - promise: TransportRequestPromise, - signal?: AbortSignal -) { +export const shimAbortSignal = (promise: TransportRequestPromise, signal?: AbortSignal) => { + if (!signal) return promise; const abortHandler = () => { promise.abort(); - signal?.removeEventListener('abort', abortHandler); + cleanup(); }; - signal?.addEventListener('abort', abortHandler); - return promise.then().finally(() => signal?.removeEventListener('abort', abortHandler)); -} + const cleanup = () => signal.removeEventListener('abort', abortHandler); + if (signal.aborted) { + promise.abort(); + } else { + signal.addEventListener('abort', abortHandler); + promise.then(cleanup, cleanup); + } + return promise; +}; diff --git a/src/plugins/data/server/search/es_search/response_utils.test.ts b/src/plugins/data/server/search/es_search/response_utils.test.ts index 5f08c871002d74..f93625980a69c2 100644 --- a/src/plugins/data/server/search/es_search/response_utils.test.ts +++ b/src/plugins/data/server/search/es_search/response_utils.test.ts @@ -18,18 +18,19 @@ */ import { getTotalLoaded, toKibanaSearchResponse } from './response_utils'; +import { SearchResponse } from 'elasticsearch'; describe('response utils', () => { describe('getTotalLoaded', () => { it('returns the total/loaded, not including skipped', () => { - const result = getTotalLoaded({ + const result = getTotalLoaded(({ _shards: { successful: 10, failed: 5, skipped: 5, total: 100, }, - }); + } as unknown) as SearchResponse); expect(result).toEqual({ total: 100, @@ -40,14 +41,14 @@ describe('response utils', () => { describe('toKibanaSearchResponse', () => { it('returns rawResponse, isPartial, isRunning, total, and loaded', () => { - const result = toKibanaSearchResponse({ + const result = toKibanaSearchResponse(({ _shards: { successful: 10, failed: 5, skipped: 5, total: 100, }, - }); + } as unknown) as SearchResponse); expect(result).toEqual({ rawResponse: { diff --git a/src/plugins/data/server/search/routes/call_msearch.ts b/src/plugins/data/server/search/routes/call_msearch.ts index 603b3ed867b234..923369297889b4 100644 --- a/src/plugins/data/server/search/routes/call_msearch.ts +++ b/src/plugins/data/server/search/routes/call_msearch.ts @@ -24,9 +24,8 @@ import { SearchResponse } from 'elasticsearch'; import { IUiSettingsClient, IScopedClusterClient, SharedGlobalConfig } from 'src/core/server'; import type { MsearchRequestBody, MsearchResponse } from '../../../common/search/search_source'; -import { toSnakeCase, shimAbortSignal } from '../../../common/search/es_search'; import { shimHitsTotal } from './shim_hits_total'; -import { getShardTimeout, getDefaultSearchParams } from '..'; +import { getShardTimeout, getDefaultSearchParams, shimAbortSignal } from '..'; /** @internal */ export function convertRequestBody( @@ -71,7 +70,7 @@ export function getCallMsearch(dependencies: CallMsearchDependencies) { const timeout = getShardTimeout(config); // trackTotalHits is not supported by msearch - const { trackTotalHits, ...defaultParams } = await getDefaultSearchParams(uiSettings); + const { track_total_hits: _, ...defaultParams } = await getDefaultSearchParams(uiSettings); const body = convertRequestBody(params.body, timeout); @@ -81,7 +80,7 @@ export function getCallMsearch(dependencies: CallMsearchDependencies) { body, }, { - querystring: toSnakeCase(defaultParams), + querystring: defaultParams, } ), params.signal diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 8d1699c4ad5ed9..51cd632aa5a28c 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -7,6 +7,7 @@ import { $Values } from '@kbn/utility-types'; import { Adapters } from 'src/plugins/inspector/common'; import { ApiResponse } from '@elastic/elasticsearch'; +import { ApiResponse as ApiResponse_2 } from '@elastic/elasticsearch/lib/Transport'; import { Assign } from '@kbn/utility-types'; import { BehaviorSubject } from 'rxjs'; import { ConfigDeprecationProvider } from '@kbn/config'; @@ -32,6 +33,7 @@ import { IScopedClusterClient } from 'src/core/server'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; import { IUiSettingsClient } from 'src/core/server'; +import { IUiSettingsClient as IUiSettingsClient_3 } from 'kibana/server'; import { KibanaRequest } from 'src/core/server'; import { LegacyAPICaller } from 'src/core/server'; import { Logger } from 'src/core/server'; @@ -56,8 +58,9 @@ import { SavedObjectsClientContract as SavedObjectsClientContract_2 } from 'kiba import { Search } from '@elastic/elasticsearch/api/requestParams'; import { SearchResponse } from 'elasticsearch'; import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; -import { ShardsResponse } from 'elasticsearch'; +import { SharedGlobalConfig as SharedGlobalConfig_2 } from 'kibana/server'; import { ToastInputFields } from 'src/core/public/notifications'; +import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; import { Type } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema'; import { UiStatsMetricType } from '@kbn/analytics'; @@ -400,25 +403,15 @@ export function getCapabilitiesForRollupIndices(indices: { [key: string]: any; }; -// Warning: (ae-forgotten-export) The symbol "IUiSettingsClient" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "getDefaultSearchParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient_2): Promise<{ - maxConcurrentShardRequests: number | undefined; - ignoreUnavailable: boolean; - trackTotalHits: boolean; -}>; +export function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient_3): Promise>; -// Warning: (ae-forgotten-export) The symbol "SharedGlobalConfig" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "getShardTimeout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function getShardTimeout(config: SharedGlobalConfig): { - timeout: string; -} | { - timeout?: undefined; -}; +export function getShardTimeout(config: SharedGlobalConfig_2): Pick; // Warning: (ae-forgotten-export) The symbol "IIndexPattern" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "getTime" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -429,6 +422,12 @@ export function getTime(indexPattern: IIndexPattern | undefined, timeRange: Time fieldName?: string; }): import("../..").RangeFilter | undefined; +// @internal +export function getTotalLoaded(response: SearchResponse): { + total: number; + loaded: number; +}; + // Warning: (ae-missing-release-tag) "IAggConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -445,18 +444,6 @@ export type IAggConfigs = AggConfigs; // @public (undocumented) export type IAggType = AggType; -// Warning: (ae-missing-release-tag) "IEsRawSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface IEsRawSearchResponse extends SearchResponse { - // (undocumented) - id?: string; - // (undocumented) - is_partial?: boolean; - // (undocumented) - is_running?: boolean; -} - // Warning: (ae-forgotten-export) The symbol "IKibanaSearchRequest" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ISearchRequestParams" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IEsSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1032,24 +1019,6 @@ export interface RefreshInterval { // // @public (undocumented) export const search: { - esSearch: { - utils: { - doSearch: (searchMethod: () => Promise, abortSignal?: AbortSignal | undefined) => import("rxjs").Observable; - shimAbortSignal: >(promise: T, signal: AbortSignal | undefined) => T; - trackSearchStatus: = import("./search").IEsSearchResponse>>(logger: import("src/core/server").Logger, usage?: import("./search").SearchUsage | undefined) => import("rxjs").UnaryFunction, import("rxjs").Observable>; - includeTotalLoaded: () => import("rxjs").OperatorFunction>, { - total: number; - loaded: number; - id?: string | undefined; - isRunning?: boolean | undefined; - isPartial?: boolean | undefined; - rawResponse: import("elasticsearch").SearchResponse; - }>; - toKibanaSearchResponse: = import("../common").IEsRawSearchResponse, KibanaResponse_1 extends import("../common").IKibanaSearchResponse = import("../common").IKibanaSearchResponse>() => import("rxjs").OperatorFunction, KibanaResponse_1>; - getTotalLoaded: typeof getTotalLoaded; - toSnakeCase: typeof toSnakeCase; - }; - }; aggs: { CidrMask: typeof CidrMask; dateHistogramInterval: typeof dateHistogramInterval; @@ -1105,6 +1074,17 @@ export interface SearchUsage { trackSuccess(duration: number): Promise; } +// Warning: (ae-missing-release-tag) "searchUsageObserver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function searchUsageObserver(logger: Logger_2, usage?: SearchUsage): { + next(response: IEsSearchResponse): void; + error(): void; +}; + +// @internal +export function shimAbortSignal>>(promise: TransportRequestPromise, signal?: AbortSignal): Promise; + // @internal export function shimHitsTotal(response: SearchResponse): { hits: { @@ -1167,6 +1147,15 @@ export type TimeRange = { mode?: 'absolute' | 'relative'; }; +// @internal +export function toKibanaSearchResponse(rawResponse: SearchResponse): { + total: number; + loaded: number; + rawResponse: SearchResponse; + isPartial: boolean; + isRunning: boolean; +}; + // Warning: (ae-missing-release-tag) "UI_SETTINGS" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1236,21 +1225,19 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // 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:243:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:258:5 - (ae-forgotten-export) The symbol "getTotalLoaded" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:259:5 - (ae-forgotten-export) The symbol "toSnakeCase" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:264:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:273:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:274:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:275:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:279:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:280:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:284:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:287:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:237:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:237:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:237:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:237:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:239:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:240:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:249:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:250:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:251:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:255:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:260:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index_patterns/index_patterns_service.ts:58:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:88:66 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts // src/plugins/data/server/search/types.ts:104:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts diff --git a/x-pack/plugins/data_enhanced/common/search/poll_search.ts b/x-pack/plugins/data_enhanced/common/search/poll_search.ts index 76fa2479edba8c..c0e289c691cfda 100644 --- a/x-pack/plugins/data_enhanced/common/search/poll_search.ts +++ b/x-pack/plugins/data_enhanced/common/search/poll_search.ts @@ -4,20 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { from, NEVER, timer } from 'rxjs'; +import { from, NEVER, Observable, timer } from 'rxjs'; import { expand, finalize, switchMap, takeUntil, takeWhile, tap } from 'rxjs/operators'; -import type { - IEsSearchResponse, - IKibanaSearchResponse, -} from '../../../../../src/plugins/data/common'; +import type { IKibanaSearchResponse } from '../../../../../src/plugins/data/common'; import { isErrorResponse, isPartialResponse } from '../../../../../src/plugins/data/common'; import { AbortError, abortSignalToPromise } from '../../../../../src/plugins/kibana_utils/common'; import type { IAsyncSearchOptions } from './types'; -export function pollSearch( +export const pollSearch = ( search: () => Promise, { pollInterval = 1000, ...options }: IAsyncSearchOptions = {} -) { +): Observable => { const aborted = options?.abortSignal ? abortSignalToPromise(options?.abortSignal) : { promise: NEVER, cleanup: () => {} }; @@ -27,8 +24,8 @@ export function pollSearch { if (isErrorResponse(response)) throw new AbortError(); }), - takeWhile(isPartialResponse, true), - takeUntil(aborted.promise), + takeWhile(isPartialResponse, true), + takeUntil(from(aborted.promise)), finalize(aborted.cleanup) ); -} +}; 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 7c962959af4d43..9aa35b460b1e85 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -13,8 +13,7 @@ import { UI_SETTINGS, IKibanaSearchRequest, } from '../../../../../src/plugins/data/public'; -import { AbortError } from '../../../../../src/plugins/data/common'; - +import { AbortError } from '../../../../../src/plugins/kibana_utils/common'; import { ENHANCED_ES_SEARCH_STRATEGY, IAsyncSearchOptions, pollSearch } from '../../common'; export class EnhancedSearchInterceptor extends SearchInterceptor { diff --git a/x-pack/plugins/data_enhanced/server/plugin.ts b/x-pack/plugins/data_enhanced/server/plugin.ts index c5c300457c6314..ad21216bb7035f 100644 --- a/x-pack/plugins/data_enhanced/server/plugin.ts +++ b/x-pack/plugins/data_enhanced/server/plugin.ts @@ -49,7 +49,7 @@ export class EnhancedDataServerPlugin implements Plugin { await eqlSearch .search( { - options: { ...options, max_retries: 2, ignore: [300] }, + options: { ...options, maxRetries: 2, ignore: [300] }, params, }, {}, @@ -178,7 +178,7 @@ describe('EQL search strategy', () => { expect(requestOptions).toEqual( expect.objectContaining({ - max_retries: 2, + maxRetries: 2, ignore: [300], }) ); diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts index 428de1c44ce6ec..849f3e9d767928 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts @@ -12,19 +12,14 @@ import type { EqlSearchStrategyResponse, IAsyncSearchOptions, } from '../../common'; -import { - getDefaultSearchParams, - SearchUsage, - searchUsageObserver, - shimAbortSignal, -} from '../../../../../src/plugins/data/server'; +import { getDefaultSearchParams, shimAbortSignal } from '../../../../../src/plugins/data/server'; import { pollSearch } from '../../common'; import { getDefaultAsyncGetParams, getIgnoreThrottled } from './request_utils'; import { toEqlKibanaSearchResponse } from './response_utils'; +import { EqlSearchResponse } from './types'; export const eqlSearchStrategyProvider = ( - logger: Logger, - usage?: SearchUsage + logger: Logger ): ISearchStrategy => { return { cancel: async (id, options, { esClient }) => { @@ -39,7 +34,7 @@ export const eqlSearchStrategyProvider = ( const search = async () => { const params = id - ? { ...getDefaultAsyncGetParams(), id } + ? getDefaultAsyncGetParams() : { ...(await getIgnoreThrottled(uiSettingsClient)), ...(await getDefaultSearchParams(uiSettingsClient)), @@ -47,16 +42,16 @@ export const eqlSearchStrategyProvider = ( ...request.params, }; const promise = id - ? client.get(params, request.options) - : client.search(params, request.options); + ? client.get({ ...params, id }, request.options) + : client.search( + params as EqlSearchStrategyRequest['params'], + request.options + ); const response = await shimAbortSignal(promise, options.abortSignal); return toEqlKibanaSearchResponse(response); }; - return pollSearch(search, options).pipe( - tap((response) => (id = response.id)), - tap(searchUsageObserver(logger, usage)) - ); + return pollSearch(search, options).pipe(tap((response) => (id = response.id))); }, }; }; 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 3e30b58d479977..e1c7d7b5fc22e2 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 @@ -32,6 +32,7 @@ import { getIgnoreThrottled, } from './request_utils'; import { toAsyncKibanaSearchResponse } from './response_utils'; +import { AsyncSearchResponse } from './types'; export const enhancedEsSearchStrategyProvider = ( config$: Observable, @@ -47,9 +48,11 @@ export const enhancedEsSearchStrategyProvider = ( const search = async () => { const params = id - ? { ...getDefaultAsyncGetParams(), id } + ? getDefaultAsyncGetParams() : { ...(await getDefaultAsyncSubmitParams(uiSettingsClient, options)), ...request.params }; - const promise = id ? client.get(params) : client.submit(params); + const promise = id + ? client.get({ ...params, id }) + : client.submit(params); const { body } = await shimAbortSignal(promise, options.abortSignal); return toAsyncKibanaSearchResponse(body); }; diff --git a/x-pack/plugins/data_enhanced/server/search/request_utils.ts b/x-pack/plugins/data_enhanced/server/search/request_utils.ts index 06463714adccf1..f54ab2199c9054 100644 --- a/x-pack/plugins/data_enhanced/server/search/request_utils.ts +++ b/x-pack/plugins/data_enhanced/server/search/request_utils.ts @@ -17,7 +17,7 @@ import { getDefaultSearchParams } from '../../../../../src/plugins/data/server'; */ export async function getIgnoreThrottled( uiSettingsClient: IUiSettingsClient -): Pick { +): Promise> { const includeFrozen = await uiSettingsClient.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN); return { ignore_throttled: !includeFrozen }; } @@ -28,16 +28,18 @@ export async function getIgnoreThrottled( export async function getDefaultAsyncSubmitParams( uiSettingsClient: IUiSettingsClient, options: ISearchOptions -): Pick< - AsyncSearchSubmit, - | 'batched_reduce_size' - | 'keep_alive' - | 'wait_for_completion_timeout' - | 'ignore_throttled' - | 'max_concurrent_shard_requests' - | 'ignore_unavailable' - | 'track_total_hits' - | 'keep_on_completion' +): Promise< + Pick< + AsyncSearchSubmit, + | 'batched_reduce_size' + | 'keep_alive' + | 'wait_for_completion_timeout' + | 'ignore_throttled' + | 'max_concurrent_shard_requests' + | 'ignore_unavailable' + | 'track_total_hits' + | 'keep_on_completion' + > > { return { batched_reduce_size: 64, diff --git a/x-pack/plugins/data_enhanced/server/search/response_utils.ts b/x-pack/plugins/data_enhanced/server/search/response_utils.ts index 3ef8c3000bc800..716e7d72d80e70 100644 --- a/x-pack/plugins/data_enhanced/server/search/response_utils.ts +++ b/x-pack/plugins/data_enhanced/server/search/response_utils.ts @@ -7,6 +7,7 @@ import { ApiResponse } from '@elastic/elasticsearch'; import { getTotalLoaded } from '../../../../../src/plugins/data/server'; import { AsyncSearchResponse, EqlSearchResponse } from './types'; +import { EqlSearchStrategyResponse } from '../../common/search'; /** * Get the Kibana representation of an async search response (see `IKibanaSearchResponse`). @@ -25,7 +26,9 @@ export function toAsyncKibanaSearchResponse(response: AsyncSearchResponse) { * Get the Kibana representation of an EQL search response (see `IKibanaSearchResponse`). * (EQL does not provide _shard info, so total/loaded cannot be calculated.) */ -export function toEqlKibanaSearchResponse(response: ApiResponse) { +export function toEqlKibanaSearchResponse( + response: ApiResponse +): EqlSearchStrategyResponse { return { id: response.body.id, rawResponse: response, From 01e8a0684792c7255b4ccaad0fa09fe5e3c9c57c Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 22 Nov 2020 14:49:27 +0200 Subject: [PATCH 14/21] eql totals --- x-pack/plugins/data_enhanced/server/search/response_utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/data_enhanced/server/search/response_utils.ts b/x-pack/plugins/data_enhanced/server/search/response_utils.ts index 716e7d72d80e70..d43025c682bf05 100644 --- a/x-pack/plugins/data_enhanced/server/search/response_utils.ts +++ b/x-pack/plugins/data_enhanced/server/search/response_utils.ts @@ -34,5 +34,6 @@ export function toEqlKibanaSearchResponse( rawResponse: response, isPartial: response.body.is_partial, isRunning: response.body.is_running, + ...getTotalLoaded(response.body), }; } From 7874b1802a0753278dd8269e7588677d47c01a80 Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 22 Nov 2020 15:13:36 +0200 Subject: [PATCH 15/21] doc --- src/plugins/data/server/server.api.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 241f0d754de5c4..ba670d46428155 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -7,7 +7,6 @@ import { $Values } from '@kbn/utility-types'; import { Adapters } from 'src/plugins/inspector/common'; import { ApiResponse } from '@elastic/elasticsearch'; -import { ApiResponse as ApiResponse_2 } from '@elastic/elasticsearch/lib/Transport'; import { Assign } from '@kbn/utility-types'; import { BehaviorSubject } from 'rxjs'; import { ConfigDeprecationProvider } from '@kbn/config'; @@ -1083,7 +1082,7 @@ export function searchUsageObserver(logger: Logger_2, usage?: SearchUsage): { }; // @internal -export function shimAbortSignal>>(promise: TransportRequestPromise, signal?: AbortSignal): Promise; +export const shimAbortSignal: (promise: TransportRequestPromise, signal?: AbortSignal | undefined) => TransportRequestPromise; // @internal export function shimHitsTotal(response: SearchResponse): { From 07d6b72ed32fc402f69bafe08affe902983b8ce1 Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 22 Nov 2020 17:12:19 +0200 Subject: [PATCH 16/21] Revert "eql totals" This reverts commit 01e8a0684792c7255b4ccaad0fa09fe5e3c9c57c. --- x-pack/plugins/data_enhanced/server/search/response_utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/data_enhanced/server/search/response_utils.ts b/x-pack/plugins/data_enhanced/server/search/response_utils.ts index d43025c682bf05..716e7d72d80e70 100644 --- a/x-pack/plugins/data_enhanced/server/search/response_utils.ts +++ b/x-pack/plugins/data_enhanced/server/search/response_utils.ts @@ -34,6 +34,5 @@ export function toEqlKibanaSearchResponse( rawResponse: response, isPartial: response.body.is_partial, isRunning: response.body.is_running, - ...getTotalLoaded(response.body), }; } From b61ebc389fbcdcd618ee7bc3e1d973686c896f96 Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 22 Nov 2020 17:37:58 +0200 Subject: [PATCH 17/21] lint --- .../data/server/search/search_service.ts | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index cc25338fd8f5df..230c0340f47228 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -152,43 +152,43 @@ export class SearchService implements Plugin { ) ); - bfetch.addBatchProcessingRoute<{ request: IKibanaSearchRequest; options?: ISearchOptions }, any>( - '/internal/bsearch', - (request) => { - const search = this.asScopedProvider(this.coreStart!)(request); - - return { - onBatchItem: async ({ request: requestData, options }) => { - return search - .search(requestData, options) - .pipe( - first(), - map(response => { - return { - ...response, - ...{ - rawResponse: shimHitsTotal(response.rawResponse), - } - } - }), - catchError((err) => { - // eslint-disable-next-line no-throw-literal - throw { - statusCode: err.statusCode || 500, - body: { - message: err.message, - attributes: { - error: err.body?.error || err.message, - }, + bfetch.addBatchProcessingRoute< + { request: IKibanaSearchRequest; options?: ISearchOptions }, + any + >('/internal/bsearch', (request) => { + const search = this.asScopedProvider(this.coreStart!)(request); + + return { + onBatchItem: async ({ request: requestData, options }) => { + return search + .search(requestData, options) + .pipe( + first(), + map((response) => { + return { + ...response, + ...{ + rawResponse: shimHitsTotal(response.rawResponse), + }, + }; + }), + catchError((err) => { + // eslint-disable-next-line no-throw-literal + throw { + statusCode: err.statusCode || 500, + body: { + message: err.message, + attributes: { + error: err.body?.error || err.message, }, - }; - }) - ) - .toPromise(); - }, - }; - } - ); + }, + }; + }) + ) + .toPromise(); + }, + }; + }); core.savedObjects.registerType(searchTelemetry); if (usageCollection) { From 98c2c4756eed58a2821eb537cb56c2a25e8a0436 Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 22 Nov 2020 17:45:07 +0200 Subject: [PATCH 18/21] response type --- src/plugins/data/server/search/search_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 230c0340f47228..9332d4afe39736 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -154,7 +154,7 @@ export class SearchService implements Plugin { bfetch.addBatchProcessingRoute< { request: IKibanaSearchRequest; options?: ISearchOptions }, - any + IKibanaSearchResponse >('/internal/bsearch', (request) => { const search = this.asScopedProvider(this.coreStart!)(request); From 2ba358fee81d5379ac6cbbb6b02debf959b93038 Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 24 Nov 2020 13:21:16 +0200 Subject: [PATCH 19/21] shim inside strategies --- .../data/server/search/es_search/response_utils.ts | 3 ++- src/plugins/data/server/search/routes/search.ts | 8 +------- src/plugins/data/server/search/search_service.ts | 2 +- .../plugins/data_enhanced/server/search/response_utils.ts | 6 +++--- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/plugins/data/server/search/es_search/response_utils.ts b/src/plugins/data/server/search/es_search/response_utils.ts index 2f502f55057b8f..246422ef210577 100644 --- a/src/plugins/data/server/search/es_search/response_utils.ts +++ b/src/plugins/data/server/search/es_search/response_utils.ts @@ -18,6 +18,7 @@ */ import { SearchResponse } from 'elasticsearch'; +import { shimHitsTotal } from '..'; /** * Get the `total`/`loaded` for this response (see `IKibanaSearchResponse`). Note that `skipped` is @@ -36,7 +37,7 @@ export function getTotalLoaded(response: SearchResponse) { */ export function toKibanaSearchResponse(rawResponse: SearchResponse) { return { - rawResponse, + rawResponse: shimHitsTotal(rawResponse), isPartial: false, isRunning: false, ...getTotalLoaded(rawResponse), diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index ed519164c8e43a..1d8084a6853648 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -21,7 +21,6 @@ import { first } from 'rxjs/operators'; import { schema } from '@kbn/config-schema'; import type { IRouter } from 'src/core/server'; import { getRequestAbortedSignal } from '../../lib'; -import { shimHitsTotal } from './shim_hits_total'; export function registerSearchRoute(router: IRouter): void { router.post( @@ -66,12 +65,7 @@ export function registerSearchRoute(router: IRouter): void { .toPromise(); return res.ok({ - body: { - ...response, - ...{ - rawResponse: shimHitsTotal(response.rawResponse), - }, - }, + body: response, }); } catch (err) { return res.customError({ diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index e2516fda753699..b44980164d0976 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -43,7 +43,7 @@ import { AggsService } from './aggs'; import { FieldFormatsStart } from '../field_formats'; import { IndexPatternsServiceStart } from '../index_patterns'; -import { getCallMsearch, registerMsearchRoute, registerSearchRoute, shimHitsTotal } from './routes'; +import { getCallMsearch, registerMsearchRoute, registerSearchRoute } from './routes'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search'; import { DataPluginStart } from '../plugin'; import { UsageCollectionSetup } from '../../../usage_collection/server'; diff --git a/x-pack/plugins/data_enhanced/server/search/response_utils.ts b/x-pack/plugins/data_enhanced/server/search/response_utils.ts index 716e7d72d80e70..e24a581f34a434 100644 --- a/x-pack/plugins/data_enhanced/server/search/response_utils.ts +++ b/x-pack/plugins/data_enhanced/server/search/response_utils.ts @@ -5,7 +5,7 @@ */ import { ApiResponse } from '@elastic/elasticsearch'; -import { getTotalLoaded } from '../../../../../src/plugins/data/server'; +import { getTotalLoaded, shimHitsTotal } from '../../../../../src/plugins/data/server'; import { AsyncSearchResponse, EqlSearchResponse } from './types'; import { EqlSearchStrategyResponse } from '../../common/search'; @@ -15,7 +15,7 @@ import { EqlSearchStrategyResponse } from '../../common/search'; export function toAsyncKibanaSearchResponse(response: AsyncSearchResponse) { return { id: response.id, - rawResponse: response.response, + rawResponse: shimHitsTotal(response.response), isPartial: response.is_partial, isRunning: response.is_running, ...getTotalLoaded(response.response), @@ -31,7 +31,7 @@ export function toEqlKibanaSearchResponse( ): EqlSearchStrategyResponse { return { id: response.body.id, - rawResponse: response, + rawResponse: shimHitsTotal(response.response), isPartial: response.body.is_partial, isRunning: response.body.is_running, }; From 1ad2f0bb34a40b900baa062162b894f0fd5487b7 Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 24 Nov 2020 14:11:09 +0200 Subject: [PATCH 20/21] shim for security --- .../server/search/es_search/response_utils.ts | 3 +-- .../data/server/search/routes/search.ts | 8 ++++++- .../server/search/response_utils.ts | 6 ++--- .../security_solution/index.ts | 22 ++++++++++++++----- .../server/search_strategy/timeline/index.ts | 22 ++++++++++++++----- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/plugins/data/server/search/es_search/response_utils.ts b/src/plugins/data/server/search/es_search/response_utils.ts index 246422ef210577..2f502f55057b8f 100644 --- a/src/plugins/data/server/search/es_search/response_utils.ts +++ b/src/plugins/data/server/search/es_search/response_utils.ts @@ -18,7 +18,6 @@ */ import { SearchResponse } from 'elasticsearch'; -import { shimHitsTotal } from '..'; /** * Get the `total`/`loaded` for this response (see `IKibanaSearchResponse`). Note that `skipped` is @@ -37,7 +36,7 @@ export function getTotalLoaded(response: SearchResponse) { */ export function toKibanaSearchResponse(rawResponse: SearchResponse) { return { - rawResponse: shimHitsTotal(rawResponse), + rawResponse, isPartial: false, isRunning: false, ...getTotalLoaded(rawResponse), diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index 1d8084a6853648..ed519164c8e43a 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -21,6 +21,7 @@ import { first } from 'rxjs/operators'; import { schema } from '@kbn/config-schema'; import type { IRouter } from 'src/core/server'; import { getRequestAbortedSignal } from '../../lib'; +import { shimHitsTotal } from './shim_hits_total'; export function registerSearchRoute(router: IRouter): void { router.post( @@ -65,7 +66,12 @@ export function registerSearchRoute(router: IRouter): void { .toPromise(); return res.ok({ - body: response, + body: { + ...response, + ...{ + rawResponse: shimHitsTotal(response.rawResponse), + }, + }, }); } catch (err) { return res.customError({ diff --git a/x-pack/plugins/data_enhanced/server/search/response_utils.ts b/x-pack/plugins/data_enhanced/server/search/response_utils.ts index e24a581f34a434..716e7d72d80e70 100644 --- a/x-pack/plugins/data_enhanced/server/search/response_utils.ts +++ b/x-pack/plugins/data_enhanced/server/search/response_utils.ts @@ -5,7 +5,7 @@ */ import { ApiResponse } from '@elastic/elasticsearch'; -import { getTotalLoaded, shimHitsTotal } from '../../../../../src/plugins/data/server'; +import { getTotalLoaded } from '../../../../../src/plugins/data/server'; import { AsyncSearchResponse, EqlSearchResponse } from './types'; import { EqlSearchStrategyResponse } from '../../common/search'; @@ -15,7 +15,7 @@ import { EqlSearchStrategyResponse } from '../../common/search'; export function toAsyncKibanaSearchResponse(response: AsyncSearchResponse) { return { id: response.id, - rawResponse: shimHitsTotal(response.response), + rawResponse: response.response, isPartial: response.is_partial, isRunning: response.is_running, ...getTotalLoaded(response.response), @@ -31,7 +31,7 @@ export function toEqlKibanaSearchResponse( ): EqlSearchStrategyResponse { return { id: response.body.id, - rawResponse: shimHitsTotal(response.response), + rawResponse: response, isPartial: response.body.is_partial, isRunning: response.body.is_running, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts index baacad65e140f6..8b2cce01cf07a6 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts @@ -4,8 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mergeMap } from 'rxjs/operators'; -import { ISearchStrategy, PluginStart } from '../../../../../../src/plugins/data/server'; +import { map, mergeMap } from 'rxjs/operators'; +import { + ISearchStrategy, + PluginStart, + shimHitsTotal, +} from '../../../../../../src/plugins/data/server'; import { ENHANCED_ES_SEARCH_STRATEGY } from '../../../../data_enhanced/common'; import { FactoryQueryTypes, @@ -28,9 +32,17 @@ export const securitySolutionSearchStrategyProvider = = securitySolutionFactory[request.factoryQueryType]; const dsl = queryFactory.buildDsl(request); - return es - .search({ ...request, params: dsl }, options, deps) - .pipe(mergeMap((esSearchRes) => queryFactory.parse(request, esSearchRes))); + return es.search({ ...request, params: dsl }, options, deps).pipe( + map((response) => { + return { + ...response, + ...{ + rawResponse: shimHitsTotal(response.rawResponse), + }, + }; + }), + mergeMap((esSearchRes) => queryFactory.parse(request, esSearchRes)) + ); }, cancel: async (id, options, deps) => { if (es.cancel) { diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts index 29ad37e76264fe..5ad00a727c3b62 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts @@ -4,8 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mergeMap } from 'rxjs/operators'; -import { ISearchStrategy, PluginStart } from '../../../../../../src/plugins/data/server'; +import { map, mergeMap } from 'rxjs/operators'; +import { + ISearchStrategy, + PluginStart, + shimHitsTotal, +} from '../../../../../../src/plugins/data/server'; import { ENHANCED_ES_SEARCH_STRATEGY } from '../../../../data_enhanced/common'; import { TimelineFactoryQueryTypes, @@ -29,9 +33,17 @@ export const securitySolutionTimelineSearchStrategyProvider = queryFactory.parse(request, esSearchRes))); + return es.search({ ...request, params: dsl }, options, deps).pipe( + map((response) => { + return { + ...response, + ...{ + rawResponse: shimHitsTotal(response.rawResponse), + }, + }; + }), + mergeMap((esSearchRes) => queryFactory.parse(request, esSearchRes)) + ); }, cancel: async (id, options, deps) => { if (es.cancel) { From 168214b3780944ca48764f87d4e9e4f52248814b Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 24 Nov 2020 17:59:28 +0200 Subject: [PATCH 21/21] fix eql params --- .../data_enhanced/server/search/eql_search_strategy.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts index 849f3e9d767928..26325afc378f7c 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts @@ -33,11 +33,14 @@ export const eqlSearchStrategyProvider = ( const client = esClient.asCurrentUser.eql; const search = async () => { + const { track_total_hits: _, ...defaultParams } = await getDefaultSearchParams( + uiSettingsClient + ); const params = id ? getDefaultAsyncGetParams() : { ...(await getIgnoreThrottled(uiSettingsClient)), - ...(await getDefaultSearchParams(uiSettingsClient)), + ...defaultParams, ...getDefaultAsyncGetParams(), ...request.params, };