mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Improve search typescript (#69333)
* [search] Refactor the way search strategies are registered/retrieved on the server * Fix types and tests and update docs * Fix failing test * Fix build of example plugin * Fix functional test * Make server strategies sync * Move strategy name into options * docs * Remove FE strategies * TypeScript of hell delete search explorer * Fix search interceptor OSS tests * typos * test cleanup * Delete search example fix interceptor async tests to use fake timers * docs * fix * return search wrapper * Update search interceptor tests and abort utils * ts * jest test fix * code review * change how logs consume search API Co-authored-by: Lukas Olson <olson.lukas@gmail.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
53ee7a762d
commit
f290c68696
104 changed files with 1048 additions and 3049 deletions
|
@ -10,16 +10,7 @@
|
|||
export declare function getSearchParamsFromRequest(searchRequest: SearchRequest, dependencies: {
|
||||
injectedMetadata: CoreStart['injectedMetadata'];
|
||||
uiSettings: IUiSettingsClient;
|
||||
}): {
|
||||
rest_total_hits_as_int: boolean;
|
||||
ignore_unavailable: boolean;
|
||||
ignore_throttled: boolean;
|
||||
max_concurrent_shard_requests: any;
|
||||
preference: any;
|
||||
timeout: string | undefined;
|
||||
index: any;
|
||||
body: any;
|
||||
};
|
||||
}): ISearchRequestParams;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
@ -31,14 +22,5 @@ export declare function getSearchParamsFromRequest(searchRequest: SearchRequest,
|
|||
|
||||
<b>Returns:</b>
|
||||
|
||||
`{
|
||||
rest_total_hits_as_int: boolean;
|
||||
ignore_unavailable: boolean;
|
||||
ignore_throttled: boolean;
|
||||
max_concurrent_shard_requests: any;
|
||||
preference: any;
|
||||
timeout: string | undefined;
|
||||
index: any;
|
||||
body: any;
|
||||
}`
|
||||
`ISearchRequestParams`
|
||||
|
||||
|
|
|
@ -15,5 +15,5 @@ export interface IEsSearchRequest extends IKibanaSearchRequest
|
|||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [indexType](./kibana-plugin-plugins-data-public.iessearchrequest.indextype.md) | <code>string</code> | |
|
||||
| [params](./kibana-plugin-plugins-data-public.iessearchrequest.params.md) | <code>SearchParams</code> | |
|
||||
| [params](./kibana-plugin-plugins-data-public.iessearchrequest.params.md) | <code>ISearchRequestParams</code> | |
|
||||
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
params: SearchParams;
|
||||
params?: ISearchRequestParams;
|
||||
```
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface IEsSearchResponse<Hits = unknown> extends IKibanaSearchResponse
|
||||
export interface IEsSearchResponse extends IKibanaSearchResponse
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [rawResponse](./kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md) | <code>SearchResponse<Hits></code> | |
|
||||
| [rawResponse](./kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md) | <code>SearchResponse<any></code> | |
|
||||
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
rawResponse: SearchResponse<Hits>;
|
||||
rawResponse: SearchResponse<any>;
|
||||
```
|
||||
|
|
|
@ -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) > [IRequestTypesMap](./kibana-plugin-plugins-data-public.irequesttypesmap.md) > [es](./kibana-plugin-plugins-data-public.irequesttypesmap.es.md)
|
||||
|
||||
## IRequestTypesMap.es property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
[ES_SEARCH_STRATEGY]: IEsSearchRequest;
|
||||
```
|
|
@ -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) > [IRequestTypesMap](./kibana-plugin-plugins-data-public.irequesttypesmap.md)
|
||||
|
||||
## IRequestTypesMap interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface IRequestTypesMap
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [es](./kibana-plugin-plugins-data-public.irequesttypesmap.es.md) | <code>IEsSearchRequest</code> | |
|
||||
| [SYNC\_SEARCH\_STRATEGY](./kibana-plugin-plugins-data-public.irequesttypesmap.sync_search_strategy.md) | <code>ISyncSearchRequest</code> | |
|
||||
|
|
@ -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) > [IRequestTypesMap](./kibana-plugin-plugins-data-public.irequesttypesmap.md) > [SYNC\_SEARCH\_STRATEGY](./kibana-plugin-plugins-data-public.irequesttypesmap.sync_search_strategy.md)
|
||||
|
||||
## IRequestTypesMap.SYNC\_SEARCH\_STRATEGY property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
[SYNC_SEARCH_STRATEGY]: ISyncSearchRequest;
|
||||
```
|
|
@ -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) > [IResponseTypesMap](./kibana-plugin-plugins-data-public.iresponsetypesmap.md) > [es](./kibana-plugin-plugins-data-public.iresponsetypesmap.es.md)
|
||||
|
||||
## IResponseTypesMap.es property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
[ES_SEARCH_STRATEGY]: IEsSearchResponse;
|
||||
```
|
|
@ -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) > [IResponseTypesMap](./kibana-plugin-plugins-data-public.iresponsetypesmap.md)
|
||||
|
||||
## IResponseTypesMap interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface IResponseTypesMap
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [es](./kibana-plugin-plugins-data-public.iresponsetypesmap.es.md) | <code>IEsSearchResponse</code> | |
|
||||
| [SYNC\_SEARCH\_STRATEGY](./kibana-plugin-plugins-data-public.iresponsetypesmap.sync_search_strategy.md) | <code>IKibanaSearchResponse</code> | |
|
||||
|
|
@ -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) > [IResponseTypesMap](./kibana-plugin-plugins-data-public.iresponsetypesmap.md) > [SYNC\_SEARCH\_STRATEGY](./kibana-plugin-plugins-data-public.iresponsetypesmap.sync_search_strategy.md)
|
||||
|
||||
## IResponseTypesMap.SYNC\_SEARCH\_STRATEGY property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
[SYNC_SEARCH_STRATEGY]: IKibanaSearchResponse;
|
||||
```
|
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare type ISearch<T extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY> = (request: IRequestTypesMap[T], options?: ISearchOptions) => Observable<IResponseTypesMap[T]>;
|
||||
export declare type ISearch = (request: IKibanaSearchRequest, options?: ISearchOptions) => Observable<IKibanaSearchResponse>;
|
||||
```
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare type ISearchGeneric = <T extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY>(request: IRequestTypesMap[T], options?: ISearchOptions, strategy?: T) => Observable<IResponseTypesMap[T]>;
|
||||
export declare type ISearchGeneric = (request: IEsSearchRequest, options?: IStrategyOptions) => Observable<IEsSearchResponse>;
|
||||
```
|
||||
|
|
|
@ -1,20 +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) > [ISearchStrategy](./kibana-plugin-plugins-data-public.isearchstrategy.md)
|
||||
|
||||
## ISearchStrategy interface
|
||||
|
||||
Search strategy interface contains a search method that takes in a request and returns a promise that resolves to a response.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface ISearchStrategy<T extends TStrategyTypes>
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [search](./kibana-plugin-plugins-data-public.isearchstrategy.search.md) | <code>ISearch<T></code> | |
|
||||
|
|
@ -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) > [ISearchStrategy](./kibana-plugin-plugins-data-public.isearchstrategy.md) > [search](./kibana-plugin-plugins-data-public.isearchstrategy.search.md)
|
||||
|
||||
## ISearchStrategy.search property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
search: ISearch<T>;
|
||||
```
|
|
@ -1,18 +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) > [ISyncSearchRequest](./kibana-plugin-plugins-data-public.isyncsearchrequest.md)
|
||||
|
||||
## ISyncSearchRequest interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface ISyncSearchRequest extends IKibanaSearchRequest
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [serverStrategy](./kibana-plugin-plugins-data-public.isyncsearchrequest.serverstrategy.md) | <code>string</code> | |
|
||||
|
|
@ -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) > [ISyncSearchRequest](./kibana-plugin-plugins-data-public.isyncsearchrequest.md) > [serverStrategy](./kibana-plugin-plugins-data-public.isyncsearchrequest.serverstrategy.md)
|
||||
|
||||
## ISyncSearchRequest.serverStrategy property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
serverStrategy: string;
|
||||
```
|
|
@ -67,11 +67,7 @@
|
|||
| [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) | |
|
||||
| [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) | Use data plugin interface instead |
|
||||
| [IndexPatternTypeMeta](./kibana-plugin-plugins-data-public.indexpatterntypemeta.md) | |
|
||||
| [IRequestTypesMap](./kibana-plugin-plugins-data-public.irequesttypesmap.md) | |
|
||||
| [IResponseTypesMap](./kibana-plugin-plugins-data-public.iresponsetypesmap.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) | |
|
||||
| [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) | |
|
||||
| [OptionedValueProp](./kibana-plugin-plugins-data-public.optionedvalueprop.md) | |
|
||||
| [Query](./kibana-plugin-plugins-data-public.query.md) | |
|
||||
|
@ -83,6 +79,7 @@
|
|||
| [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md) | |
|
||||
| [SavedQuery](./kibana-plugin-plugins-data-public.savedquery.md) | |
|
||||
| [SavedQueryService](./kibana-plugin-plugins-data-public.savedqueryservice.md) | |
|
||||
| [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) | |
|
||||
| [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | |
|
||||
| [TabbedAggColumn](./kibana-plugin-plugins-data-public.tabbedaggcolumn.md) | \* |
|
||||
| [TabbedTable](./kibana-plugin-plugins-data-public.tabbedtable.md) | \* |
|
||||
|
@ -118,7 +115,6 @@
|
|||
| [QueryStringInput](./kibana-plugin-plugins-data-public.querystringinput.md) | |
|
||||
| [search](./kibana-plugin-plugins-data-public.search.md) | |
|
||||
| [SearchBar](./kibana-plugin-plugins-data-public.searchbar.md) | |
|
||||
| [SYNC\_SEARCH\_STRATEGY](./kibana-plugin-plugins-data-public.sync_search_strategy.md) | |
|
||||
| [syncQueryStateWithUrl](./kibana-plugin-plugins-data-public.syncquerystatewithurl.md) | Helper to setup syncing of global data with the URL |
|
||||
| [UI\_SETTINGS](./kibana-plugin-plugins-data-public.ui_settings.md) | |
|
||||
|
||||
|
|
|
@ -9,14 +9,13 @@ This class should be instantiated with a `requestTimeout` corresponding with how
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
constructor(toasts: ToastsStart, application: ApplicationStart, requestTimeout?: number | undefined);
|
||||
constructor(deps: SearchInterceptorDeps, requestTimeout?: number | undefined);
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| toasts | <code>ToastsStart</code> | |
|
||||
| application | <code>ApplicationStart</code> | |
|
||||
| deps | <code>SearchInterceptorDeps</code> | |
|
||||
| requestTimeout | <code>number | undefined</code> | |
|
||||
|
||||
|
|
|
@ -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) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [application](./kibana-plugin-plugins-data-public.searchinterceptor.application.md)
|
||||
|
||||
## SearchInterceptor.application property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
protected readonly application: ApplicationStart;
|
||||
```
|
|
@ -1,11 +1,11 @@
|
|||
<!-- 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) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [toasts](./kibana-plugin-plugins-data-public.searchinterceptor.toasts.md)
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [deps](./kibana-plugin-plugins-data-public.searchinterceptor.deps.md)
|
||||
|
||||
## SearchInterceptor.toasts property
|
||||
## SearchInterceptor.deps property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
protected readonly toasts: ToastsStart;
|
||||
protected readonly deps: SearchInterceptorDeps;
|
||||
```
|
|
@ -9,5 +9,5 @@ Returns an `Observable` over the current number of pending searches. This could
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getPendingCount$: () => import("rxjs").Observable<number>;
|
||||
getPendingCount$: () => Observable<number>;
|
||||
```
|
||||
|
|
|
@ -14,20 +14,28 @@ export declare class SearchInterceptor
|
|||
|
||||
| Constructor | Modifiers | Description |
|
||||
| --- | --- | --- |
|
||||
| [(constructor)(toasts, application, requestTimeout)](./kibana-plugin-plugins-data-public.searchinterceptor._constructor_.md) | | This class should be instantiated with a <code>requestTimeout</code> corresponding with how many ms after requests are initiated that they should automatically cancel. |
|
||||
| [(constructor)(deps, requestTimeout)](./kibana-plugin-plugins-data-public.searchinterceptor._constructor_.md) | | This class should be instantiated with a <code>requestTimeout</code> corresponding with how many ms after requests are initiated that they should automatically cancel. |
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [abortController](./kibana-plugin-plugins-data-public.searchinterceptor.abortcontroller.md) | | <code>AbortController</code> | <code>abortController</code> used to signal all searches to abort. |
|
||||
| [application](./kibana-plugin-plugins-data-public.searchinterceptor.application.md) | | <code>ApplicationStart</code> | |
|
||||
| [getPendingCount$](./kibana-plugin-plugins-data-public.searchinterceptor.getpendingcount_.md) | | <code>() => import("rxjs").Observable<number></code> | Returns an <code>Observable</code> over the current number of pending searches. This could mean that one of the search requests is still in flight, or that it has only received partial responses. |
|
||||
| [deps](./kibana-plugin-plugins-data-public.searchinterceptor.deps.md) | | <code>SearchInterceptorDeps</code> | |
|
||||
| [getPendingCount$](./kibana-plugin-plugins-data-public.searchinterceptor.getpendingcount_.md) | | <code>() => Observable<number></code> | Returns an <code>Observable</code> over the current number of pending searches. This could mean that one of the search requests is still in flight, or that it has only received partial responses. |
|
||||
| [hideToast](./kibana-plugin-plugins-data-public.searchinterceptor.hidetoast.md) | | <code>() => void</code> | |
|
||||
| [longRunningToast](./kibana-plugin-plugins-data-public.searchinterceptor.longrunningtoast.md) | | <code>Toast</code> | The current long-running toast (if there is one). |
|
||||
| [pendingCount](./kibana-plugin-plugins-data-public.searchinterceptor.pendingcount.md) | | <code>number</code> | The number of pending search requests. |
|
||||
| [pendingCount$](./kibana-plugin-plugins-data-public.searchinterceptor.pendingcount_.md) | | <code>BehaviorSubject<number></code> | Observable that emits when the number of pending requests changes. |
|
||||
| [requestTimeout](./kibana-plugin-plugins-data-public.searchinterceptor.requesttimeout.md) | | <code>number | undefined</code> | |
|
||||
| [search](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | <code>(search: ISearchGeneric, request: IKibanaSearchRequest, options?: ISearchOptions | undefined) => import("rxjs").Observable<import("../../common/search").IEsSearchResponse<unknown>></code> | Searches using the given <code>search</code> method. Overrides the <code>AbortSignal</code> with one that will abort either when <code>cancelPending</code> is called, when the request times out, or when the original <code>AbortSignal</code> is aborted. Updates the <code>pendingCount</code> when the request is started/finalized. |
|
||||
| [showToast](./kibana-plugin-plugins-data-public.searchinterceptor.showtoast.md) | | <code>() => void</code> | |
|
||||
| [timeoutSubscriptions](./kibana-plugin-plugins-data-public.searchinterceptor.timeoutsubscriptions.md) | | <code>Set<Subscription></code> | The subscriptions from scheduling the automatic timeout for each request. |
|
||||
| [toasts](./kibana-plugin-plugins-data-public.searchinterceptor.toasts.md) | | <code>ToastsStart</code> | |
|
||||
| [timeoutSubscriptions](./kibana-plugin-plugins-data-public.searchinterceptor.timeoutsubscriptions.md) | | <code>Subscription</code> | The subscriptions from scheduling the automatic timeout for each request. |
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Modifiers | Description |
|
||||
| --- | --- | --- |
|
||||
| [runSearch(request, combinedSignal)](./kibana-plugin-plugins-data-public.searchinterceptor.runsearch.md) | | |
|
||||
| [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given <code>search</code> method. Overrides the <code>AbortSignal</code> with one that will abort either when <code>cancelPending</code> is called, when the request times out, or when the original <code>AbortSignal</code> is aborted. Updates the <code>pendingCount</code> when the request is started/finalized. |
|
||||
| [setupTimers(options)](./kibana-plugin-plugins-data-public.searchinterceptor.setuptimers.md) | | |
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- 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) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [pendingCount](./kibana-plugin-plugins-data-public.searchinterceptor.pendingcount.md)
|
||||
|
||||
## SearchInterceptor.pendingCount property
|
||||
|
||||
The number of pending search requests.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
protected pendingCount: number;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- 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) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [pendingCount$](./kibana-plugin-plugins-data-public.searchinterceptor.pendingcount_.md)
|
||||
|
||||
## SearchInterceptor.pendingCount$ property
|
||||
|
||||
Observable that emits when the number of pending requests changes.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
protected pendingCount$: BehaviorSubject<number>;
|
||||
```
|
|
@ -0,0 +1,23 @@
|
|||
<!-- 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) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [runSearch](./kibana-plugin-plugins-data-public.searchinterceptor.runsearch.md)
|
||||
|
||||
## SearchInterceptor.runSearch() method
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
protected runSearch(request: IEsSearchRequest, combinedSignal: AbortSignal): Observable<IEsSearchResponse>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| request | <code>IEsSearchRequest</code> | |
|
||||
| combinedSignal | <code>AbortSignal</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<IEsSearchResponse>`
|
||||
|
|
@ -2,12 +2,24 @@
|
|||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [search](./kibana-plugin-plugins-data-public.searchinterceptor.search.md)
|
||||
|
||||
## SearchInterceptor.search property
|
||||
## SearchInterceptor.search() method
|
||||
|
||||
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 the `pendingCount` when the request is started/finalized.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
search: (search: ISearchGeneric, request: IKibanaSearchRequest, options?: ISearchOptions | undefined) => import("rxjs").Observable<import("../../common/search").IEsSearchResponse<unknown>>;
|
||||
search(request: IEsSearchRequest, options?: ISearchOptions): Observable<IEsSearchResponse>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| request | <code>IEsSearchRequest</code> | |
|
||||
| options | <code>ISearchOptions</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<IEsSearchResponse>`
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!-- 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) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [setupTimers](./kibana-plugin-plugins-data-public.searchinterceptor.setuptimers.md)
|
||||
|
||||
## SearchInterceptor.setupTimers() method
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
protected setupTimers(options?: ISearchOptions): {
|
||||
combinedSignal: AbortSignal;
|
||||
cleanup: () => void;
|
||||
};
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| options | <code>ISearchOptions</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
`{
|
||||
combinedSignal: AbortSignal;
|
||||
cleanup: () => void;
|
||||
}`
|
||||
|
|
@ -9,5 +9,5 @@ The subscriptions from scheduling the automatic timeout for each request.
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
protected timeoutSubscriptions: Set<Subscription>;
|
||||
protected timeoutSubscriptions: Subscription;
|
||||
```
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- 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) > [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) > [application](./kibana-plugin-plugins-data-public.searchinterceptordeps.application.md)
|
||||
|
||||
## SearchInterceptorDeps.application property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
application: ApplicationStart;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- 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) > [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) > [http](./kibana-plugin-plugins-data-public.searchinterceptordeps.http.md)
|
||||
|
||||
## SearchInterceptorDeps.http property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
http: CoreStart['http'];
|
||||
```
|
|
@ -0,0 +1,21 @@
|
|||
<!-- 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) > [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md)
|
||||
|
||||
## SearchInterceptorDeps interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface SearchInterceptorDeps
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [application](./kibana-plugin-plugins-data-public.searchinterceptordeps.application.md) | <code>ApplicationStart</code> | |
|
||||
| [http](./kibana-plugin-plugins-data-public.searchinterceptordeps.http.md) | <code>CoreStart['http']</code> | |
|
||||
| [toasts](./kibana-plugin-plugins-data-public.searchinterceptordeps.toasts.md) | <code>ToastsStart</code> | |
|
||||
| [uiSettings](./kibana-plugin-plugins-data-public.searchinterceptordeps.uisettings.md) | <code>CoreStart['uiSettings']</code> | |
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- 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) > [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) > [toasts](./kibana-plugin-plugins-data-public.searchinterceptordeps.toasts.md)
|
||||
|
||||
## SearchInterceptorDeps.toasts property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
toasts: ToastsStart;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- 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) > [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) > [uiSettings](./kibana-plugin-plugins-data-public.searchinterceptordeps.uisettings.md)
|
||||
|
||||
## SearchInterceptorDeps.uiSettings property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
uiSettings: CoreStart['uiSettings'];
|
||||
```
|
|
@ -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) > [SYNC\_SEARCH\_STRATEGY](./kibana-plugin-plugins-data-public.sync_search_strategy.md)
|
||||
|
||||
## SYNC\_SEARCH\_STRATEGY variable
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
SYNC_SEARCH_STRATEGY = "SYNC_SEARCH_STRATEGY"
|
||||
```
|
|
@ -1,8 +0,0 @@
|
|||
## Demo search strategy
|
||||
|
||||
This example registers a custom search strategy that simply takes a name string in the request and returns the
|
||||
string `Hello {name}`
|
||||
|
||||
To see the demo search strategy in action, navigate to the `Search explorer` app.
|
||||
|
||||
To run these examples, use the command `yarn start --run-examples`.
|
|
@ -1,40 +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 { IKibanaSearchRequest, IKibanaSearchResponse } from '../../../src/plugins/data/public';
|
||||
|
||||
export const DEMO_SEARCH_STRATEGY = 'DEMO_SEARCH_STRATEGY';
|
||||
export const ASYNC_DEMO_SEARCH_STRATEGY = 'ASYNC_DEMO_SEARCH_STRATEGY';
|
||||
|
||||
export interface IDemoRequest extends IKibanaSearchRequest {
|
||||
mood: string | 'sad' | 'happy';
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IDemoResponse extends IKibanaSearchResponse {
|
||||
greeting: string;
|
||||
}
|
||||
|
||||
export interface IAsyncDemoRequest extends IKibanaSearchRequest {
|
||||
fibonacciNumbers: number;
|
||||
}
|
||||
|
||||
export interface IAsyncDemoResponse extends IKibanaSearchResponse {
|
||||
fibonacciSequence: number[];
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"id": "demoSearch",
|
||||
"version": "0.0.1",
|
||||
"kibanaVersion": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["data"],
|
||||
"optionalPlugins": [],
|
||||
"extraPublicDirs": ["common"]
|
||||
}
|
|
@ -1,43 +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 { 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';
|
||||
|
||||
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 };
|
||||
}
|
|
@ -1,65 +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 { 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
|
||||
* on the server side, without users having to pass it in explicitly, and it takes advantage of the
|
||||
* already registered SYNC_SEARCH_STRATEGY that exists on the client.
|
||||
*
|
||||
* so instead of callers having to do:
|
||||
*
|
||||
* ```
|
||||
* data.search.search(
|
||||
* { ...request, serverStrategy: DEMO_SEARCH_STRATEGY },
|
||||
* options,
|
||||
* SYNC_SEARCH_STRATEGY
|
||||
* ) as Observable<IDemoResponse>,
|
||||
*```
|
||||
|
||||
* They can instead just do
|
||||
*
|
||||
* ```
|
||||
* data.search.search(request, options, DEMO_SEARCH_STRATEGY);
|
||||
* ```
|
||||
*
|
||||
* and are ensured type safety in regard to the request and response objects.
|
||||
*/
|
||||
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 };
|
||||
}
|
|
@ -1,26 +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 { PluginInitializer } from 'kibana/public';
|
||||
|
||||
import { DemoDataPlugin } from './plugin';
|
||||
|
||||
export { DEMO_SEARCH_STRATEGY } from '../common';
|
||||
|
||||
export const plugin: PluginInitializer<void, void> = () => new DemoDataPlugin();
|
|
@ -1,65 +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 { Plugin, CoreSetup } from '../../../src/core/public';
|
||||
import {
|
||||
DEMO_SEARCH_STRATEGY,
|
||||
IDemoRequest,
|
||||
IDemoResponse,
|
||||
ASYNC_DEMO_SEARCH_STRATEGY,
|
||||
IAsyncDemoRequest,
|
||||
IAsyncDemoResponse,
|
||||
} from '../common';
|
||||
import { demoClientSearchStrategyProvider } from './demo_search_strategy';
|
||||
import { asyncDemoClientSearchStrategyProvider } from './async_demo_search_strategy';
|
||||
import { DemoDataSearchSetupDependencies, DemoDataSearchStartDependencies } from './types';
|
||||
|
||||
/**
|
||||
* Add the typescript mappings for our search strategy to the request and
|
||||
* response types. This allows typescript to require the right shapes if
|
||||
* making the call:
|
||||
* const response = context.search.search(request, {}, DEMO_SEARCH_STRATEGY);
|
||||
*
|
||||
* If the caller does not pass in the right `request` shape, typescript will
|
||||
* complain. The caller will also get a typed response.
|
||||
*/
|
||||
declare module '../../../src/plugins/data/public' {
|
||||
export interface IRequestTypesMap {
|
||||
[DEMO_SEARCH_STRATEGY]: IDemoRequest;
|
||||
[ASYNC_DEMO_SEARCH_STRATEGY]: IAsyncDemoRequest;
|
||||
}
|
||||
|
||||
export interface IResponseTypesMap {
|
||||
[DEMO_SEARCH_STRATEGY]: IDemoResponse;
|
||||
[ASYNC_DEMO_SEARCH_STRATEGY]: IAsyncDemoResponse;
|
||||
}
|
||||
}
|
||||
|
||||
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() {}
|
||||
public stop() {}
|
||||
}
|
|
@ -1,28 +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 { DataPublicPluginStart, DataPublicPluginSetup } from '../../../src/plugins/data/public';
|
||||
|
||||
export interface DemoDataSearchSetupDependencies {
|
||||
data: DataPublicPluginSetup;
|
||||
}
|
||||
|
||||
export interface DemoDataSearchStartDependencies {
|
||||
data: DataPublicPluginStart;
|
||||
}
|
|
@ -1,62 +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 { ISearchStrategy } from '../../../src/plugins/data/server';
|
||||
import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoRequest } from '../common';
|
||||
|
||||
export const asyncDemoSearchStrategyProvider = (): ISearchStrategy<
|
||||
typeof ASYNC_DEMO_SEARCH_STRATEGY
|
||||
> => {
|
||||
function getFibonacciSequence(n = 0) {
|
||||
const beginning = [0, 1].slice(0, n);
|
||||
return Array(Math.max(0, n))
|
||||
.fill(null)
|
||||
.reduce((sequence, value, i) => {
|
||||
if (i < 2) return sequence;
|
||||
return [...sequence, sequence[i - 1] + sequence[i - 2]];
|
||||
}, beginning);
|
||||
}
|
||||
|
||||
const generateId = (() => {
|
||||
let id = 0;
|
||||
return () => `${id++}`;
|
||||
})();
|
||||
|
||||
const loadedMap = new Map<string, number>();
|
||||
const totalMap = new Map<string, number>();
|
||||
|
||||
return {
|
||||
search: async (context, request: IAsyncDemoRequest) => {
|
||||
const id = request.id ?? generateId();
|
||||
|
||||
const loaded = (loadedMap.get(id) ?? 0) + 1;
|
||||
loadedMap.set(id, loaded);
|
||||
|
||||
const total = request.fibonacciNumbers ?? totalMap.get(id);
|
||||
totalMap.set(id, total);
|
||||
|
||||
const fibonacciSequence = getFibonacciSequence(loaded);
|
||||
return { id, total, loaded, fibonacciSequence };
|
||||
},
|
||||
cancel: async (context, id) => {
|
||||
loadedMap.delete(id);
|
||||
totalMap.delete(id);
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,34 +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 { ISearchStrategy } from '../../../src/plugins/data/server';
|
||||
import { DEMO_SEARCH_STRATEGY, IDemoRequest } from '../common';
|
||||
|
||||
export const demoSearchStrategyProvider = (): ISearchStrategy<typeof DEMO_SEARCH_STRATEGY> => {
|
||||
return {
|
||||
search: (context, request: IDemoRequest) => {
|
||||
return Promise.resolve({
|
||||
greeting:
|
||||
request.mood === 'happy'
|
||||
? `Lovely to meet you, ${request.name}`
|
||||
: `Hope you feel better, ${request.name}`,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,22 +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 { DemoDataPlugin } from './plugin';
|
||||
|
||||
export const plugin = () => new DemoDataPlugin();
|
|
@ -1,71 +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 { Plugin, CoreSetup } from 'kibana/server';
|
||||
import { PluginSetup as DataPluginSetup } from 'src/plugins/data/server';
|
||||
import { demoSearchStrategyProvider } from './demo_search_strategy';
|
||||
import {
|
||||
DEMO_SEARCH_STRATEGY,
|
||||
IDemoRequest,
|
||||
IDemoResponse,
|
||||
ASYNC_DEMO_SEARCH_STRATEGY,
|
||||
IAsyncDemoRequest,
|
||||
IAsyncDemoResponse,
|
||||
} from '../common';
|
||||
import { asyncDemoSearchStrategyProvider } from './async_demo_search_strategy';
|
||||
|
||||
interface IDemoSearchExplorerDeps {
|
||||
data: DataPluginSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the typescript mappings for our search strategy to the request and
|
||||
* response types. This allows typescript to require the right shapes if
|
||||
* making the call:
|
||||
* const response = context.search.search(request, DEMO_SEARCH_STRATEGY);
|
||||
*
|
||||
* If the caller does not pass in the right `request` shape, typescript will
|
||||
* complain. The caller will also get a typed response.
|
||||
*/
|
||||
declare module '../../../src/plugins/data/server' {
|
||||
export interface IRequestTypesMap {
|
||||
[DEMO_SEARCH_STRATEGY]: IDemoRequest;
|
||||
[ASYNC_DEMO_SEARCH_STRATEGY]: IAsyncDemoRequest;
|
||||
}
|
||||
|
||||
export interface IResponseTypesMap {
|
||||
[DEMO_SEARCH_STRATEGY]: IDemoResponse;
|
||||
[ASYNC_DEMO_SEARCH_STRATEGY]: IAsyncDemoResponse;
|
||||
}
|
||||
}
|
||||
|
||||
export class DemoDataPlugin implements Plugin<void, void, IDemoSearchExplorerDeps> {
|
||||
constructor() {}
|
||||
|
||||
public setup(core: CoreSetup, deps: IDemoSearchExplorerDeps) {
|
||||
deps.data.search.registerSearchStrategy(DEMO_SEARCH_STRATEGY, demoSearchStrategyProvider());
|
||||
deps.data.search.registerSearchStrategy(
|
||||
ASYNC_DEMO_SEARCH_STRATEGY,
|
||||
asyncDemoSearchStrategyProvider()
|
||||
);
|
||||
}
|
||||
|
||||
public start() {}
|
||||
public stop() {}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./target",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"common/**/*.ts",
|
||||
"public/**/*.ts",
|
||||
"public/**/*.tsx",
|
||||
"server/**/*.ts",
|
||||
"../../typings/**/*"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
## Search explorer
|
||||
|
||||
This example search explorer app shows how to use different search strategies in order to retrieve data.
|
||||
|
||||
One demo uses the built in elasticsearch search strategy, and runs a search against data in elasticsearch. The
|
||||
other demo uses the custom demo search strategy, a custom search strategy registerd inside the [demo_search plugin](../demo_search).
|
||||
|
||||
To run this example, use the command `yarn start --run-examples`.
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"id": "searchExplorer",
|
||||
"version": "0.0.1",
|
||||
"kibanaVersion": "kibana",
|
||||
"server": false,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["data", "demoSearch", "developerExamples"],
|
||||
"optionalPlugins": []
|
||||
}
|
|
@ -1,133 +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 React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { BrowserRouter as Router, Route, withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageSideBar,
|
||||
// @ts-ignore
|
||||
EuiSideNav,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { AppMountParameters, CoreStart } from '../../../src/core/public';
|
||||
import { EsSearchTest } from './es_strategy';
|
||||
import { Page } from './page';
|
||||
import { DemoStrategy } from './demo_strategy';
|
||||
import { AsyncDemoStrategy } from './async_demo_strategy';
|
||||
import { DocumentationPage } from './documentation';
|
||||
import { SearchApiPage } from './search_api';
|
||||
import { AppPluginStartDependencies, SearchBarComponentParams } from './types';
|
||||
|
||||
const Home = () => <DocumentationPage />;
|
||||
|
||||
interface PageDef {
|
||||
title: string;
|
||||
id: string;
|
||||
component: React.ReactNode;
|
||||
}
|
||||
|
||||
type NavProps = RouteComponentProps & {
|
||||
navigateToApp: CoreStart['application']['navigateToApp'];
|
||||
pages: PageDef[];
|
||||
};
|
||||
|
||||
const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => {
|
||||
const navItems = pages.map((page) => ({
|
||||
id: page.id,
|
||||
name: page.title,
|
||||
onClick: () => history.push(`/${page.id}`),
|
||||
'data-test-subj': page.id,
|
||||
}));
|
||||
|
||||
return (
|
||||
<EuiSideNav
|
||||
items={[
|
||||
{
|
||||
name: 'Search explorer',
|
||||
id: 'home',
|
||||
items: [...navItems],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const buildPage = (page: PageDef) => <Page title={page.title}>{page.component}</Page>;
|
||||
|
||||
const SearchApp = ({ basename, data, application }: SearchBarComponentParams) => {
|
||||
const pages: PageDef[] = [
|
||||
{
|
||||
id: 'home',
|
||||
title: 'Home',
|
||||
component: <Home />,
|
||||
},
|
||||
{
|
||||
title: 'Search API',
|
||||
id: 'searchAPI',
|
||||
component: <SearchApiPage />,
|
||||
},
|
||||
{
|
||||
title: 'ES search strategy',
|
||||
id: 'esSearch',
|
||||
component: <EsSearchTest search={data.search.search} />,
|
||||
},
|
||||
{
|
||||
title: 'Demo search strategy',
|
||||
id: 'demoSearch',
|
||||
component: <DemoStrategy search={data.search.search} />,
|
||||
},
|
||||
{
|
||||
title: 'Async demo search strategy',
|
||||
id: 'asyncDemoSearch',
|
||||
component: <AsyncDemoStrategy search={data.search.search} />,
|
||||
},
|
||||
];
|
||||
|
||||
const routes = pages.map((page, i) => (
|
||||
<Route key={i} path={`/${page.id}`} render={(props) => buildPage(page)} />
|
||||
));
|
||||
|
||||
return (
|
||||
<Router basename={basename}>
|
||||
<EuiPage>
|
||||
<EuiPageSideBar>
|
||||
<Nav navigateToApp={application.navigateToApp} pages={pages} />
|
||||
</EuiPageSideBar>
|
||||
<Route path="/" exact component={Home} />
|
||||
{routes}
|
||||
</EuiPage>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export const renderApp = (
|
||||
coreStart: CoreStart,
|
||||
deps: AppPluginStartDependencies,
|
||||
{ appBasePath, element }: AppMountParameters
|
||||
) => {
|
||||
ReactDOM.render(
|
||||
<SearchApp basename={appBasePath} data={deps.data} application={coreStart.application} />,
|
||||
element
|
||||
);
|
||||
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
|
@ -1,123 +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 React from 'react';
|
||||
import {
|
||||
EuiPageContentBody,
|
||||
EuiFormRow,
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiFieldNumber,
|
||||
} from '@elastic/eui';
|
||||
import { ISearchGeneric } from '../../../src/plugins/data/public';
|
||||
import { DoSearch } from './do_search';
|
||||
import { GuideSection } from './guide_section';
|
||||
|
||||
import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoRequest } from '../../demo_search/common';
|
||||
|
||||
// @ts-ignore
|
||||
import demoStrategyServerProvider from '!!raw-loader!./../../demo_search/server/async_demo_search_strategy';
|
||||
// @ts-ignore
|
||||
import demoStrategyPublicProvider from '!!raw-loader!./../../demo_search/public/async_demo_search_strategy';
|
||||
// @ts-ignore
|
||||
import demoStrategyServerPlugin from '!!raw-loader!./../../demo_search/server/plugin';
|
||||
// @ts-ignore
|
||||
import demoStrategyPublicPlugin from '!!raw-loader!./../../demo_search/public/plugin';
|
||||
|
||||
interface Props {
|
||||
search: ISearchGeneric;
|
||||
}
|
||||
|
||||
interface State {
|
||||
searching: boolean;
|
||||
fibonacciNumbers: number;
|
||||
changes: boolean;
|
||||
error?: any;
|
||||
}
|
||||
|
||||
export class AsyncDemoStrategy extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searching: false,
|
||||
changes: false,
|
||||
fibonacciNumbers: 5,
|
||||
};
|
||||
}
|
||||
|
||||
renderDemo = () => {
|
||||
const request: IAsyncDemoRequest = {
|
||||
fibonacciNumbers: this.state.fibonacciNumbers,
|
||||
};
|
||||
return (
|
||||
<React.Fragment>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow label="How many Fibonacci numbers to generate?">
|
||||
<EuiFieldNumber
|
||||
value={this.state.fibonacciNumbers}
|
||||
onChange={(e) => this.setState({ fibonacciNumbers: parseFloat(e.target.value) })}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<DoSearch
|
||||
request={request}
|
||||
strategy={ASYNC_DEMO_SEARCH_STRATEGY}
|
||||
search={(signal: AbortSignal) =>
|
||||
this.props.search(request, { signal }, ASYNC_DEMO_SEARCH_STRATEGY)
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EuiPageContentBody>
|
||||
<GuideSection
|
||||
codeSections={[
|
||||
{
|
||||
title: 'Public',
|
||||
code: [
|
||||
{ description: 'plugin.ts', snippet: demoStrategyPublicPlugin },
|
||||
{
|
||||
description: 'async_demo_search_strategy.ts',
|
||||
snippet: demoStrategyPublicProvider,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Server',
|
||||
code: [
|
||||
{ description: 'plugin.ts', snippet: demoStrategyServerPlugin },
|
||||
{
|
||||
description: 'async_demo_search_strategy.ts',
|
||||
snippet: demoStrategyServerProvider,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
demo={this.renderDemo()}
|
||||
/>
|
||||
</EuiPageContentBody>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,134 +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 React from 'react';
|
||||
import {
|
||||
EuiPageContentBody,
|
||||
EuiFormRow,
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiFieldText,
|
||||
} from '@elastic/eui';
|
||||
import { ISearchGeneric } from '../../../src/plugins/data/public';
|
||||
import { DoSearch } from './do_search';
|
||||
import { GuideSection } from './guide_section';
|
||||
|
||||
import { DEMO_SEARCH_STRATEGY } from '../../demo_search/public';
|
||||
|
||||
import { IDemoResponse, IDemoRequest } from '../../demo_search/common';
|
||||
|
||||
// @ts-ignore
|
||||
import doSearch from '!!raw-loader!./do_search.tsx';
|
||||
// @ts-ignore
|
||||
import demoStrategyServerProvider from '!!raw-loader!./../../demo_search/server/demo_search_strategy';
|
||||
// @ts-ignore
|
||||
import demoStrategyPublicProvider from '!!raw-loader!./../../demo_search/public/demo_search_strategy';
|
||||
// @ts-ignore
|
||||
import demoStrategyServerPlugin from '!!raw-loader!./../../demo_search/server/plugin';
|
||||
// @ts-ignore
|
||||
import demoStrategyPublicPlugin from '!!raw-loader!./../../demo_search/public/plugin';
|
||||
|
||||
interface Props {
|
||||
search: ISearchGeneric;
|
||||
}
|
||||
|
||||
interface State {
|
||||
results?: IDemoResponse;
|
||||
searching: boolean;
|
||||
name: string;
|
||||
mood: string;
|
||||
changes: boolean;
|
||||
error?: any;
|
||||
}
|
||||
|
||||
export class DemoStrategy extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searching: false,
|
||||
changes: false,
|
||||
name: 'Molly',
|
||||
mood: 'happy',
|
||||
};
|
||||
}
|
||||
|
||||
renderDemo = () => {
|
||||
const request: IDemoRequest = {
|
||||
name: this.state.name,
|
||||
mood: this.state.mood,
|
||||
};
|
||||
return (
|
||||
<React.Fragment>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow label="What is your name?">
|
||||
<EuiFieldText
|
||||
value={this.state.name}
|
||||
onChange={(e) => this.setState({ name: e.target.value })}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow label="How are you feeling today?">
|
||||
<EuiFieldText
|
||||
value={this.state.mood}
|
||||
onChange={(e) => this.setState({ mood: e.target.value })}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<DoSearch
|
||||
request={request}
|
||||
strategy={DEMO_SEARCH_STRATEGY}
|
||||
search={(signal: AbortSignal) =>
|
||||
this.props.search(request, { signal }, DEMO_SEARCH_STRATEGY)
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EuiPageContentBody>
|
||||
<GuideSection
|
||||
codeSections={[
|
||||
{
|
||||
title: 'Public',
|
||||
code: [
|
||||
{ description: 'plugin.ts', snippet: demoStrategyPublicPlugin },
|
||||
{ description: 'demo_search_strategy.ts', snippet: demoStrategyPublicProvider },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Server',
|
||||
code: [
|
||||
{ description: 'plugin.ts', snippet: demoStrategyServerPlugin },
|
||||
{ description: 'demo_search_strategy.ts', snippet: demoStrategyServerProvider },
|
||||
],
|
||||
},
|
||||
]}
|
||||
demo={this.renderDemo()}
|
||||
/>
|
||||
</EuiPageContentBody>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,138 +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 React from 'react';
|
||||
import { EuiButton, EuiCodeBlock, EuiFlexItem, EuiFlexGroup, EuiText } from '@elastic/eui';
|
||||
import { EuiProgress } from '@elastic/eui';
|
||||
import { Observable } from 'rxjs';
|
||||
import { IKibanaSearchResponse, IKibanaSearchRequest } from '../../../src/plugins/data/public';
|
||||
|
||||
interface Props {
|
||||
request: IKibanaSearchRequest;
|
||||
strategy?: string;
|
||||
search: (signal: AbortSignal) => Observable<IKibanaSearchResponse>;
|
||||
}
|
||||
|
||||
interface State {
|
||||
searching: boolean;
|
||||
response?: IKibanaSearchResponse;
|
||||
error?: any;
|
||||
}
|
||||
|
||||
export class DoSearch extends React.Component<Props, State> {
|
||||
private abortController?: AbortController;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searching: false,
|
||||
response: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
search = async () => {
|
||||
if (this.state.searching && this.abortController) {
|
||||
this.abortController.abort();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
searching: true,
|
||||
response: undefined,
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
this.abortController = new AbortController();
|
||||
|
||||
this.props.search(this.abortController.signal).subscribe(
|
||||
(response) => {
|
||||
this.setState({ response, error: undefined });
|
||||
},
|
||||
(error) => {
|
||||
this.setState({ error, searching: false, response: undefined });
|
||||
},
|
||||
() => {
|
||||
this.setState({ searching: false, error: undefined });
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
cancel = () => {
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let responseStr = this.state.error
|
||||
? JSON.stringify(this.state.error, null, 2)
|
||||
: JSON.stringify(this.state.response, null, 2);
|
||||
responseStr = responseStr ? responseStr.substring(0, 2000) : '';
|
||||
const requestStr = JSON.stringify(this.props.request, null, 2);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton data-test-subj="doSearch" onClick={this.search}>
|
||||
Search
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton disabled={!this.state.searching} onClick={this.cancel}>
|
||||
Cancel
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiText>Request:</EuiText>
|
||||
<EuiCodeBlock language="json" fontSize="m" paddingSize="m" color="dark">
|
||||
{this.props.strategy
|
||||
? `data.search
|
||||
(
|
||||
${requestStr},
|
||||
"${this.props.strategy}"
|
||||
)`
|
||||
: `data.search
|
||||
(
|
||||
${requestStr}
|
||||
)`}
|
||||
</EuiCodeBlock>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText>Response:</EuiText>
|
||||
<EuiProgress
|
||||
value={this.state.response?.loaded ?? 0}
|
||||
max={this.state.response?.total ?? 0}
|
||||
/>
|
||||
<EuiCodeBlock
|
||||
language="json"
|
||||
fontSize="m"
|
||||
paddingSize="m"
|
||||
color="dark"
|
||||
data-test-subj="response"
|
||||
>
|
||||
{responseStr}
|
||||
</EuiCodeBlock>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,102 +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 React from 'react';
|
||||
|
||||
import {
|
||||
EuiText,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiPageContentHeader,
|
||||
EuiPageContentHeaderSection,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiTitle,
|
||||
EuiListGroup,
|
||||
} from '@elastic/eui';
|
||||
|
||||
export const DocumentationPage = () => (
|
||||
<EuiPageBody data-test-subj="dataPluginExplorerHome">
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>Welcome to the data plugin portal!</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentHeader>
|
||||
<EuiPageContentHeaderSection>
|
||||
<EuiTitle>
|
||||
<h2>Documentation links</h2>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeaderSection>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
<EuiText>
|
||||
<h2>Search Services</h2>
|
||||
<ul>
|
||||
<li>Provide an abstraction on top of advanced query settings</li>
|
||||
|
||||
<li>
|
||||
Providing an abstraction layer for query cancellation semantics allows us to avoid
|
||||
wide-spread code changes when ES API changes, allows us to provide a minimum set of
|
||||
useful functionality first, and allows us to continue adding more advanced features
|
||||
down the road
|
||||
</li>
|
||||
|
||||
<li>Provide a clean separation of OSS and commercial search strategies.</li>
|
||||
</ul>
|
||||
<h2>Extensibility</h2>
|
||||
<p>
|
||||
Plugins can register or use different client side, and server side{' '}
|
||||
<i>search strategies</i>. Search strategies can take advantage of other search stratgies
|
||||
already registered. For example, the `DEMO_SEARCH_STRATEGY` uses the
|
||||
`ASYNC_SEARCH_STRATEGY` which uses the `SYNC_SEARCH_STRATEGY`
|
||||
</p>
|
||||
|
||||
<h2>References</h2>
|
||||
<EuiListGroup
|
||||
listItems={[
|
||||
{
|
||||
label: 'Design document',
|
||||
href:
|
||||
'https://docs.google.com/document/d/1ROLq29V1TeLux4ASQIJNllRGkv-xa5XIE72gTU6u16Q/edit#heading=h.3aa9ppqzkvdd',
|
||||
iconType: 'document',
|
||||
size: 's',
|
||||
},
|
||||
{
|
||||
label: 'Roadmap',
|
||||
href: 'https://github.com/elastic/kibana/issues/44661',
|
||||
iconType: 'logoGithub',
|
||||
size: 's',
|
||||
},
|
||||
{
|
||||
label: 'Data access API issue',
|
||||
href: 'https://github.com/elastic/kibana/issues/43371',
|
||||
iconType: 'logoGithub',
|
||||
size: 's',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
);
|
|
@ -1,136 +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 React from 'react';
|
||||
import {
|
||||
EuiPageContentBody,
|
||||
EuiFieldText,
|
||||
EuiFormRow,
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
ISearchGeneric,
|
||||
IEsSearchResponse,
|
||||
IEsSearchRequest,
|
||||
} from '../../../src/plugins/data/public';
|
||||
import { DoSearch } from './do_search';
|
||||
import { GuideSection } from './guide_section';
|
||||
|
||||
// @ts-ignore
|
||||
import serverStrategy from '!!raw-loader!./../../../src/plugins/data/server/search/es_search/es_search_strategy';
|
||||
|
||||
// @ts-ignore
|
||||
import publicStrategy from '!!raw-loader!./../../../src/plugins/data/public/search/es_search/es_search_strategy';
|
||||
|
||||
interface Props {
|
||||
search: ISearchGeneric;
|
||||
}
|
||||
|
||||
interface State {
|
||||
query: string;
|
||||
results?: IEsSearchResponse;
|
||||
index: string;
|
||||
searching: boolean;
|
||||
request: IEsSearchRequest;
|
||||
strategy?: string;
|
||||
changes: boolean;
|
||||
error?: any;
|
||||
}
|
||||
|
||||
export class EsSearchTest extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
query: '*',
|
||||
index: '*',
|
||||
searching: false,
|
||||
request: this.getRequest({ index: '*', query: '*' }),
|
||||
changes: false,
|
||||
};
|
||||
}
|
||||
|
||||
getRequest({ index, query }: { index: string; query: string }): IEsSearchRequest {
|
||||
return {
|
||||
debug: true,
|
||||
params: {
|
||||
index,
|
||||
body: {
|
||||
query: {
|
||||
query_string: {
|
||||
query,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
renderDemo() {
|
||||
const request: IEsSearchRequest = this.getRequest(this.state);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow label="Index pattern">
|
||||
<EuiFieldText
|
||||
value={this.state.index}
|
||||
onChange={(e) => this.setState({ index: e.target.value, changes: true })}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow label="Query string query">
|
||||
<EuiFieldText
|
||||
value={this.state.query}
|
||||
onChange={(e) => this.setState({ query: e.target.value, changes: true })}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<DoSearch
|
||||
request={request}
|
||||
search={(signal: AbortSignal) => this.props.search(request, { signal })}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EuiPageContentBody>
|
||||
<GuideSection
|
||||
codeSections={[
|
||||
{
|
||||
title: 'Public',
|
||||
code: [{ description: 'es_search_strategy.ts', snippet: publicStrategy }],
|
||||
},
|
||||
{
|
||||
title: 'Server',
|
||||
code: [{ description: 'es_search_strategy.ts', snippet: serverStrategy }],
|
||||
},
|
||||
]}
|
||||
demo={this.renderDemo()}
|
||||
/>
|
||||
</EuiPageContentBody>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,137 +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 React from 'react';
|
||||
|
||||
import { EuiTab, EuiTabs, EuiCodeBlock } from '@elastic/eui';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { EuiHorizontalRule } from '@elastic/eui';
|
||||
|
||||
export interface CodeSection {
|
||||
title: string;
|
||||
code: Array<{ description?: string; snippet: string }> | string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
demo?: React.ReactNode;
|
||||
codeSections?: CodeSection[];
|
||||
}
|
||||
|
||||
interface State {
|
||||
selectedTab: string;
|
||||
}
|
||||
|
||||
export class GuideSection extends React.Component<Props, State> {
|
||||
private tabs: Array<{ name: string; displayName: string }>;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
if (!props.demo && !props.codeSections) {
|
||||
throw new Error('Must supply either demo or code sections');
|
||||
}
|
||||
|
||||
if (props.demo) {
|
||||
this.tabs = [
|
||||
{
|
||||
name: 'demo',
|
||||
displayName: 'Demo',
|
||||
},
|
||||
];
|
||||
} else {
|
||||
this.tabs = [];
|
||||
}
|
||||
|
||||
if (props.codeSections) {
|
||||
props.codeSections.forEach((section) => {
|
||||
this.tabs.push({
|
||||
name: section.title,
|
||||
displayName: section.title,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.state = {
|
||||
selectedTab: this.tabs[0].name,
|
||||
};
|
||||
}
|
||||
|
||||
onSelectedTabChanged = (selectedTab: string) => {
|
||||
this.setState({
|
||||
selectedTab,
|
||||
});
|
||||
};
|
||||
|
||||
renderTabs() {
|
||||
return this.tabs.map((tab) => (
|
||||
<EuiTab
|
||||
onClick={() => this.onSelectedTabChanged(tab.name)}
|
||||
isSelected={tab.name === this.state.selectedTab}
|
||||
key={tab.name}
|
||||
>
|
||||
{tab.displayName}
|
||||
</EuiTab>
|
||||
));
|
||||
}
|
||||
|
||||
removeLicenseBlock(code: string) {
|
||||
return code.replace(/\/\*[\w\'\s\r\n\*\.\,\(\)\"\;\:\/\-]*\s*\//m, '');
|
||||
}
|
||||
|
||||
renderCodeBlocks() {
|
||||
if (!this.props.codeSections) {
|
||||
return undefined;
|
||||
}
|
||||
const section = this.props.codeSections.find((s) => s.title === this.state.selectedTab);
|
||||
|
||||
if (!section) {
|
||||
throw new Error('No section named ' + this.state.selectedTab);
|
||||
}
|
||||
const code = section.code;
|
||||
if (typeof code === 'string') {
|
||||
return <EuiCodeBlock language="ts">{this.removeLicenseBlock(code)}</EuiCodeBlock>;
|
||||
}
|
||||
|
||||
return code.map((codeBlock, i) => (
|
||||
<React.Fragment key={i}>
|
||||
<EuiSpacer />
|
||||
<h3>{codeBlock.description}</h3>
|
||||
<EuiCodeBlock language="ts">{this.removeLicenseBlock(codeBlock.snippet)}</EuiCodeBlock>
|
||||
<EuiHorizontalRule />
|
||||
</React.Fragment>
|
||||
));
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
if (this.state.selectedTab === 'demo') {
|
||||
return this.props.demo;
|
||||
} else if (this.props.codeSections) {
|
||||
return this.renderCodeBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<EuiTabs>{this.renderTabs()}</EuiTabs>
|
||||
{this.renderContent()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,22 +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 { SearchExplorerPlugin } from './plugin';
|
||||
|
||||
export const plugin = () => new SearchExplorerPlugin();
|
|
@ -1,51 +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 React from 'react';
|
||||
|
||||
import {
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
interface PageProps {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Page({ title, children }: PageProps) {
|
||||
return (
|
||||
<EuiPageBody data-test-subj="searchTestPage">
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>{title}</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentBody>{children}</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
);
|
||||
}
|
|
@ -1,65 +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 { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public';
|
||||
import { AppPluginStartDependencies } from './types';
|
||||
import { DeveloperExamplesSetup } from '../../developer_examples/public';
|
||||
interface SetupDeps {
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
}
|
||||
|
||||
export class SearchExplorerPlugin implements Plugin {
|
||||
public setup(
|
||||
core: CoreSetup<AppPluginStartDependencies, void>,
|
||||
{ developerExamples }: SetupDeps
|
||||
) {
|
||||
core.application.register({
|
||||
id: 'searchExplorer',
|
||||
title: 'Search Explorer',
|
||||
navLinkStatus: AppNavLinkStatus.hidden,
|
||||
async mount(params: AppMountParameters) {
|
||||
const [coreStart, depsStart] = await core.getStartServices();
|
||||
const { renderApp } = await import('./application');
|
||||
return renderApp(coreStart, depsStart, params);
|
||||
},
|
||||
});
|
||||
|
||||
developerExamples.register({
|
||||
appId: 'searchExplorer',
|
||||
title: 'Data search strategy services',
|
||||
description: `Data search services can be used to query Elasticsearch in away that supports background search
|
||||
and partial results, when available. It also automatically incorporates settings such as requestTimeout and includeFrozen.
|
||||
Use the provided ES search strategy, or register your own.
|
||||
`,
|
||||
links: [
|
||||
{
|
||||
label: 'README',
|
||||
href:
|
||||
'https://github.com/elastic/kibana/blob/master/src/plugins/data/public/search/README.md',
|
||||
iconType: 'logoGithub',
|
||||
size: 's',
|
||||
target: '_blank',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
public start() {}
|
||||
public stop() {}
|
||||
}
|
|
@ -1,56 +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 React from 'react';
|
||||
import { GuideSection } from './guide_section';
|
||||
|
||||
// @ts-ignore
|
||||
import publicSearch from '!!raw-loader!./../../../src/plugins/data/public/search/i_search';
|
||||
// @ts-ignore
|
||||
import publicPlugin from '!!raw-loader!./../../../src/plugins/data/public/search/search_service';
|
||||
// @ts-ignore
|
||||
import serverPlugin from '!!raw-loader!./../../../src/plugins/data/server/search/search_service';
|
||||
|
||||
export const SearchApiPage = () => (
|
||||
<GuideSection
|
||||
codeSections={[
|
||||
{
|
||||
title: 'Public',
|
||||
code: [
|
||||
{
|
||||
description: 'search_service.ts',
|
||||
snippet: publicPlugin,
|
||||
},
|
||||
{
|
||||
description: 'i_search',
|
||||
snippet: publicSearch,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Server',
|
||||
code: [
|
||||
{
|
||||
description: 'search_service.ts',
|
||||
snippet: serverPlugin,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
|
@ -1,31 +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 { CoreStart } from 'kibana/public';
|
||||
import { DataPublicPluginStart } from '../../../src/plugins/data/public';
|
||||
|
||||
export interface AppPluginStartDependencies {
|
||||
data: DataPublicPluginStart;
|
||||
}
|
||||
|
||||
export interface SearchBarComponentParams {
|
||||
application: CoreStart['application'];
|
||||
basename: string;
|
||||
data: DataPublicPluginStart;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./target",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"public/**/*.ts",
|
||||
"public/**/*.tsx",
|
||||
"server/**/*.ts",
|
||||
"../../typings/**/*",
|
||||
],
|
||||
"exclude": []
|
||||
}
|
|
@ -17,4 +17,9 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { IEsSearchRequest, IEsSearchResponse, ES_SEARCH_STRATEGY } from './types';
|
||||
export {
|
||||
ISearchRequestParams,
|
||||
IEsSearchRequest,
|
||||
IEsSearchResponse,
|
||||
ES_SEARCH_STRATEGY,
|
||||
} from './types';
|
||||
|
|
|
@ -21,11 +21,15 @@ import { IKibanaSearchRequest, IKibanaSearchResponse } from '../types';
|
|||
|
||||
export const ES_SEARCH_STRATEGY = 'es';
|
||||
|
||||
export type ISearchRequestParams = {
|
||||
trackTotalHits?: boolean;
|
||||
} & SearchParams;
|
||||
|
||||
export interface IEsSearchRequest extends IKibanaSearchRequest {
|
||||
params: SearchParams;
|
||||
params?: ISearchRequestParams;
|
||||
indexType?: string;
|
||||
}
|
||||
|
||||
export interface IEsSearchResponse<Hits = unknown> extends IKibanaSearchResponse {
|
||||
rawResponse: SearchResponse<Hits>;
|
||||
export interface IEsSearchResponse extends IKibanaSearchResponse {
|
||||
rawResponse: SearchResponse<any>;
|
||||
}
|
||||
|
|
|
@ -23,4 +23,9 @@ export { IKibanaSearchResponse, IKibanaSearchRequest } from './types';
|
|||
|
||||
export const DEFAULT_SEARCH_STRATEGY = ES_SEARCH_STRATEGY;
|
||||
|
||||
export { IEsSearchRequest, IEsSearchResponse, ES_SEARCH_STRATEGY } from './es_search';
|
||||
export {
|
||||
IEsSearchRequest,
|
||||
IEsSearchResponse,
|
||||
ES_SEARCH_STRATEGY,
|
||||
ISearchRequestParams,
|
||||
} from './es_search';
|
||||
|
|
|
@ -38,31 +38,10 @@ describe('AbortUtils', () => {
|
|||
});
|
||||
|
||||
describe('toPromise', () => {
|
||||
describe('resolves', () => {
|
||||
test('should not resolve if the signal does not abort', async () => {
|
||||
const controller = new AbortController();
|
||||
const promise = toPromise(controller.signal);
|
||||
const whenResolved = jest.fn();
|
||||
promise.then(whenResolved);
|
||||
await flushPromises();
|
||||
expect(whenResolved).not.toBeCalled();
|
||||
});
|
||||
|
||||
test('should resolve if the signal does abort', async () => {
|
||||
const controller = new AbortController();
|
||||
const promise = toPromise(controller.signal);
|
||||
const whenResolved = jest.fn();
|
||||
promise.then(whenResolved);
|
||||
controller.abort();
|
||||
await flushPromises();
|
||||
expect(whenResolved).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rejects', () => {
|
||||
test('should not reject if the signal does not abort', async () => {
|
||||
const controller = new AbortController();
|
||||
const promise = toPromise(controller.signal, true);
|
||||
const promise = toPromise(controller.signal);
|
||||
const whenRejected = jest.fn();
|
||||
promise.catch(whenRejected);
|
||||
await flushPromises();
|
||||
|
@ -71,12 +50,13 @@ describe('AbortUtils', () => {
|
|||
|
||||
test('should reject if the signal does abort', async () => {
|
||||
const controller = new AbortController();
|
||||
const promise = toPromise(controller.signal, true);
|
||||
const promise = toPromise(controller.signal);
|
||||
const whenRejected = jest.fn();
|
||||
promise.catch(whenRejected);
|
||||
controller.abort();
|
||||
await flushPromises();
|
||||
expect(whenRejected).toBeCalled();
|
||||
expect(whenRejected.mock.calls[0][0]).toBeInstanceOf(AbortError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -110,5 +90,13 @@ describe('AbortUtils', () => {
|
|||
await flushPromises();
|
||||
expect(signal.aborted).toBe(true);
|
||||
});
|
||||
|
||||
test('should be aborted if any of the signals is already aborted', async () => {
|
||||
const controller1 = new AbortController();
|
||||
const controller2 = new AbortController();
|
||||
controller1.abort();
|
||||
const signal = getCombinedSignal([controller1.signal, controller2.signal]);
|
||||
expect(signal.aborted).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,24 +35,16 @@ export class AbortError extends Error {
|
|||
* with any other expected errors (or completions).
|
||||
*
|
||||
* @param signal The `AbortSignal` to generate the `Promise` from
|
||||
* @param shouldReject If `false`, the promise will be resolved, otherwise it will be rejected
|
||||
*/
|
||||
export function toPromise(signal: AbortSignal, shouldReject?: false): Promise<undefined | Event>;
|
||||
export function toPromise(signal: AbortSignal, shouldReject?: true): Promise<never>;
|
||||
export function toPromise(signal: AbortSignal, shouldReject: boolean = false) {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const action = shouldReject ? reject : resolve;
|
||||
if (signal.aborted) action();
|
||||
signal.addEventListener('abort', action);
|
||||
export function toPromise(signal: AbortSignal): Promise<never> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (signal.aborted) reject(new AbortError());
|
||||
const abortHandler = () => {
|
||||
signal.removeEventListener('abort', abortHandler);
|
||||
reject(new AbortError());
|
||||
};
|
||||
signal.addEventListener('abort', abortHandler);
|
||||
});
|
||||
|
||||
/**
|
||||
* Below is to make sure we don't have unhandled promise rejections. Otherwise
|
||||
* Jest tests fail.
|
||||
*/
|
||||
promise.catch(() => {});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,8 +53,12 @@ export function toPromise(signal: AbortSignal, shouldReject: boolean = false) {
|
|||
* @param signals
|
||||
*/
|
||||
export function getCombinedSignal(signals: AbortSignal[]) {
|
||||
const promises = signals.map((signal) => toPromise(signal));
|
||||
const controller = new AbortController();
|
||||
Promise.race(promises).then(() => controller.abort());
|
||||
if (signals.some((signal) => signal.aborted)) {
|
||||
controller.abort();
|
||||
} else {
|
||||
const promises = signals.map((signal) => toPromise(signal));
|
||||
Promise.race(promises).catch(() => controller.abort());
|
||||
}
|
||||
return controller.signal;
|
||||
}
|
||||
|
|
|
@ -335,18 +335,13 @@ export {
|
|||
OptionedValueProp,
|
||||
// search
|
||||
ES_SEARCH_STRATEGY,
|
||||
SYNC_SEARCH_STRATEGY,
|
||||
getEsPreference,
|
||||
getSearchErrorType,
|
||||
ISearchStrategy,
|
||||
ISearch,
|
||||
ISearchOptions,
|
||||
IRequestTypesMap,
|
||||
IResponseTypesMap,
|
||||
ISearchGeneric,
|
||||
IEsSearchResponse,
|
||||
IEsSearchRequest,
|
||||
ISyncSearchRequest,
|
||||
IKibanaSearchResponse,
|
||||
IKibanaSearchRequest,
|
||||
SearchRequest,
|
||||
|
@ -366,6 +361,7 @@ export {
|
|||
TabbedAggRow,
|
||||
TabbedTable,
|
||||
SearchInterceptor,
|
||||
SearchInterceptorDeps,
|
||||
RequestTimeoutError,
|
||||
} from './search';
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import _ from 'lodash';
|
|||
import { Action } from 'history';
|
||||
import { ApplicationStart } from 'kibana/public';
|
||||
import { Assign } from '@kbn/utility-types';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import Boom from 'boom';
|
||||
import { Breadcrumb } from '@elastic/eui';
|
||||
import { BulkIndexDocumentsParams } from 'elasticsearch';
|
||||
|
@ -760,22 +761,14 @@ export function getQueryLog(uiSettings: IUiSettingsClient, storage: IStorageWrap
|
|||
// @public (undocumented)
|
||||
export function getSearchErrorType({ message }: Pick<SearchError, 'message'>): "UNSUPPORTED_QUERY" | undefined;
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "ISearchRequestParams" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "getSearchParamsFromRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export function getSearchParamsFromRequest(searchRequest: SearchRequest, dependencies: {
|
||||
injectedMetadata: CoreStart['injectedMetadata'];
|
||||
uiSettings: IUiSettingsClient_3;
|
||||
}): {
|
||||
rest_total_hits_as_int: boolean;
|
||||
ignore_unavailable: boolean;
|
||||
ignore_throttled: boolean;
|
||||
max_concurrent_shard_requests: any;
|
||||
preference: any;
|
||||
timeout: string | undefined;
|
||||
index: any;
|
||||
body: any;
|
||||
};
|
||||
}): ISearchRequestParams;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getTime" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
@ -828,15 +821,15 @@ export interface IEsSearchRequest extends IKibanaSearchRequest {
|
|||
// (undocumented)
|
||||
indexType?: string;
|
||||
// (undocumented)
|
||||
params: SearchParams;
|
||||
params?: ISearchRequestParams;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "IEsSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface IEsSearchResponse<Hits = unknown> extends IKibanaSearchResponse {
|
||||
export interface IEsSearchResponse extends IKibanaSearchResponse {
|
||||
// (undocumented)
|
||||
rawResponse: SearchResponse_2<Hits>;
|
||||
rawResponse: SearchResponse_2<any>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "IFieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
@ -1237,41 +1230,16 @@ export type InputTimeRange = TimeRange | {
|
|||
to: Moment;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "IRequestTypesMap" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface IRequestTypesMap {
|
||||
// (undocumented)
|
||||
[key: string]: IKibanaSearchRequest;
|
||||
// (undocumented)
|
||||
[ES_SEARCH_STRATEGY]: IEsSearchRequest;
|
||||
// (undocumented)
|
||||
[SYNC_SEARCH_STRATEGY]: ISyncSearchRequest;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "IResponseTypesMap" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface IResponseTypesMap {
|
||||
// (undocumented)
|
||||
[key: string]: IKibanaSearchResponse;
|
||||
// (undocumented)
|
||||
[ES_SEARCH_STRATEGY]: IEsSearchResponse;
|
||||
// (undocumented)
|
||||
[SYNC_SEARCH_STRATEGY]: IKibanaSearchResponse;
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "TStrategyTypes" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-forgotten-export) The symbol "DEFAULT_SEARCH_STRATEGY" needs to be exported by the entry point index.d.ts
|
||||
// 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<T extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY> = (request: IRequestTypesMap[T], options?: ISearchOptions) => Observable<IResponseTypesMap[T]>;
|
||||
export type ISearch = (request: IKibanaSearchRequest, options?: ISearchOptions) => Observable<IKibanaSearchResponse>;
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "IStrategyOptions" needs to be exported by the entry point index.d.ts
|
||||
// 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)
|
||||
export type ISearchGeneric = <T extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY>(request: IRequestTypesMap[T], options?: ISearchOptions, strategy?: T) => Observable<IResponseTypesMap[T]>;
|
||||
export type ISearchGeneric = (request: IEsSearchRequest, options?: IStrategyOptions) => Observable<IEsSearchResponse>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ISearchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
@ -1286,14 +1254,6 @@ export interface ISearchOptions {
|
|||
// @public (undocumented)
|
||||
export type ISearchSource = Pick<SearchSource, keyof SearchSource>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ISearchStrategy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export interface ISearchStrategy<T extends TStrategyTypes> {
|
||||
// (undocumented)
|
||||
search: ISearch<T>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "isFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -1314,14 +1274,6 @@ export const isQuery: (x: unknown) => x is Query;
|
|||
// @public (undocumented)
|
||||
export const isTimeRange: (x: unknown) => x is TimeRange;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ISyncSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface ISyncSearchRequest extends IKibanaSearchRequest {
|
||||
// (undocumented)
|
||||
serverStrategy: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export enum KBN_FIELD_TYPES {
|
||||
// (undocumented)
|
||||
|
@ -1781,22 +1733,43 @@ export class SearchError extends Error {
|
|||
//
|
||||
// @public (undocumented)
|
||||
export class SearchInterceptor {
|
||||
constructor(toasts: ToastsStart, application: ApplicationStart, requestTimeout?: number | undefined);
|
||||
constructor(deps: SearchInterceptorDeps, requestTimeout?: number | undefined);
|
||||
protected abortController: AbortController;
|
||||
// (undocumented)
|
||||
protected readonly application: ApplicationStart;
|
||||
getPendingCount$: () => import("rxjs").Observable<number>;
|
||||
protected readonly deps: SearchInterceptorDeps;
|
||||
getPendingCount$: () => Observable<number>;
|
||||
// (undocumented)
|
||||
protected hideToast: () => void;
|
||||
protected longRunningToast?: Toast;
|
||||
protected pendingCount$: BehaviorSubject<number>;
|
||||
protected pendingCount: number;
|
||||
// (undocumented)
|
||||
protected readonly requestTimeout?: number | undefined;
|
||||
search: (search: ISearchGeneric, request: IKibanaSearchRequest, options?: ISearchOptions | undefined) => import("rxjs").Observable<import("../../common/search").IEsSearchResponse<unknown>>;
|
||||
// (undocumented)
|
||||
protected runSearch(request: IEsSearchRequest, combinedSignal: AbortSignal): Observable<IEsSearchResponse>;
|
||||
search(request: IEsSearchRequest, options?: ISearchOptions): Observable<IEsSearchResponse>;
|
||||
// (undocumented)
|
||||
protected setupTimers(options?: ISearchOptions): {
|
||||
combinedSignal: AbortSignal;
|
||||
cleanup: () => void;
|
||||
};
|
||||
// (undocumented)
|
||||
protected showToast: () => void;
|
||||
protected timeoutSubscriptions: Set<Subscription>;
|
||||
protected timeoutSubscriptions: Subscription;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SearchInterceptorDeps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface SearchInterceptorDeps {
|
||||
// (undocumented)
|
||||
protected readonly toasts: ToastsStart;
|
||||
application: ApplicationStart;
|
||||
// (undocumented)
|
||||
http: CoreStart['http'];
|
||||
// (undocumented)
|
||||
toasts: ToastsStart;
|
||||
// (undocumented)
|
||||
uiSettings: CoreStart['uiSettings'];
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
@ -1869,11 +1842,6 @@ export type StatefulSearchBarProps = SearchBarOwnProps & {
|
|||
onSavedQueryIdChange?: (savedQueryId?: string) => void;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SYNC_SEARCH_STRATEGY" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const SYNC_SEARCH_STRATEGY = "SYNC_SEARCH_STRATEGY";
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "IKbnUrlStateStorage" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "syncQueryStateWithUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
@ -2011,20 +1979,20 @@ export const UI_SETTINGS: {
|
|||
// src/plugins/data/public/index.ts:233:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:233:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:233:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:373:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:373:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:373:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:373:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:375:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:376:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:369:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:371:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:372:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:381:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:382:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:395: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:40:60 - (ae-forgotten-export) The symbol "FilterStateStore" 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
|
||||
// src/plugins/data/public/types.ts:53:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts
|
||||
|
|
|
@ -1,47 +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 { CoreSetup } from '../../../../../core/public';
|
||||
import { coreMock } from '../../../../../core/public/mocks';
|
||||
import { esSearchStrategyProvider } from './es_search_strategy';
|
||||
import { ES_SEARCH_STRATEGY } from '../../../common/search/es_search';
|
||||
|
||||
describe('ES search strategy', () => {
|
||||
let mockCoreSetup: MockedKeys<CoreSetup>;
|
||||
const mockSearch = { search: jest.fn() };
|
||||
|
||||
beforeEach(() => {
|
||||
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(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]).toBe(options);
|
||||
});
|
||||
});
|
|
@ -1,43 +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 { 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';
|
||||
|
||||
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 };
|
||||
}
|
|
@ -17,5 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { esSearchStrategyProvider } from './es_search_strategy';
|
||||
export { getEsPreference } from './get_es_preference';
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { IUiSettingsClient, CoreStart } from 'kibana/public';
|
||||
import { UI_SETTINGS } from '../../../common';
|
||||
import { UI_SETTINGS, ISearchRequestParams } from '../../../common';
|
||||
import { SearchRequest } from './types';
|
||||
|
||||
const sessionId = Date.now();
|
||||
|
@ -58,7 +58,7 @@ export function getTimeout(esShardTimeout: number) {
|
|||
export function getSearchParamsFromRequest(
|
||||
searchRequest: SearchRequest,
|
||||
dependencies: { injectedMetadata: CoreStart['injectedMetadata']; uiSettings: IUiSettingsClient }
|
||||
) {
|
||||
): ISearchRequestParams {
|
||||
const { injectedMetadata, uiSettings } = dependencies;
|
||||
const esShardTimeout = injectedMetadata.getInjectedVar('esShardTimeout') as number;
|
||||
const searchParams = getSearchParams(uiSettings, esShardTimeout);
|
||||
|
|
|
@ -1,59 +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 { Observable } from 'rxjs';
|
||||
import { TStrategyTypes } from './strategy_types';
|
||||
import {
|
||||
DEFAULT_SEARCH_STRATEGY,
|
||||
IKibanaSearchRequest,
|
||||
IKibanaSearchResponse,
|
||||
} from '../../common/search';
|
||||
import { SYNC_SEARCH_STRATEGY, ISyncSearchRequest } from './sync_search_strategy';
|
||||
import {
|
||||
ES_SEARCH_STRATEGY,
|
||||
IEsSearchRequest,
|
||||
IEsSearchResponse,
|
||||
} from '../../common/search/es_search';
|
||||
|
||||
export interface ISearchOptions {
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export interface IRequestTypesMap {
|
||||
[SYNC_SEARCH_STRATEGY]: ISyncSearchRequest;
|
||||
[ES_SEARCH_STRATEGY]: IEsSearchRequest;
|
||||
[key: string]: IKibanaSearchRequest;
|
||||
}
|
||||
|
||||
export interface IResponseTypesMap {
|
||||
[SYNC_SEARCH_STRATEGY]: IKibanaSearchResponse;
|
||||
[ES_SEARCH_STRATEGY]: IEsSearchResponse;
|
||||
[key: string]: IKibanaSearchResponse;
|
||||
}
|
||||
|
||||
export type ISearchGeneric = <T extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY>(
|
||||
request: IRequestTypesMap[T],
|
||||
options?: ISearchOptions,
|
||||
strategy?: T
|
||||
) => Observable<IResponseTypesMap[T]>;
|
||||
|
||||
export type ISearch<T extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY> = (
|
||||
request: IRequestTypesMap[T],
|
||||
options?: ISearchOptions
|
||||
) => Observable<IResponseTypesMap[T]>;
|
|
@ -21,20 +21,11 @@ export * from './aggs';
|
|||
export * from './expressions';
|
||||
export * from './tabify';
|
||||
|
||||
export { ISearchSetup, ISearchStart, ISearchStrategy } from './types';
|
||||
|
||||
export {
|
||||
ISearch,
|
||||
ISearchOptions,
|
||||
IRequestTypesMap,
|
||||
IResponseTypesMap,
|
||||
ISearchGeneric,
|
||||
} from './i_search';
|
||||
export { ISearch, ISearchOptions, ISearchGeneric, ISearchSetup, ISearchStart } from './types';
|
||||
|
||||
export { IEsSearchResponse, IEsSearchRequest, ES_SEARCH_STRATEGY } from '../../common/search';
|
||||
|
||||
export { ISyncSearchRequest, SYNC_SEARCH_STRATEGY } from './sync_search_strategy';
|
||||
export { esSearchStrategyProvider, getEsPreference } from './es_search';
|
||||
export { getEsPreference } from './es_search';
|
||||
|
||||
export { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search';
|
||||
|
||||
|
@ -59,5 +50,5 @@ export {
|
|||
parseSearchSourceJSON,
|
||||
} from './search_source';
|
||||
|
||||
export { SearchInterceptor } from './search_interceptor';
|
||||
export { SearchInterceptor, SearchInterceptorDeps } from './search_interceptor';
|
||||
export { RequestTimeoutError } from './request_timeout_error';
|
||||
|
|
|
@ -26,7 +26,6 @@ export * from './search_source/mocks';
|
|||
function createSetupContract(): jest.Mocked<ISearchSetup> {
|
||||
return {
|
||||
aggs: searchAggsSetupMock(),
|
||||
registerSearchStrategy: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -34,7 +33,6 @@ function createStartContract(): jest.Mocked<ISearchStart> {
|
|||
return {
|
||||
aggs: searchAggsStartMock(),
|
||||
setInterceptor: jest.fn(),
|
||||
getSearchStrategy: jest.fn(),
|
||||
search: jest.fn(),
|
||||
searchSource: searchSourceMock,
|
||||
__LEGACY: {
|
||||
|
|
|
@ -17,114 +17,169 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { CoreStart } from '../../../../core/public';
|
||||
import { coreMock } from '../../../../core/public/mocks';
|
||||
import { IKibanaSearchRequest } from '../../common/search';
|
||||
import { RequestTimeoutError } from './request_timeout_error';
|
||||
import { IEsSearchRequest } from '../../common/search';
|
||||
import { SearchInterceptor } from './search_interceptor';
|
||||
import { AbortError } from '../../common';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const mockSearch = jest.fn();
|
||||
let searchInterceptor: SearchInterceptor;
|
||||
let mockCoreStart: MockedKeys<CoreStart>;
|
||||
|
||||
const flushPromises = () => new Promise((resolve) => setImmediate(resolve));
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('SearchInterceptor', () => {
|
||||
beforeEach(() => {
|
||||
mockCoreStart = coreMock.createStart();
|
||||
mockSearch.mockClear();
|
||||
searchInterceptor = new SearchInterceptor(
|
||||
mockCoreStart.notifications.toasts,
|
||||
mockCoreStart.application,
|
||||
{
|
||||
toasts: mockCoreStart.notifications.toasts,
|
||||
application: mockCoreStart.application,
|
||||
uiSettings: mockCoreStart.uiSettings,
|
||||
http: mockCoreStart.http,
|
||||
},
|
||||
1000
|
||||
);
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
test('should invoke `search` with the request', () => {
|
||||
const mockResponse = new Subject();
|
||||
mockSearch.mockReturnValue(mockResponse.asObservable());
|
||||
const mockRequest: IKibanaSearchRequest = {};
|
||||
const response = searchInterceptor.search(mockSearch, mockRequest);
|
||||
mockResponse.complete();
|
||||
test('Observable should resolve if fetch is successful', async () => {
|
||||
const mockResponse: any = { result: 200 };
|
||||
mockCoreStart.http.fetch.mockResolvedValueOnce(mockResponse);
|
||||
const mockRequest: IEsSearchRequest = {
|
||||
params: {},
|
||||
};
|
||||
const response = searchInterceptor.search(mockRequest);
|
||||
|
||||
response.subscribe();
|
||||
expect(mockSearch.mock.calls[0][0]).toBe(mockRequest);
|
||||
const result = await response.toPromise();
|
||||
expect(result).toBe(mockResponse);
|
||||
});
|
||||
|
||||
test('should mirror the observable to completion if the request does not time out', () => {
|
||||
const mockResponse = new Subject();
|
||||
mockSearch.mockReturnValue(mockResponse.asObservable());
|
||||
const response = searchInterceptor.search(mockSearch, {});
|
||||
test('Observable should fail if fetch has an error', async () => {
|
||||
const mockResponse: any = { result: 500 };
|
||||
mockCoreStart.http.fetch.mockRejectedValueOnce(mockResponse);
|
||||
const mockRequest: IEsSearchRequest = {
|
||||
params: {},
|
||||
};
|
||||
const response = searchInterceptor.search(mockRequest);
|
||||
|
||||
setTimeout(() => mockResponse.next('hi'), 250);
|
||||
setTimeout(() => mockResponse.complete(), 500);
|
||||
|
||||
const next = jest.fn();
|
||||
const complete = jest.fn();
|
||||
response.subscribe({ next, complete });
|
||||
|
||||
jest.advanceTimersByTime(1000);
|
||||
|
||||
expect(next).toHaveBeenCalledWith('hi');
|
||||
expect(complete).toHaveBeenCalled();
|
||||
try {
|
||||
await response.toPromise();
|
||||
} catch (e) {
|
||||
expect(e).toBe(mockResponse);
|
||||
}
|
||||
});
|
||||
|
||||
test('should mirror the observable to error if the request does not time out', () => {
|
||||
const mockResponse = new Subject();
|
||||
mockSearch.mockReturnValue(mockResponse.asObservable());
|
||||
const response = searchInterceptor.search(mockSearch, {});
|
||||
test('Observable should fail if fetch times out (test merged signal)', async () => {
|
||||
mockCoreStart.http.fetch.mockImplementationOnce((options: any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
options.signal.addEventListener('abort', () => {
|
||||
reject(new AbortError());
|
||||
});
|
||||
|
||||
setTimeout(() => mockResponse.next('hi'), 250);
|
||||
setTimeout(() => mockResponse.error('error'), 500);
|
||||
setTimeout(resolve, 5000);
|
||||
});
|
||||
});
|
||||
const mockRequest: IEsSearchRequest = {
|
||||
params: {},
|
||||
};
|
||||
const response = searchInterceptor.search(mockRequest);
|
||||
|
||||
const next = jest.fn();
|
||||
const error = jest.fn();
|
||||
const error = (e: any) => {
|
||||
expect(next).not.toBeCalled();
|
||||
expect(e).toBeInstanceOf(AbortError);
|
||||
};
|
||||
response.subscribe({ next, error });
|
||||
|
||||
jest.advanceTimersByTime(1000);
|
||||
jest.advanceTimersByTime(5000);
|
||||
|
||||
expect(next).toHaveBeenCalledWith('hi');
|
||||
expect(error).toHaveBeenCalledWith('error');
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
test('should return a `RequestTimeoutError` if the request times out', () => {
|
||||
mockSearch.mockReturnValue(new Observable());
|
||||
const response = searchInterceptor.search(mockSearch, {});
|
||||
test('Observable should fail if user aborts (test merged signal)', async () => {
|
||||
const abortController = new AbortController();
|
||||
mockCoreStart.http.fetch.mockImplementationOnce((options: any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
options.signal.addEventListener('abort', () => {
|
||||
reject(new AbortError());
|
||||
});
|
||||
|
||||
const error = jest.fn();
|
||||
setTimeout(resolve, 500);
|
||||
});
|
||||
});
|
||||
const mockRequest: IEsSearchRequest = {
|
||||
params: {},
|
||||
};
|
||||
const response = searchInterceptor.search(mockRequest, { signal: abortController.signal });
|
||||
|
||||
const next = jest.fn();
|
||||
const error = (e: any) => {
|
||||
expect(next).not.toBeCalled();
|
||||
expect(e).toBeInstanceOf(AbortError);
|
||||
};
|
||||
response.subscribe({ next, error });
|
||||
setTimeout(() => abortController.abort(), 200);
|
||||
jest.advanceTimersByTime(5000);
|
||||
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
test('Immediatelly aborts if passed an aborted abort signal', async (done) => {
|
||||
const abort = new AbortController();
|
||||
const mockRequest: IEsSearchRequest = {
|
||||
params: {},
|
||||
};
|
||||
const response = searchInterceptor.search(mockRequest, { signal: abort.signal });
|
||||
abort.abort();
|
||||
|
||||
const error = (e: any) => {
|
||||
expect(e).toBeInstanceOf(AbortError);
|
||||
expect(mockCoreStart.http.fetch).not.toBeCalled();
|
||||
done();
|
||||
};
|
||||
response.subscribe({ error });
|
||||
|
||||
jest.advanceTimersByTime(1000);
|
||||
|
||||
expect(error).toHaveBeenCalled();
|
||||
expect(error.mock.calls[0][0] instanceof RequestTimeoutError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPendingCount$', () => {
|
||||
test('should observe the number of pending requests', () => {
|
||||
let i = 0;
|
||||
const mockResponses = [new Subject(), new Subject()];
|
||||
mockSearch.mockImplementation(() => mockResponses[i++]);
|
||||
|
||||
const pendingCount$ = searchInterceptor.getPendingCount$();
|
||||
const pendingNext = jest.fn();
|
||||
pendingCount$.subscribe(pendingNext);
|
||||
|
||||
const next = jest.fn();
|
||||
pendingCount$.subscribe(next);
|
||||
const mockResponse: any = { result: 200 };
|
||||
mockCoreStart.http.fetch.mockResolvedValue(mockResponse);
|
||||
const mockRequest: IEsSearchRequest = {
|
||||
params: {},
|
||||
};
|
||||
const response = searchInterceptor.search(mockRequest);
|
||||
|
||||
const error = jest.fn();
|
||||
searchInterceptor.search(mockSearch, {}).subscribe({ error });
|
||||
searchInterceptor.search(mockSearch, {}).subscribe({ error });
|
||||
response.subscribe({
|
||||
complete: () => {
|
||||
expect(pendingNext.mock.calls).toEqual([[0], [1], [0]]);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(() => mockResponses[0].complete(), 250);
|
||||
setTimeout(() => mockResponses[1].error('error'), 500);
|
||||
test('should observe the number of pending requests on error', () => {
|
||||
const pendingCount$ = searchInterceptor.getPendingCount$();
|
||||
const pendingNext = jest.fn();
|
||||
pendingCount$.subscribe(pendingNext);
|
||||
|
||||
jest.advanceTimersByTime(500);
|
||||
const mockResponse: any = { result: 500 };
|
||||
mockCoreStart.http.fetch.mockRejectedValue(mockResponse);
|
||||
const mockRequest: IEsSearchRequest = {
|
||||
params: {},
|
||||
};
|
||||
const response = searchInterceptor.search(mockRequest);
|
||||
|
||||
expect(next).toHaveBeenCalled();
|
||||
expect(next.mock.calls).toEqual([[0], [1], [2], [1], [0]]);
|
||||
response.subscribe({
|
||||
complete: () => {
|
||||
expect(pendingNext.mock.calls).toEqual([[0], [1], [0]]);
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,17 +17,23 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, throwError, timer, Subscription, defer, fromEvent } from 'rxjs';
|
||||
import { takeUntil, finalize, mergeMapTo, filter } from 'rxjs/operators';
|
||||
import { ApplicationStart, Toast, ToastsStart } from 'kibana/public';
|
||||
import { BehaviorSubject, throwError, timer, Subscription, defer, from, Observable } from 'rxjs';
|
||||
import { finalize, filter } from 'rxjs/operators';
|
||||
import { ApplicationStart, Toast, ToastsStart, CoreStart } from 'kibana/public';
|
||||
import { getCombinedSignal, AbortError } from '../../common/utils';
|
||||
import { IKibanaSearchRequest } from '../../common/search';
|
||||
import { ISearchGeneric, ISearchOptions } from './i_search';
|
||||
import { RequestTimeoutError } from './request_timeout_error';
|
||||
import { IEsSearchRequest, IEsSearchResponse } from '../../common/search';
|
||||
import { ISearchOptions } from './types';
|
||||
import { getLongQueryNotification } from './long_query_notification';
|
||||
|
||||
const LONG_QUERY_NOTIFICATION_DELAY = 10000;
|
||||
|
||||
export interface SearchInterceptorDeps {
|
||||
toasts: ToastsStart;
|
||||
application: ApplicationStart;
|
||||
http: CoreStart['http'];
|
||||
uiSettings: CoreStart['uiSettings'];
|
||||
}
|
||||
|
||||
export class SearchInterceptor {
|
||||
/**
|
||||
* `abortController` used to signal all searches to abort.
|
||||
|
@ -37,17 +43,17 @@ export class SearchInterceptor {
|
|||
/**
|
||||
* The number of pending search requests.
|
||||
*/
|
||||
private pendingCount = 0;
|
||||
protected pendingCount = 0;
|
||||
|
||||
/**
|
||||
* Observable that emits when the number of pending requests changes.
|
||||
*/
|
||||
private pendingCount$ = new BehaviorSubject(this.pendingCount);
|
||||
protected pendingCount$ = new BehaviorSubject(this.pendingCount);
|
||||
|
||||
/**
|
||||
* The subscriptions from scheduling the automatic timeout for each request.
|
||||
*/
|
||||
protected timeoutSubscriptions: Set<Subscription> = new Set();
|
||||
protected timeoutSubscriptions: Subscription = new Subscription();
|
||||
|
||||
/**
|
||||
* The current long-running toast (if there is one).
|
||||
|
@ -62,10 +68,11 @@ export class SearchInterceptor {
|
|||
* @param requestTimeout Usually config value `elasticsearch.requestTimeout`
|
||||
*/
|
||||
constructor(
|
||||
protected readonly toasts: ToastsStart,
|
||||
protected readonly application: ApplicationStart,
|
||||
protected readonly deps: SearchInterceptorDeps,
|
||||
protected readonly requestTimeout?: number
|
||||
) {
|
||||
this.deps.http.addLoadingCountSource(this.pendingCount$);
|
||||
|
||||
// When search requests go out, a notification is scheduled allowing users to continue the
|
||||
// request past the timeout. When all search requests complete, we remove the notification.
|
||||
this.getPendingCount$()
|
||||
|
@ -81,71 +88,91 @@ export class SearchInterceptor {
|
|||
return this.pendingCount$.asObservable();
|
||||
};
|
||||
|
||||
protected runSearch(
|
||||
request: IEsSearchRequest,
|
||||
combinedSignal: AbortSignal
|
||||
): Observable<IEsSearchResponse> {
|
||||
return from(
|
||||
this.deps.http.fetch({
|
||||
path: `/internal/search/es`,
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
signal: combinedSignal,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 the `pendingCount` when the request is started/finalized.
|
||||
*/
|
||||
public search = (
|
||||
search: ISearchGeneric,
|
||||
request: IKibanaSearchRequest,
|
||||
public search(
|
||||
request: IEsSearchRequest,
|
||||
options?: ISearchOptions
|
||||
) => {
|
||||
): Observable<IEsSearchResponse> {
|
||||
// Defer the following logic until `subscribe` is actually called
|
||||
return defer(() => {
|
||||
if (options?.signal?.aborted) {
|
||||
return throwError(new AbortError());
|
||||
}
|
||||
|
||||
const { combinedSignal, cleanup } = this.setupTimers(options);
|
||||
this.pendingCount$.next(++this.pendingCount);
|
||||
|
||||
// Schedule this request to automatically timeout after some interval
|
||||
const timeoutController = new AbortController();
|
||||
const { signal: timeoutSignal } = timeoutController;
|
||||
const timeout$ = timer(this.requestTimeout);
|
||||
const subscription = timeout$.subscribe(() => timeoutController.abort());
|
||||
this.timeoutSubscriptions.add(subscription);
|
||||
|
||||
// If the request timed out, throw a `RequestTimeoutError`
|
||||
const timeoutError$ = fromEvent(timeoutSignal, 'abort').pipe(
|
||||
mergeMapTo(throwError(new RequestTimeoutError()))
|
||||
);
|
||||
|
||||
const userAbort$ = fromEvent(this.abortController.signal, 'abort').pipe(
|
||||
mergeMapTo(throwError(new AbortError()))
|
||||
);
|
||||
|
||||
// Schedule the notification to allow users to cancel or wait beyond the timeout
|
||||
const notificationSubscription = timer(LONG_QUERY_NOTIFICATION_DELAY).subscribe(
|
||||
this.showToast
|
||||
);
|
||||
|
||||
// Get a combined `AbortSignal` that will be aborted whenever the first of the following occurs:
|
||||
// 1. The user manually aborts (via `cancelPending`)
|
||||
// 2. The request times out
|
||||
// 3. The passed-in signal aborts (e.g. when re-fetching, or whenever the app determines)
|
||||
const signals = [
|
||||
this.abortController.signal,
|
||||
timeoutSignal,
|
||||
...(options?.signal ? [options.signal] : []),
|
||||
];
|
||||
const combinedSignal = getCombinedSignal(signals);
|
||||
|
||||
return search(request as any, { ...options, signal: combinedSignal }).pipe(
|
||||
takeUntil(timeoutError$),
|
||||
takeUntil(userAbort$),
|
||||
return this.runSearch(request, combinedSignal).pipe(
|
||||
finalize(() => {
|
||||
this.pendingCount$.next(--this.pendingCount);
|
||||
this.timeoutSubscriptions.delete(subscription);
|
||||
notificationSubscription.unsubscribe();
|
||||
cleanup();
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
protected setupTimers(options?: ISearchOptions) {
|
||||
// Schedule this request to automatically timeout after some interval
|
||||
const timeoutController = new AbortController();
|
||||
const { signal: timeoutSignal } = timeoutController;
|
||||
const timeout$ = timer(this.requestTimeout);
|
||||
const subscription = timeout$.subscribe(() => {
|
||||
timeoutController.abort();
|
||||
});
|
||||
this.timeoutSubscriptions.add(subscription);
|
||||
|
||||
// Schedule the notification to allow users to cancel or wait beyond the timeout
|
||||
const notificationSubscription = timer(LONG_QUERY_NOTIFICATION_DELAY).subscribe(this.showToast);
|
||||
|
||||
// Get a combined `AbortSignal` that will be aborted whenever the first of the following occurs:
|
||||
// 1. The user manually aborts (via `cancelPending`)
|
||||
// 2. The request times out
|
||||
// 3. The passed-in signal aborts (e.g. when re-fetching, or whenever the app determines)
|
||||
const signals = [
|
||||
this.abortController.signal,
|
||||
timeoutSignal,
|
||||
...(options?.signal ? [options.signal] : []),
|
||||
];
|
||||
|
||||
const combinedSignal = getCombinedSignal(signals);
|
||||
const cleanup = () => {
|
||||
this.timeoutSubscriptions.remove(subscription);
|
||||
notificationSubscription.unsubscribe();
|
||||
};
|
||||
|
||||
combinedSignal.addEventListener('abort', cleanup);
|
||||
|
||||
return {
|
||||
combinedSignal,
|
||||
cleanup,
|
||||
};
|
||||
}
|
||||
|
||||
protected showToast = () => {
|
||||
if (this.longRunningToast) return;
|
||||
this.longRunningToast = this.toasts.addInfo(
|
||||
this.longRunningToast = this.deps.toasts.addInfo(
|
||||
{
|
||||
title: 'Your query is taking awhile',
|
||||
text: getLongQueryNotification({
|
||||
application: this.application,
|
||||
application: this.deps.application,
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
@ -156,7 +183,7 @@ export class SearchInterceptor {
|
|||
|
||||
protected hideToast = () => {
|
||||
if (this.longRunningToast) {
|
||||
this.toasts.remove(this.longRunningToast);
|
||||
this.deps.toasts.remove(this.longRunningToast);
|
||||
delete this.longRunningToast;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { coreMock } from '../../../../core/public/mocks';
|
||||
import { CoreSetup } from '../../../../core/public';
|
||||
import { CoreSetup, CoreStart } from '../../../../core/public';
|
||||
import { expressionsPluginMock } from '../../../../plugins/expressions/public/mocks';
|
||||
|
||||
import { SearchService } from './search_service';
|
||||
|
@ -26,10 +26,12 @@ import { SearchService } from './search_service';
|
|||
describe('Search service', () => {
|
||||
let searchService: SearchService;
|
||||
let mockCoreSetup: MockedKeys<CoreSetup>;
|
||||
let mockCoreStart: MockedKeys<CoreStart>;
|
||||
|
||||
beforeEach(() => {
|
||||
searchService = new SearchService();
|
||||
mockCoreSetup = coreMock.createSetup();
|
||||
mockCoreStart = coreMock.createStart();
|
||||
});
|
||||
|
||||
describe('setup()', () => {
|
||||
|
@ -38,7 +40,17 @@ describe('Search service', () => {
|
|||
packageInfo: { version: '8' },
|
||||
expressions: expressionsPluginMock.createSetupContract(),
|
||||
} as any);
|
||||
expect(setup).toHaveProperty('registerSearchStrategy');
|
||||
expect(setup).toHaveProperty('aggs');
|
||||
});
|
||||
});
|
||||
|
||||
describe('start()', () => {
|
||||
it('exposes proper contract', async () => {
|
||||
const start = searchService.start(mockCoreStart, {
|
||||
indexPatterns: {},
|
||||
} as any);
|
||||
expect(start).toHaveProperty('setInterceptor');
|
||||
expect(start).toHaveProperty('search');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,17 +18,14 @@
|
|||
*/
|
||||
|
||||
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 { ISearchSetup, ISearchStart } from './types';
|
||||
import { ExpressionsSetup } from '../../../../plugins/expressions/public';
|
||||
|
||||
import { createSearchSource, SearchSource, SearchSourceDependencies } from './search_source';
|
||||
import { TStrategyTypes } from './strategy_types';
|
||||
import { getEsClient, LegacyApiCaller } from './legacy';
|
||||
import { getForceNow } from '../query/timefilter/lib/get_force_now';
|
||||
import { calculateBounds, TimeRange } from '../../common/query';
|
||||
import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search';
|
||||
import { esSearchStrategyProvider } from './es_search';
|
||||
|
||||
import { IndexPatternsContract } from '../index_patterns/index_patterns';
|
||||
import { GetInternalStartServicesFn } from '../types';
|
||||
import { SearchInterceptor } from './search_interceptor';
|
||||
|
@ -39,7 +36,7 @@ import {
|
|||
AggConfigs,
|
||||
getCalculateAutoTimeExpression,
|
||||
} from './aggs';
|
||||
import { ISearchGeneric } from './i_search';
|
||||
import { ISearchGeneric } from './types';
|
||||
|
||||
interface SearchServiceSetupDependencies {
|
||||
expressions: ExpressionsSetup;
|
||||
|
@ -51,38 +48,11 @@ interface SearchServiceStartDependencies {
|
|||
indexPatterns: IndexPatternsContract;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
||||
/**
|
||||
* A mapping of search strategies keyed by a unique identifier. Plugins can use this unique identifier
|
||||
* to override certain strategy implementations.
|
||||
*/
|
||||
private searchStrategies: TSearchStrategiesMap = {};
|
||||
|
||||
private esClient?: LegacyApiCaller;
|
||||
private readonly aggTypesRegistry = new AggTypesRegistry();
|
||||
private searchInterceptor!: SearchInterceptor;
|
||||
|
||||
private registerSearchStrategy = <T extends TStrategyTypes>(
|
||||
name: T,
|
||||
strategy: ISearchStrategy<T>
|
||||
) => {
|
||||
this.searchStrategies[name] = strategy;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* getForceNow uses window.location, so we must have a separate implementation
|
||||
* of calculateBounds on the client and the server.
|
||||
|
@ -96,11 +66,6 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
): ISearchSetup {
|
||||
this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo);
|
||||
|
||||
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();
|
||||
|
||||
// register each agg type
|
||||
|
@ -121,7 +86,6 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings),
|
||||
types: aggTypesSetup,
|
||||
},
|
||||
registerSearchStrategy: this.registerSearchStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -133,18 +97,19 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
* their own search collector instances
|
||||
*/
|
||||
this.searchInterceptor = new SearchInterceptor(
|
||||
core.notifications.toasts,
|
||||
core.application,
|
||||
{
|
||||
toasts: core.notifications.toasts,
|
||||
application: core.application,
|
||||
http: core.http,
|
||||
uiSettings: core.uiSettings,
|
||||
},
|
||||
core.injectedMetadata.getInjectedVar('esRequestTimeout') as number
|
||||
);
|
||||
|
||||
const aggTypesStart = this.aggTypesRegistry.start();
|
||||
|
||||
const search: ISearchGeneric = (request, options, strategyName) => {
|
||||
const { search: defaultSearch } = this.getSearchStrategy(
|
||||
strategyName || DEFAULT_SEARCH_STRATEGY
|
||||
);
|
||||
return this.searchInterceptor.search(defaultSearch as any, request, options);
|
||||
const search: ISearchGeneric = (request, options) => {
|
||||
return this.searchInterceptor.search(request, options);
|
||||
};
|
||||
|
||||
const legacySearch = {
|
||||
|
@ -168,7 +133,6 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
},
|
||||
types: aggTypesStart,
|
||||
},
|
||||
getSearchStrategy: this.getSearchStrategy,
|
||||
search,
|
||||
searchSource: {
|
||||
create: createSearchSource(dependencies.indexPatterns, searchSourceDependencies),
|
||||
|
|
|
@ -1,40 +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 { ES_SEARCH_STRATEGY } from '../../common/search/es_search';
|
||||
import { SYNC_SEARCH_STRATEGY } from './sync_search_strategy';
|
||||
|
||||
/**
|
||||
* Contains all known strategy type identifiers that will be used to map to
|
||||
* request and response shapes. Plugins that wish to add their own custom search
|
||||
* strategies should extend this type via:
|
||||
*
|
||||
* const MY_STRATEGY = 'MY_STRATEGY';
|
||||
*
|
||||
* declare module 'src/plugins/data/public' {
|
||||
* export interface IRequestTypesMap {
|
||||
* [MY_STRATEGY]: IMySearchRequest;
|
||||
* }
|
||||
*
|
||||
* export interface IResponseTypesMap {
|
||||
* [MY_STRATEGY]: IMySearchResponse
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export type TStrategyTypes = typeof SYNC_SEARCH_STRATEGY | typeof ES_SEARCH_STRATEGY | string;
|
|
@ -1,84 +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 { coreMock } from '../../../../core/public/mocks';
|
||||
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
|
||||
describe('Sync search strategy', () => {
|
||||
let mockCoreSetup: MockedKeys<CoreSetup>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCoreSetup = coreMock.createSetup();
|
||||
});
|
||||
|
||||
it('returns a strategy with `search` that calls the backend API', () => {
|
||||
mockCoreSetup.http.fetch.mockImplementationOnce(() => Promise.resolve());
|
||||
|
||||
const syncSearch = syncSearchStrategyProvider(mockCoreSetup);
|
||||
const request = { serverStrategy: SYNC_SEARCH_STRATEGY };
|
||||
syncSearch.search(request, {});
|
||||
|
||||
expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toEqual({
|
||||
path: `/internal/search/${SYNC_SEARCH_STRATEGY}`,
|
||||
body: JSON.stringify({
|
||||
serverStrategy: 'SYNC_SEARCH_STRATEGY',
|
||||
}),
|
||||
method: 'POST',
|
||||
signal: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('increments and decrements loading count on success', async () => {
|
||||
const expectedLoadingCountValues = [0, 1, 0];
|
||||
const receivedLoadingCountValues: number[] = [];
|
||||
|
||||
mockCoreSetup.http.fetch.mockResolvedValueOnce('response');
|
||||
|
||||
const syncSearch = syncSearchStrategyProvider(mockCoreSetup);
|
||||
const request = { serverStrategy: SYNC_SEARCH_STRATEGY };
|
||||
|
||||
const loadingCount$ = mockCoreSetup.http.addLoadingCountSource.mock.calls[0][0];
|
||||
loadingCount$.subscribe((value) => receivedLoadingCountValues.push(value));
|
||||
|
||||
await syncSearch.search(request, {}).toPromise();
|
||||
|
||||
expect(receivedLoadingCountValues).toEqual(expectedLoadingCountValues);
|
||||
});
|
||||
|
||||
it('increments and decrements loading count on failure', async () => {
|
||||
expect.assertions(1);
|
||||
const expectedLoadingCountValues = [0, 1, 0];
|
||||
const receivedLoadingCountValues: number[] = [];
|
||||
|
||||
mockCoreSetup.http.fetch.mockRejectedValueOnce('error');
|
||||
|
||||
const syncSearch = syncSearchStrategyProvider(mockCoreSetup);
|
||||
const request = { serverStrategy: SYNC_SEARCH_STRATEGY };
|
||||
|
||||
const loadingCount$ = mockCoreSetup.http.addLoadingCountSource.mock.calls[0][0];
|
||||
loadingCount$.subscribe((value) => receivedLoadingCountValues.push(value));
|
||||
|
||||
try {
|
||||
await syncSearch.search(request, {}).toPromise();
|
||||
} catch (e) {
|
||||
expect(receivedLoadingCountValues).toEqual(expectedLoadingCountValues);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, from } from 'rxjs';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
import { CoreSetup } from '../../../../core/public';
|
||||
import { IKibanaSearchRequest } from '../../common/search';
|
||||
import { ISearch } from './i_search';
|
||||
|
||||
export const SYNC_SEARCH_STRATEGY = 'SYNC_SEARCH_STRATEGY';
|
||||
|
||||
export interface ISyncSearchRequest extends IKibanaSearchRequest {
|
||||
serverStrategy: string;
|
||||
}
|
||||
|
||||
export function syncSearchStrategyProvider(core: CoreSetup) {
|
||||
const loadingCount$ = new BehaviorSubject(0);
|
||||
core.http.addLoadingCountSource(loadingCount$);
|
||||
|
||||
const search: ISearch<typeof SYNC_SEARCH_STRATEGY> = (request, options) => {
|
||||
loadingCount$.next(loadingCount$.getValue() + 1);
|
||||
|
||||
return from(
|
||||
core.http.fetch({
|
||||
path: `/internal/search/${request.serverStrategy}`,
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
signal: options?.signal,
|
||||
})
|
||||
).pipe(finalize(() => loadingCount$.next(loadingCount$.getValue() - 1)));
|
||||
};
|
||||
|
||||
return { search };
|
||||
}
|
|
@ -17,38 +17,37 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { SearchAggsSetup, SearchAggsStart } from './aggs';
|
||||
import { ISearch, ISearchGeneric } from './i_search';
|
||||
import { TStrategyTypes } from './strategy_types';
|
||||
import { LegacyApiCaller } from './legacy/es_client';
|
||||
import { SearchInterceptor } from './search_interceptor';
|
||||
import { ISearchSource, SearchSourceFields } from './search_source';
|
||||
|
||||
/**
|
||||
* Search strategy interface contains a search method that takes in
|
||||
* a request and returns a promise that resolves to a response.
|
||||
*/
|
||||
export interface ISearchStrategy<T extends TStrategyTypes> {
|
||||
search: ISearch<T>;
|
||||
import {
|
||||
IKibanaSearchRequest,
|
||||
IKibanaSearchResponse,
|
||||
IEsSearchRequest,
|
||||
IEsSearchResponse,
|
||||
} from '../../common/search';
|
||||
|
||||
export interface ISearchOptions {
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export type TSearchStrategiesMap = {
|
||||
[K in TStrategyTypes]?: ISearchStrategy<any>;
|
||||
};
|
||||
export type ISearch = (
|
||||
request: IKibanaSearchRequest,
|
||||
options?: ISearchOptions
|
||||
) => Observable<IKibanaSearchResponse>;
|
||||
|
||||
/**
|
||||
* Extension point exposed for other plugins to register their own search
|
||||
* strategies.
|
||||
*/
|
||||
export type TRegisterSearchStrategy = <T extends TStrategyTypes>(
|
||||
name: T,
|
||||
searchStrategy: ISearchStrategy<T>
|
||||
) => void;
|
||||
// Service API types
|
||||
export interface IStrategyOptions extends ISearchOptions {
|
||||
strategy?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used if a plugin needs access to an already registered search strategy.
|
||||
*/
|
||||
export type TGetSearchStrategy = <T extends TStrategyTypes>(name: T) => ISearchStrategy<T>;
|
||||
export type ISearchGeneric = (
|
||||
request: IEsSearchRequest,
|
||||
options?: IStrategyOptions
|
||||
) => Observable<IEsSearchResponse>;
|
||||
|
||||
export interface ISearchStartLegacy {
|
||||
esClient: LegacyApiCaller;
|
||||
|
@ -60,22 +59,11 @@ export interface ISearchStartLegacy {
|
|||
*/
|
||||
export interface ISearchSetup {
|
||||
aggs: SearchAggsSetup;
|
||||
/**
|
||||
* Extension point exposed for other plugins to register their own search
|
||||
* strategies.
|
||||
*/
|
||||
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>;
|
||||
|
|
|
@ -101,7 +101,7 @@ export class Execution<
|
|||
/**
|
||||
* Promise that rejects if/when abort controller sends "abort" signal.
|
||||
*/
|
||||
private readonly abortRejection = toPromise(this.abortController.signal, true);
|
||||
private readonly abortRejection = toPromise(this.abortController.signal);
|
||||
|
||||
/**
|
||||
* Races a given promise against the "abort" event of `abortController`.
|
||||
|
|
|
@ -25,7 +25,6 @@ export default async function ({ readConfigFile }) {
|
|||
|
||||
return {
|
||||
testFiles: [
|
||||
require.resolve('./search'),
|
||||
require.resolve('./embeddables'),
|
||||
require.resolve('./bfetch_explorer'),
|
||||
require.resolve('./ui_actions'),
|
||||
|
|
|
@ -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 { FtrProviderContext } from 'test/functional/ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
describe('demo search strategy', () => {
|
||||
before(async () => {
|
||||
await testSubjects.click('demoSearch');
|
||||
});
|
||||
|
||||
it('data is returned', async () => {
|
||||
await testSubjects.click('doSearch');
|
||||
await testSubjects.stringExistsInCodeBlockOrFail('response', '"Lovely to meet you, Molly"');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,35 +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 { FtrProviderContext } from 'test/functional/ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
describe('es search strategy', () => {
|
||||
before(async () => {
|
||||
await testSubjects.click('esSearch');
|
||||
});
|
||||
|
||||
it('data is returned', async () => {
|
||||
await testSubjects.click('doSearch');
|
||||
await testSubjects.stringExistsInCodeBlockOrFail('response', '"animal weights"');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,49 +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 { FtrProviderContext } from 'test/functional/ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getService, getPageObjects, loadTestFile }: FtrProviderContext) {
|
||||
const browser = getService('browser');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const PageObjects = getPageObjects(['common', 'header']);
|
||||
|
||||
describe('search services', function () {
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('../functional/fixtures/es_archiver/dashboard/current/data');
|
||||
await esArchiver.load('../functional/fixtures/es_archiver/dashboard/current/kibana');
|
||||
await kibanaServer.uiSettings.replace({
|
||||
'dateFormat:tz': 'Australia/North',
|
||||
defaultIndex: 'logstash-*',
|
||||
});
|
||||
await browser.setWindowSize(1300, 900);
|
||||
await PageObjects.common.navigateToApp('searchExplorer');
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await esArchiver.unload('../functional/fixtures/es_archiver/dashboard/current/data');
|
||||
await esArchiver.unload('../functional/fixtures/es_archiver/dashboard/current/kibana');
|
||||
});
|
||||
|
||||
loadTestFile(require.resolve('./demo_data'));
|
||||
loadTestFile(require.resolve('./es_search'));
|
||||
});
|
||||
}
|
|
@ -4,4 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { EnhancedSearchParams, IEnhancedEsSearchRequest } from './search';
|
||||
export {
|
||||
EnhancedSearchParams,
|
||||
IEnhancedEsSearchRequest,
|
||||
IAsyncSearchRequest,
|
||||
IAsyncSearchResponse,
|
||||
} from './search';
|
||||
|
|
|
@ -4,4 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { EnhancedSearchParams, IEnhancedEsSearchRequest } from './types';
|
||||
export {
|
||||
EnhancedSearchParams,
|
||||
IEnhancedEsSearchRequest,
|
||||
IAsyncSearchRequest,
|
||||
IAsyncSearchResponse,
|
||||
} from './types';
|
||||
|
|
|
@ -4,13 +4,36 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SearchParams } from 'elasticsearch';
|
||||
import { IEsSearchRequest } from '../../../../../src/plugins/data/common';
|
||||
import {
|
||||
IEsSearchRequest,
|
||||
IEsSearchResponse,
|
||||
ISearchRequestParams,
|
||||
} from '../../../../../src/plugins/data/common';
|
||||
|
||||
export interface EnhancedSearchParams extends SearchParams {
|
||||
export interface EnhancedSearchParams extends ISearchRequestParams {
|
||||
ignoreThrottled: boolean;
|
||||
}
|
||||
|
||||
export interface IAsyncSearchRequest extends IEsSearchRequest {
|
||||
/**
|
||||
* The ID received from the response from the initial request
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
params?: EnhancedSearchParams;
|
||||
}
|
||||
|
||||
export interface IAsyncSearchResponse extends IEsSearchResponse {
|
||||
/**
|
||||
* Indicates whether async search is still in flight
|
||||
*/
|
||||
is_running?: boolean;
|
||||
/**
|
||||
* Indicates whether the results returned are complete or partial
|
||||
*/
|
||||
is_partial?: boolean;
|
||||
}
|
||||
|
||||
export interface IEnhancedEsSearchRequest extends IEsSearchRequest {
|
||||
/**
|
||||
* Used to determine whether to use the _rollups_search or a regular search endpoint.
|
||||
|
|
|
@ -9,5 +9,3 @@ import { DataEnhancedPlugin, DataEnhancedSetup, DataEnhancedStart } from './plug
|
|||
export const plugin = () => new DataEnhancedPlugin();
|
||||
|
||||
export { DataEnhancedSetup, DataEnhancedStart };
|
||||
|
||||
export { ASYNC_SEARCH_STRATEGY, IAsyncSearchRequest, IAsyncSearchOptions } from './search';
|
||||
|
|
|
@ -5,18 +5,10 @@
|
|||
*/
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin } from 'src/core/public';
|
||||
import {
|
||||
DataPublicPluginSetup,
|
||||
DataPublicPluginStart,
|
||||
ES_SEARCH_STRATEGY,
|
||||
} from '../../../../src/plugins/data/public';
|
||||
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
|
||||
import { setAutocompleteService } from './services';
|
||||
import { setupKqlQuerySuggestionProvider, KUERY_LANGUAGE_NAME } from './autocomplete';
|
||||
import {
|
||||
ASYNC_SEARCH_STRATEGY,
|
||||
asyncSearchStrategyProvider,
|
||||
enhancedEsSearchStrategyProvider,
|
||||
} from './search';
|
||||
|
||||
import { EnhancedSearchInterceptor } from './search/search_interceptor';
|
||||
|
||||
export interface DataEnhancedSetupDependencies {
|
||||
|
@ -39,17 +31,17 @@ export class DataEnhancedPlugin
|
|||
KUERY_LANGUAGE_NAME,
|
||||
setupKqlQuerySuggestionProvider(core)
|
||||
);
|
||||
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) {
|
||||
setAutocompleteService(plugins.data.autocomplete);
|
||||
const enhancedSearchInterceptor = new EnhancedSearchInterceptor(
|
||||
core.notifications.toasts,
|
||||
core.application,
|
||||
{
|
||||
toasts: core.notifications.toasts,
|
||||
application: core.application,
|
||||
http: core.http,
|
||||
uiSettings: core.uiSettings,
|
||||
},
|
||||
core.injectedMetadata.getInjectedVar('esRequestTimeout') as number
|
||||
);
|
||||
plugins.data.search.setInterceptor(enhancedSearchInterceptor);
|
||||
|
|
|
@ -1,141 +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 } 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 '.';
|
||||
import { DataEnhancedStartDependencies } from '../plugin';
|
||||
|
||||
describe('Async search strategy', () => {
|
||||
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(() => {
|
||||
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(mockCoreSetup);
|
||||
|
||||
await asyncSearch.search(mockRequest, mockOptions).toPromise();
|
||||
|
||||
expect(mockSearch.mock.calls[0][0]).toEqual(mockRequest);
|
||||
expect(mockSearch.mock.calls[0][1]).toEqual({});
|
||||
expect(mockSearch).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('stops polling when the response is complete', async () => {
|
||||
mockSearch
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 1, is_running: true, is_partial: true }))
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false }))
|
||||
.mockReturnValueOnce(
|
||||
of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false })
|
||||
);
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
expect(mockSearch).toBeCalledTimes(0);
|
||||
|
||||
await asyncSearch.search(mockRequest, mockOptions).toPromise();
|
||||
|
||||
expect(mockSearch).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
it('stops polling when the response is an error', async () => {
|
||||
mockSearch
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 1, is_running: true, is_partial: true }))
|
||||
.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(mockCoreSetup);
|
||||
expect(mockSearch).toBeCalledTimes(0);
|
||||
|
||||
await asyncSearch
|
||||
.search(mockRequest, mockOptions)
|
||||
.toPromise()
|
||||
.catch(() => {
|
||||
expect(mockSearch).toBeCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
// For bug fixed in https://github.com/elastic/kibana/pull/64155
|
||||
it('Continues polling if no records are returned on first async request', async () => {
|
||||
mockSearch
|
||||
.mockReturnValueOnce(of({ id: 1, total: 0, loaded: 0, is_running: true, is_partial: true }))
|
||||
.mockReturnValueOnce(
|
||||
of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false })
|
||||
);
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
|
||||
expect(mockSearch).toBeCalledTimes(0);
|
||||
|
||||
await asyncSearch.search(mockRequest, mockOptions).toPromise();
|
||||
|
||||
expect(mockDataStart.search.getSearchStrategy).toBeCalledTimes(1);
|
||||
expect(mockSearch).toBeCalledTimes(2);
|
||||
expect(mockSearch.mock.calls[0][0]).toEqual(mockRequest);
|
||||
expect(mockSearch.mock.calls[1][0]).toEqual({ id: 1, serverStrategy: 'foo' });
|
||||
});
|
||||
|
||||
it('only sends the ID and server strategy after the first request', async () => {
|
||||
mockSearch
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 1, is_running: true, is_partial: true }))
|
||||
.mockReturnValueOnce(
|
||||
of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false })
|
||||
);
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
|
||||
expect(mockSearch).toBeCalledTimes(0);
|
||||
|
||||
await asyncSearch.search(mockRequest, mockOptions).toPromise();
|
||||
|
||||
expect(mockSearch).toBeCalledTimes(2);
|
||||
expect(mockSearch.mock.calls[0][0]).toEqual(mockRequest);
|
||||
expect(mockSearch.mock.calls[1][0]).toEqual({ id: 1, serverStrategy: 'foo' });
|
||||
});
|
||||
|
||||
it('sends a DELETE request and stops polling when the signal is aborted', async () => {
|
||||
mockSearch
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 1 }))
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 }))
|
||||
.mockReturnValueOnce(of({ id: 1, total: 2, loaded: 2 }));
|
||||
|
||||
const asyncSearch = asyncSearchStrategyProvider(mockCoreSetup);
|
||||
const abortController = new AbortController();
|
||||
const options = { ...mockOptions, signal: abortController.signal };
|
||||
|
||||
const promise = asyncSearch.search(mockRequest, options).toPromise();
|
||||
abortController.abort();
|
||||
|
||||
try {
|
||||
await promise;
|
||||
} catch (e) {
|
||||
expect(e.name).toBe('AbortError');
|
||||
expect(mockSearch).toBeCalledTimes(1);
|
||||
expect(mockCoreSetup.http.delete).toBeCalled();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,83 +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 { 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 {
|
||||
ISearch,
|
||||
ISearchStrategy,
|
||||
ISyncSearchRequest,
|
||||
SYNC_SEARCH_STRATEGY,
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
import { IAsyncSearchOptions, IAsyncSearchResponse, IAsyncSearchRequest } from './types';
|
||||
import { DataEnhancedStartDependencies } from '../plugin';
|
||||
|
||||
export const ASYNC_SEARCH_STRATEGY = 'ASYNC_SEARCH_STRATEGY';
|
||||
|
||||
declare module '../../../../../src/plugins/data/public' {
|
||||
export interface IRequestTypesMap {
|
||||
[ASYNC_SEARCH_STRATEGY]: IAsyncSearchRequest;
|
||||
}
|
||||
}
|
||||
|
||||
export function asyncSearchStrategyProvider(
|
||||
core: CoreSetup<DataEnhancedStartDependencies>
|
||||
): ISearchStrategy<typeof ASYNC_SEARCH_STRATEGY> {
|
||||
const startServices$ = from(core.getStartServices()).pipe(share());
|
||||
|
||||
const search: ISearch<typeof ASYNC_SEARCH_STRATEGY> = (
|
||||
request: ISyncSearchRequest,
|
||||
{ pollInterval = 1000, ...options }: IAsyncSearchOptions = {}
|
||||
) => {
|
||||
const { serverStrategy } = request;
|
||||
let { id } = request;
|
||||
|
||||
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;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// If the response indicates it is complete, stop polling and complete the observable
|
||||
if (!response.is_running) return EMPTY;
|
||||
|
||||
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 syncSearch.search({ id, serverStrategy }, options);
|
||||
})
|
||||
);
|
||||
}),
|
||||
takeUntil(aborted$)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
return { search };
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { 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);
|
||||
});
|
||||
});
|
|
@ -1,44 +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 { Observable } from 'rxjs';
|
||||
import { CoreSetup } from '../../../../../src/core/public';
|
||||
import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../../../src/plugins/data/common';
|
||||
import {
|
||||
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 function enhancedEsSearchStrategyProvider(
|
||||
core: CoreSetup,
|
||||
asyncStrategy: ISearchStrategy<typeof ASYNC_SEARCH_STRATEGY>
|
||||
) {
|
||||
const search: ISearch<typeof ES_SEARCH_STRATEGY> = (
|
||||
request: IEnhancedEsSearchRequest,
|
||||
options
|
||||
) => {
|
||||
const params: EnhancedSearchParams = {
|
||||
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 asyncStrategy.search(
|
||||
{ ...request, serverStrategy: ES_SEARCH_STRATEGY },
|
||||
asyncOptions
|
||||
) as Observable<IEsSearchResponse>;
|
||||
};
|
||||
|
||||
return { search };
|
||||
}
|
|
@ -4,6 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider } from './async_search_strategy';
|
||||
export { enhancedEsSearchStrategyProvider } from './es_search_strategy';
|
||||
export { IAsyncSearchRequest, IAsyncSearchOptions } from './types';
|
||||
export { IAsyncSearchOptions } from './types';
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue