mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
* Allow restored session to run missing searches and show a warning * tests and docs * improve warning * tests for new functionality NoSearchIdInSessionError type * managmeent tests * Update texts * fix search service pus * link to docs * imports * format import Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Liza Katz <lizka.k@gmail.com>
This commit is contained in:
parent
6e10c4bfc1
commit
056cf014c0
30 changed files with 366 additions and 31 deletions
|
@ -106,6 +106,7 @@ readonly links: {
|
|||
};
|
||||
readonly search: {
|
||||
readonly sessions: string;
|
||||
readonly sessionLimits: string;
|
||||
};
|
||||
readonly indexPatterns: {
|
||||
readonly introduction: string;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) > [isRestored](./kibana-plugin-plugins-data-public.ikibanasearchresponse.isrestored.md)
|
||||
|
||||
## IKibanaSearchResponse.isRestored property
|
||||
|
||||
Indicates whether the results returned are from the async-search index
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
isRestored?: boolean;
|
||||
```
|
|
@ -16,6 +16,7 @@ export interface IKibanaSearchResponse<RawResponse = any>
|
|||
| --- | --- | --- |
|
||||
| [id](./kibana-plugin-plugins-data-public.ikibanasearchresponse.id.md) | <code>string</code> | Some responses may contain a unique id to identify the request this response came from. |
|
||||
| [isPartial](./kibana-plugin-plugins-data-public.ikibanasearchresponse.ispartial.md) | <code>boolean</code> | Indicates whether the results returned are complete or partial |
|
||||
| [isRestored](./kibana-plugin-plugins-data-public.ikibanasearchresponse.isrestored.md) | <code>boolean</code> | Indicates whether the results returned are from the async-search index |
|
||||
| [isRunning](./kibana-plugin-plugins-data-public.ikibanasearchresponse.isrunning.md) | <code>boolean</code> | Indicates whether search is still in flight |
|
||||
| [loaded](./kibana-plugin-plugins-data-public.ikibanasearchresponse.loaded.md) | <code>number</code> | If relevant to the search strategy, return a loaded number that represents how progress is indicated. |
|
||||
| [rawResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md) | <code>RawResponse</code> | The raw response returned by the internal search method (usually the raw ES response) |
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
| [IndexPatternsFetcher](./kibana-plugin-plugins-data-server.indexpatternsfetcher.md) | |
|
||||
| [IndexPatternsService](./kibana-plugin-plugins-data-server.indexpatternsservice.md) | |
|
||||
| [IndexPatternsServiceProvider](./kibana-plugin-plugins-data-server.indexpatternsserviceprovider.md) | |
|
||||
| [NoSearchIdInSessionError](./kibana-plugin-plugins-data-server.nosearchidinsessionerror.md) | |
|
||||
| [OptionedParamType](./kibana-plugin-plugins-data-server.optionedparamtype.md) | |
|
||||
| [Plugin](./kibana-plugin-plugins-data-server.plugin.md) | |
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- 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) > [NoSearchIdInSessionError](./kibana-plugin-plugins-data-server.nosearchidinsessionerror.md) > [(constructor)](./kibana-plugin-plugins-data-server.nosearchidinsessionerror._constructor_.md)
|
||||
|
||||
## NoSearchIdInSessionError.(constructor)
|
||||
|
||||
Constructs a new instance of the `NoSearchIdInSessionError` class
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
constructor();
|
||||
```
|
|
@ -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) > [NoSearchIdInSessionError](./kibana-plugin-plugins-data-server.nosearchidinsessionerror.md)
|
||||
|
||||
## NoSearchIdInSessionError class
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare class NoSearchIdInSessionError extends KbnError
|
||||
```
|
||||
|
||||
## Constructors
|
||||
|
||||
| Constructor | Modifiers | Description |
|
||||
| --- | --- | --- |
|
||||
| [(constructor)()](./kibana-plugin-plugins-data-server.nosearchidinsessionerror._constructor_.md) | | Constructs a new instance of the <code>NoSearchIdInSessionError</code> class |
|
||||
|
|
@ -205,6 +205,7 @@ export class DocLinksService {
|
|||
},
|
||||
search: {
|
||||
sessions: `${KIBANA_DOCS}search-sessions.html`,
|
||||
sessionLimits: `${KIBANA_DOCS}search-sessions.html#_limitations`,
|
||||
},
|
||||
date: {
|
||||
dateMath: `${ELASTICSEARCH_DOCS}common-options.html#date-math`,
|
||||
|
@ -525,6 +526,7 @@ export interface DocLinksStart {
|
|||
};
|
||||
readonly search: {
|
||||
readonly sessions: string;
|
||||
readonly sessionLimits: string;
|
||||
};
|
||||
readonly indexPatterns: {
|
||||
readonly introduction: string;
|
||||
|
|
|
@ -585,6 +585,7 @@ export interface DocLinksStart {
|
|||
};
|
||||
readonly search: {
|
||||
readonly sessions: string;
|
||||
readonly sessionLimits: string;
|
||||
};
|
||||
readonly indexPatterns: {
|
||||
readonly introduction: string;
|
||||
|
|
|
@ -65,6 +65,11 @@ export interface IKibanaSearchResponse<RawResponse = any> {
|
|||
*/
|
||||
isPartial?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether the results returned are from the async-search index
|
||||
*/
|
||||
isRestored?: boolean;
|
||||
|
||||
/**
|
||||
* The raw response returned by the internal search method (usually the raw ES response)
|
||||
*/
|
||||
|
|
|
@ -1353,6 +1353,7 @@ export interface IKibanaSearchRequest<Params = any> {
|
|||
export interface IKibanaSearchResponse<RawResponse = any> {
|
||||
id?: string;
|
||||
isPartial?: boolean;
|
||||
isRestored?: boolean;
|
||||
isRunning?: boolean;
|
||||
loaded?: number;
|
||||
rawResponse: RawResponse;
|
||||
|
|
|
@ -12,3 +12,4 @@ export * from './timeout_error';
|
|||
export * from './utils';
|
||||
export * from './types';
|
||||
export * from './http_error';
|
||||
export * from './search_session_incomplete_warning';
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EuiLink, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
export const SearchSessionIncompleteWarning = (docLinks: CoreStart['docLinks']) => (
|
||||
<>
|
||||
<EuiSpacer size="s" />
|
||||
It needs more time to fully render. You can wait here or come back to it later.
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText textAlign="right">
|
||||
<EuiLink
|
||||
href={docLinks.links.search.sessionLimits}
|
||||
color="warning"
|
||||
target="_blank"
|
||||
data-test-subj="searchSessionIncompleteWarning"
|
||||
external
|
||||
>
|
||||
<FormattedMessage id="data.searchSession.warning.readDocs" defaultMessage="Read More" />
|
||||
</EuiLink>
|
||||
</EuiText>
|
||||
</>
|
||||
);
|
|
@ -29,6 +29,12 @@ jest.mock('./utils', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../errors/search_session_incomplete_warning', () => ({
|
||||
SearchSessionIncompleteWarning: jest.fn(),
|
||||
}));
|
||||
|
||||
import { SearchSessionIncompleteWarning } from '../errors/search_session_incomplete_warning';
|
||||
|
||||
let searchInterceptor: SearchInterceptor;
|
||||
let mockCoreSetup: MockedKeys<CoreSetup>;
|
||||
let bfetchSetup: jest.Mocked<BfetchPublicSetup>;
|
||||
|
@ -508,6 +514,7 @@ describe('SearchInterceptor', () => {
|
|||
}
|
||||
: null
|
||||
);
|
||||
sessionServiceMock.isRestore.mockReturnValue(!!opts?.isRestore);
|
||||
fetchMock.mockResolvedValue({ result: 200 });
|
||||
};
|
||||
|
||||
|
@ -562,6 +569,92 @@ describe('SearchInterceptor', () => {
|
|||
(sessionService as jest.Mocked<ISessionService>).getSearchOptions
|
||||
).toHaveBeenCalledWith(sessionId);
|
||||
});
|
||||
|
||||
test('should not show warning if a search is available during restore', async () => {
|
||||
setup({
|
||||
isRestore: true,
|
||||
isStored: true,
|
||||
sessionId: '123',
|
||||
});
|
||||
|
||||
const responses = [
|
||||
{
|
||||
time: 10,
|
||||
value: {
|
||||
isPartial: false,
|
||||
isRunning: false,
|
||||
isRestored: true,
|
||||
id: 1,
|
||||
rawResponse: {
|
||||
took: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
mockFetchImplementation(responses);
|
||||
|
||||
const response = searchInterceptor.search(
|
||||
{},
|
||||
{
|
||||
sessionId: '123',
|
||||
}
|
||||
);
|
||||
response.subscribe({ next, error, complete });
|
||||
|
||||
await timeTravel(10);
|
||||
|
||||
expect(SearchSessionIncompleteWarning).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('should show warning once if a search is not available during restore', async () => {
|
||||
setup({
|
||||
isRestore: true,
|
||||
isStored: true,
|
||||
sessionId: '123',
|
||||
});
|
||||
|
||||
const responses = [
|
||||
{
|
||||
time: 10,
|
||||
value: {
|
||||
isPartial: false,
|
||||
isRunning: false,
|
||||
isRestored: false,
|
||||
id: 1,
|
||||
rawResponse: {
|
||||
took: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
mockFetchImplementation(responses);
|
||||
|
||||
searchInterceptor
|
||||
.search(
|
||||
{},
|
||||
{
|
||||
sessionId: '123',
|
||||
}
|
||||
)
|
||||
.subscribe({ next, error, complete });
|
||||
|
||||
await timeTravel(10);
|
||||
|
||||
expect(SearchSessionIncompleteWarning).toBeCalledTimes(1);
|
||||
|
||||
searchInterceptor
|
||||
.search(
|
||||
{},
|
||||
{
|
||||
sessionId: '123',
|
||||
}
|
||||
)
|
||||
.subscribe({ next, error, complete });
|
||||
|
||||
await timeTravel(10);
|
||||
|
||||
expect(SearchSessionIncompleteWarning).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Session tracking', () => {
|
||||
|
|
|
@ -43,6 +43,7 @@ import {
|
|||
PainlessError,
|
||||
SearchTimeoutError,
|
||||
TimeoutErrorMode,
|
||||
SearchSessionIncompleteWarning,
|
||||
} from '../errors';
|
||||
import { toMountPoint } from '../../../../kibana_react/public';
|
||||
import { AbortError, KibanaServerError } from '../../../../kibana_utils/public';
|
||||
|
@ -82,6 +83,7 @@ export class SearchInterceptor {
|
|||
* @internal
|
||||
*/
|
||||
private application!: CoreStart['application'];
|
||||
private docLinks!: CoreStart['docLinks'];
|
||||
private batchedFetch!: BatchedFunc<
|
||||
{ request: IKibanaSearchRequest; options: ISearchOptionsSerializable },
|
||||
IKibanaSearchResponse
|
||||
|
@ -95,6 +97,7 @@ export class SearchInterceptor {
|
|||
|
||||
this.deps.startServices.then(([coreStart]) => {
|
||||
this.application = coreStart.application;
|
||||
this.docLinks = coreStart.docLinks;
|
||||
});
|
||||
|
||||
this.batchedFetch = deps.bfetch.batchedFunction({
|
||||
|
@ -345,6 +348,11 @@ export class SearchInterceptor {
|
|||
this.handleSearchError(e, searchOptions, searchAbortController.isTimeout())
|
||||
);
|
||||
}),
|
||||
tap((response) => {
|
||||
if (this.deps.session.isRestore() && response.isRestored === false) {
|
||||
this.showRestoreWarning(this.deps.session.getSessionId());
|
||||
}
|
||||
}),
|
||||
finalize(() => {
|
||||
this.pendingCount$.next(this.pendingCount$.getValue() - 1);
|
||||
if (untrackSearch && this.deps.session.isCurrentSession(sessionId)) {
|
||||
|
@ -371,6 +379,25 @@ export class SearchInterceptor {
|
|||
}
|
||||
);
|
||||
|
||||
private showRestoreWarningToast = (sessionId?: string) => {
|
||||
this.deps.toasts.addWarning(
|
||||
{
|
||||
title: 'Your search session is still running',
|
||||
text: toMountPoint(SearchSessionIncompleteWarning(this.docLinks)),
|
||||
},
|
||||
{
|
||||
toastLifeTimeMs: 60000,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
private showRestoreWarning = memoize(
|
||||
this.showRestoreWarningToast,
|
||||
(_: SearchTimeoutError, sessionId: string) => {
|
||||
return sessionId;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Show one error notification per session.
|
||||
* @internal
|
||||
|
|
|
@ -238,6 +238,7 @@ export {
|
|||
DataRequestHandlerContext,
|
||||
AsyncSearchResponse,
|
||||
AsyncSearchStatusResponse,
|
||||
NoSearchIdInSessionError,
|
||||
} from './search';
|
||||
|
||||
// Search namespace
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { KbnError } from '../../../../kibana_utils/common';
|
||||
|
||||
export class NoSearchIdInSessionError extends KbnError {
|
||||
constructor() {
|
||||
super('No search ID in this session matching the given search request');
|
||||
}
|
||||
}
|
|
@ -13,3 +13,4 @@ export * from './strategies/eql_search';
|
|||
export { usageProvider, SearchUsage, searchUsageObserver } from './collectors';
|
||||
export * from './aggs';
|
||||
export * from './session';
|
||||
export * from './errors/no_search_id_in_session';
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
ISearchSessionService,
|
||||
ISearchStart,
|
||||
ISearchStrategy,
|
||||
NoSearchIdInSessionError,
|
||||
} from '.';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { expressionsPluginMock } from '../../../expressions/public/mocks';
|
||||
|
@ -175,6 +176,22 @@ describe('Search service', () => {
|
|||
expect(request).toStrictEqual({ ...searchRequest, id: 'my_id' });
|
||||
});
|
||||
|
||||
it('searches even if id is not found in session during restore', async () => {
|
||||
const searchRequest = { params: {} };
|
||||
const options = { sessionId, isStored: true, isRestore: true };
|
||||
|
||||
mockSessionClient.getId = jest.fn().mockImplementation(() => {
|
||||
throw new NoSearchIdInSessionError();
|
||||
});
|
||||
|
||||
const res = await mockScopedClient.search(searchRequest, options).toPromise();
|
||||
|
||||
const [request, callOptions] = mockStrategy.search.mock.calls[0];
|
||||
expect(callOptions).toBe(options);
|
||||
expect(request).toStrictEqual({ ...searchRequest });
|
||||
expect(res.isRestored).toBe(false);
|
||||
});
|
||||
|
||||
it('does not fail if `trackId` throws', async () => {
|
||||
const searchRequest = { params: {} };
|
||||
const options = { sessionId, isStored: false, isRestore: false };
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
SharedGlobalConfig,
|
||||
StartServicesAccessor,
|
||||
} from 'src/core/server';
|
||||
import { first, switchMap, tap } from 'rxjs/operators';
|
||||
import { first, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { BfetchServerSetup } from 'src/plugins/bfetch/server';
|
||||
import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
|
||||
import type {
|
||||
|
@ -80,6 +80,7 @@ import { registerBsearchRoute } from './routes/bsearch';
|
|||
import { getKibanaContext } from './expressions/kibana_context';
|
||||
import { enhancedEsSearchStrategyProvider } from './strategies/ese_search';
|
||||
import { eqlSearchStrategyProvider } from './strategies/eql_search';
|
||||
import { NoSearchIdInSessionError } from './errors/no_search_id_in_session';
|
||||
|
||||
type StrategyMap = Record<string, ISearchStrategy<any, any>>;
|
||||
|
||||
|
@ -287,24 +288,48 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
options.strategy
|
||||
);
|
||||
|
||||
const getSearchRequest = async () =>
|
||||
!options.sessionId || !options.isRestore || request.id
|
||||
? request
|
||||
: {
|
||||
const getSearchRequest = async () => {
|
||||
if (!options.sessionId || !options.isRestore || request.id) {
|
||||
return request;
|
||||
} else {
|
||||
try {
|
||||
const id = await deps.searchSessionsClient.getId(request, options);
|
||||
this.logger.debug(`Found search session id for request ${id}`);
|
||||
return {
|
||||
...request,
|
||||
id: await deps.searchSessionsClient.getId(request, options),
|
||||
id,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof NoSearchIdInSessionError) {
|
||||
this.logger.debug('Ignoring missing search ID');
|
||||
return request;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return from(getSearchRequest()).pipe(
|
||||
const searchRequest$ = from(getSearchRequest());
|
||||
const search$ = searchRequest$.pipe(
|
||||
switchMap((searchRequest) => strategy.search(searchRequest, options, deps)),
|
||||
tap((response) => {
|
||||
if (!options.sessionId || !response.id || options.isRestore) return;
|
||||
withLatestFrom(searchRequest$),
|
||||
tap(([response, requestWithId]) => {
|
||||
if (!options.sessionId || !response.id || (options.isRestore && requestWithId.id)) return;
|
||||
// intentionally swallow tracking error, as it shouldn't fail the search
|
||||
deps.searchSessionsClient.trackId(request, response.id, options).catch((trackErr) => {
|
||||
this.logger.error(trackErr);
|
||||
});
|
||||
}),
|
||||
map(([response, requestWithId]) => {
|
||||
return {
|
||||
...response,
|
||||
isRestored: !!requestWithId.id,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return search$;
|
||||
} catch (e) {
|
||||
return throwError(e);
|
||||
}
|
||||
|
|
|
@ -1211,6 +1211,14 @@ export enum METRIC_TYPES {
|
|||
TOP_HITS = "top_hits"
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "KbnError" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "NoSearchIdInSessionError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export class NoSearchIdInSessionError extends KbnError {
|
||||
constructor();
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "OptionedParamType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -1543,18 +1551,18 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
|
|||
// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:128:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:128:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:246:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:247:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:258:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:271:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:245:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:245:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:247:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:248:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:258:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:259:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:264:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:268:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:271:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/index.ts:272:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/server/plugin.ts:81: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:115:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ describe('Background Search Session management status labels', () => {
|
|||
id: 'wtywp9u2802hahgp-gsla',
|
||||
restoreUrl: '/app/great-app-url/#45',
|
||||
reloadUrl: '/app/great-app-url/#45',
|
||||
numSearches: 1,
|
||||
appId: 'security',
|
||||
status: SearchSessionStatus.IN_PROGRESS,
|
||||
created: '2020-12-02T00:19:32Z',
|
||||
|
|
|
@ -70,6 +70,7 @@ describe('Background Search Session Management Table', () => {
|
|||
status: SearchSessionStatus.IN_PROGRESS,
|
||||
created: '2020-12-02T00:19:32Z',
|
||||
expires: '2020-12-07T00:19:32Z',
|
||||
idMapping: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -95,10 +96,12 @@ describe('Background Search Session Management Table', () => {
|
|||
);
|
||||
});
|
||||
|
||||
expect(table.find('thead th').map((node) => node.text())).toMatchInlineSnapshot(`
|
||||
expect(table.find('thead th .euiTableCellContent__text').map((node) => node.text()))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"App",
|
||||
"Name",
|
||||
"# Searches",
|
||||
"Status",
|
||||
"Created",
|
||||
"Expiration",
|
||||
|
@ -130,6 +133,7 @@ describe('Background Search Session Management Table', () => {
|
|||
Array [
|
||||
"App",
|
||||
"Namevery background search ",
|
||||
"# Searches0",
|
||||
"StatusExpired",
|
||||
"Created2 Dec, 2020, 00:19:32",
|
||||
"Expiration--",
|
||||
|
|
|
@ -52,6 +52,7 @@ describe('Search Sessions Management API', () => {
|
|||
status: 'complete',
|
||||
initialState: {},
|
||||
restoreState: {},
|
||||
idMapping: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -78,6 +79,7 @@ describe('Search Sessions Management API', () => {
|
|||
"id": "hello-pizza-123",
|
||||
"initialState": Object {},
|
||||
"name": "Veggie",
|
||||
"numSearches": 0,
|
||||
"reloadUrl": "hello-cool-undefined-url",
|
||||
"restoreState": Object {},
|
||||
"restoreUrl": "hello-cool-undefined-url",
|
||||
|
@ -100,6 +102,7 @@ describe('Search Sessions Management API', () => {
|
|||
expires: moment().subtract(3, 'days'),
|
||||
initialState: {},
|
||||
restoreState: {},
|
||||
idMapping: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -90,6 +90,7 @@ const mapToUISession = (urls: UrlGeneratorsStart, config: SessionsConfigSchema)
|
|||
urlGeneratorId,
|
||||
initialState,
|
||||
restoreState,
|
||||
idMapping,
|
||||
} = savedObject.attributes;
|
||||
|
||||
const status = getUIStatus(savedObject.attributes);
|
||||
|
@ -113,6 +114,7 @@ const mapToUISession = (urls: UrlGeneratorsStart, config: SessionsConfigSchema)
|
|||
reloadUrl,
|
||||
initialState,
|
||||
restoreState,
|
||||
numSearches: Object.keys(idMapping).length,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ describe('Search Sessions Management table column factory', () => {
|
|||
reloadUrl: '/app/great-app-url',
|
||||
restoreUrl: '/app/great-app-url/#42',
|
||||
appId: 'discovery',
|
||||
numSearches: 3,
|
||||
status: SearchSessionStatus.IN_PROGRESS,
|
||||
created: '2020-12-02T00:19:32Z',
|
||||
expires: '2020-12-07T00:19:32Z',
|
||||
|
@ -95,6 +96,12 @@ describe('Search Sessions Management table column factory', () => {
|
|||
"sortable": true,
|
||||
"width": "20%",
|
||||
},
|
||||
Object {
|
||||
"field": "numSearches",
|
||||
"name": "# Searches",
|
||||
"render": [Function],
|
||||
"sortable": true,
|
||||
},
|
||||
Object {
|
||||
"field": "status",
|
||||
"name": "Status",
|
||||
|
@ -146,10 +153,29 @@ describe('Search Sessions Management table column factory', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// Num of searches column
|
||||
describe('num of searches', () => {
|
||||
test('renders', () => {
|
||||
const [, , numOfSearches] = getColumns(
|
||||
mockCoreStart,
|
||||
mockPluginsSetup,
|
||||
api,
|
||||
mockConfig,
|
||||
tz,
|
||||
handleAction
|
||||
) as Array<EuiTableFieldDataColumnType<UISession>>;
|
||||
|
||||
const numOfSearchesLine = mount(
|
||||
numOfSearches.render!(mockSession.numSearches, mockSession) as ReactElement
|
||||
);
|
||||
expect(numOfSearchesLine.text()).toMatchInlineSnapshot(`"3"`);
|
||||
});
|
||||
});
|
||||
|
||||
// Status column
|
||||
describe('status', () => {
|
||||
test('render in_progress', () => {
|
||||
const [, , status] = getColumns(
|
||||
const [, , , status] = getColumns(
|
||||
mockCoreStart,
|
||||
mockPluginsSetup,
|
||||
api,
|
||||
|
@ -165,7 +191,7 @@ describe('Search Sessions Management table column factory', () => {
|
|||
});
|
||||
|
||||
test('error handling', () => {
|
||||
const [, , status] = getColumns(
|
||||
const [, , , status] = getColumns(
|
||||
mockCoreStart,
|
||||
mockPluginsSetup,
|
||||
api,
|
||||
|
@ -188,7 +214,7 @@ describe('Search Sessions Management table column factory', () => {
|
|||
test('render using Browser timezone', () => {
|
||||
tz = 'Browser';
|
||||
|
||||
const [, , , createdDateCol] = getColumns(
|
||||
const [, , , , createdDateCol] = getColumns(
|
||||
mockCoreStart,
|
||||
mockPluginsSetup,
|
||||
api,
|
||||
|
@ -205,7 +231,7 @@ describe('Search Sessions Management table column factory', () => {
|
|||
test('render using AK timezone', () => {
|
||||
tz = 'US/Alaska';
|
||||
|
||||
const [, , , createdDateCol] = getColumns(
|
||||
const [, , , , createdDateCol] = getColumns(
|
||||
mockCoreStart,
|
||||
mockPluginsSetup,
|
||||
api,
|
||||
|
@ -220,7 +246,7 @@ describe('Search Sessions Management table column factory', () => {
|
|||
});
|
||||
|
||||
test('error handling', () => {
|
||||
const [, , , createdDateCol] = getColumns(
|
||||
const [, , , , createdDateCol] = getColumns(
|
||||
mockCoreStart,
|
||||
mockPluginsSetup,
|
||||
api,
|
||||
|
|
|
@ -120,6 +120,20 @@ export const getColumns = (
|
|||
},
|
||||
},
|
||||
|
||||
// # Searches
|
||||
{
|
||||
field: 'numSearches',
|
||||
name: i18n.translate('xpack.data.mgmt.searchSessions.table.numSearches', {
|
||||
defaultMessage: '# Searches',
|
||||
}),
|
||||
sortable: true,
|
||||
render: (numSearches: UISession['numSearches'], session) => (
|
||||
<TableText color="subdued" data-test-subj="sessionManagementNumSearchesCol">
|
||||
{numSearches}
|
||||
</TableText>
|
||||
),
|
||||
},
|
||||
|
||||
// Session status
|
||||
{
|
||||
field: 'status',
|
||||
|
|
|
@ -34,6 +34,7 @@ export interface UISession {
|
|||
created: string;
|
||||
expires: string | null;
|
||||
status: UISearchSessionState;
|
||||
numSearches: number;
|
||||
actions?: ACTION[];
|
||||
reloadUrl: string;
|
||||
restoreUrl: string;
|
||||
|
|
|
@ -24,7 +24,11 @@ import {
|
|||
ENHANCED_ES_SEARCH_STRATEGY,
|
||||
SEARCH_SESSION_TYPE,
|
||||
} from '../../../../../../src/plugins/data/common';
|
||||
import { esKuery, ISearchSessionService } from '../../../../../../src/plugins/data/server';
|
||||
import {
|
||||
esKuery,
|
||||
ISearchSessionService,
|
||||
NoSearchIdInSessionError,
|
||||
} from '../../../../../../src/plugins/data/server';
|
||||
import { AuthenticatedUser, SecurityPluginSetup } from '../../../../security/server';
|
||||
import {
|
||||
TaskManagerSetupContract,
|
||||
|
@ -436,7 +440,7 @@ export class SearchSessionService
|
|||
const requestHash = createRequestHash(searchRequest.params);
|
||||
if (!session.attributes.idMapping.hasOwnProperty(requestHash)) {
|
||||
this.logger.error(`getId | ${sessionId} | ${requestHash} not found`);
|
||||
throw new Error('No search ID in this session matching the given search request');
|
||||
throw new NoSearchIdInSessionError();
|
||||
}
|
||||
this.logger.debug(`getId | ${sessionId} | ${requestHash}`);
|
||||
|
||||
|
|
|
@ -403,7 +403,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const { id: id1 } = searchRes1.body;
|
||||
|
||||
// it might take the session a moment to be created
|
||||
await new Promise((resolve) => setTimeout(resolve, 2500));
|
||||
await retry.waitFor('search session created', async () => {
|
||||
const response = await supertest
|
||||
.get(`/internal/session/${sessionId}`)
|
||||
.set('kbn-xsrf', 'foo');
|
||||
return response.body.statusCode === undefined;
|
||||
});
|
||||
|
||||
const getSessionFirstTime = await supertest
|
||||
.get(`/internal/session/${sessionId}`)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue