[data.search] Add search session methods to search service contract (#87966)
* [data.search] Add search session methods to search service contract * Fix types * Fix tests and switch to cancel * Update docs * Fix types/tests * Fix tests * Update status of SO before cancelling search requests * Add API integration test * Fix types * Update expiration route to use config defaultExpiration * Fix test * Update docs * New logic for extend * Remove declare module * Review feedback * fix ts * Remove test that is no longer valid * Fix undefined bug * Use DataRequestHandlerContext in maps * ts Co-authored-by: Liza K <liza.katz@elastic.co>
This commit is contained in:
parent
2145768c0d
commit
a9273ca001
|
@ -1,18 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [DataApiRequestHandlerContext](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md)
|
||||
|
||||
## DataApiRequestHandlerContext interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface DataApiRequestHandlerContext extends ISearchClient
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [session](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.session.md) | <code>IScopedSessionService</code> | |
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [DataApiRequestHandlerContext](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md) > [session](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.session.md)
|
||||
|
||||
## DataApiRequestHandlerContext.session property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
session: IScopedSessionService;
|
||||
```
|
|
@ -1,18 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IScopedSessionService](./kibana-plugin-plugins-data-server.iscopedsessionservice.md)
|
||||
|
||||
## IScopedSessionService interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface IScopedSessionService
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [search](./kibana-plugin-plugins-data-server.iscopedsessionservice.search.md) | <code><Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(strategy: ISearchStrategy<Request, Response>, ...args: Parameters<ISearchStrategy<Request, Response>['search']>) => Observable<Response></code> | |
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IScopedSessionService](./kibana-plugin-plugins-data-server.iscopedsessionservice.md) > [search](./kibana-plugin-plugins-data-server.iscopedsessionservice.search.md)
|
||||
|
||||
## IScopedSessionService.search property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
search: <Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(strategy: ISearchStrategy<Request, Response>, ...args: Parameters<ISearchStrategy<Request, Response>['search']>) => Observable<Response>;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchSessionService](./kibana-plugin-plugins-data-server.isearchsessionservice.md) > [asScopedProvider](./kibana-plugin-plugins-data-server.isearchsessionservice.asscopedprovider.md)
|
||||
|
||||
## ISearchSessionService.asScopedProvider property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
asScopedProvider: (core: CoreStart) => (request: KibanaRequest) => IScopedSearchSessionsClient<T>;
|
||||
```
|
|
@ -0,0 +1,18 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchSessionService](./kibana-plugin-plugins-data-server.isearchsessionservice.md)
|
||||
|
||||
## ISearchSessionService interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface ISearchSessionService<T = unknown>
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [asScopedProvider](./kibana-plugin-plugins-data-server.isearchsessionservice.asscopedprovider.md) | <code>(core: CoreStart) => (request: KibanaRequest) => IScopedSearchSessionsClient<T></code> | |
|
||||
|
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
asScoped: (request: KibanaRequest) => ISearchClient;
|
||||
asScoped: (request: KibanaRequest) => IScopedSearchClient;
|
||||
```
|
||||
|
|
|
@ -15,7 +15,7 @@ export interface ISearchStart<SearchStrategyRequest extends IKibanaSearchRequest
|
|||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [aggs](./kibana-plugin-plugins-data-server.isearchstart.aggs.md) | <code>AggsStart</code> | |
|
||||
| [asScoped](./kibana-plugin-plugins-data-server.isearchstart.asscoped.md) | <code>(request: KibanaRequest) => ISearchClient</code> | |
|
||||
| [asScoped](./kibana-plugin-plugins-data-server.isearchstart.asscoped.md) | <code>(request: KibanaRequest) => IScopedSearchClient</code> | |
|
||||
| [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) | <code>(name?: string) => ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse></code> | Get other registered search strategies by name (or, by default, the Elasticsearch strategy). For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. |
|
||||
| [searchSource](./kibana-plugin-plugins-data-server.isearchstart.searchsource.md) | <code>{</code><br/><code> asScoped: (request: KibanaRequest) => Promise<ISearchStartSearchSource>;</code><br/><code> }</code> | |
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISessionService](./kibana-plugin-plugins-data-server.isessionservice.md) > [asScopedProvider](./kibana-plugin-plugins-data-server.isessionservice.asscopedprovider.md)
|
||||
|
||||
## ISessionService.asScopedProvider property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
asScopedProvider: (core: CoreStart) => (request: KibanaRequest) => IScopedSessionService;
|
||||
```
|
|
@ -1,18 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISessionService](./kibana-plugin-plugins-data-server.isessionservice.md)
|
||||
|
||||
## ISessionService interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface ISessionService
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [asScopedProvider](./kibana-plugin-plugins-data-server.isessionservice.asscopedprovider.md) | <code>(core: CoreStart) => (request: KibanaRequest) => IScopedSessionService</code> | |
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
| [IndexPatternsService](./kibana-plugin-plugins-data-server.indexpatternsservice.md) | |
|
||||
| [OptionedParamType](./kibana-plugin-plugins-data-server.optionedparamtype.md) | |
|
||||
| [Plugin](./kibana-plugin-plugins-data-server.plugin.md) | |
|
||||
| [SessionService](./kibana-plugin-plugins-data-server.sessionservice.md) | The OSS session service. See data\_enhanced in X-Pack for the search session service. |
|
||||
|
||||
## Enumerations
|
||||
|
||||
|
@ -45,7 +44,6 @@
|
|||
| --- | --- |
|
||||
| [AggFunctionsMapping](./kibana-plugin-plugins-data-server.aggfunctionsmapping.md) | A global list of the expression function definitions for each agg type function. |
|
||||
| [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) | |
|
||||
| [DataApiRequestHandlerContext](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md) | |
|
||||
| [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | |
|
||||
| [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) | |
|
||||
| [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | |
|
||||
|
@ -53,12 +51,11 @@
|
|||
| [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | |
|
||||
| [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) | |
|
||||
| [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | Interface for an index pattern saved object |
|
||||
| [IScopedSessionService](./kibana-plugin-plugins-data-server.iscopedsessionservice.md) | |
|
||||
| [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) | |
|
||||
| [ISearchSessionService](./kibana-plugin-plugins-data-server.isearchsessionservice.md) | |
|
||||
| [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) | |
|
||||
| [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) | |
|
||||
| [ISearchStrategy](./kibana-plugin-plugins-data-server.isearchstrategy.md) | Search strategy interface contains a search method that takes in a request and returns a promise that resolves to a response. |
|
||||
| [ISessionService](./kibana-plugin-plugins-data-server.isessionservice.md) | |
|
||||
| [KueryNode](./kibana-plugin-plugins-data-server.kuerynode.md) | |
|
||||
| [OptionedValueProp](./kibana-plugin-plugins-data-server.optionedvalueprop.md) | |
|
||||
| [PluginSetup](./kibana-plugin-plugins-data-server.pluginsetup.md) | |
|
||||
|
@ -110,5 +107,6 @@
|
|||
| [KibanaContext](./kibana-plugin-plugins-data-server.kibanacontext.md) | |
|
||||
| [ParsedInterval](./kibana-plugin-plugins-data-server.parsedinterval.md) | |
|
||||
| [Query](./kibana-plugin-plugins-data-server.query.md) | |
|
||||
| [SearchRequestHandlerContext](./kibana-plugin-plugins-data-server.searchrequesthandlercontext.md) | |
|
||||
| [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) | |
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchRequestHandlerContext](./kibana-plugin-plugins-data-server.searchrequesthandlercontext.md)
|
||||
|
||||
## SearchRequestHandlerContext type
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare type SearchRequestHandlerContext = IScopedSearchClient;
|
||||
```
|
|
@ -16,5 +16,6 @@ export interface SearchStrategyDependencies
|
|||
| --- | --- | --- |
|
||||
| [esClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.esclient.md) | <code>IScopedClusterClient</code> | |
|
||||
| [savedObjectsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.savedobjectsclient.md) | <code>SavedObjectsClientContract</code> | |
|
||||
| [searchSessionsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.searchsessionsclient.md) | <code>IScopedSearchSessionsClient</code> | |
|
||||
| [uiSettingsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.uisettingsclient.md) | <code>IUiSettingsClient</code> | |
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SearchStrategyDependencies](./kibana-plugin-plugins-data-server.searchstrategydependencies.md) > [searchSessionsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.searchsessionsclient.md)
|
||||
|
||||
## SearchStrategyDependencies.searchSessionsClient property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
searchSessionsClient: IScopedSearchSessionsClient;
|
||||
```
|
|
@ -1,13 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SessionService](./kibana-plugin-plugins-data-server.sessionservice.md) > [(constructor)](./kibana-plugin-plugins-data-server.sessionservice._constructor_.md)
|
||||
|
||||
## SessionService.(constructor)
|
||||
|
||||
Constructs a new instance of the `SessionService` class
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
constructor();
|
||||
```
|
|
@ -1,26 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SessionService](./kibana-plugin-plugins-data-server.sessionservice.md) > [asScopedProvider](./kibana-plugin-plugins-data-server.sessionservice.asscopedprovider.md)
|
||||
|
||||
## SessionService.asScopedProvider() method
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
asScopedProvider(core: CoreStart): (request: KibanaRequest) => {
|
||||
search: <Request_1 extends IKibanaSearchRequest<any>, Response_1 extends IKibanaSearchResponse<any>>(strategy: ISearchStrategy<Request_1, Response_1>, request: Request_1, options: import("../../../common").ISearchOptions, deps: import("../types").SearchStrategyDependencies) => import("rxjs").Observable<Response_1>;
|
||||
};
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| core | <code>CoreStart</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
`(request: KibanaRequest) => {
|
||||
search: <Request_1 extends IKibanaSearchRequest<any>, Response_1 extends IKibanaSearchResponse<any>>(strategy: ISearchStrategy<Request_1, Response_1>, request: Request_1, options: import("../../../common").ISearchOptions, deps: import("../types").SearchStrategyDependencies) => import("rxjs").Observable<Response_1>;
|
||||
}`
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SessionService](./kibana-plugin-plugins-data-server.sessionservice.md)
|
||||
|
||||
## SessionService class
|
||||
|
||||
The OSS session service. See data\_enhanced in X-Pack for the search session service.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare class SessionService implements ISessionService
|
||||
```
|
||||
|
||||
## Constructors
|
||||
|
||||
| Constructor | Modifiers | Description |
|
||||
| --- | --- | --- |
|
||||
| [(constructor)()](./kibana-plugin-plugins-data-server.sessionservice._constructor_.md) | | Constructs a new instance of the <code>SessionService</code> class |
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Modifiers | Description |
|
||||
| --- | --- | --- |
|
||||
| [asScopedProvider(core)](./kibana-plugin-plugins-data-server.sessionservice.asscopedprovider.md) | | |
|
||||
| [search(strategy, args)](./kibana-plugin-plugins-data-server.sessionservice.search.md) | | |
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [SessionService](./kibana-plugin-plugins-data-server.sessionservice.md) > [search](./kibana-plugin-plugins-data-server.sessionservice.search.md)
|
||||
|
||||
## SessionService.search() method
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
search<Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(strategy: ISearchStrategy<Request, Response>, ...args: Parameters<ISearchStrategy<Request, Response>['search']>): import("rxjs").Observable<Response>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| strategy | <code>ISearchStrategy<Request, Response></code> | |
|
||||
| args | <code>Parameters<ISearchStrategy<Request, Response>['search']></code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
`import("rxjs").Observable<Response>`
|
||||
|
|
@ -12,10 +12,9 @@ import type {
|
|||
CoreStart,
|
||||
Plugin,
|
||||
Logger,
|
||||
RequestHandlerContext,
|
||||
} from 'src/core/server';
|
||||
|
||||
import type { DataApiRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||
|
||||
import {
|
||||
SearchExamplesPluginSetup,
|
||||
|
@ -45,9 +44,7 @@ export class SearchExamplesPlugin
|
|||
deps: SearchExamplesPluginSetupDeps
|
||||
) {
|
||||
this.logger.debug('search_examples: Setup');
|
||||
const router = core.http.createRouter<
|
||||
RequestHandlerContext & { search: DataApiRequestHandlerContext }
|
||||
>();
|
||||
const router = core.http.createRouter<DataRequestHandlerContext>();
|
||||
|
||||
core.getStartServices().then(([_, depsStart]) => {
|
||||
const myStrategy = mySearchStrategyProvider(depsStart.data);
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IRouter, RequestHandlerContext } from 'kibana/server';
|
||||
import { DataApiRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import type { IRouter } from 'kibana/server';
|
||||
import { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import { registerServerSearchRoute } from './server_search_route';
|
||||
|
||||
export function registerRoutes(
|
||||
router: IRouter<RequestHandlerContext & { search: DataApiRequestHandlerContext }>
|
||||
) {
|
||||
export function registerRoutes(router: IRouter<DataRequestHandlerContext>) {
|
||||
registerServerSearchRoute(router);
|
||||
}
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
import { IEsSearchRequest } from 'src/plugins/data/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { IEsSearchResponse } from 'src/plugins/data/common';
|
||||
import type { DataApiRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import type { IRouter, RequestHandlerContext } from 'src/core/server';
|
||||
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import type { IRouter } from 'src/core/server';
|
||||
import { SERVER_SEARCH_ROUTE_PATH } from '../../common';
|
||||
|
||||
export function registerServerSearchRoute(
|
||||
router: IRouter<RequestHandlerContext & { search: DataApiRequestHandlerContext }>
|
||||
) {
|
||||
export function registerServerSearchRoute(router: IRouter<DataRequestHandlerContext>) {
|
||||
router.get(
|
||||
{
|
||||
path: SERVER_SEARCH_ROUTE_PATH,
|
||||
|
|
|
@ -17,6 +17,7 @@ export function getSessionsClientMock(): jest.Mocked<ISessionsClient> {
|
|||
create: jest.fn(),
|
||||
find: jest.fn(),
|
||||
update: jest.fn(),
|
||||
extend: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -68,6 +68,12 @@ export class SessionsClient {
|
|||
});
|
||||
}
|
||||
|
||||
public extend(sessionId: string, keepAlive: string): Promise<SavedObjectsFindResponse> {
|
||||
return this.http!.post(`/internal/session/${encodeURIComponent(sessionId)}/_extend`, {
|
||||
body: JSON.stringify({ keepAlive }),
|
||||
});
|
||||
}
|
||||
|
||||
public delete(sessionId: string): Promise<void> {
|
||||
return this.http!.delete(`/internal/session/${encodeURIComponent(sessionId)}`);
|
||||
}
|
||||
|
|
|
@ -229,10 +229,9 @@ export {
|
|||
searchUsageObserver,
|
||||
shimAbortSignal,
|
||||
SearchUsage,
|
||||
SessionService,
|
||||
ISessionService,
|
||||
IScopedSessionService,
|
||||
DataApiRequestHandlerContext,
|
||||
SearchSessionService,
|
||||
ISearchSessionService,
|
||||
SearchRequestHandlerContext,
|
||||
DataRequestHandlerContext,
|
||||
} from './search';
|
||||
|
||||
|
|
|
@ -6,9 +6,14 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createSearchSetupMock, createSearchStartMock } from './search/mocks';
|
||||
import {
|
||||
createSearchSetupMock,
|
||||
createSearchStartMock,
|
||||
createSearchRequestHandlerContext,
|
||||
} from './search/mocks';
|
||||
import { createFieldFormatsSetupMock, createFieldFormatsStartMock } from './field_formats/mocks';
|
||||
import { createIndexPatternsStartMock } from './index_patterns/mocks';
|
||||
import { DataRequestHandlerContext } from './search';
|
||||
|
||||
function createSetupContract() {
|
||||
return {
|
||||
|
@ -25,7 +30,14 @@ function createStartContract() {
|
|||
};
|
||||
}
|
||||
|
||||
function createRequestHandlerContext() {
|
||||
return ({
|
||||
search: createSearchRequestHandlerContext(),
|
||||
} as unknown) as jest.Mocked<DataRequestHandlerContext>;
|
||||
}
|
||||
|
||||
export const dataPluginMock = {
|
||||
createSetupContract,
|
||||
createStartContract,
|
||||
createRequestHandlerContext,
|
||||
};
|
||||
|
|
|
@ -10,6 +10,8 @@ import { ISearchSetup, ISearchStart } from './types';
|
|||
import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks';
|
||||
import { searchSourceMock } from './search_source/mocks';
|
||||
|
||||
export { createSearchSessionsClientMock } from './session/mocks';
|
||||
|
||||
export function createSearchSetupMock(): jest.Mocked<ISearchSetup> {
|
||||
return {
|
||||
aggs: searchAggsSetupMock(),
|
||||
|
@ -22,10 +24,21 @@ export function createSearchStartMock(): jest.Mocked<ISearchStart> {
|
|||
return {
|
||||
aggs: searchAggsStartMock(),
|
||||
getSearchStrategy: jest.fn(),
|
||||
asScoped: jest.fn().mockReturnValue({
|
||||
search: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
}),
|
||||
asScoped: jest.fn().mockReturnValue(createSearchRequestHandlerContext()),
|
||||
searchSource: searchSourceMock.createStartContract(),
|
||||
};
|
||||
}
|
||||
|
||||
export function createSearchRequestHandlerContext() {
|
||||
return {
|
||||
search: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
extend: jest.fn(),
|
||||
saveSession: jest.fn(),
|
||||
getSession: jest.fn(),
|
||||
findSessions: jest.fn(),
|
||||
updateSession: jest.fn(),
|
||||
extendSession: jest.fn(),
|
||||
cancelSession: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,21 +7,17 @@
|
|||
*/
|
||||
|
||||
import { catchError, first } from 'rxjs/operators';
|
||||
import { CoreStart, KibanaRequest } from 'src/core/server';
|
||||
import { BfetchServerSetup } from 'src/plugins/bfetch/server';
|
||||
import {
|
||||
IKibanaSearchRequest,
|
||||
IKibanaSearchResponse,
|
||||
ISearchClient,
|
||||
ISearchOptions,
|
||||
} from '../../../common/search';
|
||||
|
||||
type GetScopedProider = (coreStart: CoreStart) => (request: KibanaRequest) => ISearchClient;
|
||||
import { ISearchStart } from '../types';
|
||||
|
||||
export function registerBsearchRoute(
|
||||
bfetch: BfetchServerSetup,
|
||||
coreStartPromise: Promise<[CoreStart, {}, {}]>,
|
||||
getScopedProvider: GetScopedProider
|
||||
getScoped: ISearchStart['asScoped']
|
||||
): void {
|
||||
bfetch.addBatchProcessingRoute<
|
||||
{ request: IKibanaSearchRequest; options?: ISearchOptions },
|
||||
|
@ -33,8 +29,7 @@ export function registerBsearchRoute(
|
|||
* @throws `KibanaServerError`
|
||||
*/
|
||||
onBatchItem: async ({ request: requestData, options }) => {
|
||||
const coreStart = await coreStartPromise;
|
||||
const search = getScopedProvider(coreStart[0])(request);
|
||||
const search = getScoped(request);
|
||||
return search
|
||||
.search(requestData, options)
|
||||
.pipe(
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, Observable, throwError } from 'rxjs';
|
||||
import { BehaviorSubject, from, Observable, throwError } from 'rxjs';
|
||||
import { pick } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
|
@ -18,10 +19,11 @@ import {
|
|||
SharedGlobalConfig,
|
||||
StartServicesAccessor,
|
||||
} from 'src/core/server';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { first, switchMap } from 'rxjs/operators';
|
||||
import { BfetchServerSetup } from 'src/plugins/bfetch/server';
|
||||
import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
|
||||
import type {
|
||||
IScopedSearchClient,
|
||||
ISearchSetup,
|
||||
ISearchStart,
|
||||
ISearchStrategy,
|
||||
|
@ -46,7 +48,6 @@ import {
|
|||
IEsSearchResponse,
|
||||
IKibanaSearchRequest,
|
||||
IKibanaSearchResponse,
|
||||
ISearchClient,
|
||||
ISearchOptions,
|
||||
kibana,
|
||||
kibanaContext,
|
||||
|
@ -62,8 +63,9 @@ import {
|
|||
} from '../../common/search/aggs/buckets/shard_delay';
|
||||
import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn';
|
||||
import { ConfigSchema } from '../../config';
|
||||
import { IScopedSessionService, ISessionService, SessionService } from './session';
|
||||
import { ISearchSessionService, SearchSessionService } from './session';
|
||||
import { KbnServerError } from '../../../kibana_utils/server';
|
||||
import { tapFirst } from '../../common';
|
||||
import { registerBsearchRoute } from './routes/bsearch';
|
||||
|
||||
type StrategyMap = Record<string, ISearchStrategy<any, any>>;
|
||||
|
@ -92,14 +94,14 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
private readonly searchSourceService = new SearchSourceService();
|
||||
private defaultSearchStrategyName: string = ES_SEARCH_STRATEGY;
|
||||
private searchStrategies: StrategyMap = {};
|
||||
private sessionService: ISessionService;
|
||||
private coreStart?: CoreStart;
|
||||
private sessionService: ISearchSessionService;
|
||||
private asScoped!: ISearchStart['asScoped'];
|
||||
|
||||
constructor(
|
||||
private initializerContext: PluginInitializerContext<ConfigSchema>,
|
||||
private readonly logger: Logger
|
||||
) {
|
||||
this.sessionService = new SessionService();
|
||||
this.sessionService = new SearchSessionService();
|
||||
}
|
||||
|
||||
public setup(
|
||||
|
@ -116,16 +118,10 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
registerSearchRoute(router);
|
||||
registerMsearchRoute(router, routeDependencies);
|
||||
|
||||
core.getStartServices().then(([coreStart]) => {
|
||||
this.coreStart = coreStart;
|
||||
});
|
||||
|
||||
core.http.registerRouteHandlerContext<DataRequestHandlerContext, 'search'>(
|
||||
'search',
|
||||
async (context, request) => {
|
||||
const search = this.asScopedProvider(this.coreStart!)(request);
|
||||
const session = this.sessionService.asScopedProvider(this.coreStart!)(request);
|
||||
return { ...search, session };
|
||||
return this.asScoped(request);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -138,7 +134,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
)
|
||||
);
|
||||
|
||||
registerBsearchRoute(bfetch, core.getStartServices(), this.asScopedProvider);
|
||||
registerBsearchRoute(bfetch, (request: KibanaRequest) => this.asScoped(request));
|
||||
|
||||
core.savedObjects.registerType(searchTelemetry);
|
||||
if (usageCollection) {
|
||||
|
@ -181,7 +177,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
{ fieldFormats, indexPatterns }: SearchServiceStartDependencies
|
||||
): ISearchStart {
|
||||
const { elasticsearch, savedObjects, uiSettings } = core;
|
||||
const asScoped = this.asScopedProvider(core);
|
||||
this.asScoped = this.asScopedProvider(core);
|
||||
return {
|
||||
aggs: this.aggsService.start({
|
||||
fieldFormats,
|
||||
|
@ -189,7 +185,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
indexPatterns,
|
||||
}),
|
||||
getSearchStrategy: this.getSearchStrategy,
|
||||
asScoped,
|
||||
asScoped: this.asScoped,
|
||||
searchSource: {
|
||||
asScoped: async (request: KibanaRequest) => {
|
||||
const esClient = elasticsearch.client.asScoped(request);
|
||||
|
@ -208,7 +204,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
|
||||
const searchSourceDependencies: SearchSourceDependencies = {
|
||||
getConfig: <T = any>(key: string): T => uiSettingsCache[key],
|
||||
search: asScoped(request).search,
|
||||
search: this.asScoped(request).search,
|
||||
onResponse: (req, res) => res,
|
||||
legacy: {
|
||||
callMsearch: getCallMsearch({
|
||||
|
@ -241,49 +237,6 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
this.searchStrategies[name] = strategy;
|
||||
};
|
||||
|
||||
private search = <
|
||||
SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest,
|
||||
SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse
|
||||
>(
|
||||
session: IScopedSessionService,
|
||||
request: SearchStrategyRequest,
|
||||
options: ISearchOptions,
|
||||
deps: SearchStrategyDependencies
|
||||
) => {
|
||||
try {
|
||||
const strategy = this.getSearchStrategy<SearchStrategyRequest, SearchStrategyResponse>(
|
||||
options.strategy
|
||||
);
|
||||
return session.search(strategy, request, options, deps);
|
||||
} catch (e) {
|
||||
return throwError(e);
|
||||
}
|
||||
};
|
||||
|
||||
private cancel = (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => {
|
||||
const strategy = this.getSearchStrategy(options.strategy);
|
||||
if (!strategy.cancel) {
|
||||
throw new KbnServerError(
|
||||
`Search strategy ${options.strategy} doesn't support cancellations`,
|
||||
400
|
||||
);
|
||||
}
|
||||
return strategy.cancel(id, options, deps);
|
||||
};
|
||||
|
||||
private extend = (
|
||||
id: string,
|
||||
keepAlive: string,
|
||||
options: ISearchOptions,
|
||||
deps: SearchStrategyDependencies
|
||||
) => {
|
||||
const strategy = this.getSearchStrategy(options.strategy);
|
||||
if (!strategy.extend) {
|
||||
throw new KbnServerError(`Search strategy ${options.strategy} does not support extend`, 400);
|
||||
}
|
||||
return strategy.extend(id, keepAlive, options, deps);
|
||||
};
|
||||
|
||||
private getSearchStrategy = <
|
||||
SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest,
|
||||
SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse
|
||||
|
@ -298,22 +251,128 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
return strategy;
|
||||
};
|
||||
|
||||
private search = <
|
||||
SearchStrategyRequest extends IKibanaSearchRequest,
|
||||
SearchStrategyResponse extends IKibanaSearchResponse
|
||||
>(
|
||||
deps: SearchStrategyDependencies,
|
||||
request: SearchStrategyRequest,
|
||||
options: ISearchOptions
|
||||
) => {
|
||||
try {
|
||||
const strategy = this.getSearchStrategy<SearchStrategyRequest, SearchStrategyResponse>(
|
||||
options.strategy
|
||||
);
|
||||
|
||||
const getSearchRequest = async () =>
|
||||
!options.sessionId || !options.isRestore || request.id
|
||||
? request
|
||||
: {
|
||||
...request,
|
||||
id: await deps.searchSessionsClient.getId(request, options),
|
||||
};
|
||||
|
||||
return from(getSearchRequest()).pipe(
|
||||
switchMap((searchRequest) => strategy.search(searchRequest, options, deps)),
|
||||
tapFirst((response) => {
|
||||
if (request.id || !options.sessionId || !response.id || options.isRestore) return;
|
||||
deps.searchSessionsClient.trackId(request, response.id, options);
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
return throwError(e);
|
||||
}
|
||||
};
|
||||
|
||||
private cancel = (deps: SearchStrategyDependencies, id: string, options: ISearchOptions = {}) => {
|
||||
const strategy = this.getSearchStrategy(options.strategy);
|
||||
if (!strategy.cancel) {
|
||||
throw new KbnServerError(
|
||||
`Search strategy ${options.strategy} doesn't support cancellations`,
|
||||
400
|
||||
);
|
||||
}
|
||||
return strategy.cancel(id, options, deps);
|
||||
};
|
||||
|
||||
private extend = (
|
||||
deps: SearchStrategyDependencies,
|
||||
id: string,
|
||||
keepAlive: string,
|
||||
options: ISearchOptions = {}
|
||||
) => {
|
||||
const strategy = this.getSearchStrategy(options.strategy);
|
||||
if (!strategy.extend) {
|
||||
throw new KbnServerError(`Search strategy ${options.strategy} does not support extend`, 400);
|
||||
}
|
||||
return strategy.extend(id, keepAlive, options, deps);
|
||||
};
|
||||
|
||||
private cancelSession = async (deps: SearchStrategyDependencies, sessionId: string) => {
|
||||
const searchIdMapping = await deps.searchSessionsClient.getSearchIdMapping(sessionId);
|
||||
const response = await deps.searchSessionsClient.cancel(sessionId);
|
||||
|
||||
for (const [searchId, strategyName] of searchIdMapping.entries()) {
|
||||
const searchOptions = {
|
||||
sessionId,
|
||||
strategy: strategyName,
|
||||
isStored: true,
|
||||
};
|
||||
this.cancel(deps, searchId, searchOptions);
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
private extendSession = async (
|
||||
deps: SearchStrategyDependencies,
|
||||
sessionId: string,
|
||||
expires: Date
|
||||
) => {
|
||||
const searchIdMapping = await deps.searchSessionsClient.getSearchIdMapping(sessionId);
|
||||
const keepAlive = `${moment(expires).diff(moment())}ms`;
|
||||
|
||||
for (const [searchId, strategyName] of searchIdMapping.entries()) {
|
||||
const searchOptions = {
|
||||
sessionId,
|
||||
strategy: strategyName,
|
||||
isStored: true,
|
||||
};
|
||||
await this.extend(deps, searchId, keepAlive, searchOptions);
|
||||
}
|
||||
|
||||
return deps.searchSessionsClient.extend(sessionId, expires);
|
||||
};
|
||||
|
||||
private asScopedProvider = (core: CoreStart) => {
|
||||
const { elasticsearch, savedObjects, uiSettings } = core;
|
||||
const getSessionAsScoped = this.sessionService.asScopedProvider(core);
|
||||
return (request: KibanaRequest): ISearchClient => {
|
||||
const scopedSession = getSessionAsScoped(request);
|
||||
return (request: KibanaRequest): IScopedSearchClient => {
|
||||
const savedObjectsClient = savedObjects.getScopedClient(request);
|
||||
const searchSessionsClient = getSessionAsScoped(request);
|
||||
const deps = {
|
||||
searchSessionsClient,
|
||||
savedObjectsClient,
|
||||
esClient: elasticsearch.client.asScoped(request),
|
||||
uiSettingsClient: uiSettings.asScopedToClient(savedObjectsClient),
|
||||
};
|
||||
return {
|
||||
search: (searchRequest, options = {}) =>
|
||||
this.search(scopedSession, searchRequest, options, deps),
|
||||
cancel: (id, options = {}) => this.cancel(id, options, deps),
|
||||
extend: (id, keepAlive, options = {}) => this.extend(id, keepAlive, options, deps),
|
||||
search: <
|
||||
SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest,
|
||||
SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse
|
||||
>(
|
||||
searchRequest: SearchStrategyRequest,
|
||||
options: ISearchOptions = {}
|
||||
) =>
|
||||
this.search<SearchStrategyRequest, SearchStrategyResponse>(deps, searchRequest, options),
|
||||
cancel: this.cancel.bind(this, deps),
|
||||
extend: this.extend.bind(this, deps),
|
||||
saveSession: searchSessionsClient.save,
|
||||
getSession: searchSessionsClient.get,
|
||||
findSessions: searchSessionsClient.find,
|
||||
updateSession: searchSessionsClient.update,
|
||||
extendSession: this.extendSession.bind(this, deps),
|
||||
cancelSession: this.cancelSession.bind(this, deps),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
25
src/plugins/data/server/search/session/mocks.ts
Normal file
25
src/plugins/data/server/search/session/mocks.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { IScopedSearchSessionsClient } from './types';
|
||||
|
||||
export function createSearchSessionsClientMock<T = unknown>(): jest.Mocked<
|
||||
IScopedSearchSessionsClient<T>
|
||||
> {
|
||||
return {
|
||||
getId: jest.fn(),
|
||||
trackId: jest.fn(),
|
||||
getSearchIdMapping: jest.fn(),
|
||||
save: jest.fn(),
|
||||
get: jest.fn(),
|
||||
find: jest.fn(),
|
||||
update: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
extend: jest.fn(),
|
||||
};
|
||||
}
|
|
@ -1,27 +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
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { SearchStrategyDependencies } from '../types';
|
||||
import { SessionService } from './session_service';
|
||||
|
||||
describe('SessionService', () => {
|
||||
it('search invokes `strategy.search`', async () => {
|
||||
const service = new SessionService();
|
||||
const mockSearch = jest.fn().mockReturnValue(of({}));
|
||||
const mockStrategy = { search: mockSearch };
|
||||
const mockRequest = { id: 'bar' };
|
||||
const mockOptions = { sessionId: '1234' };
|
||||
const mockDeps = { savedObjectsClient: {} } as SearchStrategyDependencies;
|
||||
|
||||
await service.search(mockStrategy, mockRequest, mockOptions, mockDeps);
|
||||
|
||||
expect(mockSearch).toHaveBeenCalled();
|
||||
expect(mockSearch).toHaveBeenCalledWith(mockRequest, mockOptions, mockDeps);
|
||||
});
|
||||
});
|
|
@ -6,27 +6,41 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { CoreStart, KibanaRequest } from 'kibana/server';
|
||||
import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../../common';
|
||||
import { ISearchStrategy } from '../types';
|
||||
import { ISessionService } from './types';
|
||||
import { ISearchSessionService } from './types';
|
||||
|
||||
/**
|
||||
* The OSS session service. See data_enhanced in X-Pack for the search session service.
|
||||
* The OSS session service, which leaves most search session-related logic unimplemented.
|
||||
* @see x-pack/plugins/data_enhanced/server/search/session/session_service.ts
|
||||
* @internal
|
||||
*/
|
||||
export class SessionService implements ISessionService {
|
||||
export class SearchSessionService implements ISearchSessionService {
|
||||
constructor() {}
|
||||
|
||||
public search<Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(
|
||||
strategy: ISearchStrategy<Request, Response>,
|
||||
...args: Parameters<ISearchStrategy<Request, Response>['search']>
|
||||
) {
|
||||
return strategy.search(...args);
|
||||
}
|
||||
|
||||
public asScopedProvider(core: CoreStart) {
|
||||
return (request: KibanaRequest) => ({
|
||||
search: this.search,
|
||||
public asScopedProvider() {
|
||||
return () => ({
|
||||
getId: () => {
|
||||
throw new Error('getId not implemented in OSS search session service');
|
||||
},
|
||||
trackId: async () => {},
|
||||
getSearchIdMapping: async () => new Map<string, string>(),
|
||||
save: async () => {
|
||||
throw new Error('save not implemented in OSS search session service');
|
||||
},
|
||||
get: async () => {
|
||||
throw new Error('get not implemented in OSS search session service');
|
||||
},
|
||||
find: async () => {
|
||||
throw new Error('find not implemented in OSS search session service');
|
||||
},
|
||||
update: async () => {
|
||||
throw new Error('update not implemented in OSS search session service');
|
||||
},
|
||||
extend: async () => {
|
||||
throw new Error('extend not implemented in OSS search session service');
|
||||
},
|
||||
cancel: async () => {
|
||||
throw new Error('cancel not implemented in OSS search session service');
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,19 +6,32 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { CoreStart, KibanaRequest } from 'kibana/server';
|
||||
import { ISearchStrategy } from '../types';
|
||||
import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../../common/search';
|
||||
import {
|
||||
CoreStart,
|
||||
KibanaRequest,
|
||||
SavedObject,
|
||||
SavedObjectsFindOptions,
|
||||
SavedObjectsFindResponse,
|
||||
SavedObjectsUpdateResponse,
|
||||
} from 'kibana/server';
|
||||
import { IKibanaSearchRequest, ISearchOptions } from '../../../common/search';
|
||||
|
||||
export interface IScopedSessionService {
|
||||
search: <Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(
|
||||
strategy: ISearchStrategy<Request, Response>,
|
||||
...args: Parameters<ISearchStrategy<Request, Response>['search']>
|
||||
) => Observable<Response>;
|
||||
[prop: string]: any;
|
||||
export interface IScopedSearchSessionsClient<T = unknown> {
|
||||
getId: (request: IKibanaSearchRequest, options: ISearchOptions) => Promise<string>;
|
||||
trackId: (
|
||||
request: IKibanaSearchRequest,
|
||||
searchId: string,
|
||||
options: ISearchOptions
|
||||
) => Promise<void>;
|
||||
getSearchIdMapping: (sessionId: string) => Promise<Map<string, string>>;
|
||||
save: (sessionId: string, attributes: Partial<T>) => Promise<SavedObject<T>>;
|
||||
get: (sessionId: string) => Promise<SavedObject<T>>;
|
||||
find: (options: Omit<SavedObjectsFindOptions, 'type'>) => Promise<SavedObjectsFindResponse<T>>;
|
||||
update: (sessionId: string, attributes: Partial<T>) => Promise<SavedObjectsUpdateResponse<T>>;
|
||||
cancel: (sessionId: string) => Promise<{}>;
|
||||
extend: (sessionId: string, expires: Date) => Promise<SavedObjectsUpdateResponse<T>>;
|
||||
}
|
||||
|
||||
export interface ISessionService {
|
||||
asScopedProvider: (core: CoreStart) => (request: KibanaRequest) => IScopedSessionService;
|
||||
export interface ISearchSessionService<T = unknown> {
|
||||
asScopedProvider: (core: CoreStart) => (request: KibanaRequest) => IScopedSearchSessionsClient<T>;
|
||||
}
|
||||
|
|
|
@ -25,17 +25,18 @@ import {
|
|||
import { AggsSetup, AggsStart } from './aggs';
|
||||
import { SearchUsage } from './collectors';
|
||||
import { IEsSearchRequest, IEsSearchResponse } from './es_search';
|
||||
import { ISessionService, IScopedSessionService } from './session';
|
||||
import { IScopedSearchSessionsClient, ISearchSessionService } from './session';
|
||||
|
||||
export interface SearchEnhancements {
|
||||
defaultStrategy: string;
|
||||
sessionService: ISessionService;
|
||||
sessionService: ISearchSessionService;
|
||||
}
|
||||
|
||||
export interface SearchStrategyDependencies {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
esClient: IScopedClusterClient;
|
||||
uiSettingsClient: IUiSettingsClient;
|
||||
searchSessionsClient: IScopedSearchSessionsClient;
|
||||
}
|
||||
|
||||
export interface ISearchSetup {
|
||||
|
@ -85,6 +86,15 @@ export interface ISearchStrategy<
|
|||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface IScopedSearchClient extends ISearchClient {
|
||||
saveSession: IScopedSearchSessionsClient['save'];
|
||||
getSession: IScopedSearchSessionsClient['get'];
|
||||
findSessions: IScopedSearchSessionsClient['find'];
|
||||
updateSession: IScopedSearchSessionsClient['update'];
|
||||
cancelSession: IScopedSearchSessionsClient['cancel'];
|
||||
extendSession: IScopedSearchSessionsClient['extend'];
|
||||
}
|
||||
|
||||
export interface ISearchStart<
|
||||
SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest,
|
||||
SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse
|
||||
|
@ -98,21 +108,19 @@ export interface ISearchStart<
|
|||
getSearchStrategy: (
|
||||
name?: string // Name of the search strategy (defaults to the Elasticsearch strategy)
|
||||
) => ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse>;
|
||||
asScoped: (request: KibanaRequest) => ISearchClient;
|
||||
asScoped: (request: KibanaRequest) => IScopedSearchClient;
|
||||
searchSource: {
|
||||
asScoped: (request: KibanaRequest) => Promise<ISearchStartSearchSource>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DataApiRequestHandlerContext extends ISearchClient {
|
||||
session: IScopedSessionService;
|
||||
}
|
||||
export type SearchRequestHandlerContext = IScopedSearchClient;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface DataRequestHandlerContext extends RequestHandlerContext {
|
||||
search: DataApiRequestHandlerContext;
|
||||
search: SearchRequestHandlerContext;
|
||||
}
|
||||
|
||||
export type DataPluginRouter = IRouter<DataRequestHandlerContext>;
|
||||
|
|
|
@ -55,9 +55,13 @@ import { RecursiveReadonly } from '@kbn/utility-types';
|
|||
import { RequestAdapter } from 'src/plugins/inspector/common';
|
||||
import { RequestHandlerContext } from 'src/core/server';
|
||||
import { RequestStatistics } from 'src/plugins/inspector/common';
|
||||
import { SavedObject } from 'src/core/server';
|
||||
import { SavedObject } from 'kibana/server';
|
||||
import { SavedObject as SavedObject_2 } from 'src/core/server';
|
||||
import { SavedObjectsClientContract } from 'src/core/server';
|
||||
import { SavedObjectsClientContract as SavedObjectsClientContract_2 } from 'kibana/server';
|
||||
import { SavedObjectsFindOptions } from 'kibana/server';
|
||||
import { SavedObjectsFindResponse } from 'kibana/server';
|
||||
import { SavedObjectsUpdateResponse } from 'kibana/server';
|
||||
import { Search } from '@elastic/elasticsearch/api/requestParams';
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common';
|
||||
|
@ -305,19 +309,10 @@ export const castEsToKbnFieldTypeName: (esType: ES_FIELD_TYPES | string) => KBN_
|
|||
// @public (undocumented)
|
||||
export const config: PluginConfigDescriptor<ConfigSchema>;
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "ISearchClient" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "DataApiRequestHandlerContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface DataApiRequestHandlerContext extends ISearchClient {
|
||||
// (undocumented)
|
||||
session: IScopedSessionService;
|
||||
}
|
||||
|
||||
// @internal (undocumented)
|
||||
export interface DataRequestHandlerContext extends RequestHandlerContext {
|
||||
// (undocumented)
|
||||
search: DataApiRequestHandlerContext;
|
||||
search: SearchRequestHandlerContext;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -910,16 +905,6 @@ export class IndexPatternsService implements Plugin_3<void, IndexPatternsService
|
|||
};
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "IScopedSessionService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface IScopedSessionService {
|
||||
// (undocumented)
|
||||
[prop: string]: any;
|
||||
// (undocumented)
|
||||
search: <Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(strategy: ISearchStrategy<Request, Response>, ...args: Parameters<ISearchStrategy<Request, Response>['search']>) => Observable<Response>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ISearchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -932,6 +917,16 @@ export interface ISearchOptions {
|
|||
strategy?: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ISearchSessionService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface ISearchSessionService<T = unknown> {
|
||||
// Warning: (ae-forgotten-export) The symbol "IScopedSearchSessionsClient" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
asScopedProvider: (core: CoreStart) => (request: KibanaRequest) => IScopedSearchSessionsClient<T>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ISearchSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -956,8 +951,10 @@ export interface ISearchStart<SearchStrategyRequest extends IKibanaSearchRequest
|
|||
//
|
||||
// (undocumented)
|
||||
aggs: AggsStart;
|
||||
// Warning: (ae-forgotten-export) The symbol "IScopedSearchClient" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
asScoped: (request: KibanaRequest_2) => ISearchClient;
|
||||
asScoped: (request: KibanaRequest_2) => IScopedSearchClient;
|
||||
getSearchStrategy: (name?: string) => ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse>;
|
||||
// (undocumented)
|
||||
searchSource: {
|
||||
|
@ -977,14 +974,6 @@ export interface ISearchStrategy<SearchStrategyRequest extends IKibanaSearchRequ
|
|||
search: (request: SearchStrategyRequest, options: ISearchOptions, deps: SearchStrategyDependencies) => Observable<SearchStrategyResponse>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ISessionService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface ISessionService {
|
||||
// (undocumented)
|
||||
asScopedProvider: (core: CoreStart) => (request: KibanaRequest) => IScopedSessionService;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export enum KBN_FIELD_TYPES {
|
||||
// (undocumented)
|
||||
|
@ -1246,6 +1235,28 @@ export const search: {
|
|||
tabifyGetColumns: typeof tabifyGetColumns;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SearchRequestHandlerContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export type SearchRequestHandlerContext = IScopedSearchClient;
|
||||
|
||||
// @internal
|
||||
export class SearchSessionService implements ISearchSessionService {
|
||||
constructor();
|
||||
// (undocumented)
|
||||
asScopedProvider(): () => {
|
||||
getId: () => never;
|
||||
trackId: () => Promise<void>;
|
||||
getSearchIdMapping: () => Promise<Map<string, string>>;
|
||||
save: () => Promise<never>;
|
||||
get: () => Promise<never>;
|
||||
find: () => Promise<never>;
|
||||
update: () => Promise<never>;
|
||||
extend: () => Promise<never>;
|
||||
cancel: () => Promise<never>;
|
||||
};
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SearchStrategyDependencies" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -1255,6 +1266,8 @@ export interface SearchStrategyDependencies {
|
|||
// (undocumented)
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
// (undocumented)
|
||||
searchSessionsClient: IScopedSearchSessionsClient;
|
||||
// (undocumented)
|
||||
uiSettingsClient: IUiSettingsClient;
|
||||
}
|
||||
|
||||
|
@ -1276,19 +1289,6 @@ export function searchUsageObserver(logger: Logger_2, usage?: SearchUsage): {
|
|||
error(): void;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SessionService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export class SessionService implements ISessionService {
|
||||
constructor();
|
||||
// (undocumented)
|
||||
asScopedProvider(core: CoreStart): (request: KibanaRequest) => {
|
||||
search: <Request_1 extends IKibanaSearchRequest<any>, Response_1 extends IKibanaSearchResponse<any>>(strategy: ISearchStrategy<Request_1, Response_1>, request: Request_1, options: import("../../../common").ISearchOptions, deps: import("../types").SearchStrategyDependencies) => import("rxjs").Observable<Response_1>;
|
||||
};
|
||||
// (undocumented)
|
||||
search<Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(strategy: ISearchStrategy<Request, Response>, ...args: Parameters<ISearchStrategy<Request, Response>['search']>): import("rxjs").Observable<Response>;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export const shimAbortSignal: <T>(promise: TransportRequestPromise<T>, signal?: AbortSignal | undefined) => TransportRequestPromise<T>;
|
||||
|
||||
|
@ -1414,23 +1414,23 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
|
|||
// src/plugins/data/server/index.ts:100:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:126:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:126:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:240:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:240:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:240:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:240:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:242:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:243:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:252:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:253:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:254:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:258:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:259:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:266:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:239:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:239:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:239:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:239:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:241:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:242:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:251:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:252:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:253:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:258:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:265:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:266:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index_patterns/index_patterns_service.ts:59:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/plugin.ts:79:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/search/types.ts:103:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/search/types.ts:113:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
|
|
|
@ -11,12 +11,8 @@ import { first } from 'rxjs/operators';
|
|||
import { TypeOf, schema } from '@kbn/config-schema';
|
||||
import { RecursiveReadonly } from '@kbn/utility-types';
|
||||
import { deepFreeze } from '@kbn/std';
|
||||
import type { RequestHandlerContext } from 'src/core/server';
|
||||
|
||||
import type {
|
||||
PluginStart,
|
||||
DataApiRequestHandlerContext,
|
||||
} from '../../../../src/plugins/data/server';
|
||||
import type { PluginStart, DataRequestHandlerContext } from '../../../../src/plugins/data/server';
|
||||
import { CoreSetup, PluginInitializerContext } from '../../../../src/core/server';
|
||||
import { configSchema } from '../config';
|
||||
import loadFunctions from './lib/load_functions';
|
||||
|
@ -73,9 +69,7 @@ export class Plugin {
|
|||
|
||||
const logger = this.initializerContext.logger.get('timelion');
|
||||
|
||||
const router = core.http.createRouter<
|
||||
RequestHandlerContext & { search: DataApiRequestHandlerContext }
|
||||
>();
|
||||
const router = core.http.createRouter<DataRequestHandlerContext>();
|
||||
|
||||
const deps = {
|
||||
configManager,
|
||||
|
|
|
@ -7,12 +7,10 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { IRouter, RequestHandlerContext } from 'kibana/server';
|
||||
import type { DataApiRequestHandlerContext } from '../../../data/server';
|
||||
import { IRouter } from 'kibana/server';
|
||||
import type { DataRequestHandlerContext } from '../../../data/server';
|
||||
|
||||
export function validateEsRoute(
|
||||
router: IRouter<RequestHandlerContext & { search: DataApiRequestHandlerContext }>
|
||||
) {
|
||||
export function validateEsRoute(router: IRouter<DataRequestHandlerContext>) {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/timelion/validate/es',
|
||||
|
|
|
@ -6,11 +6,8 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IRouter, RequestHandlerContext } from 'src/core/server';
|
||||
import type { DataApiRequestHandlerContext } from '../../data/server';
|
||||
|
||||
export interface VisTypeTimeseriesRequestHandlerContext extends RequestHandlerContext {
|
||||
search: DataApiRequestHandlerContext;
|
||||
}
|
||||
import type { IRouter } from 'src/core/server';
|
||||
import type { DataRequestHandlerContext } from '../../data/server';
|
||||
|
||||
export type VisTypeTimeseriesRequestHandlerContext = DataRequestHandlerContext;
|
||||
export type VisTypeTimeseriesRouter = IRouter<VisTypeTimeseriesRequestHandlerContext>;
|
||||
|
|
|
@ -1,26 +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 type { DataRequestHandlerContext } from '../../../../../src/plugins/data/server';
|
||||
import { coreMock } from '../../../../../src/core/server/mocks';
|
||||
|
||||
export function createSearchRequestHandlerContext() {
|
||||
return ({
|
||||
core: coreMock.createRequestHandlerContext(),
|
||||
search: {
|
||||
search: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
session: {
|
||||
search: jest.fn(),
|
||||
save: jest.fn(),
|
||||
get: jest.fn(),
|
||||
find: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
},
|
||||
} as unknown) as jest.Mocked<DataRequestHandlerContext>;
|
||||
}
|
|
@ -12,7 +12,7 @@ import type {
|
|||
PluginStart as DataPluginStart,
|
||||
DataRequestHandlerContext,
|
||||
} from '../../../../../src/plugins/data/server';
|
||||
import { createSearchRequestHandlerContext } from './mocks';
|
||||
import { dataPluginMock } from '../../../../../src/plugins/data/server/mocks';
|
||||
import { registerSessionRoutes } from './session';
|
||||
|
||||
describe('registerSessionRoutes', () => {
|
||||
|
@ -23,11 +23,11 @@ describe('registerSessionRoutes', () => {
|
|||
beforeEach(() => {
|
||||
mockCoreSetup = coreMock.createSetup();
|
||||
mockLogger = coreMock.createPluginInitializerContext().logger.get();
|
||||
mockContext = createSearchRequestHandlerContext();
|
||||
mockContext = dataPluginMock.createRequestHandlerContext();
|
||||
registerSessionRoutes(mockCoreSetup.http.createRouter(), mockLogger);
|
||||
});
|
||||
|
||||
it('save calls session.save with sessionId and attributes', async () => {
|
||||
it('save calls saveSession with sessionId and attributes', async () => {
|
||||
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
|
||||
const name = 'my saved background search session';
|
||||
const body = { sessionId, name };
|
||||
|
@ -40,10 +40,10 @@ describe('registerSessionRoutes', () => {
|
|||
|
||||
saveHandler(mockContext, mockRequest, mockResponse);
|
||||
|
||||
expect(mockContext.search!.session.save).toHaveBeenCalledWith(sessionId, { name });
|
||||
expect(mockContext.search!.saveSession).toHaveBeenCalledWith(sessionId, { name });
|
||||
});
|
||||
|
||||
it('get calls session.get with sessionId', async () => {
|
||||
it('get calls getSession with sessionId', async () => {
|
||||
const id = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
|
||||
const params = { id };
|
||||
|
||||
|
@ -55,10 +55,10 @@ describe('registerSessionRoutes', () => {
|
|||
|
||||
getHandler(mockContext, mockRequest, mockResponse);
|
||||
|
||||
expect(mockContext.search!.session.get).toHaveBeenCalledWith(id);
|
||||
expect(mockContext.search!.getSession).toHaveBeenCalledWith(id);
|
||||
});
|
||||
|
||||
it('find calls session.find with options', async () => {
|
||||
it('find calls findSession with options', async () => {
|
||||
const page = 1;
|
||||
const perPage = 5;
|
||||
const sortField = 'my_field';
|
||||
|
@ -74,10 +74,10 @@ describe('registerSessionRoutes', () => {
|
|||
|
||||
findHandler(mockContext, mockRequest, mockResponse);
|
||||
|
||||
expect(mockContext.search!.session.find).toHaveBeenCalledWith(body);
|
||||
expect(mockContext.search!.findSessions).toHaveBeenCalledWith(body);
|
||||
});
|
||||
|
||||
it('update calls session.update with id and attributes', async () => {
|
||||
it('update calls updateSession with id and attributes', async () => {
|
||||
const id = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
|
||||
const name = 'my saved background search session';
|
||||
const expires = new Date().toISOString();
|
||||
|
@ -92,10 +92,10 @@ describe('registerSessionRoutes', () => {
|
|||
|
||||
updateHandler(mockContext, mockRequest, mockResponse);
|
||||
|
||||
expect(mockContext.search!.session.update).toHaveBeenCalledWith(id, body);
|
||||
expect(mockContext.search!.updateSession).toHaveBeenCalledWith(id, body);
|
||||
});
|
||||
|
||||
it('delete calls session.delete with id', async () => {
|
||||
it('delete calls cancelSession with id', async () => {
|
||||
const id = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
|
||||
const params = { id };
|
||||
|
||||
|
@ -107,6 +107,23 @@ describe('registerSessionRoutes', () => {
|
|||
|
||||
deleteHandler(mockContext, mockRequest, mockResponse);
|
||||
|
||||
expect(mockContext.search!.session.delete).toHaveBeenCalledWith(id);
|
||||
expect(mockContext.search!.cancelSession).toHaveBeenCalledWith(id);
|
||||
});
|
||||
|
||||
it('extend calls extendSession with id', async () => {
|
||||
const id = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
|
||||
const expires = new Date().toISOString();
|
||||
const params = { id };
|
||||
const body = { expires };
|
||||
|
||||
const mockRequest = httpServerMock.createKibanaRequest({ params, body });
|
||||
const mockResponse = httpServerMock.createResponseFactory();
|
||||
|
||||
const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value;
|
||||
const [, , [, extendHandler]] = mockRouter.post.mock.calls;
|
||||
|
||||
extendHandler(mockContext, mockRequest, mockResponse);
|
||||
|
||||
expect(mockContext.search.extendSession).toHaveBeenCalledWith(id, new Date(expires));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger:
|
|||
} = request.body;
|
||||
|
||||
try {
|
||||
const response = await context.search!.session.save(sessionId, {
|
||||
const response = await context.search!.saveSession(sessionId, {
|
||||
name,
|
||||
appId,
|
||||
expires,
|
||||
|
@ -68,7 +68,7 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger:
|
|||
async (context, request, res) => {
|
||||
const { id } = request.params;
|
||||
try {
|
||||
const response = await context.search!.session.get(id);
|
||||
const response = await context.search!.getSession(id);
|
||||
|
||||
return res.ok({
|
||||
body: response,
|
||||
|
@ -97,7 +97,7 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger:
|
|||
async (context, request, res) => {
|
||||
const { page, perPage, sortField, sortOrder, filter } = request.body;
|
||||
try {
|
||||
const response = await context.search!.session.find({
|
||||
const response = await context.search!.findSessions({
|
||||
page,
|
||||
perPage,
|
||||
sortField,
|
||||
|
@ -127,7 +127,7 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger:
|
|||
async (context, request, res) => {
|
||||
const { id } = request.params;
|
||||
try {
|
||||
await context.search!.session.delete(id);
|
||||
await context.search!.cancelSession(id);
|
||||
|
||||
return res.ok();
|
||||
} catch (e) {
|
||||
|
@ -155,7 +155,7 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger:
|
|||
const { id } = request.params;
|
||||
const { name, expires } = request.body;
|
||||
try {
|
||||
const response = await context.search!.session.update(id, { name, expires });
|
||||
const response = await context.search!.updateSession(id, { name, expires });
|
||||
|
||||
return res.ok({
|
||||
body: response,
|
||||
|
@ -166,4 +166,33 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger:
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
{
|
||||
path: '/internal/session/{id}/_extend',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
body: schema.object({
|
||||
expires: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, res) => {
|
||||
const { id } = request.params;
|
||||
const { expires } = request.body;
|
||||
try {
|
||||
const response = await context.search!.extendSession(id, new Date(expires));
|
||||
|
||||
return res.ok({
|
||||
body: response,
|
||||
});
|
||||
} catch (e) {
|
||||
const err = e.output?.payload || e;
|
||||
logger.error(err);
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,20 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, of } from 'rxjs';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import type { SavedObject, SavedObjectsClientContract } from 'kibana/server';
|
||||
import type { SearchStrategyDependencies } from '../../../../../../src/plugins/data/server';
|
||||
import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks';
|
||||
import { SearchSessionStatus } from '../../../common';
|
||||
import { SEARCH_SESSION_TYPE } from '../../saved_objects';
|
||||
import { SearchSessionDependencies, SearchSessionService, SessionInfo } from './session_service';
|
||||
import { SearchSessionService, SessionInfo } from './session_service';
|
||||
import { createRequestHash } from './utils';
|
||||
import moment from 'moment';
|
||||
import { coreMock } from 'src/core/server/mocks';
|
||||
import { ConfigSchema } from '../../../config';
|
||||
// @ts-ignore
|
||||
import { taskManagerMock } from '../../../../task_manager/server/mocks';
|
||||
import { SearchStatus } from './types';
|
||||
|
||||
const INMEM_TRACKING_INTERVAL = 10000;
|
||||
const MAX_UPDATE_RETRIES = 3;
|
||||
|
@ -139,13 +137,13 @@ describe('SearchSessionService', () => {
|
|||
});
|
||||
|
||||
it('search throws if `name` is not provided', () => {
|
||||
expect(() => service.save(sessionId, {}, { savedObjectsClient })).rejects.toMatchInlineSnapshot(
|
||||
expect(() => service.save({ savedObjectsClient }, sessionId, {})).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Name is required]`
|
||||
);
|
||||
});
|
||||
|
||||
it('save throws if `name` is not provided', () => {
|
||||
expect(() => service.save(sessionId, {}, { savedObjectsClient })).rejects.toMatchInlineSnapshot(
|
||||
expect(() => service.save({ savedObjectsClient }, sessionId, {})).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Name is required]`
|
||||
);
|
||||
});
|
||||
|
@ -153,7 +151,7 @@ describe('SearchSessionService', () => {
|
|||
it('get calls saved objects client', async () => {
|
||||
savedObjectsClient.get.mockResolvedValue(mockSavedObject);
|
||||
|
||||
const response = await service.get(sessionId, { savedObjectsClient });
|
||||
const response = await service.get({ savedObjectsClient }, sessionId);
|
||||
|
||||
expect(response).toBe(mockSavedObject);
|
||||
expect(savedObjectsClient.get).toHaveBeenCalledWith(SEARCH_SESSION_TYPE, sessionId);
|
||||
|
@ -173,7 +171,7 @@ describe('SearchSessionService', () => {
|
|||
savedObjectsClient.find.mockResolvedValue(mockResponse);
|
||||
|
||||
const options = { page: 0, perPage: 5 };
|
||||
const response = await service.find(options, { savedObjectsClient });
|
||||
const response = await service.find({ savedObjectsClient }, options);
|
||||
|
||||
expect(response).toBe(mockResponse);
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
|
@ -190,7 +188,7 @@ describe('SearchSessionService', () => {
|
|||
savedObjectsClient.update.mockResolvedValue(mockUpdateSavedObject);
|
||||
|
||||
const attributes = { name: 'new_name' };
|
||||
const response = await service.update(sessionId, attributes, { savedObjectsClient });
|
||||
const response = await service.update({ savedObjectsClient }, sessionId, attributes);
|
||||
|
||||
expect(response).toBe(mockUpdateSavedObject);
|
||||
expect(savedObjectsClient.update).toHaveBeenCalledWith(
|
||||
|
@ -200,93 +198,11 @@ describe('SearchSessionService', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('delete calls saved objects client', async () => {
|
||||
savedObjectsClient.delete.mockResolvedValue({});
|
||||
it('cancel updates object status', async () => {
|
||||
await service.cancel({ savedObjectsClient }, sessionId);
|
||||
|
||||
const response = await service.delete(sessionId, { savedObjectsClient });
|
||||
|
||||
expect(response).toEqual({});
|
||||
expect(savedObjectsClient.delete).toHaveBeenCalledWith(SEARCH_SESSION_TYPE, sessionId);
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
const mockSearch = jest.fn().mockReturnValue(of({}));
|
||||
const mockStrategy = { search: mockSearch };
|
||||
const mockSearchDeps = {} as SearchStrategyDependencies;
|
||||
const mockDeps = {} as SearchSessionDependencies;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSearch.mockClear();
|
||||
});
|
||||
|
||||
it('searches using the original request if not restoring', async () => {
|
||||
const searchRequest = { params: {} };
|
||||
const options = { sessionId, isStored: false, isRestore: false };
|
||||
|
||||
await service
|
||||
.search(mockStrategy, searchRequest, options, mockSearchDeps, mockDeps)
|
||||
.toPromise();
|
||||
|
||||
expect(mockSearch).toBeCalledWith(searchRequest, options, mockSearchDeps);
|
||||
});
|
||||
|
||||
it('searches using the original request if `id` is provided', async () => {
|
||||
const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0';
|
||||
const searchRequest = { id: searchId, params: {} };
|
||||
const options = { sessionId, isStored: true, isRestore: true };
|
||||
|
||||
await service
|
||||
.search(mockStrategy, searchRequest, options, mockSearchDeps, mockDeps)
|
||||
.toPromise();
|
||||
|
||||
expect(mockSearch).toBeCalledWith(searchRequest, options, mockSearchDeps);
|
||||
});
|
||||
|
||||
it('searches by looking up an `id` if restoring and `id` is not provided', async () => {
|
||||
const searchRequest = { params: {} };
|
||||
const options = { sessionId, isStored: true, isRestore: true };
|
||||
const spyGetId = jest.spyOn(service, 'getId').mockResolvedValueOnce('my_id');
|
||||
|
||||
await service
|
||||
.search(mockStrategy, searchRequest, options, mockSearchDeps, mockDeps)
|
||||
.toPromise();
|
||||
|
||||
expect(mockSearch).toBeCalledWith({ ...searchRequest, id: 'my_id' }, options, mockSearchDeps);
|
||||
|
||||
spyGetId.mockRestore();
|
||||
});
|
||||
|
||||
it('calls `trackId` once if the response contains an `id` and not restoring', async () => {
|
||||
const searchRequest = { params: {} };
|
||||
const options = { sessionId, isStored: false, isRestore: false };
|
||||
const spyTrackId = jest.spyOn(service, 'trackId').mockResolvedValue();
|
||||
mockSearch.mockReturnValueOnce(of({ id: 'my_id' }, { id: 'my_id' }));
|
||||
|
||||
await service
|
||||
.search(mockStrategy, searchRequest, options, mockSearchDeps, mockDeps)
|
||||
.toPromise();
|
||||
|
||||
expect(spyTrackId).toBeCalledTimes(1);
|
||||
expect(spyTrackId).toBeCalledWith(searchRequest, 'my_id', options, {});
|
||||
|
||||
spyTrackId.mockRestore();
|
||||
});
|
||||
|
||||
it('does not call `trackId` if restoring', async () => {
|
||||
const searchRequest = { params: {} };
|
||||
const options = { sessionId, isStored: true, isRestore: true };
|
||||
const spyGetId = jest.spyOn(service, 'getId').mockResolvedValueOnce('my_id');
|
||||
const spyTrackId = jest.spyOn(service, 'trackId').mockResolvedValue();
|
||||
mockSearch.mockReturnValueOnce(of({ id: 'my_id' }));
|
||||
|
||||
await service
|
||||
.search(mockStrategy, searchRequest, options, mockSearchDeps, mockDeps)
|
||||
.toPromise();
|
||||
|
||||
expect(spyTrackId).not.toBeCalled();
|
||||
|
||||
spyGetId.mockRestore();
|
||||
spyTrackId.mockRestore();
|
||||
expect(savedObjectsClient.update).toHaveBeenCalledWith(SEARCH_SESSION_TYPE, sessionId, {
|
||||
status: SearchSessionStatus.CANCELLED,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -309,20 +225,21 @@ describe('SearchSessionService', () => {
|
|||
get: () => mockIdMapping,
|
||||
});
|
||||
|
||||
await service.trackId(
|
||||
searchRequest,
|
||||
searchId,
|
||||
{ sessionId, isStored, strategy: MOCK_STRATEGY },
|
||||
{ savedObjectsClient }
|
||||
);
|
||||
await service.trackId({ savedObjectsClient }, searchRequest, searchId, {
|
||||
sessionId,
|
||||
isStored,
|
||||
strategy: MOCK_STRATEGY,
|
||||
});
|
||||
|
||||
expect(savedObjectsClient.update).not.toHaveBeenCalled();
|
||||
|
||||
await service.save(
|
||||
sessionId,
|
||||
{ name, created, expires, appId, urlGeneratorId },
|
||||
{ savedObjectsClient }
|
||||
);
|
||||
await service.save({ savedObjectsClient }, sessionId, {
|
||||
name,
|
||||
created,
|
||||
expires,
|
||||
appId,
|
||||
urlGeneratorId,
|
||||
});
|
||||
|
||||
expect(savedObjectsClient.create).toHaveBeenCalledWith(
|
||||
SEARCH_SESSION_TYPE,
|
||||
|
@ -346,30 +263,6 @@ describe('SearchSessionService', () => {
|
|||
expect(setParams.ids.get(requestHash).strategy).toBe(MOCK_STRATEGY);
|
||||
expect(setSessionId).toBe(sessionId);
|
||||
});
|
||||
|
||||
it('updates saved object when `isStored` is `true`', async () => {
|
||||
const searchRequest = { params: {} };
|
||||
const requestHash = createRequestHash(searchRequest.params);
|
||||
const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0';
|
||||
const isStored = true;
|
||||
|
||||
await service.trackId(
|
||||
searchRequest,
|
||||
searchId,
|
||||
{ sessionId, isStored, strategy: MOCK_STRATEGY },
|
||||
{ savedObjectsClient }
|
||||
);
|
||||
|
||||
expect(savedObjectsClient.update).toHaveBeenCalledWith(SEARCH_SESSION_TYPE, sessionId, {
|
||||
idMapping: {
|
||||
[requestHash]: {
|
||||
id: searchId,
|
||||
strategy: MOCK_STRATEGY,
|
||||
status: SearchStatus.IN_PROGRESS,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getId', () => {
|
||||
|
@ -377,7 +270,7 @@ describe('SearchSessionService', () => {
|
|||
const searchRequest = { params: {} };
|
||||
|
||||
expect(() =>
|
||||
service.getId(searchRequest, {}, { savedObjectsClient })
|
||||
service.getId({ savedObjectsClient }, searchRequest, {})
|
||||
).rejects.toMatchInlineSnapshot(`[Error: Session ID is required]`);
|
||||
});
|
||||
|
||||
|
@ -385,7 +278,7 @@ describe('SearchSessionService', () => {
|
|||
const searchRequest = { params: {} };
|
||||
|
||||
expect(() =>
|
||||
service.getId(searchRequest, { sessionId, isStored: false }, { savedObjectsClient })
|
||||
service.getId({ savedObjectsClient }, searchRequest, { sessionId, isStored: false })
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Cannot get search ID from a session that is not stored]`
|
||||
);
|
||||
|
@ -395,11 +288,11 @@ describe('SearchSessionService', () => {
|
|||
const searchRequest = { params: {} };
|
||||
|
||||
expect(() =>
|
||||
service.getId(
|
||||
searchRequest,
|
||||
{ sessionId, isStored: true, isRestore: false },
|
||||
{ savedObjectsClient }
|
||||
)
|
||||
service.getId({ savedObjectsClient }, searchRequest, {
|
||||
sessionId,
|
||||
isStored: true,
|
||||
isRestore: false,
|
||||
})
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Get search ID is only supported when restoring a session]`
|
||||
);
|
||||
|
@ -427,16 +320,47 @@ describe('SearchSessionService', () => {
|
|||
};
|
||||
savedObjectsClient.get.mockResolvedValue(mockSession);
|
||||
|
||||
const id = await service.getId(
|
||||
searchRequest,
|
||||
{ sessionId, isStored: true, isRestore: true },
|
||||
{ savedObjectsClient }
|
||||
);
|
||||
const id = await service.getId({ savedObjectsClient }, searchRequest, {
|
||||
sessionId,
|
||||
isStored: true,
|
||||
isRestore: true,
|
||||
});
|
||||
|
||||
expect(id).toBe(searchId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSearchIdMapping', () => {
|
||||
it('retrieves the search IDs and strategies from the saved object', async () => {
|
||||
const mockSession = {
|
||||
id: 'd7170a35-7e2c-48d6-8dec-9a056721b489',
|
||||
type: SEARCH_SESSION_TYPE,
|
||||
attributes: {
|
||||
name: 'my_name',
|
||||
appId: 'my_app_id',
|
||||
urlGeneratorId: 'my_url_generator_id',
|
||||
idMapping: {
|
||||
foo: {
|
||||
id: 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0',
|
||||
strategy: MOCK_STRATEGY,
|
||||
},
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
};
|
||||
savedObjectsClient.get.mockResolvedValue(mockSession);
|
||||
const searchIdMapping = await service.getSearchIdMapping(
|
||||
{ savedObjectsClient },
|
||||
mockSession.id
|
||||
);
|
||||
expect(searchIdMapping).toMatchInlineSnapshot(`
|
||||
Map {
|
||||
"FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0" => "ese",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Monitor', () => {
|
||||
it('schedules the next iteration', async () => {
|
||||
const findSpy = jest.fn().mockResolvedValue({ saved_objects: [] });
|
||||
|
|
|
@ -5,39 +5,33 @@
|
|||
*/
|
||||
|
||||
import moment, { Moment } from 'moment';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { first, switchMap } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import {
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
KibanaRequest,
|
||||
SavedObjectsClient,
|
||||
SavedObjectsClientContract,
|
||||
Logger,
|
||||
SavedObject,
|
||||
CoreSetup,
|
||||
SavedObjectsBulkUpdateObject,
|
||||
SavedObjectsClient,
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsFindOptions,
|
||||
} from '../../../../../../src/core/server';
|
||||
import {
|
||||
IKibanaSearchRequest,
|
||||
IKibanaSearchResponse,
|
||||
ISearchOptions,
|
||||
KueryNode,
|
||||
nodeBuilder,
|
||||
tapFirst,
|
||||
} from '../../../../../../src/plugins/data/common';
|
||||
import {
|
||||
ISearchStrategy,
|
||||
ISessionService,
|
||||
SearchStrategyDependencies,
|
||||
} from '../../../../../../src/plugins/data/server';
|
||||
import { ISearchSessionService } from '../../../../../../src/plugins/data/server';
|
||||
import {
|
||||
TaskManagerSetupContract,
|
||||
TaskManagerStartContract,
|
||||
} from '../../../../task_manager/server';
|
||||
import {
|
||||
SearchSessionSavedObjectAttributes,
|
||||
SearchSessionRequestInfo,
|
||||
SearchSessionSavedObjectAttributes,
|
||||
SearchSessionStatus,
|
||||
} from '../../../common';
|
||||
import { SEARCH_SESSION_TYPE } from '../../saved_objects';
|
||||
|
@ -66,7 +60,11 @@ interface StartDependencies {
|
|||
|
||||
type SearchSessionsConfig = ConfigSchema['search']['sessions'];
|
||||
|
||||
export class SearchSessionService implements ISessionService {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class SearchSessionService
|
||||
implements ISearchSessionService<SearchSessionSavedObjectAttributes> {
|
||||
/**
|
||||
* Map of sessionId to { [requestHash]: searchId }
|
||||
* @private
|
||||
|
@ -228,33 +226,9 @@ export class SearchSessionService implements ISessionService {
|
|||
return updateResults.saved_objects;
|
||||
}
|
||||
|
||||
public search<Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(
|
||||
strategy: ISearchStrategy<Request, Response>,
|
||||
searchRequest: Request,
|
||||
options: ISearchOptions,
|
||||
searchDeps: SearchStrategyDependencies,
|
||||
deps: SearchSessionDependencies
|
||||
): Observable<Response> {
|
||||
// If this is a restored background search session, look up the ID using the provided sessionId
|
||||
const getSearchRequest = async () =>
|
||||
!options.isRestore || searchRequest.id
|
||||
? searchRequest
|
||||
: {
|
||||
...searchRequest,
|
||||
id: await this.getId(searchRequest, options, deps),
|
||||
};
|
||||
|
||||
return from(getSearchRequest()).pipe(
|
||||
switchMap((request) => strategy.search(request, options, searchDeps)),
|
||||
tapFirst((response) => {
|
||||
if (searchRequest.id || !options.sessionId || !response.id || options.isRestore) return;
|
||||
this.trackId(searchRequest, response.id, options, deps);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Generate the `userId` from the realm type/realm name/username
|
||||
public save = async (
|
||||
{ savedObjectsClient }: SearchSessionDependencies,
|
||||
sessionId: string,
|
||||
{
|
||||
name,
|
||||
|
@ -265,8 +239,7 @@ export class SearchSessionService implements ISessionService {
|
|||
urlGeneratorId,
|
||||
initialState = {},
|
||||
restoreState = {},
|
||||
}: Partial<SearchSessionSavedObjectAttributes>,
|
||||
{ savedObjectsClient }: SearchSessionDependencies
|
||||
}: Partial<SearchSessionSavedObjectAttributes>
|
||||
) => {
|
||||
if (!name) throw new Error('Name is required');
|
||||
if (!appId) throw new Error('AppId is required');
|
||||
|
@ -296,7 +269,7 @@ export class SearchSessionService implements ISessionService {
|
|||
};
|
||||
|
||||
// TODO: Throw an error if this session doesn't belong to this user
|
||||
public get = (sessionId: string, { savedObjectsClient }: SearchSessionDependencies) => {
|
||||
public get = ({ savedObjectsClient }: SearchSessionDependencies, sessionId: string) => {
|
||||
this.logger.debug(`get | ${sessionId}`);
|
||||
return savedObjectsClient.get<SearchSessionSavedObjectAttributes>(
|
||||
SEARCH_SESSION_TYPE,
|
||||
|
@ -306,8 +279,8 @@ export class SearchSessionService implements ISessionService {
|
|||
|
||||
// TODO: Throw an error if this session doesn't belong to this user
|
||||
public find = (
|
||||
options: Omit<SavedObjectsFindOptions, 'type'>,
|
||||
{ savedObjectsClient }: SearchSessionDependencies
|
||||
{ savedObjectsClient }: SearchSessionDependencies,
|
||||
options: Omit<SavedObjectsFindOptions, 'type'>
|
||||
) => {
|
||||
return savedObjectsClient.find<SearchSessionSavedObjectAttributes>({
|
||||
...options,
|
||||
|
@ -317,9 +290,9 @@ export class SearchSessionService implements ISessionService {
|
|||
|
||||
// TODO: Throw an error if this session doesn't belong to this user
|
||||
public update = (
|
||||
{ savedObjectsClient }: SearchSessionDependencies,
|
||||
sessionId: string,
|
||||
attributes: Partial<SearchSessionSavedObjectAttributes>,
|
||||
{ savedObjectsClient }: SearchSessionDependencies
|
||||
attributes: Partial<SearchSessionSavedObjectAttributes>
|
||||
) => {
|
||||
this.logger.debug(`update | ${sessionId}`);
|
||||
return savedObjectsClient.update<SearchSessionSavedObjectAttributes>(
|
||||
|
@ -329,9 +302,17 @@ export class SearchSessionService implements ISessionService {
|
|||
);
|
||||
};
|
||||
|
||||
public extend(deps: SearchSessionDependencies, sessionId: string, expires: Date) {
|
||||
this.logger.debug(`extend | ${sessionId}`);
|
||||
|
||||
return this.update(deps, sessionId, { expires: expires.toISOString() });
|
||||
}
|
||||
|
||||
// TODO: Throw an error if this session doesn't belong to this user
|
||||
public delete = (sessionId: string, { savedObjectsClient }: SearchSessionDependencies) => {
|
||||
return savedObjectsClient.delete(SEARCH_SESSION_TYPE, sessionId);
|
||||
public cancel = (deps: SearchSessionDependencies, sessionId: string) => {
|
||||
return this.update(deps, sessionId, {
|
||||
status: SearchSessionStatus.CANCELLED,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -340,10 +321,10 @@ export class SearchSessionService implements ISessionService {
|
|||
* @internal
|
||||
*/
|
||||
public trackId = async (
|
||||
deps: SearchSessionDependencies,
|
||||
searchRequest: IKibanaSearchRequest,
|
||||
searchId: string,
|
||||
{ sessionId, isStored, strategy }: ISearchOptions,
|
||||
deps: SearchSessionDependencies
|
||||
{ sessionId, strategy }: ISearchOptions
|
||||
) => {
|
||||
if (!sessionId || !searchId) return;
|
||||
this.logger.debug(`trackId | ${sessionId} | ${searchId}`);
|
||||
|
@ -354,33 +335,34 @@ export class SearchSessionService implements ISessionService {
|
|||
status: SearchStatus.IN_PROGRESS,
|
||||
};
|
||||
|
||||
// If there is already a saved object for this session, update it to include this request/ID.
|
||||
// Otherwise, just update the in-memory mapping for this session for when the session is saved.
|
||||
if (isStored) {
|
||||
const attributes = {
|
||||
idMapping: { [requestHash]: searchInfo },
|
||||
};
|
||||
await this.update(sessionId, attributes, deps);
|
||||
} else {
|
||||
const map = this.sessionSearchMap.get(sessionId) ?? {
|
||||
insertTime: moment(),
|
||||
retryCount: 0,
|
||||
ids: new Map<string, SearchSessionRequestInfo>(),
|
||||
};
|
||||
map.ids.set(requestHash, searchInfo);
|
||||
this.sessionSearchMap.set(sessionId, map);
|
||||
}
|
||||
// Update the in-memory mapping for this session for when the session is saved.
|
||||
const map = this.sessionSearchMap.get(sessionId) ?? {
|
||||
insertTime: moment(),
|
||||
retryCount: 0,
|
||||
ids: new Map<string, SearchSessionRequestInfo>(),
|
||||
};
|
||||
map.ids.set(requestHash, searchInfo);
|
||||
this.sessionSearchMap.set(sessionId, map);
|
||||
};
|
||||
|
||||
public async getSearchIdMapping(deps: SearchSessionDependencies, sessionId: string) {
|
||||
const searchSession = await this.get(deps, sessionId);
|
||||
const searchIdMapping = new Map<string, string>();
|
||||
Object.values(searchSession.attributes.idMapping).forEach((requestInfo) => {
|
||||
searchIdMapping.set(requestInfo.id, requestInfo.strategy);
|
||||
});
|
||||
return searchIdMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up an existing search ID that matches the given request in the given session so that the
|
||||
* request can continue rather than restart.
|
||||
* @internal
|
||||
*/
|
||||
public getId = async (
|
||||
deps: SearchSessionDependencies,
|
||||
searchRequest: IKibanaSearchRequest,
|
||||
{ sessionId, isStored, isRestore }: ISearchOptions,
|
||||
deps: SearchSessionDependencies
|
||||
{ sessionId, isStored, isRestore }: ISearchOptions
|
||||
) => {
|
||||
if (!sessionId) {
|
||||
throw new Error('Session ID is required');
|
||||
|
@ -390,7 +372,7 @@ export class SearchSessionService implements ISessionService {
|
|||
throw new Error('Get search ID is only supported when restoring a session');
|
||||
}
|
||||
|
||||
const session = await this.get(sessionId, deps);
|
||||
const session = await this.get(deps, sessionId);
|
||||
const requestHash = createRequestHash(searchRequest.params);
|
||||
if (!session.attributes.idMapping.hasOwnProperty(requestHash)) {
|
||||
throw new Error('No search ID in this session matching the given search request');
|
||||
|
@ -406,17 +388,15 @@ export class SearchSessionService implements ISessionService {
|
|||
});
|
||||
const deps = { savedObjectsClient };
|
||||
return {
|
||||
search: <Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(
|
||||
strategy: ISearchStrategy<Request, Response>,
|
||||
...args: Parameters<ISearchStrategy<Request, Response>['search']>
|
||||
) => this.search(strategy, ...args, deps),
|
||||
save: (sessionId: string, attributes: Partial<SearchSessionSavedObjectAttributes>) =>
|
||||
this.save(sessionId, attributes, deps),
|
||||
get: (sessionId: string) => this.get(sessionId, deps),
|
||||
find: (options: SavedObjectsFindOptions) => this.find(options, deps),
|
||||
update: (sessionId: string, attributes: Partial<SearchSessionSavedObjectAttributes>) =>
|
||||
this.update(sessionId, attributes, deps),
|
||||
delete: (sessionId: string) => this.delete(sessionId, deps),
|
||||
getId: this.getId.bind(this, deps),
|
||||
trackId: this.trackId.bind(this, deps),
|
||||
getSearchIdMapping: this.getSearchIdMapping.bind(this, deps),
|
||||
save: this.save.bind(this, deps),
|
||||
get: this.get.bind(this, deps),
|
||||
find: this.find.bind(this, deps),
|
||||
update: this.update.bind(this, deps),
|
||||
extend: this.extend.bind(this, deps),
|
||||
cancel: this.cancel.bind(this, deps),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
ISearchStrategy,
|
||||
SearchStrategyDependencies,
|
||||
} from 'src/plugins/data/server';
|
||||
import { createSearchSessionsClientMock } from '../../../../../../src/plugins/data/server/search/mocks';
|
||||
import { InfraSource } from '../../lib/sources';
|
||||
import { createInfraSourcesMock } from '../../lib/sources/mocks';
|
||||
import {
|
||||
|
@ -307,6 +308,7 @@ const createSearchStrategyDependenciesMock = (): SearchStrategyDependencies => (
|
|||
uiSettingsClient: uiSettingsServiceMock.createClient(),
|
||||
esClient: elasticsearchServiceMock.createScopedClusterClient(),
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
searchSessionsClient: createSearchSessionsClientMock(),
|
||||
});
|
||||
|
||||
// using the official data mock from within x-pack doesn't type-check successfully,
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
logEntrySearchRequestStateRT,
|
||||
logEntrySearchStrategyProvider,
|
||||
} from './log_entry_search_strategy';
|
||||
import { createSearchSessionsClientMock } from '../../../../../../src/plugins/data/server/search/mocks';
|
||||
|
||||
describe('LogEntry search strategy', () => {
|
||||
it('handles initial search requests', async () => {
|
||||
|
@ -244,6 +245,7 @@ const createSearchStrategyDependenciesMock = (): SearchStrategyDependencies => (
|
|||
uiSettingsClient: uiSettingsServiceMock.createClient(),
|
||||
esClient: elasticsearchServiceMock.createScopedClusterClient(),
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
searchSessionsClient: createSearchSessionsClientMock(),
|
||||
});
|
||||
|
||||
// using the official data mock from within x-pack doesn't type-check successfully,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import type { RequestHandlerContext } from 'src/core/server';
|
||||
import type { DataApiRequestHandlerContext } from '../../../../src/plugins/data/server';
|
||||
import type { SearchRequestHandlerContext } from '../../../../src/plugins/data/server';
|
||||
import { MlPluginSetup } from '../../ml/server';
|
||||
|
||||
export type MlSystem = ReturnType<MlPluginSetup['mlSystemProvider']>;
|
||||
|
@ -27,5 +27,5 @@ export type InfraRequestHandlerContext = InfraMlRequestHandlerContext &
|
|||
*/
|
||||
export interface InfraPluginRequestHandlerContext extends RequestHandlerContext {
|
||||
infra: InfraRequestHandlerContext;
|
||||
search: DataApiRequestHandlerContext;
|
||||
search: SearchRequestHandlerContext;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
import geojsonvt from 'geojson-vt';
|
||||
// @ts-expect-error
|
||||
import vtpbf from 'vt-pbf';
|
||||
import { Logger, RequestHandlerContext } from 'src/core/server';
|
||||
import type { DataApiRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import { Logger } from 'src/core/server';
|
||||
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import { Feature, FeatureCollection, Polygon } from 'geojson';
|
||||
import {
|
||||
ES_GEO_FIELD_TYPE,
|
||||
|
@ -45,7 +45,7 @@ export async function getGridTile({
|
|||
z: number;
|
||||
geometryFieldName: string;
|
||||
index: string;
|
||||
context: RequestHandlerContext & { search: DataApiRequestHandlerContext };
|
||||
context: DataRequestHandlerContext;
|
||||
logger: Logger;
|
||||
requestBody: any;
|
||||
requestType: RENDER_AS;
|
||||
|
@ -125,7 +125,7 @@ export async function getTile({
|
|||
z: number;
|
||||
geometryFieldName: string;
|
||||
index: string;
|
||||
context: RequestHandlerContext & { search: DataApiRequestHandlerContext };
|
||||
context: DataRequestHandlerContext;
|
||||
logger: Logger;
|
||||
requestBody: any;
|
||||
geoFieldType: ES_GEO_FIELD_TYPE;
|
||||
|
|
|
@ -6,14 +6,9 @@
|
|||
|
||||
import rison from 'rison-node';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import {
|
||||
KibanaRequest,
|
||||
KibanaResponseFactory,
|
||||
Logger,
|
||||
RequestHandlerContext,
|
||||
} from 'src/core/server';
|
||||
import { KibanaRequest, KibanaResponseFactory, Logger } from 'src/core/server';
|
||||
import { IRouter } from 'src/core/server';
|
||||
import type { DataApiRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import {
|
||||
MVT_GETTILE_API_PATH,
|
||||
API_ROOT_PATH,
|
||||
|
@ -29,7 +24,7 @@ export function initMVTRoutes({
|
|||
router,
|
||||
logger,
|
||||
}: {
|
||||
router: IRouter<RequestHandlerContext & { search: DataApiRequestHandlerContext }>;
|
||||
router: IRouter<DataRequestHandlerContext>;
|
||||
logger: Logger;
|
||||
}) {
|
||||
router.get(
|
||||
|
@ -49,7 +44,7 @@ export function initMVTRoutes({
|
|||
},
|
||||
},
|
||||
async (
|
||||
context: RequestHandlerContext & { search: DataApiRequestHandlerContext },
|
||||
context: DataRequestHandlerContext,
|
||||
request: KibanaRequest<unknown, Record<string, any>, unknown>,
|
||||
response: KibanaResponseFactory
|
||||
) => {
|
||||
|
@ -91,7 +86,7 @@ export function initMVTRoutes({
|
|||
},
|
||||
},
|
||||
async (
|
||||
context: RequestHandlerContext & { search: DataApiRequestHandlerContext },
|
||||
context: DataRequestHandlerContext,
|
||||
request: KibanaRequest<unknown, Record<string, any>, unknown>,
|
||||
response: KibanaResponseFactory
|
||||
) => {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { SearchSessionStatus } from '../../../../plugins/data_enhanced/common';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
@ -29,11 +30,11 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await supertest.get(`/internal/session/${sessionId}`).set('kbn-xsrf', 'foo').expect(200);
|
||||
});
|
||||
|
||||
it('should fail to delete an unknown session', async () => {
|
||||
it('should fail to cancel an unknown session', async () => {
|
||||
await supertest.delete(`/internal/session/123`).set('kbn-xsrf', 'foo').expect(404);
|
||||
});
|
||||
|
||||
it('should create and delete a session', async () => {
|
||||
it('should create and cancel a session', async () => {
|
||||
const sessionId = `my-session-${Math.random()}`;
|
||||
await supertest
|
||||
.post(`/internal/session`)
|
||||
|
@ -49,7 +50,13 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
await supertest.delete(`/internal/session/${sessionId}`).set('kbn-xsrf', 'foo').expect(200);
|
||||
|
||||
await supertest.get(`/internal/session/${sessionId}`).set('kbn-xsrf', 'foo').expect(404);
|
||||
const resp = await supertest
|
||||
.get(`/internal/session/${sessionId}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
|
||||
const { status } = resp.body.attributes;
|
||||
expect(status).to.equal(SearchSessionStatus.CANCELLED);
|
||||
});
|
||||
|
||||
it('should sync search ids into session', async () => {
|
||||
|
@ -123,6 +130,39 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(idMappings).to.contain(id1);
|
||||
expect(idMappings).to.contain(id2);
|
||||
});
|
||||
|
||||
it('should create and extend a session', async () => {
|
||||
const sessionId = `my-session-${Math.random()}`;
|
||||
await supertest
|
||||
.post(`/internal/session`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
sessionId,
|
||||
name: 'My Session',
|
||||
appId: 'discover',
|
||||
expires: '123',
|
||||
urlGeneratorId: 'discover',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await supertest
|
||||
.post(`/internal/session/${sessionId}/_extend`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
expires: '2021-02-26T21:02:43.742Z',
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to extend a nonexistent session', async () => {
|
||||
await supertest
|
||||
.post(`/internal/session/123/_extend`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
expires: '2021-02-26T21:02:43.742Z',
|
||||
})
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue