mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
discover query cancellation (#176202)
This commit is contained in:
parent
4010b318d9
commit
721f48cad3
10 changed files with 128 additions and 10 deletions
|
@ -80,6 +80,7 @@ export const DiscoverHistogramLayout = ({
|
|||
container={container}
|
||||
css={histogramLayoutCss}
|
||||
renderCustomChartToggleActions={renderCustomChartToggleActions}
|
||||
abortController={stateContainer.dataState.getAbortController()}
|
||||
>
|
||||
<DiscoverMainContent
|
||||
{...mainContentProps}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import './discover_layout.scss';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState, ReactElement } from 'react';
|
||||
import React, { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { EuiPage, EuiPageBody, EuiPanel, useEuiBackgroundColor } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -46,6 +46,7 @@ import { addLog } from '../../../../utils/add_log';
|
|||
import { DiscoverResizableLayout } from './discover_resizable_layout';
|
||||
import { ESQLTechPreviewCallout } from './esql_tech_preview_callout';
|
||||
import { PanelsToggle, PanelsToggleProps } from '../../../../components/panels_toggle';
|
||||
import { sendErrorMsg } from '../../hooks/use_saved_search_messages';
|
||||
|
||||
const SidebarMemoized = React.memo(DiscoverSidebarResponsive);
|
||||
const TopNavMemoized = React.memo(DiscoverTopNav);
|
||||
|
@ -240,6 +241,16 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
|||
panelsToggle,
|
||||
]);
|
||||
|
||||
const isLoading =
|
||||
documentState.fetchStatus === FetchStatus.LOADING ||
|
||||
documentState.fetchStatus === FetchStatus.PARTIAL;
|
||||
|
||||
const onCancelClick = useCallback(() => {
|
||||
stateContainer.dataState.cancel();
|
||||
sendErrorMsg(stateContainer.dataState.data$.documents$);
|
||||
sendErrorMsg(stateContainer.dataState.data$.main$);
|
||||
}, [stateContainer.dataState]);
|
||||
|
||||
return (
|
||||
<EuiPage
|
||||
className={classNames('dscPage', { 'dscPage--serverless': serverless })}
|
||||
|
@ -271,6 +282,8 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
|
|||
textBasedLanguageModeErrors={textBasedLanguageModeErrors}
|
||||
textBasedLanguageModeWarning={textBasedLanguageModeWarning}
|
||||
onFieldEdited={onFieldEdited}
|
||||
isLoading={isLoading}
|
||||
onCancelClick={onCancelClick}
|
||||
/>
|
||||
<EuiPageBody className="dscPageBody" aria-describedby="savedSearchTitle">
|
||||
<div
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query';
|
||||
import { DataViewType, type DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { AggregateQuery, Query, TimeRange } from '@kbn/es-query';
|
||||
import { type DataView, DataViewType } from '@kbn/data-views-plugin/public';
|
||||
import type { DataViewPickerProps } from '@kbn/unified-search-plugin/public';
|
||||
import { ENABLE_ESQL } from '@kbn/discover-utils';
|
||||
import { useSavedSearchInitial } from '../../services/discover_state_provider';
|
||||
|
@ -33,6 +33,8 @@ export interface DiscoverTopNavProps {
|
|||
textBasedLanguageModeErrors?: Error;
|
||||
textBasedLanguageModeWarning?: string;
|
||||
onFieldEdited: () => Promise<void>;
|
||||
isLoading?: boolean;
|
||||
onCancelClick?: () => void;
|
||||
}
|
||||
|
||||
export const DiscoverTopNav = ({
|
||||
|
@ -42,6 +44,8 @@ export const DiscoverTopNav = ({
|
|||
textBasedLanguageModeErrors,
|
||||
textBasedLanguageModeWarning,
|
||||
onFieldEdited,
|
||||
isLoading,
|
||||
onCancelClick,
|
||||
}: DiscoverTopNavProps) => {
|
||||
const query = useAppStateSelector((state) => state.query);
|
||||
const adHocDataViews = useInternalStateSelector((state) => state.adHocDataViews);
|
||||
|
@ -201,6 +205,8 @@ export const DiscoverTopNav = ({
|
|||
appName="discover"
|
||||
indexPatterns={[dataView]}
|
||||
onQuerySubmit={updateQuery}
|
||||
onCancel={onCancelClick}
|
||||
isLoading={isLoading}
|
||||
onSavedQueryIdChange={updateSavedQueryId}
|
||||
query={query}
|
||||
savedQueryId={savedQuery}
|
||||
|
|
|
@ -115,7 +115,7 @@ export function sendLoadingMoreFinishedMsg(
|
|||
/**
|
||||
* Send ERROR message
|
||||
*/
|
||||
export function sendErrorMsg(data$: DataMain$ | DataDocuments$ | DataTotalHits$, error: Error) {
|
||||
export function sendErrorMsg(data$: DataMain$ | DataDocuments$ | DataTotalHits$, error?: Error) {
|
||||
const recordRawType = data$.getValue().recordRawType;
|
||||
data$.next({
|
||||
fetchStatus: FetchStatus.ERROR,
|
||||
|
|
|
@ -123,6 +123,17 @@ export interface DiscoverDataStateContainer {
|
|||
* resetting all data observable to initial state
|
||||
*/
|
||||
reset: (savedSearch: SavedSearch) => void;
|
||||
|
||||
/**
|
||||
* cancels the running queries
|
||||
*/
|
||||
cancel: () => void;
|
||||
|
||||
/**
|
||||
* gets active AbortController for running queries
|
||||
*/
|
||||
getAbortController: () => AbortController;
|
||||
|
||||
/**
|
||||
* Available Inspector Adaptor allowing to get details about recent requests to ES
|
||||
*/
|
||||
|
@ -314,6 +325,15 @@ export function getDataStateContainer({
|
|||
sendResetMsg(dataSubjects, getInitialFetchStatus(), recordType);
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
abortController?.abort();
|
||||
abortControllerFetchMore?.abort();
|
||||
};
|
||||
|
||||
const getAbortController = () => {
|
||||
return abortController;
|
||||
};
|
||||
|
||||
return {
|
||||
fetch: fetchQuery,
|
||||
fetchMore,
|
||||
|
@ -324,5 +344,7 @@ export function getDataStateContainer({
|
|||
reset,
|
||||
inspectorAdapters,
|
||||
getInitialFetchStatus,
|
||||
cancel,
|
||||
getAbortController,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const retry = getService('retry');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const filterBar = getService('filterBar');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'header']);
|
||||
|
||||
describe('Discover request cancellation', () => {
|
||||
before(async () => {
|
||||
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
|
||||
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
|
||||
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
|
||||
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await PageObjects.common.navigateToApp('discover');
|
||||
});
|
||||
|
||||
it('should allow cancelling active requests', async () => {
|
||||
await PageObjects.discover.selectIndexPattern('logstash-*');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
expect(await PageObjects.discover.hasNoResults()).to.be(false);
|
||||
await testSubjects.existOrFail('querySubmitButton');
|
||||
await testSubjects.missingOrFail('queryCancelButton');
|
||||
await filterBar.addDslFilter(
|
||||
JSON.stringify({
|
||||
error_query: {
|
||||
indices: [
|
||||
{
|
||||
error_type: 'none',
|
||||
name: 'logstash-*',
|
||||
stall_time_seconds: 30,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
false
|
||||
);
|
||||
await retry.try(async () => {
|
||||
await testSubjects.missingOrFail('querySubmitButton');
|
||||
await testSubjects.existOrFail('queryCancelButton');
|
||||
});
|
||||
await testSubjects.click('queryCancelButton');
|
||||
await retry.try(async () => {
|
||||
expect(await PageObjects.discover.hasNoResults()).to.be(true);
|
||||
await testSubjects.existOrFail('querySubmitButton');
|
||||
await testSubjects.missingOrFail('queryCancelButton');
|
||||
});
|
||||
await filterBar.removeAllFilters();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -34,5 +34,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./_hide_announcements'));
|
||||
loadTestFile(require.resolve('./_data_view_edit'));
|
||||
loadTestFile(require.resolve('./_field_list_new_fields'));
|
||||
loadTestFile(require.resolve('./_request_cancellation'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ export class DiscoverPageObject extends FtrService {
|
|||
}
|
||||
|
||||
public async clickHistogramBar() {
|
||||
await this.elasticChart.waitForRenderComplete();
|
||||
await this.elasticChart.waitForRenderComplete(undefined, 5000);
|
||||
const el = await this.elasticChart.getCanvas();
|
||||
|
||||
await this.browser.getActions().move({ x: 0, y: 0, origin: el._webElement }).click().perform();
|
||||
|
|
|
@ -321,7 +321,7 @@ export class FilterBarService extends FtrService {
|
|||
await this.addFilterAndSelectDataView(null, filter);
|
||||
}
|
||||
|
||||
public async addDslFilter(value: string) {
|
||||
public async addDslFilter(value: string, waitUntilLoadingHasFinished = true) {
|
||||
await this.testSubjects.click('addFilter');
|
||||
await this.testSubjects.click('editQueryDSL');
|
||||
await this.monacoEditor.waitCodeEditorReady('addFilterPopover');
|
||||
|
@ -331,7 +331,9 @@ export class FilterBarService extends FtrService {
|
|||
await this.retry.try(async () => {
|
||||
await this.testSubjects.waitForDeleted('saveFilter');
|
||||
});
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
if (waitUntilLoadingHasFinished) {
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,10 +40,11 @@ export class ElasticChartService extends FtrService {
|
|||
return await this.find.existsByCssSelector('.echChart canvas:last-of-type');
|
||||
}
|
||||
|
||||
public async waitForRenderComplete(dataTestSubj?: string) {
|
||||
const chart = await this.getChart(dataTestSubj);
|
||||
public async waitForRenderComplete(dataTestSubj?: string, timeout?: number) {
|
||||
const chart = await this.getChart(dataTestSubj, timeout);
|
||||
const rendered = await chart.findAllByCssSelector(
|
||||
'.echChartStatus[data-ech-render-complete=true]'
|
||||
'.echChartStatus[data-ech-render-complete=true]',
|
||||
timeout
|
||||
);
|
||||
expect(rendered.length).to.equal(1);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue