mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Search] Refactor service to register search strategies, not providers (#60342)
* Add async search strategy * Add async search * Fix async strategy and add tests * Move types to separate file * Revert changes to demo search * Update demo search strategy to use async * Add async es search strategy * Return response as rawResponse * Poll after initial request * Add cancellation to search strategies * Add tests * Simplify async search strategy * Move loadingCount to search strategy * Update abort controller library * Bootstrap * Abort when the request is aborted * Add utility and update value suggestions route * Fix bad merge conflict * Update tests * Move to data_enhanced plugin * Remove bad merge * Revert switching abort controller libraries * Revert package.json in lib * Move to previous abort controller * Add support for frozen indices * Fix test to use fake timers to run debounced handlers * Revert changes to example plugin * Fix loading bar not going away when cancelling * Call getSearchStrategy instead of passing directly * Add async demo search strategy * Fix error with setting state * Update how aborting works * Fix type checks * Add test for loading count * Attempt to fix broken example test * Revert changes to test * Fix test * Update name to camelCase * Fix failing test * Don't require data_enhanced in example plugin * Actually send DELETE request * Use waitForCompletion parameter * Use default search params * Add support for rollups * Only make changes needed for frozen indices/rollups * Only make changes needed for frozen indices/rollups * Add back in async functionality * Fix tests/types * Fix issue with sending empty body in GET * Don't include skipped in loaded/total * Don't wait before polling the next time * Add search interceptor for bulk managing searches * Simplify search logic * Fix merge error * Review feedback * UI to stop async searches * Add service for running beyond timeout * Refactor abort utils * Remove unneeded changes * Add tests * Refactor search service to register strategies directly * Remove accidental change * re-generate docs * Fix merge * types * doc * eslint * Fix async strategy jest test * type fix * Use getStartServices in search strategies * Code review + snapshot * eslint * Type script Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Liza K <liza.katz@elastic.co>
This commit is contained in:
parent
16fbbf4345
commit
8174b5ce29
27 changed files with 329 additions and 434 deletions
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchContext](./kibana-plugin-plugins-data-public.isearchcontext.md) > [core](./kibana-plugin-plugins-data-public.isearchcontext.core.md)
|
||||
|
||||
## ISearchContext.core property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
core: CoreStart;
|
||||
```
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchContext](./kibana-plugin-plugins-data-public.isearchcontext.md) > [getSearchStrategy](./kibana-plugin-plugins-data-public.isearchcontext.getsearchstrategy.md)
|
||||
|
||||
## ISearchContext.getSearchStrategy property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getSearchStrategy: <T extends TStrategyTypes>(name: T) => TSearchStrategyProvider<T>;
|
||||
```
|
|
@ -1,19 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchContext](./kibana-plugin-plugins-data-public.isearchcontext.md)
|
||||
|
||||
## ISearchContext interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface ISearchContext
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [core](./kibana-plugin-plugins-data-public.isearchcontext.core.md) | <code>CoreStart</code> | |
|
||||
| [getSearchStrategy](./kibana-plugin-plugins-data-public.isearchcontext.getsearchstrategy.md) | <code><T extends TStrategyTypes>(name: T) => TSearchStrategyProvider<T></code> | |
|
||||
|
|
@ -67,7 +67,6 @@
|
|||
| [IndexPatternTypeMeta](./kibana-plugin-plugins-data-public.indexpatterntypemeta.md) | |
|
||||
| [IRequestTypesMap](./kibana-plugin-plugins-data-public.irequesttypesmap.md) | |
|
||||
| [IResponseTypesMap](./kibana-plugin-plugins-data-public.iresponsetypesmap.md) | |
|
||||
| [ISearchContext](./kibana-plugin-plugins-data-public.isearchcontext.md) | |
|
||||
| [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) | |
|
||||
| [ISearchStrategy](./kibana-plugin-plugins-data-public.isearchstrategy.md) | Search strategy interface contains a search method that takes in a request and returns a promise that resolves to a response. |
|
||||
| [ISyncSearchRequest](./kibana-plugin-plugins-data-public.isyncsearchrequest.md) | |
|
||||
|
@ -157,5 +156,4 @@
|
|||
| [TabbedAggRow](./kibana-plugin-plugins-data-public.tabbedaggrow.md) | \* |
|
||||
| [TimefilterContract](./kibana-plugin-plugins-data-public.timefiltercontract.md) | |
|
||||
| [TimeHistoryContract](./kibana-plugin-plugins-data-public.timehistorycontract.md) | |
|
||||
| [TSearchStrategyProvider](./kibana-plugin-plugins-data-public.tsearchstrategyprovider.md) | Search strategy provider creates an instance of a search strategy with the request handler context bound to it. This way every search strategy can use whatever information they require from the request context. |
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [TSearchStrategyProvider](./kibana-plugin-plugins-data-public.tsearchstrategyprovider.md)
|
||||
|
||||
## TSearchStrategyProvider type
|
||||
|
||||
Search strategy provider creates an instance of a search strategy with the request handler context bound to it. This way every search strategy can use whatever information they require from the request context.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare type TSearchStrategyProvider<T extends TStrategyTypes> = (context: ISearchContext) => ISearchStrategy<T>;
|
||||
```
|
|
@ -17,53 +17,27 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import {
|
||||
ISearchContext,
|
||||
TSearchStrategyProvider,
|
||||
ISearchStrategy,
|
||||
} from '../../../src/plugins/data/public';
|
||||
|
||||
import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoResponse } from '../common';
|
||||
import { Observable, from } from 'rxjs';
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { flatMap } from 'rxjs/operators';
|
||||
import { ISearch } from '../../../src/plugins/data/public';
|
||||
import { ASYNC_SEARCH_STRATEGY } from '../../../x-pack/plugins/data_enhanced/public';
|
||||
import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoResponse } from '../common';
|
||||
import { DemoDataSearchStartDependencies } from './types';
|
||||
|
||||
/**
|
||||
* This demo search strategy provider simply provides a shortcut for calling the DEMO_ASYNC_SEARCH_STRATEGY
|
||||
* on the server side, without users having to pass it in explicitly, and it takes advantage of the
|
||||
* already registered ASYNC_SEARCH_STRATEGY that exists on the client.
|
||||
*
|
||||
* so instead of callers having to do:
|
||||
*
|
||||
* ```
|
||||
* search(
|
||||
* { ...request, serverStrategy: DEMO_ASYNC_SEARCH_STRATEGY },
|
||||
* options,
|
||||
* ASYNC_SEARCH_STRATEGY
|
||||
* ) as Observable<IDemoResponse>,
|
||||
*```
|
||||
|
||||
* They can instead just do
|
||||
*
|
||||
* ```
|
||||
* search(request, options, DEMO_ASYNC_SEARCH_STRATEGY);
|
||||
* ```
|
||||
*
|
||||
* and are ensured type safety in regard to the request and response objects.
|
||||
*
|
||||
* @param context - context supplied by other plugins.
|
||||
* @param search - a search function to access other strategies that have already been registered.
|
||||
*/
|
||||
export const asyncDemoClientSearchStrategyProvider: TSearchStrategyProvider<typeof ASYNC_DEMO_SEARCH_STRATEGY> = (
|
||||
context: ISearchContext
|
||||
): ISearchStrategy<typeof ASYNC_DEMO_SEARCH_STRATEGY> => {
|
||||
const asyncStrategyProvider = context.getSearchStrategy(ASYNC_SEARCH_STRATEGY);
|
||||
const { search } = asyncStrategyProvider(context);
|
||||
return {
|
||||
search: (request, options) => {
|
||||
return search(
|
||||
{ ...request, serverStrategy: ASYNC_DEMO_SEARCH_STRATEGY },
|
||||
options
|
||||
) as Observable<IAsyncDemoResponse>;
|
||||
},
|
||||
export function asyncDemoClientSearchStrategyProvider(core: CoreSetup) {
|
||||
const search: ISearch<typeof ASYNC_DEMO_SEARCH_STRATEGY> = (request, options) => {
|
||||
return from(core.getStartServices()).pipe(
|
||||
flatMap((startServices) => {
|
||||
const asyncStrategy = (startServices[1] as DemoDataSearchStartDependencies).data.search.getSearchStrategy(
|
||||
ASYNC_SEARCH_STRATEGY
|
||||
);
|
||||
return asyncStrategy.search(
|
||||
{ ...request, serverStrategy: ASYNC_DEMO_SEARCH_STRATEGY },
|
||||
options
|
||||
) as Observable<IAsyncDemoResponse>;
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
return { search };
|
||||
}
|
||||
|
|
|
@ -17,11 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { ISearchContext, SYNC_SEARCH_STRATEGY } from '../../../src/plugins/data/public';
|
||||
import { TSearchStrategyProvider, ISearchStrategy } from '../../../src/plugins/data/public';
|
||||
|
||||
import { Observable, from } from 'rxjs';
|
||||
import { flatMap } from 'rxjs/operators';
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { ISearch, SYNC_SEARCH_STRATEGY } from '../../../src/plugins/data/public';
|
||||
import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common';
|
||||
import { DemoDataSearchStartDependencies } from './types';
|
||||
|
||||
/**
|
||||
* This demo search strategy provider simply provides a shortcut for calling the DEMO_SEARCH_STRATEGY
|
||||
|
@ -31,7 +32,7 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common';
|
|||
* so instead of callers having to do:
|
||||
*
|
||||
* ```
|
||||
* context.search(
|
||||
* data.search.search(
|
||||
* { ...request, serverStrategy: DEMO_SEARCH_STRATEGY },
|
||||
* options,
|
||||
* SYNC_SEARCH_STRATEGY
|
||||
|
@ -41,24 +42,24 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common';
|
|||
* They can instead just do
|
||||
*
|
||||
* ```
|
||||
* context.search(request, options, DEMO_SEARCH_STRATEGY);
|
||||
* data.search.search(request, options, DEMO_SEARCH_STRATEGY);
|
||||
* ```
|
||||
*
|
||||
* and are ensured type safety in regard to the request and response objects.
|
||||
*
|
||||
* @param context - context supplied by other plugins.
|
||||
* @param search - a search function to access other strategies that have already been registered.
|
||||
*/
|
||||
export const demoClientSearchStrategyProvider: TSearchStrategyProvider<typeof DEMO_SEARCH_STRATEGY> = (
|
||||
context: ISearchContext
|
||||
): ISearchStrategy<typeof DEMO_SEARCH_STRATEGY> => {
|
||||
const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY);
|
||||
const { search } = syncStrategyProvider(context);
|
||||
return {
|
||||
search: (request, options) => {
|
||||
return search({ ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, options) as Observable<
|
||||
IDemoResponse
|
||||
>;
|
||||
},
|
||||
export function demoClientSearchStrategyProvider(core: CoreSetup) {
|
||||
const search: ISearch<typeof DEMO_SEARCH_STRATEGY> = (request, options) => {
|
||||
return from(core.getStartServices()).pipe(
|
||||
flatMap((startServices) => {
|
||||
const syncStrategy = (startServices[1] as DemoDataSearchStartDependencies).data.search.getSearchStrategy(
|
||||
SYNC_SEARCH_STRATEGY
|
||||
);
|
||||
return syncStrategy.search(
|
||||
{ ...request, serverStrategy: DEMO_SEARCH_STRATEGY },
|
||||
options
|
||||
) as Observable<IDemoResponse>;
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
return { search };
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { DataPublicPluginSetup } from '../../../src/plugins/data/public';
|
||||
import { Plugin, CoreSetup } from '../../../src/core/public';
|
||||
import {
|
||||
DEMO_SEARCH_STRATEGY,
|
||||
|
@ -29,10 +28,7 @@ import {
|
|||
} from '../common';
|
||||
import { demoClientSearchStrategyProvider } from './demo_search_strategy';
|
||||
import { asyncDemoClientSearchStrategyProvider } from './async_demo_search_strategy';
|
||||
|
||||
interface DemoDataSearchSetupDependencies {
|
||||
data: DataPublicPluginSetup;
|
||||
}
|
||||
import { DemoDataSearchSetupDependencies, DemoDataSearchStartDependencies } from './types';
|
||||
|
||||
/**
|
||||
* Add the typescript mappings for our search strategy to the request and
|
||||
|
@ -55,16 +51,13 @@ declare module '../../../src/plugins/data/public' {
|
|||
}
|
||||
}
|
||||
|
||||
export class DemoDataPlugin implements Plugin {
|
||||
public setup(core: CoreSetup, deps: DemoDataSearchSetupDependencies) {
|
||||
deps.data.search.registerSearchStrategyProvider(
|
||||
DEMO_SEARCH_STRATEGY,
|
||||
demoClientSearchStrategyProvider
|
||||
);
|
||||
deps.data.search.registerSearchStrategyProvider(
|
||||
ASYNC_DEMO_SEARCH_STRATEGY,
|
||||
asyncDemoClientSearchStrategyProvider
|
||||
);
|
||||
export class DemoDataPlugin
|
||||
implements Plugin<void, void, DemoDataSearchSetupDependencies, DemoDataSearchStartDependencies> {
|
||||
public setup(core: CoreSetup, { data }: DemoDataSearchSetupDependencies) {
|
||||
const demoClientSearchStrategy = demoClientSearchStrategyProvider(core);
|
||||
const asyncDemoClientSearchStrategy = asyncDemoClientSearchStrategyProvider(core);
|
||||
data.search.registerSearchStrategy(DEMO_SEARCH_STRATEGY, demoClientSearchStrategy);
|
||||
data.search.registerSearchStrategy(ASYNC_DEMO_SEARCH_STRATEGY, asyncDemoClientSearchStrategy);
|
||||
}
|
||||
|
||||
public start() {}
|
||||
|
|
28
examples/demo_search/public/types.ts
Normal file
28
examples/demo_search/public/types.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { DataPublicPluginStart, DataPublicPluginSetup } from '../../../src/plugins/data/public';
|
||||
|
||||
export interface DemoDataSearchSetupDependencies {
|
||||
data: DataPublicPluginSetup;
|
||||
}
|
||||
|
||||
export interface DemoDataSearchStartDependencies {
|
||||
data: DataPublicPluginStart;
|
||||
}
|
|
@ -341,8 +341,6 @@ export {
|
|||
SYNC_SEARCH_STRATEGY,
|
||||
getEsPreference,
|
||||
getSearchErrorType,
|
||||
ISearchContext,
|
||||
TSearchStrategyProvider,
|
||||
ISearchStrategy,
|
||||
ISearch,
|
||||
ISearchOptions,
|
||||
|
|
|
@ -21,11 +21,17 @@ import { Plugin, IndexPatternsContract } from '.';
|
|||
import { fieldFormatsServiceMock } from './field_formats/mocks';
|
||||
import { searchSetupMock, searchStartMock } from './search/mocks';
|
||||
import { queryServiceMock } from './query/mocks';
|
||||
import { AutocompleteStart, AutocompleteSetup } from './autocomplete';
|
||||
|
||||
export type Setup = jest.Mocked<ReturnType<Plugin['setup']>>;
|
||||
export type Start = jest.Mocked<ReturnType<Plugin['start']>>;
|
||||
|
||||
const autocompleteMock: any = {
|
||||
const automcompleteSetupMock: jest.Mocked<AutocompleteSetup> = {
|
||||
addQuerySuggestionProvider: jest.fn(),
|
||||
getQuerySuggestions: jest.fn(),
|
||||
};
|
||||
|
||||
const autocompleteStartMock: jest.Mocked<AutocompleteStart> = {
|
||||
getValueSuggestions: jest.fn(),
|
||||
getQuerySuggestions: jest.fn(),
|
||||
hasQuerySuggestions: jest.fn(),
|
||||
|
@ -34,7 +40,7 @@ const autocompleteMock: any = {
|
|||
const createSetupContract = (): Setup => {
|
||||
const querySetupMock = queryServiceMock.createSetupContract();
|
||||
return {
|
||||
autocomplete: autocompleteMock,
|
||||
autocomplete: automcompleteSetupMock,
|
||||
search: searchSetupMock,
|
||||
fieldFormats: fieldFormatsServiceMock.createSetupContract(),
|
||||
query: querySetupMock,
|
||||
|
@ -48,7 +54,7 @@ const createStartContract = (): Start => {
|
|||
createFiltersFromValueClickAction: jest.fn().mockResolvedValue(['yes']),
|
||||
createFiltersFromRangeSelectAction: jest.fn(),
|
||||
},
|
||||
autocomplete: autocompleteMock,
|
||||
autocomplete: autocompleteStartMock,
|
||||
search: searchStartMock,
|
||||
fieldFormats: fieldFormatsServiceMock.createStartContract(),
|
||||
query: queryStartMock,
|
||||
|
|
|
@ -1116,16 +1116,6 @@ export interface IResponseTypesMap {
|
|||
// @public (undocumented)
|
||||
export type ISearch<T extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY> = (request: IRequestTypesMap[T], options?: ISearchOptions) => Observable<IResponseTypesMap[T]>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ISearchContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface ISearchContext {
|
||||
// (undocumented)
|
||||
core: CoreStart;
|
||||
// (undocumented)
|
||||
getSearchStrategy: <T extends TStrategyTypes>(name: T) => TSearchStrategyProvider<T>;
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
@ -1788,11 +1778,6 @@ export interface TimeRange {
|
|||
to: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "TSearchStrategyProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export type TSearchStrategyProvider<T extends TStrategyTypes> = (context: ISearchContext) => ISearchStrategy<T>;
|
||||
|
||||
// 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)
|
||||
|
@ -1866,20 +1851,20 @@ 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:377:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:379:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:380:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:378:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:401: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:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts
|
||||
|
|
|
@ -17,40 +17,31 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreSetup } from '../../../../../core/public';
|
||||
import { coreMock } from '../../../../../core/public/mocks';
|
||||
import { esSearchStrategyProvider } from './es_search_strategy';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { ES_SEARCH_STRATEGY } from '../../../common/search/es_search';
|
||||
|
||||
describe('ES search strategy', () => {
|
||||
let mockCoreStart: MockedKeys<CoreStart>;
|
||||
const mockSearch = jest.fn();
|
||||
let mockCoreSetup: MockedKeys<CoreSetup>;
|
||||
const mockSearch = { search: jest.fn() };
|
||||
|
||||
beforeEach(() => {
|
||||
mockCoreStart = coreMock.createStart();
|
||||
mockSearch.mockClear();
|
||||
mockCoreSetup = coreMock.createSetup();
|
||||
mockSearch.search.mockClear();
|
||||
});
|
||||
|
||||
it('returns a strategy with `search` that calls the sync search `search`', () => {
|
||||
const request = { params: {} };
|
||||
const options = {};
|
||||
|
||||
const esSearch = esSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn().mockImplementation(() => {
|
||||
return () => {
|
||||
return {
|
||||
search: mockSearch,
|
||||
};
|
||||
};
|
||||
}),
|
||||
});
|
||||
const esSearch = esSearchStrategyProvider(mockCoreSetup, mockSearch);
|
||||
esSearch.search(request, options);
|
||||
|
||||
expect(mockSearch.mock.calls[0][0]).toEqual({
|
||||
expect(mockSearch.search.mock.calls[0][0]).toEqual({
|
||||
...request,
|
||||
serverStrategy: ES_SEARCH_STRATEGY,
|
||||
});
|
||||
expect(mockSearch.mock.calls[0][1]).toBe(options);
|
||||
expect(mockSearch.search.mock.calls[0][1]).toBe(options);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,25 +18,26 @@
|
|||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { CoreSetup } from '../../../../../core/public';
|
||||
import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../common/search';
|
||||
import { ISearch } from '../i_search';
|
||||
import { ISearchStrategy } from '../types';
|
||||
import { SYNC_SEARCH_STRATEGY } from '../sync_search_strategy';
|
||||
import { getEsPreference } from './get_es_preference';
|
||||
import { ISearchContext, TSearchStrategyProvider, ISearchStrategy } from '../types';
|
||||
|
||||
export const esSearchStrategyProvider: TSearchStrategyProvider<typeof ES_SEARCH_STRATEGY> = (
|
||||
context: ISearchContext
|
||||
): ISearchStrategy<typeof ES_SEARCH_STRATEGY> => {
|
||||
const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY);
|
||||
const { search } = syncStrategyProvider(context);
|
||||
return {
|
||||
search: (request, options) => {
|
||||
request.params = {
|
||||
preference: getEsPreference(context.core.uiSettings),
|
||||
...request.params,
|
||||
};
|
||||
return search({ ...request, serverStrategy: ES_SEARCH_STRATEGY }, options) as Observable<
|
||||
IEsSearchResponse
|
||||
>;
|
||||
},
|
||||
export function esSearchStrategyProvider(
|
||||
core: CoreSetup,
|
||||
syncStrategy: ISearchStrategy<typeof SYNC_SEARCH_STRATEGY>
|
||||
) {
|
||||
const search: ISearch<typeof ES_SEARCH_STRATEGY> = (request, options) => {
|
||||
request.params = {
|
||||
preference: getEsPreference(core.uiSettings),
|
||||
...request.params,
|
||||
};
|
||||
return syncStrategy.search(
|
||||
{ ...request, serverStrategy: ES_SEARCH_STRATEGY },
|
||||
options
|
||||
) as Observable<IEsSearchResponse>;
|
||||
};
|
||||
};
|
||||
return { search };
|
||||
}
|
||||
|
|
|
@ -21,13 +21,7 @@ export * from './aggs';
|
|||
export * from './expressions';
|
||||
export * from './tabify';
|
||||
|
||||
export {
|
||||
ISearchSetup,
|
||||
ISearchStart,
|
||||
ISearchContext,
|
||||
TSearchStrategyProvider,
|
||||
ISearchStrategy,
|
||||
} from './types';
|
||||
export { ISearchSetup, ISearchStart, ISearchStrategy } from './types';
|
||||
|
||||
export {
|
||||
ISearch,
|
||||
|
|
|
@ -18,18 +18,20 @@
|
|||
*/
|
||||
|
||||
import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks';
|
||||
import { ISearchStart } from './types';
|
||||
import { ISearchSetup, ISearchStart } from './types';
|
||||
import { searchSourceMock, createSearchSourceMock } from './search_source/mocks';
|
||||
|
||||
const searchSetupMock = {
|
||||
export * from './search_source/mocks';
|
||||
|
||||
const searchSetupMock: jest.Mocked<ISearchSetup> = {
|
||||
aggs: searchAggsSetupMock(),
|
||||
registerSearchStrategyContext: jest.fn(),
|
||||
registerSearchStrategyProvider: jest.fn(),
|
||||
registerSearchStrategy: jest.fn(),
|
||||
};
|
||||
|
||||
const searchStartMock: jest.Mocked<ISearchStart> = {
|
||||
aggs: searchAggsStartMock(),
|
||||
setInterceptor: jest.fn(),
|
||||
getSearchStrategy: jest.fn(),
|
||||
search: jest.fn(),
|
||||
searchSource: searchSourceMock,
|
||||
__LEGACY: {
|
||||
|
|
|
@ -38,7 +38,7 @@ describe('Search service', () => {
|
|||
packageInfo: { version: '8' },
|
||||
expressions: expressionsPluginMock.createSetupContract(),
|
||||
} as any);
|
||||
expect(setup).toHaveProperty('registerSearchStrategyProvider');
|
||||
expect(setup).toHaveProperty('registerSearchStrategy');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
*/
|
||||
|
||||
import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public';
|
||||
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
|
||||
import { ISearchSetup, ISearchStart, TSearchStrategiesMap, ISearchStrategy } from './types';
|
||||
import { ExpressionsSetup } from '../../../../plugins/expressions/public';
|
||||
|
||||
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
|
||||
import { createSearchSource, SearchSource, SearchSourceDependencies } from './search_source';
|
||||
import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types';
|
||||
import { TStrategyTypes } from './strategy_types';
|
||||
import { getEsClient, LegacyApiCaller } from './legacy';
|
||||
import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search';
|
||||
|
@ -54,11 +54,8 @@ interface SearchServiceStartDependencies {
|
|||
}
|
||||
|
||||
/**
|
||||
* The search plugin exposes two registration methods for other plugins:
|
||||
* - registerSearchStrategyProvider for plugins to add their own custom
|
||||
* search strategies
|
||||
* - registerSearchStrategyContext for plugins to expose information
|
||||
* and/or functionality for other search strategies to use
|
||||
* The search plugin exposes a method `registerSearchStrategy` for other plugins
|
||||
* to add their own custom search strategies.
|
||||
*
|
||||
* It also comes with two search strategy implementations - SYNC_SEARCH_STRATEGY and ES_SEARCH_STRATEGY.
|
||||
*/
|
||||
|
@ -73,17 +70,19 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
private readonly aggTypesRegistry = new AggTypesRegistry();
|
||||
private searchInterceptor!: SearchInterceptor;
|
||||
|
||||
private registerSearchStrategyProvider = <T extends TStrategyTypes>(
|
||||
private registerSearchStrategy = <T extends TStrategyTypes>(
|
||||
name: T,
|
||||
strategyProvider: TSearchStrategyProvider<T>
|
||||
strategy: ISearchStrategy<T>
|
||||
) => {
|
||||
this.searchStrategies[name] = strategyProvider;
|
||||
this.searchStrategies[name] = strategy;
|
||||
};
|
||||
|
||||
private getSearchStrategy = <T extends TStrategyTypes>(name: T): TSearchStrategyProvider<T> => {
|
||||
const strategyProvider = this.searchStrategies[name];
|
||||
if (!strategyProvider) throw new Error(`Search strategy ${name} not found`);
|
||||
return strategyProvider;
|
||||
private getSearchStrategy = <T extends TStrategyTypes>(name: T): ISearchStrategy<T> => {
|
||||
const strategy = this.searchStrategies[name];
|
||||
if (!strategy) {
|
||||
throw new Error(`Search strategy ${name} not found`);
|
||||
}
|
||||
return strategy;
|
||||
};
|
||||
|
||||
public setup(
|
||||
|
@ -91,8 +90,11 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
{ expressions, packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies
|
||||
): ISearchSetup {
|
||||
this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo);
|
||||
this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider);
|
||||
this.registerSearchStrategyProvider(ES_SEARCH_STRATEGY, esSearchStrategyProvider);
|
||||
|
||||
const syncSearchStrategy = syncSearchStrategyProvider(core);
|
||||
const esSearchStrategy = esSearchStrategyProvider(core, syncSearchStrategy);
|
||||
this.registerSearchStrategy(SYNC_SEARCH_STRATEGY, syncSearchStrategy);
|
||||
this.registerSearchStrategy(ES_SEARCH_STRATEGY, esSearchStrategy);
|
||||
|
||||
const aggTypesSetup = this.aggTypesRegistry.setup();
|
||||
|
||||
|
@ -114,7 +116,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings),
|
||||
types: aggTypesSetup,
|
||||
},
|
||||
registerSearchStrategyProvider: this.registerSearchStrategyProvider,
|
||||
registerSearchStrategy: this.registerSearchStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -134,12 +136,10 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
const aggTypesStart = this.aggTypesRegistry.start();
|
||||
|
||||
const search: ISearchGeneric = (request, options, strategyName) => {
|
||||
const strategyProvider = this.getSearchStrategy(strategyName || DEFAULT_SEARCH_STRATEGY);
|
||||
const searchStrategy = strategyProvider({
|
||||
core,
|
||||
getSearchStrategy: this.getSearchStrategy,
|
||||
});
|
||||
return this.searchInterceptor.search(searchStrategy.search as any, request, options);
|
||||
const { search: defaultSearch } = this.getSearchStrategy(
|
||||
strategyName || DEFAULT_SEARCH_STRATEGY
|
||||
);
|
||||
return this.searchInterceptor.search(defaultSearch as any, request, options);
|
||||
};
|
||||
|
||||
const legacySearch = {
|
||||
|
@ -164,6 +164,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
},
|
||||
types: aggTypesStart,
|
||||
},
|
||||
getSearchStrategy: this.getSearchStrategy,
|
||||
search,
|
||||
searchSource: {
|
||||
create: createSearchSource(dependencies.indexPatterns, searchSourceDependencies),
|
||||
|
|
|
@ -19,26 +19,23 @@
|
|||
|
||||
import { coreMock } from '../../../../core/public/mocks';
|
||||
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
|
||||
describe('Sync search strategy', () => {
|
||||
let mockCoreStart: MockedKeys<CoreStart>;
|
||||
let mockCoreSetup: MockedKeys<CoreSetup>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCoreStart = coreMock.createStart();
|
||||
mockCoreSetup = coreMock.createSetup();
|
||||
});
|
||||
|
||||
it('returns a strategy with `search` that calls the backend API', () => {
|
||||
mockCoreStart.http.fetch.mockImplementationOnce(() => Promise.resolve());
|
||||
mockCoreSetup.http.fetch.mockImplementationOnce(() => Promise.resolve());
|
||||
|
||||
const syncSearch = syncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn(),
|
||||
});
|
||||
const syncSearch = syncSearchStrategyProvider(mockCoreSetup);
|
||||
const request = { serverStrategy: SYNC_SEARCH_STRATEGY };
|
||||
syncSearch.search(request, {});
|
||||
|
||||
expect(mockCoreStart.http.fetch.mock.calls[0][0]).toEqual({
|
||||
expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toEqual({
|
||||
path: `/internal/search/${SYNC_SEARCH_STRATEGY}`,
|
||||
body: JSON.stringify({
|
||||
serverStrategy: 'SYNC_SEARCH_STRATEGY',
|
||||
|
@ -52,15 +49,12 @@ describe('Sync search strategy', () => {
|
|||
const expectedLoadingCountValues = [0, 1, 0];
|
||||
const receivedLoadingCountValues: number[] = [];
|
||||
|
||||
mockCoreStart.http.fetch.mockResolvedValueOnce('response');
|
||||
mockCoreSetup.http.fetch.mockResolvedValueOnce('response');
|
||||
|
||||
const syncSearch = syncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn(),
|
||||
});
|
||||
const syncSearch = syncSearchStrategyProvider(mockCoreSetup);
|
||||
const request = { serverStrategy: SYNC_SEARCH_STRATEGY };
|
||||
|
||||
const loadingCount$ = mockCoreStart.http.addLoadingCountSource.mock.calls[0][0];
|
||||
const loadingCount$ = mockCoreSetup.http.addLoadingCountSource.mock.calls[0][0];
|
||||
loadingCount$.subscribe((value) => receivedLoadingCountValues.push(value));
|
||||
|
||||
await syncSearch.search(request, {}).toPromise();
|
||||
|
@ -73,15 +67,12 @@ describe('Sync search strategy', () => {
|
|||
const expectedLoadingCountValues = [0, 1, 0];
|
||||
const receivedLoadingCountValues: number[] = [];
|
||||
|
||||
mockCoreStart.http.fetch.mockRejectedValueOnce('error');
|
||||
mockCoreSetup.http.fetch.mockRejectedValueOnce('error');
|
||||
|
||||
const syncSearch = syncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn(),
|
||||
});
|
||||
const syncSearch = syncSearchStrategyProvider(mockCoreSetup);
|
||||
const request = { serverStrategy: SYNC_SEARCH_STRATEGY };
|
||||
|
||||
const loadingCount$ = mockCoreStart.http.addLoadingCountSource.mock.calls[0][0];
|
||||
const loadingCount$ = mockCoreSetup.http.addLoadingCountSource.mock.calls[0][0];
|
||||
loadingCount$.subscribe((value) => receivedLoadingCountValues.push(value));
|
||||
|
||||
try {
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
import { BehaviorSubject, from } from 'rxjs';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
import { CoreSetup } from '../../../../core/public';
|
||||
import { IKibanaSearchRequest } from '../../common/search';
|
||||
import { ISearch, ISearchOptions } from './i_search';
|
||||
import { TSearchStrategyProvider, ISearchStrategy, ISearchContext } from './types';
|
||||
import { ISearch } from './i_search';
|
||||
|
||||
export const SYNC_SEARCH_STRATEGY = 'SYNC_SEARCH_STRATEGY';
|
||||
|
||||
|
@ -29,27 +29,22 @@ export interface ISyncSearchRequest extends IKibanaSearchRequest {
|
|||
serverStrategy: string;
|
||||
}
|
||||
|
||||
export const syncSearchStrategyProvider: TSearchStrategyProvider<typeof SYNC_SEARCH_STRATEGY> = (
|
||||
context: ISearchContext
|
||||
): ISearchStrategy<typeof SYNC_SEARCH_STRATEGY> => {
|
||||
export function syncSearchStrategyProvider(core: CoreSetup) {
|
||||
const loadingCount$ = new BehaviorSubject(0);
|
||||
context.core.http.addLoadingCountSource(loadingCount$);
|
||||
core.http.addLoadingCountSource(loadingCount$);
|
||||
|
||||
const search: ISearch<typeof SYNC_SEARCH_STRATEGY> = (
|
||||
request: ISyncSearchRequest,
|
||||
options: ISearchOptions = {}
|
||||
) => {
|
||||
const search: ISearch<typeof SYNC_SEARCH_STRATEGY> = (request, options) => {
|
||||
loadingCount$.next(loadingCount$.getValue() + 1);
|
||||
|
||||
return from(
|
||||
context.core.http.fetch({
|
||||
core.http.fetch({
|
||||
path: `/internal/search/${request.serverStrategy}`,
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
signal: options.signal,
|
||||
signal: options?.signal,
|
||||
})
|
||||
).pipe(finalize(() => loadingCount$.next(loadingCount$.getValue() - 1)));
|
||||
};
|
||||
|
||||
return { search };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { SearchAggsSetup, SearchAggsStart } from './aggs';
|
||||
import { ISearch, ISearchGeneric } from './i_search';
|
||||
import { TStrategyTypes } from './strategy_types';
|
||||
|
@ -25,11 +24,6 @@ import { LegacyApiCaller } from './legacy/es_client';
|
|||
import { SearchInterceptor } from './search_interceptor';
|
||||
import { ISearchSource, SearchSourceFields } from './search_source';
|
||||
|
||||
export interface ISearchContext {
|
||||
core: CoreStart;
|
||||
getSearchStrategy: <T extends TStrategyTypes>(name: T) => TSearchStrategyProvider<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search strategy interface contains a search method that takes in
|
||||
* a request and returns a promise that resolves to a response.
|
||||
|
@ -39,27 +33,23 @@ export interface ISearchStrategy<T extends TStrategyTypes> {
|
|||
}
|
||||
|
||||
export type TSearchStrategiesMap = {
|
||||
[K in TStrategyTypes]?: TSearchStrategyProvider<any>;
|
||||
[K in TStrategyTypes]?: ISearchStrategy<any>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search strategy provider creates an instance of a search strategy with the request
|
||||
* handler context bound to it. This way every search strategy can use
|
||||
* whatever information they require from the request context.
|
||||
*/
|
||||
export type TSearchStrategyProvider<T extends TStrategyTypes> = (
|
||||
context: ISearchContext
|
||||
) => ISearchStrategy<T>;
|
||||
|
||||
/**
|
||||
* Extension point exposed for other plugins to register their own search
|
||||
* strategies.
|
||||
*/
|
||||
export type TRegisterSearchStrategyProvider = <T extends TStrategyTypes>(
|
||||
export type TRegisterSearchStrategy = <T extends TStrategyTypes>(
|
||||
name: T,
|
||||
searchStrategyProvider: TSearchStrategyProvider<T>
|
||||
searchStrategy: ISearchStrategy<T>
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Used if a plugin needs access to an already registered search strategy.
|
||||
*/
|
||||
export type TGetSearchStrategy = <T extends TStrategyTypes>(name: T) => ISearchStrategy<T>;
|
||||
|
||||
export interface ISearchStartLegacy {
|
||||
esClient: LegacyApiCaller;
|
||||
}
|
||||
|
@ -74,12 +64,18 @@ export interface ISearchSetup {
|
|||
* Extension point exposed for other plugins to register their own search
|
||||
* strategies.
|
||||
*/
|
||||
registerSearchStrategyProvider: TRegisterSearchStrategyProvider;
|
||||
registerSearchStrategy: TRegisterSearchStrategy;
|
||||
}
|
||||
|
||||
export interface ISearchStart {
|
||||
aggs: SearchAggsStart;
|
||||
setInterceptor: (searchInterceptor: SearchInterceptor) => void;
|
||||
|
||||
/**
|
||||
* Used if a plugin needs access to an already registered search strategy.
|
||||
*/
|
||||
getSearchStrategy: TGetSearchStrategy;
|
||||
|
||||
search: ISearchGeneric;
|
||||
searchSource: {
|
||||
create: (fields?: SearchSourceFields) => Promise<ISearchSource>;
|
||||
|
|
|
@ -277,6 +277,7 @@ exports[`SavedObjectsTable import should show the flyout 1`] = `
|
|||
"getMetrics": [Function],
|
||||
},
|
||||
},
|
||||
"getSearchStrategy": [MockFunction],
|
||||
"search": [MockFunction],
|
||||
"searchSource": Object {
|
||||
"create": [MockFunction],
|
||||
|
|
|
@ -29,19 +29,20 @@ export interface DataEnhancedStartDependencies {
|
|||
export type DataEnhancedSetup = ReturnType<DataEnhancedPlugin['setup']>;
|
||||
export type DataEnhancedStart = ReturnType<DataEnhancedPlugin['start']>;
|
||||
|
||||
export class DataEnhancedPlugin implements Plugin {
|
||||
constructor() {}
|
||||
|
||||
public setup(core: CoreSetup, { data }: DataEnhancedSetupDependencies) {
|
||||
export class DataEnhancedPlugin
|
||||
implements Plugin<void, void, DataEnhancedSetupDependencies, DataEnhancedStartDependencies> {
|
||||
public setup(
|
||||
core: CoreSetup<DataEnhancedStartDependencies>,
|
||||
{ data }: DataEnhancedSetupDependencies
|
||||
) {
|
||||
data.autocomplete.addQuerySuggestionProvider(
|
||||
KUERY_LANGUAGE_NAME,
|
||||
setupKqlQuerySuggestionProvider(core)
|
||||
);
|
||||
data.search.registerSearchStrategyProvider(ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider);
|
||||
data.search.registerSearchStrategyProvider(
|
||||
ES_SEARCH_STRATEGY,
|
||||
enhancedEsSearchStrategyProvider
|
||||
);
|
||||
const asyncSearchStrategy = asyncSearchStrategyProvider(core);
|
||||
const esSearchStrategy = enhancedEsSearchStrategyProvider(core, asyncSearchStrategy);
|
||||
data.search.registerSearchStrategy(ASYNC_SEARCH_STRATEGY, asyncSearchStrategy);
|
||||
data.search.registerSearchStrategy(ES_SEARCH_STRATEGY, esSearchStrategy);
|
||||
}
|
||||
|
||||
public start(core: CoreStart, plugins: DataEnhancedStartDependencies) {
|
||||
|
|
|
@ -6,35 +6,37 @@
|
|||
|
||||
import { of } from 'rxjs';
|
||||
import { AbortController } from 'abort-controller';
|
||||
import { CoreSetup } from '../../../../../src/core/public';
|
||||
import { coreMock } from '../../../../../src/core/public/mocks';
|
||||
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
|
||||
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
|
||||
import { asyncSearchStrategyProvider } from './async_search_strategy';
|
||||
import { IAsyncSearchOptions } from './types';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { IAsyncSearchOptions } from '.';
|
||||
import { DataEnhancedStartDependencies } from '../plugin';
|
||||
|
||||
describe('Async search strategy', () => {
|
||||
let mockCoreStart: MockedKeys<CoreStart>;
|
||||
let mockCoreSetup: jest.Mocked<CoreSetup<DataEnhancedStartDependencies>>;
|
||||
let mockDataStart: jest.Mocked<DataPublicPluginStart>;
|
||||
const mockSearch = jest.fn();
|
||||
const mockRequest = { params: {}, serverStrategy: 'foo' };
|
||||
const mockOptions: IAsyncSearchOptions = { pollInterval: 0 };
|
||||
|
||||
beforeEach(() => {
|
||||
mockCoreStart = coreMock.createStart();
|
||||
mockCoreSetup = coreMock.createSetup();
|
||||
mockDataStart = dataPluginMock.createStartContract();
|
||||
(mockDataStart.search.getSearchStrategy as jest.Mock).mockReturnValue({ search: mockSearch });
|
||||
mockCoreSetup.getStartServices.mockResolvedValue([
|
||||
undefined as any,
|
||||
{ data: mockDataStart },
|
||||
undefined,
|
||||
]);
|
||||
mockSearch.mockReset();
|
||||
});
|
||||
|
||||
it('only sends one request if the first response is complete', async () => {
|
||||
mockSearch.mockReturnValueOnce(of({ id: 1, total: 1, loaded: 1 }));
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn().mockImplementation(() => {
|
||||
return () => {
|
||||
return {
|
||||
search: mockSearch,
|
||||
};
|
||||
};
|
||||
}),
|
||||
});
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
|
||||
await asyncSearch.search(mockRequest, mockOptions).toPromise();
|
||||
|
||||
|
@ -51,17 +53,7 @@ describe('Async search strategy', () => {
|
|||
of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false })
|
||||
);
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn().mockImplementation(() => {
|
||||
return () => {
|
||||
return {
|
||||
search: mockSearch,
|
||||
};
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
expect(mockSearch).toBeCalledTimes(0);
|
||||
|
||||
await asyncSearch.search(mockRequest, mockOptions).toPromise();
|
||||
|
@ -75,17 +67,7 @@ describe('Async search strategy', () => {
|
|||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: true }))
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: true }));
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn().mockImplementation(() => {
|
||||
return () => {
|
||||
return {
|
||||
search: mockSearch,
|
||||
};
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
expect(mockSearch).toBeCalledTimes(0);
|
||||
|
||||
await asyncSearch
|
||||
|
@ -104,16 +86,7 @@ describe('Async search strategy', () => {
|
|||
of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false })
|
||||
);
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn().mockImplementation(() => {
|
||||
return () => {
|
||||
return {
|
||||
search: mockSearch,
|
||||
};
|
||||
};
|
||||
}),
|
||||
});
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
|
||||
expect(mockSearch).toBeCalledTimes(0);
|
||||
|
||||
|
@ -131,16 +104,7 @@ describe('Async search strategy', () => {
|
|||
of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false })
|
||||
);
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn().mockImplementation(() => {
|
||||
return () => {
|
||||
return {
|
||||
search: mockSearch,
|
||||
};
|
||||
};
|
||||
}),
|
||||
});
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
|
||||
expect(mockSearch).toBeCalledTimes(0);
|
||||
|
||||
|
@ -157,16 +121,7 @@ describe('Async search strategy', () => {
|
|||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 }))
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 }));
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider({
|
||||
core: mockCoreStart,
|
||||
getSearchStrategy: jest.fn().mockImplementation(() => {
|
||||
return () => {
|
||||
return {
|
||||
search: mockSearch,
|
||||
};
|
||||
};
|
||||
}),
|
||||
});
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
const abortController = new AbortController();
|
||||
const options = { ...mockOptions, signal: abortController.signal };
|
||||
|
||||
|
@ -178,7 +133,7 @@ describe('Async search strategy', () => {
|
|||
} catch (e) {
|
||||
expect(e.name).toBe('AbortError');
|
||||
expect(mockSearch).toBeCalledTimes(1);
|
||||
expect(mockCoreStart.http.delete).toBeCalled();
|
||||
expect(mockCoreSetup.http.delete).toBeCalled();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,17 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EMPTY, fromEvent, NEVER, Observable, throwError, timer } from 'rxjs';
|
||||
import { mergeMap, expand, takeUntil } from 'rxjs/operators';
|
||||
import { EMPTY, fromEvent, NEVER, throwError, timer, Observable, from } from 'rxjs';
|
||||
import { mergeMap, expand, takeUntil, share, flatMap } from 'rxjs/operators';
|
||||
import { CoreSetup } from '../../../../../src/core/public';
|
||||
import { AbortError } from '../../../../../src/plugins/data/common';
|
||||
import {
|
||||
IKibanaSearchResponse,
|
||||
ISearchContext,
|
||||
ISearch,
|
||||
ISearchStrategy,
|
||||
ISyncSearchRequest,
|
||||
SYNC_SEARCH_STRATEGY,
|
||||
TSearchStrategyProvider,
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
import { IAsyncSearchRequest, IAsyncSearchOptions, IAsyncSearchResponse } from './types';
|
||||
import { IAsyncSearchOptions, IAsyncSearchResponse, IAsyncSearchRequest } from './types';
|
||||
import { DataEnhancedStartDependencies } from '../plugin';
|
||||
|
||||
export const ASYNC_SEARCH_STRATEGY = 'ASYNC_SEARCH_STRATEGY';
|
||||
|
||||
|
@ -24,55 +25,59 @@ declare module '../../../../../src/plugins/data/public' {
|
|||
}
|
||||
}
|
||||
|
||||
export const asyncSearchStrategyProvider: TSearchStrategyProvider<typeof ASYNC_SEARCH_STRATEGY> = (
|
||||
context: ISearchContext
|
||||
): ISearchStrategy<typeof ASYNC_SEARCH_STRATEGY> => {
|
||||
const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY);
|
||||
const { search } = syncStrategyProvider(context);
|
||||
return {
|
||||
search: (
|
||||
request: IAsyncSearchRequest,
|
||||
{ pollInterval = 1000, ...options }: IAsyncSearchOptions = {}
|
||||
): Observable<IKibanaSearchResponse> => {
|
||||
const { serverStrategy } = request;
|
||||
let id: string | undefined = request.id;
|
||||
export function asyncSearchStrategyProvider(
|
||||
core: CoreSetup<DataEnhancedStartDependencies>
|
||||
): ISearchStrategy<typeof ASYNC_SEARCH_STRATEGY> {
|
||||
const startServices$ = from(core.getStartServices()).pipe(share());
|
||||
|
||||
const aborted$ = options.signal
|
||||
? fromEvent(options.signal, 'abort').pipe(
|
||||
mergeMap(() => {
|
||||
// If we haven't received the response to the initial request, including the ID, then
|
||||
// we don't need to send a follow-up request to delete this search. Otherwise, we
|
||||
// send the follow-up request to delete this search, then throw an abort error.
|
||||
if (id !== undefined) {
|
||||
context.core.http.delete(`/internal/search/${request.serverStrategy}/${id}`);
|
||||
}
|
||||
return throwError(new AbortError());
|
||||
})
|
||||
)
|
||||
: NEVER;
|
||||
const search: ISearch<typeof ASYNC_SEARCH_STRATEGY> = (
|
||||
request: ISyncSearchRequest,
|
||||
{ pollInterval = 1000, ...options }: IAsyncSearchOptions = {}
|
||||
) => {
|
||||
const { serverStrategy } = request;
|
||||
let { id } = request;
|
||||
|
||||
return search(request, options).pipe(
|
||||
expand((response: IAsyncSearchResponse) => {
|
||||
// If the response indicates of an error, stop polling and complete the observable
|
||||
if (!response || (response.is_partial && !response.is_running)) {
|
||||
const aborted$ = options.signal
|
||||
? fromEvent(options.signal, 'abort').pipe(
|
||||
mergeMap(() => {
|
||||
// If we haven't received the response to the initial request, including the ID, then
|
||||
// we don't need to send a follow-up request to delete this search. Otherwise, we
|
||||
// send the follow-up request to delete this search, then throw an abort error.
|
||||
if (id !== undefined) {
|
||||
core.http.delete(`/internal/search/${request.serverStrategy}/${id}`);
|
||||
}
|
||||
return throwError(new AbortError());
|
||||
}
|
||||
})
|
||||
)
|
||||
: NEVER;
|
||||
|
||||
// If the response indicates it is complete, stop polling and complete the observable
|
||||
if (!response.is_running) return EMPTY;
|
||||
return startServices$.pipe(
|
||||
flatMap((startServices) => {
|
||||
const syncSearch = startServices[1].data.search.getSearchStrategy(SYNC_SEARCH_STRATEGY);
|
||||
return (syncSearch.search(request, options) as Observable<IAsyncSearchResponse>).pipe(
|
||||
expand((response) => {
|
||||
// If the response indicates of an error, stop polling and complete the observable
|
||||
if (!response || (response.is_partial && !response.is_running)) {
|
||||
return throwError(new AbortError());
|
||||
}
|
||||
|
||||
id = response.id;
|
||||
// If the response indicates it is complete, stop polling and complete the observable
|
||||
if (!response.is_running) return EMPTY;
|
||||
|
||||
// Delay by the given poll interval
|
||||
return timer(pollInterval).pipe(
|
||||
// Send future requests using just the ID from the response
|
||||
mergeMap(() => {
|
||||
return search({ id, serverStrategy }, options);
|
||||
})
|
||||
);
|
||||
}),
|
||||
takeUntil(aborted$)
|
||||
);
|
||||
},
|
||||
id = response.id;
|
||||
|
||||
// Delay by the given poll interval
|
||||
return timer(pollInterval).pipe(
|
||||
// Send future requests using just the ID from the response
|
||||
mergeMap(() => {
|
||||
return search({ id, serverStrategy }, options);
|
||||
})
|
||||
);
|
||||
}),
|
||||
takeUntil(aborted$)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
return { search };
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 { CoreSetup } from '../../../../../src/core/public';
|
||||
import { coreMock } from '../../../../../src/core/public/mocks';
|
||||
import { ES_SEARCH_STRATEGY } from '../../../../../src/plugins/data/common';
|
||||
import { enhancedEsSearchStrategyProvider } from './es_search_strategy';
|
||||
import { IAsyncSearchOptions } from '.';
|
||||
|
||||
describe('Enhanced ES search strategy', () => {
|
||||
let mockCoreSetup: jest.Mocked<CoreSetup>;
|
||||
const mockSearch = { search: jest.fn() };
|
||||
|
||||
beforeEach(() => {
|
||||
mockCoreSetup = coreMock.createSetup();
|
||||
mockSearch.search.mockClear();
|
||||
});
|
||||
|
||||
it('returns a strategy with `search` that calls the async search `search`', () => {
|
||||
const request = { params: {} };
|
||||
const options: IAsyncSearchOptions = { pollInterval: 0 };
|
||||
|
||||
const esSearch = enhancedEsSearchStrategyProvider(mockCoreSetup, mockSearch);
|
||||
esSearch.search(request, options);
|
||||
|
||||
expect(mockSearch.search.mock.calls[0][0]).toEqual({
|
||||
...request,
|
||||
serverStrategy: ES_SEARCH_STRATEGY,
|
||||
});
|
||||
expect(mockSearch.search.mock.calls[0][1]).toEqual(options);
|
||||
});
|
||||
});
|
|
@ -5,42 +5,40 @@
|
|||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { CoreSetup } from '../../../../../src/core/public';
|
||||
import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../../../src/plugins/data/common';
|
||||
import {
|
||||
TSearchStrategyProvider,
|
||||
ISearchContext,
|
||||
ISearch,
|
||||
getEsPreference,
|
||||
ISearchStrategy,
|
||||
UI_SETTINGS,
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
import { IEnhancedEsSearchRequest, EnhancedSearchParams } from '../../common';
|
||||
import { ASYNC_SEARCH_STRATEGY } from './async_search_strategy';
|
||||
import { IAsyncSearchOptions } from './types';
|
||||
|
||||
export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider<typeof ES_SEARCH_STRATEGY> = (
|
||||
context: ISearchContext
|
||||
) => {
|
||||
const asyncStrategyProvider = context.getSearchStrategy(ASYNC_SEARCH_STRATEGY);
|
||||
const { search: asyncSearch } = asyncStrategyProvider(context);
|
||||
|
||||
export function enhancedEsSearchStrategyProvider(
|
||||
core: CoreSetup,
|
||||
asyncStrategy: ISearchStrategy<typeof ASYNC_SEARCH_STRATEGY>
|
||||
) {
|
||||
const search: ISearch<typeof ES_SEARCH_STRATEGY> = (
|
||||
request: IEnhancedEsSearchRequest,
|
||||
options
|
||||
) => {
|
||||
const params: EnhancedSearchParams = {
|
||||
ignoreThrottled: !context.core.uiSettings.get<boolean>(UI_SETTINGS.SEARCH_INCLUDE_FROZEN),
|
||||
preference: getEsPreference(context.core.uiSettings),
|
||||
ignoreThrottled: !core.uiSettings.get<boolean>(UI_SETTINGS.SEARCH_INCLUDE_FROZEN),
|
||||
preference: getEsPreference(core.uiSettings),
|
||||
...request.params,
|
||||
};
|
||||
request.params = params;
|
||||
|
||||
const asyncOptions: IAsyncSearchOptions = { pollInterval: 0, ...options };
|
||||
|
||||
return asyncSearch(
|
||||
return asyncStrategy.search(
|
||||
{ ...request, serverStrategy: ES_SEARCH_STRATEGY },
|
||||
asyncOptions
|
||||
) as Observable<IEsSearchResponse>;
|
||||
};
|
||||
|
||||
return { search };
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue