Skip to content

Commit

Permalink
Merge branch 'main' of github.com:elastic/kibana into issue-170854-bu…
Browse files Browse the repository at this point in the history
…dget-consumed
  • Loading branch information
simianhacker committed Nov 10, 2023
2 parents 08daad7 + b84881a commit 7f2d4a4
Show file tree
Hide file tree
Showing 134 changed files with 2,667 additions and 686 deletions.
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pageLoadAssetSize:
licensing: 29004
links: 44490
lists: 22900
logExplorer: 39045
logExplorer: 54342
logsShared: 281060
logstash: 53548
management: 46112
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ describe('copyValueToClipboard', () => {
columnDisplayName: 'text_message',
});

expect(result).toBe('"text_message"');
expect(result).toBe('text_message');
expect(execCommandMock).toHaveBeenCalledWith('copy');
expect(servicesMock.toastNotifications.addInfo).toHaveBeenCalledWith({
title: 'Copied to clipboard',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,7 @@ export const copyColumnNameToClipboard = ({
columnDisplayName: string;
toastNotifications: ToastsStart;
}): string | null => {
const nameFormattedResult = convertNameToString(columnDisplayName);
const textToCopy = nameFormattedResult.formattedString;
const copied = copyToClipboard(textToCopy);
const copied = copyToClipboard(columnDisplayName);

if (!copied) {
toastNotifications.addWarning({
Expand All @@ -147,16 +145,9 @@ export const copyColumnNameToClipboard = ({
defaultMessage: 'Copied to clipboard',
});

if (nameFormattedResult.withFormula) {
toastNotifications.addWarning({
title: toastTitle,
text: WARNING_FOR_FORMULAS,
});
} else {
toastNotifications.addInfo({
title: toastTitle,
});
}
toastNotifications.addInfo({
title: toastTitle,
});

return textToCopy;
return columnDisplayName;
};
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const shareModalStrings = {
*/
export const getDashboardBreadcrumb = () =>
i18n.translate('dashboard.dashboardAppBreadcrumbsTitle', {
defaultMessage: 'Dashboard',
defaultMessage: 'Dashboards',
});

export const topNavStrings = {
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/dashboard/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export class DashboardPlugin

const app: App = {
id: DASHBOARD_APP_ID,
title: 'Dashboard',
title: 'Dashboards',
order: 2500,
euiIconType: 'logoKibana',
defaultPath: `#${LANDING_PAGE_PATH}`,
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/data/common/search/strategies/es_search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export type ISearchRequestParams = {
trackTotalHits?: boolean;
} & estypes.SearchRequest;

export interface IEsSearchRequest extends IKibanaSearchRequest<ISearchRequestParams> {
export interface IEsSearchRequest<T extends ISearchRequestParams = ISearchRequestParams>
extends IKibanaSearchRequest<T> {
indexType?: string;
}

Expand Down
4 changes: 3 additions & 1 deletion src/plugins/data/server/search/routes/bsearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { catchError } from 'rxjs/operators';
import { BfetchServerSetup } from '@kbn/bfetch-plugin/server';
import type { ExecutionContextSetup } from '@kbn/core/server';
import apm from 'elastic-apm-node';
import { getRequestAbortedSignal } from '../..';
import {
IKibanaSearchRequest,
IKibanaSearchResponse,
Expand All @@ -28,6 +29,7 @@ export function registerBsearchRoute(
IKibanaSearchResponse
>('/internal/bsearch', (request) => {
const search = getScoped(request);
const abortSignal = getRequestAbortedSignal(request.events.aborted$);
return {
/**
* @param requestOptions
Expand All @@ -39,7 +41,7 @@ export function registerBsearchRoute(
apm.addLabels(executionContextService.getAsLabels());

return firstValueFrom(
search.search(requestData, restOptions).pipe(
search.search(requestData, { ...restOptions, abortSignal }).pipe(
catchError((err) => {
// Re-throw as object, to get attributes passed to the client
// eslint-disable-next-line no-throw-literal
Expand Down
14 changes: 7 additions & 7 deletions src/plugins/data/server/search/search_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
}
};

private cancel = async (
deps: SearchStrategyDependencies,
id: string,
options: ISearchOptions = {}
) => {
private cancel = (deps: SearchStrategyDependencies, id: string, options: ISearchOptions = {}) => {
const strategy = this.getSearchStrategy(options.strategy);
if (!strategy.cancel) {
throw new KbnServerError(
Expand All @@ -468,14 +464,18 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
private cancelSessionSearches = async (deps: SearchStrategyDependencies, sessionId: string) => {
const searchIdMapping = await deps.searchSessionsClient.getSearchIdMapping(sessionId);
await Promise.allSettled(
Array.from(searchIdMapping).map(([searchId, strategyName]) => {
Array.from(searchIdMapping).map(async ([searchId, strategyName]) => {
const searchOptions = {
sessionId,
strategy: strategyName,
isStored: true,
};

return this.cancel(deps, searchId, searchOptions);
try {
await this.cancel(deps, searchId, searchOptions);
} catch (e) {
this.logger.error(`cancelSessionSearches error: ${e.message}`);
}
})
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getMockSearchConfig } from '../../../../config.mock';

const getMockEqlResponse = () => ({
body: {
id: 'my-search-id',
is_partial: false,
is_running: false,
took: 162,
Expand Down Expand Up @@ -54,13 +55,16 @@ describe('EQL search strategy', () => {
describe('search()', () => {
let mockEqlSearch: jest.Mock;
let mockEqlGet: jest.Mock;
let mockEqlDelete: jest.Mock;
let mockDeps: SearchStrategyDependencies;
let params: Required<EqlSearchStrategyRequest>['params'];
let options: Required<EqlSearchStrategyRequest>['options'];

beforeEach(() => {
mockEqlSearch = jest.fn().mockResolvedValueOnce(getMockEqlResponse());
mockEqlGet = jest.fn().mockResolvedValueOnce(getMockEqlResponse());
mockEqlDelete = jest.fn();

mockDeps = {
uiSettingsClient: {
get: jest.fn(),
Expand All @@ -70,6 +74,7 @@ describe('EQL search strategy', () => {
eql: {
get: mockEqlGet,
search: mockEqlSearch,
delete: mockEqlDelete,
},
},
},
Expand Down Expand Up @@ -124,6 +129,34 @@ describe('EQL search strategy', () => {
});
});

it('should delete when aborted', async () => {
const response = getMockEqlResponse();
mockEqlSearch.mockReset().mockResolvedValueOnce({
...response,
body: {
...response.body,
is_running: true,
},
});
const eqlSearch = await eqlSearchStrategyProvider(mockSearchConfig, mockLogger);
const abortController = new AbortController();
const abortSignal = abortController.signal;

// Abort after an incomplete first response is returned
setTimeout(() => abortController.abort(), 100);

let err: any;
try {
await eqlSearch.search({ options, params }, { abortSignal }, mockDeps).toPromise();
} catch (e) {
err = e;
}

expect(mockEqlSearch).toBeCalled();
expect(err).not.toBeUndefined();
expect(mockEqlDelete).toBeCalled();
});

describe('arguments', () => {
it('sends along async search options', async () => {
const eqlSearch = await eqlSearchStrategyProvider(mockSearchConfig, mockLogger);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type { TransportResult } from '@elastic/elasticsearch';
import { tap } from 'rxjs/operators';
import type { IScopedClusterClient, Logger } from '@kbn/core/server';
import { getKbnServerError } from '@kbn/kibana-utils-plugin/server';
import { SearchConfigSchema } from '../../../../config';
import {
EqlSearchStrategyRequest,
Expand All @@ -27,15 +28,19 @@ export const eqlSearchStrategyProvider = (
searchConfig: SearchConfigSchema,
logger: Logger
): ISearchStrategy<EqlSearchStrategyRequest, EqlSearchStrategyResponse> => {
async function cancelAsyncSearch(id: string, esClient: IScopedClusterClient) {
function cancelAsyncSearch(id: string, esClient: IScopedClusterClient) {
const client = esClient.asCurrentUser.eql;
await client.delete({ id });
return client.delete({ id });
}

return {
cancel: async (id, options, { esClient }) => {
logger.debug(`_eql/delete ${id}`);
await cancelAsyncSearch(id, esClient);
try {
await cancelAsyncSearch(id, esClient);
} catch (e) {
throw getKbnServerError(e);
}
},

search: ({ id, ...request }, options: IAsyncSearchOptions, { esClient, uiSettingsClient }) => {
Expand Down Expand Up @@ -85,8 +90,15 @@ export const eqlSearchStrategyProvider = (
};

const cancel = async () => {
if (id) {
if (!id) return;
try {
await cancelAsyncSearch(id, esClient);
} catch (e) {
// A 404 means either this search request does not exist, or that it is already cancelled
if (e.meta?.statusCode === 404) return;

// Log all other (unexpected) error messages
logger.error(`cancelEqlSearch error: ${e.message}`);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,30 @@ describe('ES search strategy', () => {
expect(request).toHaveProperty('keep_alive', '60000ms');
});

it('allows overriding keep_alive and wait_for_completion_timeout', async () => {
mockGetCaller.mockResolvedValueOnce(mockAsyncResponse);

const params = {
index: 'logstash-*',
body: { query: {} },
wait_for_completion_timeout: '10s',
keep_alive: '5m',
};
const esSearch = await enhancedEsSearchStrategyProvider(
mockLegacyConfig$,
mockSearchConfig,
mockLogger
);

await esSearch.search({ id: 'foo', params }, {}, mockDeps).toPromise();

expect(mockGetCaller).toBeCalled();
const request = mockGetCaller.mock.calls[0][0];
expect(request.id).toEqual('foo');
expect(request).toHaveProperty('wait_for_completion_timeout', '10s');
expect(request).toHaveProperty('keep_alive', '5m');
});

it('sets transport options on POST requests', async () => {
const transportOptions = { maxRetries: 1 };
mockSubmitCaller.mockResolvedValueOnce(mockAsyncResponse);
Expand Down Expand Up @@ -260,6 +284,38 @@ describe('ES search strategy', () => {

expect(mockApiCaller).toBeCalledTimes(0);
});

it('should delete when aborted', async () => {
mockSubmitCaller.mockResolvedValueOnce({
...mockAsyncResponse,
body: {
...mockAsyncResponse.body,
is_running: true,
},
});

const params = { index: 'logstash-*', body: { query: {} } };
const esSearch = await enhancedEsSearchStrategyProvider(
mockLegacyConfig$,
mockSearchConfig,
mockLogger
);
const abortController = new AbortController();
const abortSignal = abortController.signal;

// Abort after an incomplete first response is returned
setTimeout(() => abortController.abort(), 100);

let err: KbnServerError | undefined;
try {
await esSearch.search({ params }, { abortSignal }, mockDeps).toPromise();
} catch (e) {
err = e;
}
expect(mockSubmitCaller).toBeCalled();
expect(err).not.toBeUndefined();
expect(mockDeleteCaller).toBeCalled();
});
});

describe('with sessionId', () => {
Expand Down Expand Up @@ -367,6 +423,44 @@ describe('ES search strategy', () => {
expect(request).toHaveProperty('wait_for_completion_timeout');
expect(request).not.toHaveProperty('keep_alive');
});

it('should not delete a saved session when aborted', async () => {
mockSubmitCaller.mockResolvedValueOnce({
...mockAsyncResponse,
body: {
...mockAsyncResponse.body,
is_running: true,
},
});

const params = { index: 'logstash-*', body: { query: {} } };
const esSearch = await enhancedEsSearchStrategyProvider(
mockLegacyConfig$,
mockSearchConfig,
mockLogger
);
const abortController = new AbortController();
const abortSignal = abortController.signal;

// Abort after an incomplete first response is returned
setTimeout(() => abortController.abort(), 100);

let err: KbnServerError | undefined;
try {
await esSearch
.search(
{ params },
{ abortSignal, sessionId: '1', isSearchStored: true, isStored: true },
mockDeps
)
.toPromise();
} catch (e) {
err = e;
}
expect(mockSubmitCaller).toBeCalled();
expect(err).not.toBeUndefined();
expect(mockDeleteCaller).not.toBeCalled();
});
});

it('throws normalized error if ResponseError is thrown', async () => {
Expand Down
Loading

0 comments on commit 7f2d4a4

Please sign in to comment.