From 63c04d38f29f42ff36692d03a14126d5cf1a85a4 Mon Sep 17 00:00:00 2001 From: Liza K Date: Thu, 17 Sep 2020 14:30:21 +0300 Subject: [PATCH 01/27] OSS error alignemnt --- src/plugins/data/public/index.ts | 7 +- src/plugins/data/public/search/errors.ts | 70 ++++++++++++++ src/plugins/data/public/search/index.ts | 2 +- .../public/search/painless_error.tsx} | 49 +++++----- .../public/search/request_timeout_error.ts | 30 ------ .../data/public/search/search_interceptor.ts | 46 ++++++++- .../data/public/search/search_service.ts | 3 + src/plugins/data/public/search/types.ts | 2 + .../public/application/angular/discover.js | 29 ++---- .../components/discover_legacy.tsx | 6 +- .../components/fetch_error/fetch_error.scss | 3 - .../components/fetch_error/fetch_error.tsx | 96 ------------------- .../components/fetch_error/index.ts | 20 ---- 13 files changed, 155 insertions(+), 208 deletions(-) create mode 100644 src/plugins/data/public/search/errors.ts rename src/plugins/{discover/public/application/angular/get_painless_error.ts => data/public/search/painless_error.tsx} (52%) delete mode 100644 src/plugins/data/public/search/request_timeout_error.ts delete mode 100644 src/plugins/discover/public/application/components/fetch_error/fetch_error.scss delete mode 100644 src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx delete mode 100644 src/plugins/discover/public/application/components/fetch_error/index.ts diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 5038af9409316..09d2d08f9ce30 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -360,8 +360,6 @@ export { ISearchGeneric, ISearchSource, parseSearchSourceJSON, - RequestTimeoutError, - SearchError, SearchInterceptor, SearchInterceptorDeps, SearchRequest, @@ -370,6 +368,11 @@ export { // expression functions and types EsdslExpressionFunctionDefinition, EsRawResponseExpressionTypeDefinition, + // errors + RequestTimeoutError, + SearchError, + SearchTimeoutError, + PainlessError, } from './search'; export type { SearchSource } from './search'; diff --git a/src/plugins/data/public/search/errors.ts b/src/plugins/data/public/search/errors.ts new file mode 100644 index 0000000000000..b2b470b34201f --- /dev/null +++ b/src/plugins/data/public/search/errors.ts @@ -0,0 +1,70 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* eslint-disable max-classes-per-file */ + +import { get } from 'lodash'; +import { HttpFetchError } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { KbnError } from '../../../kibana_utils/common'; +import { IEsSearchRequest } from '.'; + +/** + * Class used to signify that a request timed out. Useful for applications to conditionally handle + * this type of error differently than other errors. + */ +export class RequestTimeoutError extends Error { + constructor(message = 'Request timed out') { + super(message); + this.message = message; + this.name = 'RequestTimeoutError'; + } +} + +/** + * Request Failure - When an entire multi request fails + * @param {Error} err - the Error that came back + */ +export class SearchTimeoutError extends KbnError { + constructor(err: HttpFetchError | null = null, customMessage: string) { + super(customMessage || `Request timeout: ${JSON.stringify(err?.message)}`); + } +} + +export class PainlessError extends KbnError { + constructor(err: Error, request: IEsSearchRequest) { + const rootCause = get(err, 'body.attributes.error.root_cause'); + const [{ script }] = rootCause; + + super( + i18n.translate('discover.painlessError.painlessScriptedFieldErrorMessage', { + defaultMessage: "Error with Painless scripted field '{script}'.", + values: { script }, + }) + ); + } +} + +export function isPainlessError(error: any) { + const rootCause = get(error, 'body.attributes.error.root_cause'); + if (!rootCause) return false; + + const [{ lang }] = rootCause; + return lang === 'painless'; +} diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index c1af9699acbb2..4a0b0665d4705 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -49,4 +49,4 @@ export { } from './search_source'; export { SearchInterceptor, SearchInterceptorDeps } from './search_interceptor'; -export { RequestTimeoutError } from './request_timeout_error'; +export * from './errors'; diff --git a/src/plugins/discover/public/application/angular/get_painless_error.ts b/src/plugins/data/public/search/painless_error.tsx similarity index 52% rename from src/plugins/discover/public/application/angular/get_painless_error.ts rename to src/plugins/data/public/search/painless_error.tsx index e1e98d9df27b1..41a697ce76459 100644 --- a/src/plugins/discover/public/application/angular/get_painless_error.ts +++ b/src/plugins/data/public/search/painless_error.tsx @@ -17,33 +17,28 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; -import { get } from 'lodash'; +import React from 'react'; +import { EuiButton, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ApplicationStart } from 'kibana/public'; +import { PainlessError } from './errors'; -export function getPainlessError(error: Error) { - const rootCause: Array<{ lang: string; script: string }> | undefined = get( - error, - 'body.attributes.error.root_cause' - ); - const message: string = get(error, 'body.message'); - - if (!rootCause) { - return; - } - - const [{ lang, script }] = rootCause; - - if (lang !== 'painless') { - return; +export const getPainlessErrorMessage = (application: ApplicationStart, e: PainlessError) => { + function onClick() { + application.navigateToApp('management', { + path: `/kibana/indexPatterns`, + }); } - return { - lang, - script, - message: i18n.translate('discover.painlessError.painlessScriptedFieldErrorMessage', { - defaultMessage: "Error with Painless scripted field '{script}'.", - values: { script }, - }), - error: message, - }; -} + return ( + <> + {e.message} + +
+ + + +
+ + ); +}; diff --git a/src/plugins/data/public/search/request_timeout_error.ts b/src/plugins/data/public/search/request_timeout_error.ts deleted file mode 100644 index 92894deb4f0ff..0000000000000 --- a/src/plugins/data/public/search/request_timeout_error.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * Class used to signify that a request timed out. Useful for applications to conditionally handle - * this type of error differently than other errors. - */ -export class RequestTimeoutError extends Error { - constructor(message = 'Request timed out') { - super(message); - this.message = message; - this.name = 'RequestTimeoutError'; - } -} diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 888e12a4285b1..ee4734727593e 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -17,7 +17,7 @@ * under the License. */ -import { trimEnd, debounce } from 'lodash'; +import { get, trimEnd, debounce } from 'lodash'; import { BehaviorSubject, throwError, @@ -31,6 +31,7 @@ import { import { catchError, finalize } from 'rxjs/operators'; import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; +import { KbnError } from 'src/plugins/kibana_utils/common'; import { getCombinedSignal, AbortError, @@ -40,6 +41,9 @@ import { ES_SEARCH_STRATEGY, } from '../../common'; import { SearchUsageCollector } from './collectors'; +import { SearchTimeoutError, PainlessError, isPainlessError } from './errors'; +import { getPainlessErrorMessage } from './painless_error'; +import { toMountPoint } from '../../../kibana_react/public'; export interface SearchInterceptorDeps { http: CoreSetup['http']; @@ -84,6 +88,30 @@ export class SearchInterceptor { }); } + protected getTimeoutMessage() { + return i18n.translate('data.search.upgradeLicense', { + defaultMessage: + 'One or more queries timed out. With our free Basic tier, your queries never time out.', + }); + } + + public showError(e: Error | KbnError) { + if (e instanceof AbortError) return; + + if (e instanceof PainlessError) { + this.deps.toasts.addDanger({ + title: 'Search Error', + text: toMountPoint(getPainlessErrorMessage(this.application, e)), + }); + return; + } + + this.deps.toasts.addDanger({ + title: 'Search Error', + text: e.message, + }); + } + /** * @internal */ @@ -120,17 +148,24 @@ export class SearchInterceptor { return throwError(new AbortError()); } - const { combinedSignal, cleanup } = this.setupAbortSignal({ + const { timeoutSignal, combinedSignal, cleanup } = this.setupAbortSignal({ abortSignal: options?.abortSignal, }); this.pendingCount$.next(this.pendingCount$.getValue() + 1); return this.runSearch(request, combinedSignal, options?.strategy).pipe( catchError((e: any) => { - if (e.body?.attributes?.error === 'Request timed out') { - this.showTimeoutError(e); + if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { + // Handle a client or a server side timeout + return throwError(new SearchTimeoutError(e, this.getTimeoutMessage())); + } else if (options?.abortSignal?.aborted) { + // In the case an application initiated abort, throw the existing AbortError. + return throwError(e); + } else if (isPainlessError(e)) { + return throwError(new PainlessError(e, request)); + } else { + return throwError(e); } - return throwError(e); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); @@ -179,6 +214,7 @@ export class SearchInterceptor { return { combinedSignal, + timeoutSignal, cleanup, }; } diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index c41e1f78ee74e..77c64dc2f557a 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -107,6 +107,9 @@ export class SearchService implements Plugin { return { aggs: this.aggsService.start({ fieldFormats, uiSettings }), search, + showError: (e: any) => { + this.searchInterceptor.showError(e); + }, searchSource: { /** * creates searchsource based on serialized search source fields diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index 83a542269046f..8cc11f1d6d164 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -93,6 +93,8 @@ export interface ISearchStart { * {@link ISearchGeneric} */ search: ISearchGeneric; + + showError: (e: any) => void; /** * high level search * {@link ISearchStartSearchSource} diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 7871cc4b16464..3e76789c461f1 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -27,6 +27,12 @@ import { i18n } from '@kbn/i18n'; import { getState, splitState } from './discover_state'; import { RequestAdapter } from '../../../../inspector/public'; +import { + esFilters, + indexPatterns as indexPatternsUtils, + connectToQueryState, + syncQueryStateWithUrl, +} from '../../../../data/public'; import { SavedObjectSaveModal, showSaveModal } from '../../../../saved_objects/public'; import { getSortArray, getSortForSearchSource } from './doc_table'; import { createFixedScroll } from './directives/fixed_scroll'; @@ -34,7 +40,6 @@ import * as columnActions from './doc_table/actions/columns'; import indexTemplateLegacy from './discover_legacy.html'; import { showOpenSearchPanel } from '../components/top_nav/show_open_search_panel'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; -import { getPainlessError } from './get_painless_error'; import { discoverResponseHandler } from './response_handler'; import { getRequestInspectorStats, @@ -65,12 +70,7 @@ const { import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; import { validateTimeRange } from '../helpers/validate_time_range'; -import { - esFilters, - indexPatterns as indexPatternsUtils, - connectToQueryState, - syncQueryStateWithUrl, -} from '../../../../data/public'; + import { getIndexPatternId } from '../helpers/get_index_pattern_id'; import { addFatalError } from '../../../../kibana_legacy/public'; import { @@ -786,18 +786,9 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise // If the request was aborted then no need to surface this error in the UI if (error instanceof Error && error.name === 'AbortError') return; - const fetchError = getPainlessError(error); - - if (fetchError) { - $scope.fetchError = fetchError; - } else { - toastNotifications.addError(error, { - title: i18n.translate('discover.errorLoadingData', { - defaultMessage: 'Error loading data', - }), - toastMessage: error.shortMessage || error.body?.message, - }); - } + $scope.fetchStatus = fetchStatuses.NO_RESULTS; + $scope.rows = []; + data.search.showError(error); }); }; diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index 1a98843649259..9e963b1ace40b 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -31,7 +31,6 @@ import { DiscoverNoResults } from '../angular/directives/no_results'; import { DiscoverUninitialized } from '../angular/directives/uninitialized'; import { DiscoverHistogram } from '../angular/directives/histogram'; import { LoadingSpinner } from './loading_spinner/loading_spinner'; -import { DiscoverFetchError, FetchError } from './fetch_error/fetch_error'; import { DocTableLegacy } from '../angular/doc_table/create_doc_table_react'; import { SkipBottomButton } from './skip_bottom_button'; import { @@ -54,7 +53,6 @@ export interface DiscoverLegacyProps { addColumn: (column: string) => void; fetch: () => void; fetchCounter: number; - fetchError: FetchError; fieldCounts: Record; histogramData: Chart; hits: number; @@ -95,7 +93,6 @@ export function DiscoverLegacy({ addColumn, fetch, fetchCounter, - fetchError, fieldCounts, histogramData, hits, @@ -216,8 +213,7 @@ export function DiscoverLegacy({ {resultState === 'uninitialized' && } {/* @TODO: Solved in the Angular way to satisfy functional test - should be improved*/} - {fetchError && } -
+
diff --git a/src/plugins/discover/public/application/components/fetch_error/fetch_error.scss b/src/plugins/discover/public/application/components/fetch_error/fetch_error.scss deleted file mode 100644 index a587b2897e3a0..0000000000000 --- a/src/plugins/discover/public/application/components/fetch_error/fetch_error.scss +++ /dev/null @@ -1,3 +0,0 @@ -.discoverFetchError { - max-width: 1000px; -} diff --git a/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx b/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx deleted file mode 100644 index dc8f1238eac6f..0000000000000 --- a/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import './fetch_error.scss'; -import React, { Fragment } from 'react'; -import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -import { EuiFlexGroup, EuiFlexItem, EuiCallOut, EuiCodeBlock, EuiSpacer } from '@elastic/eui'; -import { getServices } from '../../../kibana_services'; - -export interface FetchError { - lang: string; - script: string; - message: string; - error: string; -} - -interface Props { - fetchError: FetchError; -} - -export const DiscoverFetchError = ({ fetchError }: Props) => { - if (!fetchError) { - return null; - } - - let body; - - if (fetchError.lang === 'painless') { - const { chrome } = getServices(); - const mangagementUrlObj = chrome.navLinks.get('kibana:stack_management'); - const managementUrl = mangagementUrlObj ? mangagementUrlObj.url : ''; - const url = `${managementUrl}/kibana/indexPatterns`; - - body = ( -

- - ), - managementLink: ( - - - - ), - }} - /> -

- ); - } - - return ( - - - - - - - - {body} - - {fetchError.error} - - - - - - - - ); -}; diff --git a/src/plugins/discover/public/application/components/fetch_error/index.ts b/src/plugins/discover/public/application/components/fetch_error/index.ts deleted file mode 100644 index 0206bc48257ac..0000000000000 --- a/src/plugins/discover/public/application/components/fetch_error/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './fetch_error'; From fc6d3c27f32f090f5d7007e2fe6b8ea78cf0c954 Mon Sep 17 00:00:00 2001 From: Liza K Date: Thu, 17 Sep 2020 16:05:20 +0300 Subject: [PATCH 02/27] Adjust error messages in xpack --- src/plugins/data/public/index.ts | 1 + .../data/public/search/{ => errors}/errors.ts | 14 ++- .../data/public/search/errors/index.ts | 22 +++++ .../search/{ => errors}/painless_error.tsx | 0 .../public/search/errors/timeout_error.tsx | 94 +++++++++++++++++++ .../data/public/search/search_interceptor.ts | 58 ++++++++---- .../public/search/search_interceptor.ts | 28 +++--- 7 files changed, 184 insertions(+), 33 deletions(-) rename src/plugins/data/public/search/{ => errors}/errors.ts (87%) create mode 100644 src/plugins/data/public/search/errors/index.ts rename src/plugins/data/public/search/{ => errors}/painless_error.tsx (100%) create mode 100644 src/plugins/data/public/search/errors/timeout_error.tsx diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 09d2d08f9ce30..c98f23a209b37 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -372,6 +372,7 @@ export { RequestTimeoutError, SearchError, SearchTimeoutError, + TimeoutErrorMode, PainlessError, } from './search'; diff --git a/src/plugins/data/public/search/errors.ts b/src/plugins/data/public/search/errors/errors.ts similarity index 87% rename from src/plugins/data/public/search/errors.ts rename to src/plugins/data/public/search/errors/errors.ts index b2b470b34201f..956e8739a9ee6 100644 --- a/src/plugins/data/public/search/errors.ts +++ b/src/plugins/data/public/search/errors/errors.ts @@ -22,7 +22,7 @@ import { get } from 'lodash'; import { HttpFetchError } from 'kibana/public'; import { i18n } from '@kbn/i18n'; -import { KbnError } from '../../../kibana_utils/common'; +import { KbnError } from '../../../../kibana_utils/common'; import { IEsSearchRequest } from '.'; /** @@ -37,13 +37,21 @@ export class RequestTimeoutError extends Error { } } +export enum TimeoutErrorMode { + UPGRADE, + CONTACT, + CHANGE, +} + /** * Request Failure - When an entire multi request fails * @param {Error} err - the Error that came back */ export class SearchTimeoutError extends KbnError { - constructor(err: HttpFetchError | null = null, customMessage: string) { - super(customMessage || `Request timeout: ${JSON.stringify(err?.message)}`); + public mode: TimeoutErrorMode; + constructor(err: HttpFetchError | null = null, mode: TimeoutErrorMode) { + super(`Request timeout: ${JSON.stringify(err?.message)}`); + this.mode = mode; } } diff --git a/src/plugins/data/public/search/errors/index.ts b/src/plugins/data/public/search/errors/index.ts new file mode 100644 index 0000000000000..b9ab0ed5b3d01 --- /dev/null +++ b/src/plugins/data/public/search/errors/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './painless_error'; +export * from './timeout_error'; +export * from './errors'; diff --git a/src/plugins/data/public/search/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx similarity index 100% rename from src/plugins/data/public/search/painless_error.tsx rename to src/plugins/data/public/search/errors/painless_error.tsx diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx new file mode 100644 index 0000000000000..2cb306e0eaa8a --- /dev/null +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -0,0 +1,94 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButton, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ApplicationStart } from 'kibana/public'; +import { SearchTimeoutError, TimeoutErrorMode } from './errors'; + +export const getTimeoutErrorMessage = (application: ApplicationStart, e: SearchTimeoutError) => { + function getMessage() { + switch (e.mode) { + case TimeoutErrorMode.UPGRADE: + return i18n.translate('data.search.upgradeLicense', { + defaultMessage: + 'One or more queries timed out. With our free Basic tier, your queries never time out.', + }); + case TimeoutErrorMode.CONTACT: + return i18n.translate('xpack.data.search.timeoutContactAdmin', { + defaultMessage: + 'One or more queries timed out. Contact your system administrator to increase the run time.', + }); + case TimeoutErrorMode.CHANGE: + return i18n.translate('xpack.data.search.timeoutIncreaseSetting', { + defaultMessage: + 'One or more queries timed out. Increase run time with the search timeout advanced setting.', + }); + } + } + + function getActionText() { + switch (e.mode) { + case TimeoutErrorMode.UPGRADE: + return i18n.translate('data.search.upgradeLicenseActionText', { + defaultMessage: 'Upgrade', + }); + break; + case TimeoutErrorMode.CHANGE: + return i18n.translate('data.search.timeoutIncreaseSettingActionText', { + defaultMessage: 'Go to Advanced Settings', + }); + break; + } + } + + function onClick() { + switch (e.mode) { + case TimeoutErrorMode.UPGRADE: + application.navigateToApp('management', { + path: `/kibana/indexPatterns`, + }); + break; + case TimeoutErrorMode.CHANGE: + application.navigateToApp('management', { + path: `/kibana/settings`, + }); + break; + } + } + + const actionText = getActionText(); + return ( + <> + {getMessage()} + {actionText && ( + <> + +
+ + {actionText} + +
+ + )} + + ); +}; diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index ee4734727593e..047b858e080a0 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -41,8 +41,14 @@ import { ES_SEARCH_STRATEGY, } from '../../common'; import { SearchUsageCollector } from './collectors'; -import { SearchTimeoutError, PainlessError, isPainlessError } from './errors'; -import { getPainlessErrorMessage } from './painless_error'; +import { + SearchTimeoutError, + PainlessError, + isPainlessError, + getPainlessErrorMessage, + getTimeoutErrorMessage, + TimeoutErrorMode, +} from './errors'; import { toMountPoint } from '../../../kibana_react/public'; export interface SearchInterceptorDeps { @@ -88,11 +94,27 @@ export class SearchInterceptor { }); } - protected getTimeoutMessage() { - return i18n.translate('data.search.upgradeLicense', { - defaultMessage: - 'One or more queries timed out. With our free Basic tier, your queries never time out.', - }); + protected getTimeoutMode() { + return TimeoutErrorMode.UPGRADE; + } + + protected getSearchError( + e: any, + request: IEsSearchRequest, + timeoutSignal: AbortSignal, + appAbortSignal?: AbortSignal + ) { + if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { + // Handle a client or a server side timeout + return new SearchTimeoutError(e, this.getTimeoutMode()); + } else if (appAbortSignal?.aborted) { + // In the case an application initiated abort, throw the existing AbortError. + return e; + } else if (isPainlessError(e)) { + return new PainlessError(e, request); + } else { + return e; + } } public showError(e: Error | KbnError) { @@ -106,6 +128,14 @@ export class SearchInterceptor { return; } + if (e instanceof SearchTimeoutError) { + this.deps.toasts.addDanger({ + title: 'Timeout', + text: toMountPoint(getTimeoutErrorMessage(this.application, e)), + }); + return; + } + this.deps.toasts.addDanger({ title: 'Search Error', text: e.message, @@ -155,17 +185,7 @@ export class SearchInterceptor { return this.runSearch(request, combinedSignal, options?.strategy).pipe( catchError((e: any) => { - if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { - // Handle a client or a server side timeout - return throwError(new SearchTimeoutError(e, this.getTimeoutMessage())); - } else if (options?.abortSignal?.aborted) { - // In the case an application initiated abort, throw the existing AbortError. - return throwError(e); - } else if (isPainlessError(e)) { - return throwError(new PainlessError(e, request)); - } else { - return throwError(e); - } + return throwError(this.getSearchError(e, request, timeoutSignal, options?.abortSignal)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); @@ -207,7 +227,7 @@ export class SearchInterceptor { const combinedSignal = getCombinedSignal(signals); const cleanup = () => { - this.timeoutSubscriptions.remove(subscription); + subscription.unsubscribe(); }; combinedSignal.addEventListener('abort', cleanup); diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index f7ae9fc6d0f91..683cf8ffbd43b 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -5,7 +5,7 @@ */ import { throwError, EMPTY, timer, from, Subscription } from 'rxjs'; -import { mergeMap, expand, takeUntil, finalize, tap } from 'rxjs/operators'; +import { mergeMap, expand, takeUntil, finalize, tap, catchError } from 'rxjs/operators'; import { debounce } from 'lodash'; import { i18n } from '@kbn/i18n'; import { @@ -14,6 +14,7 @@ import { UI_SETTINGS, } from '../../../../../src/plugins/data/public'; import { AbortError, toPromise } from '../../../../../src/plugins/data/common'; +import { TimeoutErrorMode } from '../../../../../src/plugins/data/public'; import { IAsyncSearchOptions } from '.'; import { IAsyncSearchRequest, ENHANCED_ES_SEARCH_STRATEGY } from '../../common'; @@ -54,7 +55,7 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { ) { let { id } = request; - const { combinedSignal, cleanup } = this.setupAbortSignal({ + const { combinedSignal, timeoutSignal, cleanup } = this.setupAbortSignal({ abortSignal: options.abortSignal, timeout: this.searchTimeout, }); @@ -85,15 +86,14 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { ); }), takeUntil(aborted$), - tap({ - error: () => { - // If we haven't received the response to the initial request, including the ID, then - // we don't need to send a follow-up request to delete this search. Otherwise, we - // send the follow-up request to delete this search, then throw an abort error. - if (id !== undefined) { - this.deps.http.delete(`/internal/search/${strategy}/${id}`); - } - }, + catchError((e: any) => { + // If we haven't received the response to the initial request, including the ID, then + // we don't need to send a follow-up request to delete this search. Otherwise, we + // send the follow-up request to delete this search, then throw an abort error. + if (id !== undefined) { + this.deps.http.delete(`/internal/search/${strategy}/${id}`); + } + return throwError(this.getSearchError(e, request, timeoutSignal, options?.abortSignal)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); @@ -102,6 +102,12 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { ); } + protected getTimeoutMode() { + return this.application.capabilities.advancedSettings?.save + ? TimeoutErrorMode.CHANGE + : TimeoutErrorMode.CONTACT; + } + // Right now we are debouncing but we will hook this up with background sessions to show only one // error notification per session. protected showTimeoutError = debounce( From 154a146903ddaf0b4d1c0b9713329b96ed74cf4f Mon Sep 17 00:00:00 2001 From: Liza K Date: Thu, 17 Sep 2020 17:13:44 +0300 Subject: [PATCH 03/27] Add getErrorMessage --- src/plugins/data/public/index.ts | 1 - .../data/public/search/errors/errors.ts | 78 ------------------- .../data/public/search/errors/index.ts | 2 +- .../public/search/errors/painless_error.tsx | 61 ++++++++++----- .../public/search/errors/timeout_error.tsx | 69 ++++++++++------ .../data/public/search/search_interceptor.ts | 4 +- 6 files changed, 90 insertions(+), 125 deletions(-) delete mode 100644 src/plugins/data/public/search/errors/errors.ts diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index c98f23a209b37..3f34fc2cb230b 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -369,7 +369,6 @@ export { EsdslExpressionFunctionDefinition, EsRawResponseExpressionTypeDefinition, // errors - RequestTimeoutError, SearchError, SearchTimeoutError, TimeoutErrorMode, diff --git a/src/plugins/data/public/search/errors/errors.ts b/src/plugins/data/public/search/errors/errors.ts deleted file mode 100644 index 956e8739a9ee6..0000000000000 --- a/src/plugins/data/public/search/errors/errors.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* eslint-disable max-classes-per-file */ - -import { get } from 'lodash'; -import { HttpFetchError } from 'kibana/public'; -import { i18n } from '@kbn/i18n'; -import { KbnError } from '../../../../kibana_utils/common'; -import { IEsSearchRequest } from '.'; - -/** - * Class used to signify that a request timed out. Useful for applications to conditionally handle - * this type of error differently than other errors. - */ -export class RequestTimeoutError extends Error { - constructor(message = 'Request timed out') { - super(message); - this.message = message; - this.name = 'RequestTimeoutError'; - } -} - -export enum TimeoutErrorMode { - UPGRADE, - CONTACT, - CHANGE, -} - -/** - * Request Failure - When an entire multi request fails - * @param {Error} err - the Error that came back - */ -export class SearchTimeoutError extends KbnError { - public mode: TimeoutErrorMode; - constructor(err: HttpFetchError | null = null, mode: TimeoutErrorMode) { - super(`Request timeout: ${JSON.stringify(err?.message)}`); - this.mode = mode; - } -} - -export class PainlessError extends KbnError { - constructor(err: Error, request: IEsSearchRequest) { - const rootCause = get(err, 'body.attributes.error.root_cause'); - const [{ script }] = rootCause; - - super( - i18n.translate('discover.painlessError.painlessScriptedFieldErrorMessage', { - defaultMessage: "Error with Painless scripted field '{script}'.", - values: { script }, - }) - ); - } -} - -export function isPainlessError(error: any) { - const rootCause = get(error, 'body.attributes.error.root_cause'); - if (!rootCause) return false; - - const [{ lang }] = rootCause; - return lang === 'painless'; -} diff --git a/src/plugins/data/public/search/errors/index.ts b/src/plugins/data/public/search/errors/index.ts index b9ab0ed5b3d01..9e34342b3d8f9 100644 --- a/src/plugins/data/public/search/errors/index.ts +++ b/src/plugins/data/public/search/errors/index.ts @@ -19,4 +19,4 @@ export * from './painless_error'; export * from './timeout_error'; -export * from './errors'; +export * from './request_timeout_error'; diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx index 41a697ce76459..61eedaf1c54dc 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -18,27 +18,52 @@ */ import React from 'react'; +import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; import { EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { ApplicationStart } from 'kibana/public'; -import { PainlessError } from './errors'; +import { KbnError } from '../../../../kibana_utils/common'; +import { IEsSearchRequest } from '.'; -export const getPainlessErrorMessage = (application: ApplicationStart, e: PainlessError) => { - function onClick() { - application.navigateToApp('management', { - path: `/kibana/indexPatterns`, - }); +export class PainlessError extends KbnError { + constructor(err: Error, request: IEsSearchRequest) { + const rootCause = get(err, 'body.attributes.error.root_cause'); + const [{ script }] = rootCause; + + super( + i18n.translate('discover.painlessError.painlessScriptedFieldErrorMessage', { + defaultMessage: "Error with Painless scripted field '{script}'.", + values: { script }, + }) + ); } - return ( - <> - {e.message} - -
- - - -
- - ); -}; + public getErrorMessage(application: ApplicationStart) { + function onClick() { + application.navigateToApp('management', { + path: `/kibana/indexPatterns`, + }); + } + + return ( + <> + {this.message} + +
+ + + +
+ + ); + } +} + +export function isPainlessError(error: any) { + const rootCause = get(error, 'body.attributes.error.root_cause'); + if (!rootCause) return false; + + const [{ lang }] = rootCause; + return lang === 'painless'; +} diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index 2cb306e0eaa8a..714e8d49ca5ef 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -22,11 +22,28 @@ import { i18n } from '@kbn/i18n'; import { EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { ApplicationStart } from 'kibana/public'; -import { SearchTimeoutError, TimeoutErrorMode } from './errors'; +import { HttpFetchError } from 'kibana/public'; +import { KbnError } from '../../../../kibana_utils/common'; -export const getTimeoutErrorMessage = (application: ApplicationStart, e: SearchTimeoutError) => { - function getMessage() { - switch (e.mode) { +export enum TimeoutErrorMode { + UPGRADE, + CONTACT, + CHANGE, +} + +/** + * Request Failure - When an entire multi request fails + * @param {Error} err - the Error that came back + */ +export class SearchTimeoutError extends KbnError { + public mode: TimeoutErrorMode; + constructor(err: HttpFetchError | null = null, mode: TimeoutErrorMode) { + super(`Request timeout: ${JSON.stringify(err?.message)}`); + this.mode = mode; + } + + private getMessage() { + switch (this.mode) { case TimeoutErrorMode.UPGRADE: return i18n.translate('data.search.upgradeLicense', { defaultMessage: @@ -45,8 +62,8 @@ export const getTimeoutErrorMessage = (application: ApplicationStart, e: SearchT } } - function getActionText() { - switch (e.mode) { + private getActionText() { + switch (this.mode) { case TimeoutErrorMode.UPGRADE: return i18n.translate('data.search.upgradeLicenseActionText', { defaultMessage: 'Upgrade', @@ -60,8 +77,8 @@ export const getTimeoutErrorMessage = (application: ApplicationStart, e: SearchT } } - function onClick() { - switch (e.mode) { + private onClick() { + switch (this.mode) { case TimeoutErrorMode.UPGRADE: application.navigateToApp('management', { path: `/kibana/indexPatterns`, @@ -75,20 +92,22 @@ export const getTimeoutErrorMessage = (application: ApplicationStart, e: SearchT } } - const actionText = getActionText(); - return ( - <> - {getMessage()} - {actionText && ( - <> - -
- - {actionText} - -
- - )} - - ); -}; + public getErrorMessage(application: ApplicationStart) { + const actionText = this.getActionText(); + return ( + <> + {this.getMessage()} + {actionText && ( + <> + +
+ + {actionText} + +
+ + )} + + ); + } +} diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 047b858e080a0..9b654bcf291b0 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -123,7 +123,7 @@ export class SearchInterceptor { if (e instanceof PainlessError) { this.deps.toasts.addDanger({ title: 'Search Error', - text: toMountPoint(getPainlessErrorMessage(this.application, e)), + text: toMountPoint(e.getErrorMessage(this.application)), }); return; } @@ -131,7 +131,7 @@ export class SearchInterceptor { if (e instanceof SearchTimeoutError) { this.deps.toasts.addDanger({ title: 'Timeout', - text: toMountPoint(getTimeoutErrorMessage(this.application, e)), + text: toMountPoint(e.getErrorMessage(this.application)), }); return; } From 12e7898bdbdaa3314671f920806c9e263dbe23d3 Mon Sep 17 00:00:00 2001 From: Liza K Date: Thu, 17 Sep 2020 17:18:33 +0300 Subject: [PATCH 04/27] Use showError in vizualize Add original error to expression exception --- .../expressions/common/expression_types/specs/error.ts | 1 + src/plugins/expressions/common/util/create_error.ts | 1 + .../public/application/utils/get_visualization_instance.ts | 6 +----- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/plugins/expressions/common/expression_types/specs/error.ts b/src/plugins/expressions/common/expression_types/specs/error.ts index 35554954d0828..c95a019f4e8d2 100644 --- a/src/plugins/expressions/common/expression_types/specs/error.ts +++ b/src/plugins/expressions/common/expression_types/specs/error.ts @@ -30,6 +30,7 @@ export type ExpressionValueError = ExpressionValueBoxed< message: string; name?: string; stack?: string; + original?: Error; }; info?: unknown; } diff --git a/src/plugins/expressions/common/util/create_error.ts b/src/plugins/expressions/common/util/create_error.ts index 876e7dfec799c..27cb79bf83511 100644 --- a/src/plugins/expressions/common/util/create_error.ts +++ b/src/plugins/expressions/common/util/create_error.ts @@ -32,5 +32,6 @@ export const createError = (err: string | ErrorLike): ExpressionValueError => ({ : undefined, message: typeof err === 'string' ? err : String(err.message), name: typeof err === 'object' ? err.name || 'Error' : 'Error', + original: err, }, }); diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts index 3ffca578f8052..9f2aedd7278a3 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts @@ -51,11 +51,7 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( embeddableHandler.getOutput$().subscribe((output) => { if (output.error) { - toastNotifications.addError(output.error, { - title: i18n.translate('visualize.error.title', { - defaultMessage: 'Visualization error', - }), - }); + data.search.showError(output.error.original || ouput.error); } }); From f1390e2303ec04a3773b1f02ec538dfe6ed87e36 Mon Sep 17 00:00:00 2001 From: Liza K Date: Thu, 17 Sep 2020 19:56:22 +0300 Subject: [PATCH 05/27] Cleanup --- src/plugins/data/public/search/errors/index.ts | 1 - src/plugins/data/public/search/errors/painless_error.tsx | 2 +- src/plugins/data/public/search/errors/timeout_error.tsx | 5 ++--- src/plugins/data/public/search/search_interceptor.ts | 9 +-------- .../discover/public/application/angular/discover.js | 2 ++ src/plugins/expressions/common/util/create_error.ts | 2 +- 6 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/plugins/data/public/search/errors/index.ts b/src/plugins/data/public/search/errors/index.ts index 9e34342b3d8f9..6082e758a8bad 100644 --- a/src/plugins/data/public/search/errors/index.ts +++ b/src/plugins/data/public/search/errors/index.ts @@ -19,4 +19,3 @@ export * from './painless_error'; export * from './timeout_error'; -export * from './request_timeout_error'; diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx index 61eedaf1c54dc..b0cd51266c04d 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -24,7 +24,7 @@ import { EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { ApplicationStart } from 'kibana/public'; import { KbnError } from '../../../../kibana_utils/common'; -import { IEsSearchRequest } from '.'; +import { IEsSearchRequest } from '..'; export class PainlessError extends KbnError { constructor(err: Error, request: IEsSearchRequest) { diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index 714e8d49ca5ef..47e6feea10172 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -20,7 +20,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; import { ApplicationStart } from 'kibana/public'; import { HttpFetchError } from 'kibana/public'; import { KbnError } from '../../../../kibana_utils/common'; @@ -77,7 +76,7 @@ export class SearchTimeoutError extends KbnError { } } - private onClick() { + private onClick(application: ApplicationStart) { switch (this.mode) { case TimeoutErrorMode.UPGRADE: application.navigateToApp('management', { @@ -101,7 +100,7 @@ export class SearchTimeoutError extends KbnError { <>
- + this.onClick(application)} size="s"> {actionText}
diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 9b654bcf291b0..9fa2242fd7f9b 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -41,14 +41,7 @@ import { ES_SEARCH_STRATEGY, } from '../../common'; import { SearchUsageCollector } from './collectors'; -import { - SearchTimeoutError, - PainlessError, - isPainlessError, - getPainlessErrorMessage, - getTimeoutErrorMessage, - TimeoutErrorMode, -} from './errors'; +import { SearchTimeoutError, PainlessError, isPainlessError, TimeoutErrorMode } from './errors'; import { toMountPoint } from '../../../kibana_react/public'; export interface SearchInterceptorDeps { diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 3e76789c461f1..bb197864d7e7e 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -788,6 +788,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise $scope.fetchStatus = fetchStatuses.NO_RESULTS; $scope.rows = []; + + // Add error.toToast data.search.showError(error); }); }; diff --git a/src/plugins/expressions/common/util/create_error.ts b/src/plugins/expressions/common/util/create_error.ts index 27cb79bf83511..cde328e04bc4d 100644 --- a/src/plugins/expressions/common/util/create_error.ts +++ b/src/plugins/expressions/common/util/create_error.ts @@ -32,6 +32,6 @@ export const createError = (err: string | ErrorLike): ExpressionValueError => ({ : undefined, message: typeof err === 'string' ? err : String(err.message), name: typeof err === 'object' ? err.name || 'Error' : 'Error', - original: err, + original: typeof err === 'object' ? err : undefined, }, }); From 1f9c3405a58d702c5fd51419024242e4ed140b9a Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 20 Sep 2020 14:35:59 +0300 Subject: [PATCH 06/27] ts, doc and i18n fixes --- ...plugin-plugins-data-public.isearchstart.md | 1 + ...gins-data-public.isearchstart.showerror.md | 11 +++ .../kibana-plugin-plugins-data-public.md | 4 +- ...data-public.painlesserror._constructor_.md | 21 +++++ ...ta-public.painlesserror.geterrormessage.md | 22 +++++ ...lugin-plugins-data-public.painlesserror.md | 24 ++++++ ...ublic.requesttimeouterror._constructor_.md | 20 ----- ...plugins-data-public.requesttimeouterror.md | 20 ----- ...public.searchinterceptor.getsearcherror.md | 25 ++++++ ...public.searchinterceptor.gettimeoutmode.md | 15 ++++ ...n-plugins-data-public.searchinterceptor.md | 5 +- ...data-public.searchinterceptor.showerror.md | 22 +++++ ...blic.searchinterceptor.showtimeouterror.md | 2 +- ...public.searchtimeouterror._constructor_.md | 21 +++++ ...blic.searchtimeouterror.geterrormessage.md | 22 +++++ ...-plugins-data-public.searchtimeouterror.md | 32 +++++++ ...ins-data-public.searchtimeouterror.mode.md | 11 +++ ...in-plugins-data-public.timeouterrormode.md | 20 +++++ src/plugins/data/public/public.api.md | 84 ++++++++++++++----- .../public/search/errors/painless_error.tsx | 2 +- .../public/search/errors/timeout_error.tsx | 4 +- src/plugins/data/public/search/mocks.ts | 1 + .../data/public/search/search_interceptor.ts | 23 +++-- .../expressions/common/util/create_error.ts | 8 +- .../utils/get_visualization_instance.ts | 15 ++-- .../translations/translations/ja-JP.json | 6 -- .../translations/translations/zh-CN.json | 6 -- 27 files changed, 341 insertions(+), 106 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.mode.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timeouterrormode.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md index cee213fc6e7e3..39f10837c28d8 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md @@ -19,4 +19,5 @@ export interface ISearchStart | [aggs](./kibana-plugin-plugins-data-public.isearchstart.aggs.md) | AggsStart | agg config sub service [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) | | [search](./kibana-plugin-plugins-data-public.isearchstart.search.md) | ISearchGeneric | low level search [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | [searchSource](./kibana-plugin-plugins-data-public.isearchstart.searchsource.md) | ISearchStartSearchSource | high level search [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | +| [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) | (e: any) => void | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md new file mode 100644 index 0000000000000..e4ba77a32c54c --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) > [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) + +## ISearchStart.showError property + +Signature: + +```typescript +showError: (e: any) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index f51549c81fb62..e68a9e2f6eb9a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -17,10 +17,11 @@ | [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) | | | [IndexPatternSelect](./kibana-plugin-plugins-data-public.indexpatternselect.md) | | | [OptionedParamType](./kibana-plugin-plugins-data-public.optionedparamtype.md) | | +| [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) | | | [Plugin](./kibana-plugin-plugins-data-public.plugin.md) | | -| [RequestTimeoutError](./kibana-plugin-plugins-data-public.requesttimeouterror.md) | Class used to signify that a request timed out. Useful for applications to conditionally handle this type of error differently than other errors. | | [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) | | | [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) | \* | +| [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) | Request Failure - When an entire multi request fails | | [TimeHistory](./kibana-plugin-plugins-data-public.timehistory.md) | | ## Enumerations @@ -33,6 +34,7 @@ | [METRIC\_TYPES](./kibana-plugin-plugins-data-public.metric_types.md) | | | [QuerySuggestionTypes](./kibana-plugin-plugins-data-public.querysuggestiontypes.md) | | | [SortDirection](./kibana-plugin-plugins-data-public.sortdirection.md) | | +| [TimeoutErrorMode](./kibana-plugin-plugins-data-public.timeouterrormode.md) | | ## Functions diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md new file mode 100644 index 0000000000000..e2260f2e4f1d9 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) > [(constructor)](./kibana-plugin-plugins-data-public.painlesserror._constructor_.md) + +## PainlessError.(constructor) + +Constructs a new instance of the `PainlessError` class + +Signature: + +```typescript +constructor(err: Error, request: IEsSearchRequest); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| err | Error | | +| request | IEsSearchRequest | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md new file mode 100644 index 0000000000000..a3b4c51c6c331 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) > [getErrorMessage](./kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md) + +## PainlessError.getErrorMessage() method + +Signature: + +```typescript +getErrorMessage(application: ApplicationStart): JSX.Element; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| application | ApplicationStart | | + +Returns: + +`JSX.Element` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md new file mode 100644 index 0000000000000..02b51c7881b82 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) + +## PainlessError class + +Signature: + +```typescript +export declare class PainlessError extends KbnError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(err, request)](./kibana-plugin-plugins-data-public.painlesserror._constructor_.md) | | Constructs a new instance of the PainlessError class | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [getErrorMessage(application)](./kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md) | | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md deleted file mode 100644 index 25e472817b46d..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RequestTimeoutError](./kibana-plugin-plugins-data-public.requesttimeouterror.md) > [(constructor)](./kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md) - -## RequestTimeoutError.(constructor) - -Constructs a new instance of the `RequestTimeoutError` class - -Signature: - -```typescript -constructor(message?: string); -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| message | string | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror.md deleted file mode 100644 index 84b2fc3fe0b17..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RequestTimeoutError](./kibana-plugin-plugins-data-public.requesttimeouterror.md) - -## RequestTimeoutError class - -Class used to signify that a request timed out. Useful for applications to conditionally handle this type of error differently than other errors. - -Signature: - -```typescript -export declare class RequestTimeoutError extends Error -``` - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)(message)](./kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md) | | Constructs a new instance of the RequestTimeoutError class | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md new file mode 100644 index 0000000000000..dbb136e7baffc --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [getSearchError](./kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md) + +## SearchInterceptor.getSearchError() method + +Signature: + +```typescript +protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| e | any | | +| request | IEsSearchRequest | | +| timeoutSignal | AbortSignal | | +| appAbortSignal | AbortSignal | | + +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md new file mode 100644 index 0000000000000..8ecd8b8c5ac22 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [getTimeoutMode](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) + +## SearchInterceptor.getTimeoutMode() method + +Signature: + +```typescript +protected getTimeoutMode(): TimeoutErrorMode; +``` +Returns: + +`TimeoutErrorMode` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index 5cee345db6cd2..bb2eed860334b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -21,11 +21,14 @@ export declare class SearchInterceptor | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [deps](./kibana-plugin-plugins-data-public.searchinterceptor.deps.md) | | SearchInterceptorDeps | | -| [showTimeoutError](./kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md) | | ((e: Error) => void) & import("lodash").Cancelable | | +| [showTimeoutError](./kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md) | | ((e: SearchTimeoutError) => void) & import("lodash").Cancelable | | ## Methods | Method | Modifiers | Description | | --- | --- | --- | +| [getSearchError(e, request, timeoutSignal, appAbortSignal)](./kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md) | | | +| [getTimeoutMode()](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) | | | | [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given search method. Overrides the AbortSignal with one that will abort either when cancelPending is called, when the request times out, or when the original AbortSignal is aborted. Updates pendingCount$ when the request is started/finalized. | +| [showError(e)](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) | | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md new file mode 100644 index 0000000000000..43c0d86a153d9 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [showError](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) + +## SearchInterceptor.showError() method + +Signature: + +```typescript +showError(e: Error | KbnError): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| e | Error | KbnError | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md index 91ecb2821acbf..45bd18982e371 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md @@ -7,5 +7,5 @@ Signature: ```typescript -protected showTimeoutError: ((e: Error) => void) & import("lodash").Cancelable; +protected showTimeoutError: ((e: SearchTimeoutError) => void) & import("lodash").Cancelable; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md new file mode 100644 index 0000000000000..4505d8ca905b7 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) > [(constructor)](./kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md) + +## SearchTimeoutError.(constructor) + +Constructs a new instance of the `SearchTimeoutError` class + +Signature: + +```typescript +constructor(err: HttpFetchError | null | undefined, mode: TimeoutErrorMode); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| err | HttpFetchError | null | undefined | | +| mode | TimeoutErrorMode | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md new file mode 100644 index 0000000000000..58ef953c9d7db --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) > [getErrorMessage](./kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md) + +## SearchTimeoutError.getErrorMessage() method + +Signature: + +```typescript +getErrorMessage(application: ApplicationStart): JSX.Element; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| application | ApplicationStart | | + +Returns: + +`JSX.Element` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.md new file mode 100644 index 0000000000000..5c0bec04dcfbc --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) + +## SearchTimeoutError class + +Request Failure - When an entire multi request fails + +Signature: + +```typescript +export declare class SearchTimeoutError extends KbnError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(err, mode)](./kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md) | | Constructs a new instance of the SearchTimeoutError class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [mode](./kibana-plugin-plugins-data-public.searchtimeouterror.mode.md) | | TimeoutErrorMode | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [getErrorMessage(application)](./kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md) | | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.mode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.mode.md new file mode 100644 index 0000000000000..d534a73eca2ec --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.mode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) > [mode](./kibana-plugin-plugins-data-public.searchtimeouterror.mode.md) + +## SearchTimeoutError.mode property + +Signature: + +```typescript +mode: TimeoutErrorMode; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timeouterrormode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timeouterrormode.md new file mode 100644 index 0000000000000..8ad63e2c1e9b4 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timeouterrormode.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [TimeoutErrorMode](./kibana-plugin-plugins-data-public.timeouterrormode.md) + +## TimeoutErrorMode enum + +Signature: + +```typescript +export declare enum TimeoutErrorMode +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| CHANGE | 2 | | +| CONTACT | 1 | | +| UPGRADE | 0 | | + diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 7ce53a219fb44..756862a3d2900 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -8,6 +8,7 @@ import { $Values } from '@kbn/utility-types'; import _ from 'lodash'; import { Action } from 'history'; import { ApiResponse } from '@elastic/elasticsearch/lib/Transport'; +import { ApplicationStart } from 'kibana/public'; import { Assign } from '@kbn/utility-types'; import { BehaviorSubject } from 'rxjs'; import Boom from 'boom'; @@ -29,12 +30,14 @@ import { ExpressionAstFunction } from 'src/plugins/expressions/common'; import { ExpressionsSetup } from 'src/plugins/expressions/public'; import { History } from 'history'; import { Href } from 'history'; +import { HttpFetchError } from 'kibana/public'; import { IconType } from '@elastic/eui'; import { InjectedIntl } from '@kbn/i18n/react'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource as ISearchSource_2 } from 'src/plugins/data/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { IUiSettingsClient } from 'src/core/public'; +import { KbnError } from 'src/plugins/kibana_utils/common'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; import { Location } from 'history'; @@ -1432,6 +1435,8 @@ export interface ISearchStart { aggs: AggsStart; search: ISearchGeneric; searchSource: ISearchStartSearchSource; + // (undocumented) + showError: (e: any) => void; } // @public @@ -1588,6 +1593,16 @@ export interface OptionedValueProp { value: string; } +// Warning: (ae-forgotten-export) The symbol "KbnError" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PainlessError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class PainlessError extends KbnError_2 { + constructor(err: Error, request: IEsSearchRequest); + // (undocumented) + getErrorMessage(application: ApplicationStart): JSX.Element; +} + // Warning: (ae-forgotten-export) The symbol "parseEsInterval" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ParsedInterval" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1812,13 +1827,6 @@ export interface RefreshInterval { value: number; } -// Warning: (ae-missing-release-tag) "RequestTimeoutError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public -export class RequestTimeoutError extends Error { - constructor(message?: string); -} - // Warning: (ae-missing-release-tag) "SavedQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1942,6 +1950,10 @@ export class SearchInterceptor { protected application: CoreStart['application']; // (undocumented) protected readonly deps: SearchInterceptorDeps; + // (undocumented) + protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): any; + // (undocumented) + protected getTimeoutMode(): TimeoutErrorMode; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) @@ -1953,10 +1965,13 @@ export class SearchInterceptor { timeout?: number; }): { combinedSignal: AbortSignal; + timeoutSignal: AbortSignal; cleanup: () => void; }; // (undocumented) - protected showTimeoutError: ((e: Error) => void) & import("lodash").Cancelable; + showError(e: Error | KbnError): void; + // (undocumented) + protected showTimeoutError: ((e: SearchTimeoutError) => void) & import("lodash").Cancelable; // @internal protected timeoutSubscriptions: Subscription; } @@ -2072,6 +2087,17 @@ export interface SearchSourceFields { version?: boolean; } +// Warning: (ae-missing-release-tag) "SearchTimeoutError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export class SearchTimeoutError extends KbnError_2 { + constructor(err: HttpFetchError | null | undefined, mode: TimeoutErrorMode); + // (undocumented) + getErrorMessage(application: ApplicationStart): JSX.Element; + // (undocumented) + mode: TimeoutErrorMode; + } + // Warning: (ae-missing-release-tag) "SortDirection" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2144,6 +2170,18 @@ export class TimeHistory { // @public (undocumented) export type TimeHistoryContract = PublicMethodsOf; +// Warning: (ae-missing-release-tag) "TimeoutErrorMode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum TimeoutErrorMode { + // (undocumented) + CHANGE = 2, + // (undocumented) + CONTACT = 1, + // (undocumented) + UPGRADE = 0 +} + // Warning: (ae-missing-release-tag) "TimeRange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2233,21 +2271,21 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:404:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:45:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx index b0cd51266c04d..dcd73d6aa9fca 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -32,7 +32,7 @@ export class PainlessError extends KbnError { const [{ script }] = rootCause; super( - i18n.translate('discover.painlessError.painlessScriptedFieldErrorMessage', { + i18n.translate('data.painlessError.painlessScriptedFieldErrorMessage', { defaultMessage: "Error with Painless scripted field '{script}'.", values: { script }, }) diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index 47e6feea10172..41d4cae2946fa 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -49,12 +49,12 @@ export class SearchTimeoutError extends KbnError { 'One or more queries timed out. With our free Basic tier, your queries never time out.', }); case TimeoutErrorMode.CONTACT: - return i18n.translate('xpack.data.search.timeoutContactAdmin', { + return i18n.translate('data.search.timeoutContactAdmin', { defaultMessage: 'One or more queries timed out. Contact your system administrator to increase the run time.', }); case TimeoutErrorMode.CHANGE: - return i18n.translate('xpack.data.search.timeoutIncreaseSetting', { + return i18n.translate('data.search.timeoutIncreaseSetting', { defaultMessage: 'One or more queries timed out. Increase run time with the search timeout advanced setting.', }); diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index f4ed7d8b122b9..bd1cb9026e77e 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -34,6 +34,7 @@ function createStartContract(): jest.Mocked { return { aggs: searchAggsStartMock(), search: jest.fn(), + showError: jest.fn(), searchSource: searchSourceMock, }; } diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 9fa2242fd7f9b..f945836c20f65 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -30,7 +30,6 @@ import { } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; -import { i18n } from '@kbn/i18n'; import { KbnError } from 'src/plugins/kibana_utils/common'; import { getCombinedSignal, @@ -98,8 +97,10 @@ export class SearchInterceptor { appAbortSignal?: AbortSignal ) { if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { + const err = new SearchTimeoutError(e, this.getTimeoutMode()); + this.showTimeoutError(err); // Handle a client or a server side timeout - return new SearchTimeoutError(e, this.getTimeoutMode()); + return err; } else if (appAbortSignal?.aborted) { // In the case an application initiated abort, throw the existing AbortError. return e; @@ -122,10 +123,10 @@ export class SearchInterceptor { } if (e instanceof SearchTimeoutError) { - this.deps.toasts.addDanger({ - title: 'Timeout', - text: toMountPoint(e.getErrorMessage(this.application)), - }); + // this.deps.toasts.addDanger({ + // title: 'Timeout', + // text: toMountPoint(e.getErrorMessage(this.application)), + // }); return; } @@ -204,7 +205,6 @@ export class SearchInterceptor { const timeout$ = timeout ? timer(timeout) : NEVER; const subscription = timeout$.subscribe(() => { timeoutController.abort(); - this.showTimeoutError(new AbortError()); }); this.timeoutSubscriptions.add(subscription); @@ -235,13 +235,10 @@ export class SearchInterceptor { // Right now we are debouncing but we will hook this up with background sessions to show only one // error notification per session. protected showTimeoutError = debounce( - (e: Error) => { - this.deps.toasts.addError(e, { + (e: SearchTimeoutError) => { + this.deps.toasts.addDanger({ title: 'Timed out', - toastMessage: i18n.translate('data.search.upgradeLicense', { - defaultMessage: - 'One or more queries timed out. With our free Basic tier, your queries never time out.', - }), + text: toMountPoint(e.getErrorMessage(this.application)), }); }, 60000, diff --git a/src/plugins/expressions/common/util/create_error.ts b/src/plugins/expressions/common/util/create_error.ts index cde328e04bc4d..9116ac2730290 100644 --- a/src/plugins/expressions/common/util/create_error.ts +++ b/src/plugins/expressions/common/util/create_error.ts @@ -21,7 +21,11 @@ import { ExpressionValueError } from '../../common'; type ErrorLike = Partial>; -export const createError = (err: string | ErrorLike): ExpressionValueError => ({ +function isErrorObj(e: any): e is Error { + return e instanceof Error; +} + +export const createError = (err: string | Error | ErrorLike): ExpressionValueError => ({ type: 'error', error: { stack: @@ -32,6 +36,6 @@ export const createError = (err: string | ErrorLike): ExpressionValueError => ({ : undefined, message: typeof err === 'string' ? err : String(err.message), name: typeof err === 'object' ? err.name || 'Error' : 'Error', - original: typeof err === 'object' ? err : undefined, + original: isErrorObj(err) ? err : undefined, }, }); diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts index 9f2aedd7278a3..62cc26cb17778 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts @@ -17,7 +17,6 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; import { SerializedVis, Vis, @@ -28,6 +27,7 @@ import { import { SearchSourceFields } from 'src/plugins/data/public'; import { SavedObject } from 'src/plugins/saved_objects/public'; import { cloneDeep } from 'lodash'; +import { ExpressionValueError } from 'src/plugins/expressions/public'; import { createSavedSearchesLoader } from '../../../../discover/public'; import { VisualizeServices } from '../types'; @@ -35,14 +35,7 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( vis: Vis, visualizeServices: VisualizeServices ) => { - const { - chrome, - data, - overlays, - createVisEmbeddableFromObject, - savedObjects, - toastNotifications, - } = visualizeServices; + const { chrome, data, overlays, createVisEmbeddableFromObject, savedObjects } = visualizeServices; const embeddableHandler = (await createVisEmbeddableFromObject(vis, { timeRange: data.query.timefilter.timefilter.getTime(), filters: data.query.filterManager.getFilters(), @@ -51,7 +44,9 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( embeddableHandler.getOutput$().subscribe((output) => { if (output.error) { - data.search.showError(output.error.original || ouput.error); + data.search.showError( + ((output.error as unknown) as ExpressionValueError).error?.original || output.error + ); } }); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ad997ece9e9f5..54a675fb6e6f8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1367,10 +1367,6 @@ "discover.embeddable.inspectorRequestDataTitle": "データ", "discover.embeddable.inspectorRequestDescription": "このリクエストはElasticsearchにクエリをかけ、検索データを取得します。", "discover.embeddable.search.displayName": "検索", - "discover.errorLoadingData": "データの読み込み中にエラーが発生", - "discover.fetchError.howToAddressErrorDescription": "このエラーは、{scriptedFields}タブにある {managementLink}の{fetchErrorScript}フィールドを編集することで解決できます。", - "discover.fetchError.managmentLinkText": "管理>インデックスパターン", - "discover.fetchError.scriptedFieldsText": "「スクリプトフィールド」", "discover.fieldChooser.detailViews.emptyStringText": "空の文字列", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "{field}を除外:\"{value}\"", "discover.fieldChooser.detailViews.filterValueButtonAriaLabel": "{field}を除外:\"{value}\"", @@ -1445,7 +1441,6 @@ "discover.notifications.invalidTimeRangeTitle": "無効な時間範囲", "discover.notifications.notSavedSearchTitle": "検索「{savedSearchTitle}」は保存されませんでした。", "discover.notifications.savedSearchTitle": "検索「{savedSearchTitle}」が保存されました。", - "discover.painlessError.painlessScriptedFieldErrorMessage": "Painlessスクリプトのフィールド「{script}」のエラー.", "discover.reloadSavedSearchButton": "検索をリセット", "discover.rootBreadcrumb": "発見", "discover.savedSearch.savedObjectName": "保存検索", @@ -4386,7 +4381,6 @@ "visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "indexPatternまたはsavedSearchIdが必要です", "visualize.createVisualization.noVisTypeErrorMessage": "有効なビジュアライゼーションタイプを指定してください", "visualize.editor.createBreadcrumb": "作成", - "visualize.error.title": "ビジュアライゼーションエラー", "visualize.helpMenu.appName": "可視化", "visualize.linkedToSearch.unlinkSuccessNotificationText": "保存された検索「{searchTitle}」からリンクが解除されました", "visualize.listing.betaTitle": "ベータ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4a3d86a0dd898..b749cd8cd7496 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1368,10 +1368,6 @@ "discover.embeddable.inspectorRequestDataTitle": "数据", "discover.embeddable.inspectorRequestDescription": "此请求将查询 Elasticsearch 以获取搜索的数据。", "discover.embeddable.search.displayName": "搜索", - "discover.errorLoadingData": "加载数据时出错", - "discover.fetchError.howToAddressErrorDescription": "您可以通过编辑{managementLink}中{scriptedFields}选项卡下的“{fetchErrorScript}”字段来解决此错误。", - "discover.fetchError.managmentLinkText": "“管理”>“索引模式”", - "discover.fetchError.scriptedFieldsText": "“脚本字段”", "discover.fieldChooser.detailViews.emptyStringText": "空字符串", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "筛除 {field}:“{value}”", "discover.fieldChooser.detailViews.filterValueButtonAriaLabel": "筛留 {field}:“{value}”", @@ -1446,7 +1442,6 @@ "discover.notifications.invalidTimeRangeTitle": "时间范围无效", "discover.notifications.notSavedSearchTitle": "搜索“{savedSearchTitle}”未保存。", "discover.notifications.savedSearchTitle": "搜索“{savedSearchTitle}”已保存", - "discover.painlessError.painlessScriptedFieldErrorMessage": "Painless 脚本字段“{script}”有错误。", "discover.reloadSavedSearchButton": "重置搜索", "discover.rootBreadcrumb": "Discover", "discover.savedSearch.savedObjectName": "已保存搜索", @@ -4387,7 +4382,6 @@ "visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "必须提供 indexPattern 或 savedSearchId", "visualize.createVisualization.noVisTypeErrorMessage": "必须提供有效的可视化类型", "visualize.editor.createBreadcrumb": "创建", - "visualize.error.title": "可视化错误", "visualize.helpMenu.appName": "Visualize", "visualize.linkedToSearch.unlinkSuccessNotificationText": "已取消与已保存搜索“{searchTitle}”的链接", "visualize.listing.betaTitle": "公测版", From e6a794d7ddb537ec2f9b34b6dd99c2f7ddeb8fed Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 20 Sep 2020 14:58:27 +0300 Subject: [PATCH 07/27] Fix jest tests --- .../application/utils/get_visualization_instance.test.ts | 4 +++- .../data_enhanced/public/search/search_interceptor.test.ts | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts index 31f0fc5f94479..bb4fabb189a27 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts @@ -50,6 +50,8 @@ describe('getVisualizationInstance', () => { }; savedVisMock = {}; // @ts-expect-error + mockServices.data.search.showError.mockImplementation(() => {}); + // @ts-expect-error mockServices.savedVisualizations.get.mockImplementation(() => savedVisMock); // @ts-expect-error mockServices.visualizations.convertToSerializedVis.mockImplementation(() => serializedVisMock); @@ -119,6 +121,6 @@ describe('getVisualizationInstance', () => { error: 'error', }); - expect(mockServices.toastNotifications.addError).toHaveBeenCalled(); + expect(mockServices.data.search.showError).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts index af2fc85602541..6e34e4c1964c5 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts @@ -8,6 +8,7 @@ import { coreMock } from '../../../../../src/core/public/mocks'; import { EnhancedSearchInterceptor } from './search_interceptor'; import { CoreSetup, CoreStart } from 'kibana/public'; import { AbortError, UI_SETTINGS } from '../../../../../src/plugins/data/common'; +import { SearchTimeoutError } from 'src/plugins/data/public'; const timeTravel = (msToRun = 0) => { jest.advanceTimersByTime(msToRun); @@ -265,7 +266,7 @@ describe('EnhancedSearchInterceptor', () => { await timeTravel(1000); expect(error).toHaveBeenCalled(); - expect(error.mock.calls[0][0]).toBeInstanceOf(AbortError); + expect(error.mock.calls[0][0]).toBeInstanceOf(SearchTimeoutError); expect(mockCoreSetup.http.fetch).toHaveBeenCalled(); expect(mockCoreSetup.http.delete).not.toHaveBeenCalled(); }); @@ -305,7 +306,7 @@ describe('EnhancedSearchInterceptor', () => { await timeTravel(1000); expect(error).toHaveBeenCalled(); - expect(error.mock.calls[0][0]).toBeInstanceOf(AbortError); + expect(error.mock.calls[0][0]).toBeInstanceOf(SearchTimeoutError); expect(mockCoreSetup.http.fetch).toHaveBeenCalledTimes(2); expect(mockCoreSetup.http.delete).toHaveBeenCalled(); }); From 13be80c6dfe5924c2d8d1404976e583b0d8d5cfc Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 20 Sep 2020 15:54:39 +0300 Subject: [PATCH 08/27] Fix functional test --- test/functional/apps/discover/{_errors.js => _errors.ts} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename test/functional/apps/discover/{_errors.js => _errors.ts} (86%) diff --git a/test/functional/apps/discover/_errors.js b/test/functional/apps/discover/_errors.ts similarity index 86% rename from test/functional/apps/discover/_errors.js rename to test/functional/apps/discover/_errors.ts index 614059dc8ac94..b139e7cfe3bb8 100644 --- a/test/functional/apps/discover/_errors.js +++ b/test/functional/apps/discover/_errors.ts @@ -21,10 +21,10 @@ import expect from '@kbn/expect'; export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); - const testSubjects = getService('testSubjects'); + const toasts = getService('toasts'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); - describe('errors', function describeIndexTests() { + describe.only('errors', function describeIndexTests() { before(async function () { await esArchiver.loadIfNeeded('logstash_functional'); await esArchiver.load('invalid_scripted_field'); @@ -38,8 +38,8 @@ export default function ({ getService, getPageObjects }) { describe('invalid scripted field error', () => { it('is rendered', async () => { - const isFetchErrorVisible = await testSubjects.exists('discoverFetchError'); - expect(isFetchErrorVisible).to.be(true); + const toast = await toasts.getToastElement(1); + expect(toast).not.to.be(undefined); }); }); }); From 3e9dc9bcb816d36229f2720afe7fef7912decf63 Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 20 Sep 2020 15:56:42 +0300 Subject: [PATCH 09/27] functional test --- test/functional/apps/discover/_errors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_errors.ts b/test/functional/apps/discover/_errors.ts index b139e7cfe3bb8..1699f3cbc7e93 100644 --- a/test/functional/apps/discover/_errors.ts +++ b/test/functional/apps/discover/_errors.ts @@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) { const toasts = getService('toasts'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); - describe.only('errors', function describeIndexTests() { + describe('errors', function describeIndexTests() { before(async function () { await esArchiver.loadIfNeeded('logstash_functional'); await esArchiver.load('invalid_scripted_field'); From 6f0b0807068dae1611db59196c5f65a03b5c6fea Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 20 Sep 2020 16:33:58 +0300 Subject: [PATCH 10/27] ts --- test/functional/apps/discover/_errors.ts | 3 ++- .../plugins/data_enhanced/public/search/search_interceptor.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/discover/_errors.ts b/test/functional/apps/discover/_errors.ts index 1699f3cbc7e93..aad8524e46b3a 100644 --- a/test/functional/apps/discover/_errors.ts +++ b/test/functional/apps/discover/_errors.ts @@ -18,8 +18,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }): FtrProviderContext { const esArchiver = getService('esArchiver'); const toasts = getService('toasts'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index 683cf8ffbd43b..c2634fdf3981b 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -5,7 +5,7 @@ */ import { throwError, EMPTY, timer, from, Subscription } from 'rxjs'; -import { mergeMap, expand, takeUntil, finalize, tap, catchError } from 'rxjs/operators'; +import { mergeMap, expand, takeUntil, finalize, catchError } from 'rxjs/operators'; import { debounce } from 'lodash'; import { i18n } from '@kbn/i18n'; import { From 021d12f83d17f0a972eba8cca56a0a2957856332 Mon Sep 17 00:00:00 2001 From: Liza K Date: Mon, 21 Sep 2020 12:13:50 +0300 Subject: [PATCH 11/27] Update functional tests --- .../public/search/search_interceptor.test.ts | 83 ++++++++----------- .../data/public/search/search_interceptor.ts | 3 +- test/functional/apps/discover/_errors.ts | 2 +- 3 files changed, 37 insertions(+), 51 deletions(-) diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index 7bfa6f0ab1bc5..9694b861b59fa 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -22,6 +22,7 @@ import { coreMock } from '../../../../core/public/mocks'; import { IEsSearchRequest } from '../../common/search'; import { SearchInterceptor } from './search_interceptor'; import { AbortError } from '../../common'; +import { SearchTimeoutError, PainlessError } from './errors'; let searchInterceptor: SearchInterceptor; let mockCoreSetup: MockedKeys; @@ -53,8 +54,8 @@ describe('SearchInterceptor', () => { expect(result).toBe(mockResponse); }); - test('Observable should fail if fetch has an error', async () => { - const mockResponse: any = { result: 500 }; + test('Observable should fail if fetch has an internal error', async () => { + const mockResponse: any = { result: 500, message: 'Internal Error' }; mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); const mockRequest: IEsSearchRequest = { params: {}, @@ -68,64 +69,50 @@ describe('SearchInterceptor', () => { } }); - test('Observable should fail if fetch times out (test merged signal)', async () => { - mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { - return new Promise((resolve, reject) => { - options.signal.addEventListener('abort', () => { - reject(new AbortError()); - }); - - setTimeout(resolve, 5000); - }); - }); + test('Should throw SearchTimeoutError on server timeout', async (done) => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); const mockRequest: IEsSearchRequest = { params: {}, }; const response = searchInterceptor.search(mockRequest); - const next = jest.fn(); - const error = (e: any) => { - expect(next).not.toBeCalled(); - expect(e).toBeInstanceOf(AbortError); - }; - response.subscribe({ next, error }); - - jest.advanceTimersByTime(5000); - - await flushPromises(); + try { + await response.toPromise(); + } catch (e) { + expect(e).toBeInstanceOf(SearchTimeoutError); + done(); + } }); - test('Should not timeout if requestTimeout is undefined', async () => { - searchInterceptor = new SearchInterceptor({ - startServices: mockCoreSetup.getStartServices(), - uiSettings: mockCoreSetup.uiSettings, - http: mockCoreSetup.http, - toasts: mockCoreSetup.notifications.toasts, - }); - mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { - return new Promise((resolve, reject) => { - options.signal.addEventListener('abort', () => { - reject(new AbortError()); - }); - - setTimeout(resolve, 5000); - }); - }); + test('Should throw Painless error on server error', async (done) => { + const mockResponse: any = { + result: 500, + body: { + attributes: { + error: { + root_cause: [{ lang: 'painless' }], + }, + }, + }, + }; + mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); const mockRequest: IEsSearchRequest = { params: {}, }; const response = searchInterceptor.search(mockRequest); - expect.assertions(1); - const next = jest.fn(); - const complete = () => { - expect(next).toBeCalled(); - }; - response.subscribe({ next, complete }); - - jest.advanceTimersByTime(5000); - - await flushPromises(); + try { + await response.toPromise(); + } catch (e) { + expect(e).toBeInstanceOf(PainlessError); + done(); + } }); test('Observable should fail if user aborts (test merged signal)', async () => { diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index f945836c20f65..6ce9edaac6116 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -130,9 +130,8 @@ export class SearchInterceptor { return; } - this.deps.toasts.addDanger({ + this.deps.toasts.addError(e, { title: 'Search Error', - text: e.message, }); } diff --git a/test/functional/apps/discover/_errors.ts b/test/functional/apps/discover/_errors.ts index aad8524e46b3a..521a05f29bdbd 100644 --- a/test/functional/apps/discover/_errors.ts +++ b/test/functional/apps/discover/_errors.ts @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }): FtrProviderContext { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const toasts = getService('toasts'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); From 30354505a6317dd7a0129a10b625800a89472d7c Mon Sep 17 00:00:00 2001 From: Liza K Date: Mon, 21 Sep 2020 13:27:59 +0300 Subject: [PATCH 12/27] Add unit tests to interceptor and timeout error --- .../search/errors/timeout_error.test.tsx | 62 ++++++++ .../public/search/errors/timeout_error.tsx | 3 +- .../public/search/search_interceptor.test.ts | 27 +++- .../data/public/search/search_interceptor.ts | 139 ++++++++++-------- .../public/search/search_interceptor.ts | 38 +---- 5 files changed, 173 insertions(+), 96 deletions(-) create mode 100644 src/plugins/data/public/search/errors/timeout_error.test.tsx diff --git a/src/plugins/data/public/search/errors/timeout_error.test.tsx b/src/plugins/data/public/search/errors/timeout_error.test.tsx new file mode 100644 index 0000000000000..87b491b976ebc --- /dev/null +++ b/src/plugins/data/public/search/errors/timeout_error.test.tsx @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SearchTimeoutError, TimeoutErrorMode } from './timeout_error'; + +import { coreMock } from '../../../../../core/public/mocks'; +const startMock = coreMock.createStart(); + +import { mount } from 'enzyme'; +import { AbortError } from 'src/plugins/data/common'; + +describe('SearchTimeoutError', () => { + beforeEach(() => { + jest.clearAllMocks(); + startMock.application.navigateToApp.mockImplementation(jest.fn()); + }); + + it('Should navigate to upgrade', () => { + const e = new SearchTimeoutError(new AbortError(), TimeoutErrorMode.UPGRADE); + const component = mount(e.getErrorMessage(startMock.application)); + + expect(component.find('EuiButton').length).toBe(1); + component.find('EuiButton').simulate('click'); + expect(startMock.application.navigateToApp).toHaveBeenCalledWith('management', { + path: '/kibana/indexPatterns', + }); + }); + + it('Should create contact admin message', () => { + const e = new SearchTimeoutError(new AbortError(), TimeoutErrorMode.CONTACT); + const component = mount(e.getErrorMessage(startMock.application)); + + expect(component.find('EuiButton').length).toBe(0); + }); + + it('Should navigate to settings', () => { + const e = new SearchTimeoutError(new AbortError(), TimeoutErrorMode.CHANGE); + const component = mount(e.getErrorMessage(startMock.application)); + + expect(component.find('EuiButton').length).toBe(1); + component.find('EuiButton').simulate('click'); + expect(startMock.application.navigateToApp).toHaveBeenCalledWith('management', { + path: '/kibana/settings', + }); + }); +}); diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index 41d4cae2946fa..e1e1114c4f171 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -21,7 +21,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiSpacer } from '@elastic/eui'; import { ApplicationStart } from 'kibana/public'; -import { HttpFetchError } from 'kibana/public'; import { KbnError } from '../../../../kibana_utils/common'; export enum TimeoutErrorMode { @@ -36,7 +35,7 @@ export enum TimeoutErrorMode { */ export class SearchTimeoutError extends KbnError { public mode: TimeoutErrorMode; - constructor(err: HttpFetchError | null = null, mode: TimeoutErrorMode) { + constructor(err: Error, mode: TimeoutErrorMode) { super(`Request timeout: ${JSON.stringify(err?.message)}`); this.mode = mode; } diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index 9694b861b59fa..029870a3a6c45 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -69,7 +69,7 @@ describe('SearchInterceptor', () => { } }); - test('Should throw SearchTimeoutError on server timeout', async (done) => { + test('Should throw SearchTimeoutError on server timeout AND show toast', async (done) => { const mockResponse: any = { result: 500, body: { @@ -86,10 +86,35 @@ describe('SearchInterceptor', () => { await response.toPromise(); } catch (e) { expect(e).toBeInstanceOf(SearchTimeoutError); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); done(); } }); + test('Search error should be debounced', async (done) => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + try { + await searchInterceptor.search(mockRequest).toPromise(); + } catch (e) { + expect(e).toBeInstanceOf(SearchTimeoutError); + try { + await searchInterceptor.search(mockRequest).toPromise(); + } catch (e2) { + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); + done(); + } + } + }); + test('Should throw Painless error on server error', async (done) => { const mockResponse: any = { result: 500, diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 6ce9edaac6116..4262088ede65b 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -86,20 +86,30 @@ export class SearchInterceptor { }); } + /* + * @returns `TimeoutErrorMode` indicating what action should be taken in case of a request timeout based on license and permissions. + * @internal + */ protected getTimeoutMode() { return TimeoutErrorMode.UPGRADE; } + /* + * @returns `Error` a search service specific error or the original error, if a specific error can't be recognized. + * @internal + */ protected getSearchError( e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal - ) { + ): Error { if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { + // Handle a client or a server side timeout const err = new SearchTimeoutError(e, this.getTimeoutMode()); + + // Show the timeout error here, so that it's shown regardless of how an application chooses to handle errors. this.showTimeoutError(err); - // Handle a client or a server side timeout return err; } else if (appAbortSignal?.aborted) { // In the case an application initiated abort, throw the existing AbortError. @@ -111,30 +121,6 @@ export class SearchInterceptor { } } - public showError(e: Error | KbnError) { - if (e instanceof AbortError) return; - - if (e instanceof PainlessError) { - this.deps.toasts.addDanger({ - title: 'Search Error', - text: toMountPoint(e.getErrorMessage(this.application)), - }); - return; - } - - if (e instanceof SearchTimeoutError) { - // this.deps.toasts.addDanger({ - // title: 'Timeout', - // text: toMountPoint(e.getErrorMessage(this.application)), - // }); - return; - } - - this.deps.toasts.addError(e, { - title: 'Search Error', - }); - } - /** * @internal */ @@ -156,38 +142,6 @@ export class SearchInterceptor { ); } - /** - * Searches using the given `search` method. Overrides the `AbortSignal` with one that will abort - * either when `cancelPending` is called, when the request times out, or when the original - * `AbortSignal` is aborted. Updates `pendingCount$` when the request is started/finalized. - */ - public search( - request: IEsSearchRequest, - options?: ISearchOptions - ): Observable { - // Defer the following logic until `subscribe` is actually called - return defer(() => { - if (options?.abortSignal?.aborted) { - return throwError(new AbortError()); - } - - const { timeoutSignal, combinedSignal, cleanup } = this.setupAbortSignal({ - abortSignal: options?.abortSignal, - }); - this.pendingCount$.next(this.pendingCount$.getValue() + 1); - - return this.runSearch(request, combinedSignal, options?.strategy).pipe( - catchError((e: any) => { - return throwError(this.getSearchError(e, request, timeoutSignal, options?.abortSignal)); - }), - finalize(() => { - this.pendingCount$.next(this.pendingCount$.getValue() - 1); - cleanup(); - }) - ); - }); - } - /** * @internal */ @@ -231,9 +185,12 @@ export class SearchInterceptor { }; } - // Right now we are debouncing but we will hook this up with background sessions to show only one - // error notification per session. - protected showTimeoutError = debounce( + /** + * Right now we are debouncing but we will hook this up with background sessions to show only one + * error notification per session. + * @internal + */ + private showTimeoutError = debounce( (e: SearchTimeoutError) => { this.deps.toasts.addDanger({ title: 'Timed out', @@ -245,6 +202,66 @@ export class SearchInterceptor { leading: true, } ); + + /** + * Searches using the given `search` method. Overrides the `AbortSignal` with one that will abort + * either when `cancelPending` is called, when the request times out, or when the original + * `AbortSignal` is aborted. Updates `pendingCount$` when the request is started/finalized. + * + * @param request + * @options + * @returns `Observalbe` emitting the search response or an error. + */ + public search( + request: IEsSearchRequest, + options?: ISearchOptions + ): Observable { + // Defer the following logic until `subscribe` is actually called + return defer(() => { + if (options?.abortSignal?.aborted) { + return throwError(new AbortError()); + } + + const { timeoutSignal, combinedSignal, cleanup } = this.setupAbortSignal({ + abortSignal: options?.abortSignal, + }); + this.pendingCount$.next(this.pendingCount$.getValue() + 1); + + return this.runSearch(request, combinedSignal, options?.strategy).pipe( + catchError((e: any) => { + return throwError(this.getSearchError(e, request, timeoutSignal, options?.abortSignal)); + }), + finalize(() => { + this.pendingCount$.next(this.pendingCount$.getValue() - 1); + cleanup(); + }) + ); + }); + } + + /* + * + */ + public showError(e: Error | KbnError) { + if (e instanceof AbortError) return; + + if (e instanceof SearchTimeoutError) { + // The SearchTimeoutError is shown by the interceptor in getSearchError (regardless of how the app chooses to handle errors) + return; + } + + if (e instanceof PainlessError) { + this.deps.toasts.addDanger({ + title: 'Search Error', + text: toMountPoint(e.getErrorMessage(this.application)), + }); + return; + } + + this.deps.toasts.addError(e, { + title: 'Search Error', + }); + } } export type ISearchInterceptor = PublicMethodsOf; diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index c2634fdf3981b..d22e2df37f34d 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -6,8 +6,6 @@ import { throwError, EMPTY, timer, from, Subscription } from 'rxjs'; import { mergeMap, expand, takeUntil, finalize, catchError } from 'rxjs/operators'; -import { debounce } from 'lodash'; -import { i18n } from '@kbn/i18n'; import { SearchInterceptor, SearchInterceptorDeps, @@ -40,6 +38,12 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { this.uiSettingsSub.unsubscribe(); } + protected getTimeoutMode() { + return this.application.capabilities.advancedSettings?.save + ? TimeoutErrorMode.CHANGE + : TimeoutErrorMode.CONTACT; + } + /** * Abort our `AbortController`, which in turn aborts any intercepted searches. */ @@ -101,34 +105,4 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { }) ); } - - protected getTimeoutMode() { - return this.application.capabilities.advancedSettings?.save - ? TimeoutErrorMode.CHANGE - : TimeoutErrorMode.CONTACT; - } - - // Right now we are debouncing but we will hook this up with background sessions to show only one - // error notification per session. - protected showTimeoutError = debounce( - (e: Error) => { - const message = this.application.capabilities.advancedSettings?.save - ? i18n.translate('xpack.data.search.timeoutIncreaseSetting', { - defaultMessage: - 'One or more queries timed out. Increase run time with the search.timeout advanced setting.', - }) - : i18n.translate('xpack.data.search.timeoutContactAdmin', { - defaultMessage: - 'One or more queries timed out. Contact your system administrator to increase the run time.', - }); - this.deps.toasts.addError(e, { - title: 'Timed out', - toastMessage: message, - }); - }, - 60000, - { - leading: true, - } - ); } From dd48dd2fbb2f0fbc8eb52a597d844b6b16f1a766 Mon Sep 17 00:00:00 2001 From: Liza K Date: Mon, 21 Sep 2020 14:09:57 +0300 Subject: [PATCH 13/27] expose toasts test function --- test/functional/services/toasts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/services/toasts.ts b/test/functional/services/toasts.ts index a70e4ba464ae8..f5416a44e3b5a 100644 --- a/test/functional/services/toasts.ts +++ b/test/functional/services/toasts.ts @@ -63,7 +63,7 @@ export function ToastsProvider({ getService }: FtrProviderContext) { } } - private async getToastElement(index: number) { + public async getToastElement(index: number) { const list = await this.getGlobalToastList(); return await list.findByCssSelector(`.euiToast:nth-child(${index})`); } From 21b44b7b42288156881c6953d9a9695190871a23 Mon Sep 17 00:00:00 2001 From: Liza K Date: Mon, 21 Sep 2020 14:12:53 +0300 Subject: [PATCH 14/27] doc --- .../data/public/kibana-plugin-plugins-data-public.md | 6 +++--- ...ns-data-public.searchinterceptor.getsearcherror.md | 4 ++-- ...na-plugin-plugins-data-public.searchinterceptor.md | 1 - ...in-plugins-data-public.searchinterceptor.search.md | 2 ++ ...-data-public.searchinterceptor.showtimeouterror.md | 11 ----------- ...ns-data-public.searchtimeouterror._constructor_.md | 4 ++-- src/plugins/data/public/public.api.md | 7 ++----- 7 files changed, 11 insertions(+), 24 deletions(-) delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index e68a9e2f6eb9a..8d326f25e0593 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -59,7 +59,6 @@ | [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) | | | [FieldMappingSpec](./kibana-plugin-plugins-data-public.fieldmappingspec.md) | | -| [Filter](./kibana-plugin-plugins-data-public.filter.md) | | | [IDataPluginServices](./kibana-plugin-plugins-data-public.idatapluginservices.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-public.iessearchrequest.md) | | | [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) | | @@ -77,7 +76,6 @@ | [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | high level search service | | [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) | | | [OptionedValueProp](./kibana-plugin-plugins-data-public.optionedvalueprop.md) | | -| [Query](./kibana-plugin-plugins-data-public.query.md) | | | [QueryState](./kibana-plugin-plugins-data-public.querystate.md) | All query state service state | | [QueryStateChange](./kibana-plugin-plugins-data-public.querystatechange.md) | | | [QuerySuggestionBasic](./kibana-plugin-plugins-data-public.querysuggestionbasic.md) | \* | @@ -92,7 +90,6 @@ | [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | search source fields | | [TabbedAggColumn](./kibana-plugin-plugins-data-public.tabbedaggcolumn.md) | \* | | [TabbedTable](./kibana-plugin-plugins-data-public.tabbedtable.md) | \* | -| [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) | | ## Variables @@ -147,6 +144,7 @@ | [FieldFormatsContentType](./kibana-plugin-plugins-data-public.fieldformatscontenttype.md) | \* | | [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md) | | | [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) | | +| [Filter](./kibana-plugin-plugins-data-public.filter.md) | | | [IAggConfig](./kibana-plugin-plugins-data-public.iaggconfig.md) | AggConfig This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app. | | [IAggType](./kibana-plugin-plugins-data-public.iaggtype.md) | | | [IFieldFormat](./kibana-plugin-plugins-data-public.ifieldformat.md) | | @@ -164,6 +162,7 @@ | [ParsedInterval](./kibana-plugin-plugins-data-public.parsedinterval.md) | | | [PhraseFilter](./kibana-plugin-plugins-data-public.phrasefilter.md) | | | [PhrasesFilter](./kibana-plugin-plugins-data-public.phrasesfilter.md) | | +| [Query](./kibana-plugin-plugins-data-public.query.md) | | | [QueryStart](./kibana-plugin-plugins-data-public.querystart.md) | | | [QuerySuggestion](./kibana-plugin-plugins-data-public.querysuggestion.md) | \* | | [QuerySuggestionGetFn](./kibana-plugin-plugins-data-public.querysuggestiongetfn.md) | | @@ -175,4 +174,5 @@ | [TabbedAggRow](./kibana-plugin-plugins-data-public.tabbedaggrow.md) | \* | | [TimefilterContract](./kibana-plugin-plugins-data-public.timefiltercontract.md) | | | [TimeHistoryContract](./kibana-plugin-plugins-data-public.timehistorycontract.md) | | +| [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md index dbb136e7baffc..bd3b32d2a4f92 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md @@ -7,7 +7,7 @@ Signature: ```typescript -protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): any; +protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; ``` ## Parameters @@ -21,5 +21,5 @@ protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: Abort Returns: -`any` +`Error` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index bb2eed860334b..e1aeaa5834be4 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -21,7 +21,6 @@ export declare class SearchInterceptor | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [deps](./kibana-plugin-plugins-data-public.searchinterceptor.deps.md) | | SearchInterceptorDeps | | -| [showTimeoutError](./kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md) | | ((e: SearchTimeoutError) => void) & import("lodash").Cancelable | | ## Methods diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md index 1752d183a8737..84cc85c42e0d1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md @@ -23,3 +23,5 @@ search(request: IEsSearchRequest, options?: ISearchOptions): Observable` +`Observalbe` emitting the search response or an error. + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md deleted file mode 100644 index 45bd18982e371..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [showTimeoutError](./kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md) - -## SearchInterceptor.showTimeoutError property - -Signature: - -```typescript -protected showTimeoutError: ((e: SearchTimeoutError) => void) & import("lodash").Cancelable; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md index 4505d8ca905b7..1c6370c7d0356 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md @@ -9,13 +9,13 @@ Constructs a new instance of the `SearchTimeoutError` class Signature: ```typescript -constructor(err: HttpFetchError | null | undefined, mode: TimeoutErrorMode); +constructor(err: Error, mode: TimeoutErrorMode); ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| err | HttpFetchError | null | undefined | | +| err | Error | | | mode | TimeoutErrorMode | | diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index d15ce94030851..14ada29940a7d 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -30,7 +30,6 @@ import { ExpressionAstFunction } from 'src/plugins/expressions/common'; import { ExpressionsSetup } from 'src/plugins/expressions/public'; import { History } from 'history'; import { Href } from 'history'; -import { HttpFetchError } from 'kibana/public'; import { IconType } from '@elastic/eui'; import { InjectedIntl } from '@kbn/i18n/react'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; @@ -1942,7 +1941,7 @@ export class SearchInterceptor { // (undocumented) protected readonly deps: SearchInterceptorDeps; // (undocumented) - protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): any; + protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; // (undocumented) protected getTimeoutMode(): TimeoutErrorMode; // @internal @@ -1961,8 +1960,6 @@ export class SearchInterceptor { }; // (undocumented) showError(e: Error | KbnError): void; - // (undocumented) - protected showTimeoutError: ((e: SearchTimeoutError) => void) & import("lodash").Cancelable; // @internal protected timeoutSubscriptions: Subscription; } @@ -2082,7 +2079,7 @@ export interface SearchSourceFields { // // @public export class SearchTimeoutError extends KbnError_2 { - constructor(err: HttpFetchError | null | undefined, mode: TimeoutErrorMode); + constructor(err: Error, mode: TimeoutErrorMode); // (undocumented) getErrorMessage(application: ApplicationStart): JSX.Element; // (undocumented) From aaa08cc53b9f1b6eeb0d6b9683699ea3c60c96ff Mon Sep 17 00:00:00 2001 From: Liza K Date: Mon, 21 Sep 2020 14:42:45 +0300 Subject: [PATCH 15/27] typos --- src/plugins/discover/public/application/angular/discover.js | 1 - src/plugins/expressions/common/util/create_error.ts | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index bb197864d7e7e..a396033e5dedb 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -789,7 +789,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise $scope.fetchStatus = fetchStatuses.NO_RESULTS; $scope.rows = []; - // Add error.toToast data.search.showError(error); }); }; diff --git a/src/plugins/expressions/common/util/create_error.ts b/src/plugins/expressions/common/util/create_error.ts index 9116ac2730290..9bdab74efd6f9 100644 --- a/src/plugins/expressions/common/util/create_error.ts +++ b/src/plugins/expressions/common/util/create_error.ts @@ -21,10 +21,6 @@ import { ExpressionValueError } from '../../common'; type ErrorLike = Partial>; -function isErrorObj(e: any): e is Error { - return e instanceof Error; -} - export const createError = (err: string | Error | ErrorLike): ExpressionValueError => ({ type: 'error', error: { @@ -36,6 +32,6 @@ export const createError = (err: string | Error | ErrorLike): ExpressionValueErr : undefined, message: typeof err === 'string' ? err : String(err.message), name: typeof err === 'object' ? err.name || 'Error' : 'Error', - original: isErrorObj(err) ? err : undefined, + original: err instanceof Error ? err : undefined, }, }); From 551335de7b022309333954222f8efc8b806c474d Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 22 Sep 2020 10:56:33 +0300 Subject: [PATCH 16/27] review 1 --- ...ic.searchinterceptor.handlesearcherror.md} | 6 +-- ...n-plugins-data-public.searchinterceptor.md | 2 +- ...data-public.searchinterceptor.showerror.md | 4 +- src/plugins/data/public/public.api.md | 16 +++--- .../public/search/errors/painless_error.tsx | 11 +++-- .../public/search/search_interceptor.test.ts | 2 +- .../data/public/search/search_interceptor.ts | 49 ++++++------------- .../data/public/search/search_service.ts | 2 +- .../public/search/search_interceptor.ts | 2 +- 9 files changed, 36 insertions(+), 58 deletions(-) rename docs/development/plugins/data/public/{kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md => kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md} (61%) diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md similarity index 61% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md index bd3b32d2a4f92..1a849ecb1a03b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md @@ -1,13 +1,13 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [getSearchError](./kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [handleSearchError](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) -## SearchInterceptor.getSearchError() method +## SearchInterceptor.handleSearchError() method Signature: ```typescript -protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; +protected handleSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; ``` ## Parameters diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index e1aeaa5834be4..a02a6116d7ae0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -26,8 +26,8 @@ export declare class SearchInterceptor | Method | Modifiers | Description | | --- | --- | --- | -| [getSearchError(e, request, timeoutSignal, appAbortSignal)](./kibana-plugin-plugins-data-public.searchinterceptor.getsearcherror.md) | | | | [getTimeoutMode()](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) | | | +| [handleSearchError(e, request, timeoutSignal, appAbortSignal)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | | [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given search method. Overrides the AbortSignal with one that will abort either when cancelPending is called, when the request times out, or when the original AbortSignal is aborted. Updates pendingCount$ when the request is started/finalized. | | [showError(e)](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) | | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md index 43c0d86a153d9..92e851c783dd0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md @@ -7,14 +7,14 @@ Signature: ```typescript -showError(e: Error | KbnError): void; +showError(e: Error): void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| e | Error | KbnError | | +| e | Error | | Returns: diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 14ada29940a7d..2d9b689c08b36 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -36,7 +36,6 @@ import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource as ISearchSource_2 } from 'src/plugins/data/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { IUiSettingsClient } from 'src/core/public'; -import { KbnError } from 'src/plugins/kibana_utils/common'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; import { Location } from 'history'; @@ -71,7 +70,6 @@ import { SavedObjectsClientContract } from 'src/core/public'; import { Search } from '@elastic/elasticsearch/api/requestParams'; import { SearchResponse } from 'elasticsearch'; import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; -import { Subscription } from 'rxjs'; import { ToastInputFields } from 'src/core/public/notifications'; import { ToastsSetup } from 'kibana/public'; import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; @@ -1589,7 +1587,7 @@ export interface OptionedValueProp { // Warning: (ae-missing-release-tag) "PainlessError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class PainlessError extends KbnError_2 { +export class PainlessError extends KbnError { constructor(err: Error, request: IEsSearchRequest); // (undocumented) getErrorMessage(application: ApplicationStart): JSX.Element; @@ -1941,9 +1939,9 @@ export class SearchInterceptor { // (undocumented) protected readonly deps: SearchInterceptorDeps; // (undocumented) - protected getSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; - // (undocumented) protected getTimeoutMode(): TimeoutErrorMode; + // (undocumented) + protected handleSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) @@ -1959,10 +1957,8 @@ export class SearchInterceptor { cleanup: () => void; }; // (undocumented) - showError(e: Error | KbnError): void; - // @internal - protected timeoutSubscriptions: Subscription; -} + showError(e: Error): void; + } // Warning: (ae-missing-release-tag) "SearchInterceptorDeps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2078,7 +2074,7 @@ export interface SearchSourceFields { // Warning: (ae-missing-release-tag) "SearchTimeoutError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export class SearchTimeoutError extends KbnError_2 { +export class SearchTimeoutError extends KbnError { constructor(err: Error, mode: TimeoutErrorMode); // (undocumented) getErrorMessage(application: ApplicationStart): JSX.Element; diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx index dcd73d6aa9fca..524272b87fac4 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -18,7 +18,6 @@ */ import React from 'react'; -import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -28,7 +27,7 @@ import { IEsSearchRequest } from '..'; export class PainlessError extends KbnError { constructor(err: Error, request: IEsSearchRequest) { - const rootCause = get(err, 'body.attributes.error.root_cause'); + const rootCause = getRootCause(err); const [{ script }] = rootCause; super( @@ -60,8 +59,12 @@ export class PainlessError extends KbnError { } } -export function isPainlessError(error: any) { - const rootCause = get(error, 'body.attributes.error.root_cause'); +function getRootCause(err: Error) { + return (err as any).body?.attributes?.error?.root_cause; +} + +export function isPainlessError(err: Error) { + const rootCause = getRootCause(err); if (!rootCause) return false; const [{ lang }] = rootCause; diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index 029870a3a6c45..8ff80efb7adec 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -91,7 +91,7 @@ describe('SearchInterceptor', () => { } }); - test('Search error should be debounced', async (done) => { + test('Search error should be throttled', async (done) => { const mockResponse: any = { result: 500, body: { diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 4262088ede65b..a209d7d2147cf 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -17,20 +17,10 @@ * under the License. */ -import { get, trimEnd, debounce } from 'lodash'; -import { - BehaviorSubject, - throwError, - timer, - Subscription, - defer, - from, - Observable, - NEVER, -} from 'rxjs'; +import { get, trimEnd, throttle } from 'lodash'; +import { BehaviorSubject, throwError, timer, defer, from, Observable, NEVER } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; -import { KbnError } from 'src/plugins/kibana_utils/common'; import { getCombinedSignal, AbortError, @@ -64,12 +54,6 @@ export class SearchInterceptor { */ protected pendingCount$ = new BehaviorSubject(0); - /** - * The subscriptions from scheduling the automatic timeout for each request. - * @internal - */ - protected timeoutSubscriptions: Subscription = new Subscription(); - /** * @internal */ @@ -98,7 +82,7 @@ export class SearchInterceptor { * @returns `Error` a search service specific error or the original error, if a specific error can't be recognized. * @internal */ - protected getSearchError( + protected handleSearchError( e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, @@ -159,7 +143,6 @@ export class SearchInterceptor { const subscription = timeout$.subscribe(() => { timeoutController.abort(); }); - this.timeoutSubscriptions.add(subscription); // Get a combined `AbortSignal` that will be aborted whenever the first of the following occurs: // 1. The user manually aborts (via `cancelPending`) @@ -186,22 +169,16 @@ export class SearchInterceptor { } /** - * Right now we are debouncing but we will hook this up with background sessions to show only one + * Right now we are throttling but we will hook this up with background sessions to show only one * error notification per session. * @internal */ - private showTimeoutError = debounce( - (e: SearchTimeoutError) => { - this.deps.toasts.addDanger({ - title: 'Timed out', - text: toMountPoint(e.getErrorMessage(this.application)), - }); - }, - 60000, - { - leading: true, - } - ); + private showTimeoutError = throttle((e: SearchTimeoutError) => { + this.deps.toasts.addDanger({ + title: 'Timed out', + text: toMountPoint(e.getErrorMessage(this.application)), + }); + }, 30000); /** * Searches using the given `search` method. Overrides the `AbortSignal` with one that will abort @@ -229,7 +206,9 @@ export class SearchInterceptor { return this.runSearch(request, combinedSignal, options?.strategy).pipe( catchError((e: any) => { - return throwError(this.getSearchError(e, request, timeoutSignal, options?.abortSignal)); + return throwError( + this.handleSearchError(e, request, timeoutSignal, options?.abortSignal) + ); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); @@ -242,7 +221,7 @@ export class SearchInterceptor { /* * */ - public showError(e: Error | KbnError) { + public showError(e: Error) { if (e instanceof AbortError) return; if (e instanceof SearchTimeoutError) { diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 443b2b142184c..173baba5cab6f 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -111,7 +111,7 @@ export class SearchService implements Plugin { return { aggs: this.aggsService.start({ fieldFormats, uiSettings }), search, - showError: (e: any) => { + showError: (e: Error) => { this.searchInterceptor.showError(e); }, searchSource: { diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index d22e2df37f34d..5809eb4fc49c4 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -97,7 +97,7 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { if (id !== undefined) { this.deps.http.delete(`/internal/search/${strategy}/${id}`); } - return throwError(this.getSearchError(e, request, timeoutSignal, options?.abortSignal)); + return throwError(this.handleSearchError(e, request, timeoutSignal, options?.abortSignal)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); From 57b2a74ec4d088d88046829f86e7d63ad676b516 Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 22 Sep 2020 15:10:21 +0300 Subject: [PATCH 17/27] Code review --- .../public/search/errors/painless_error.tsx | 39 +++++++--- .../public/search/errors/timeout_error.tsx | 6 +- .../data/public/search/errors/types.ts | 73 +++++++++++++++++++ test/functional/apps/discover/_errors.ts | 3 +- 4 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 src/plugins/data/public/search/errors/types.ts diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx index 524272b87fac4..2fd053430e924 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -19,23 +19,25 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButton, EuiSpacer } from '@elastic/eui'; +import { EuiButton, EuiSpacer, EuiText, EuiCodeBlock } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { ApplicationStart } from 'kibana/public'; import { KbnError } from '../../../../kibana_utils/common'; +import { EsError, isEsError } from './types'; import { IEsSearchRequest } from '..'; export class PainlessError extends KbnError { - constructor(err: Error, request: IEsSearchRequest) { - const rootCause = getRootCause(err); - const [{ script }] = rootCause; + painlessStack: any; + constructor(err: EsError, request: IEsSearchRequest) { + const rootCause = getRootCause(err as EsError); super( i18n.translate('data.painlessError.painlessScriptedFieldErrorMessage', { defaultMessage: "Error with Painless scripted field '{script}'.", - values: { script }, + values: { script: rootCause?.script }, }) ); + this.painlessStack = rootCause?.script_stack.join('\n'); } public getErrorMessage(application: ApplicationStart) { @@ -49,24 +51,37 @@ export class PainlessError extends KbnError { <> {this.message} -
+ + + {this.painlessStack} + + -
+ ); } } -function getRootCause(err: Error) { - return (err as any).body?.attributes?.error?.root_cause; +function getFailedShards(err: EsError) { + const failedShards = + err.body?.attributes?.error?.failed_shards || + err.body?.attributes?.error?.caused_by?.failed_shards; + return failedShards ? failedShards[0] : undefined; +} + +function getRootCause(err: EsError) { + return getFailedShards(err)?.reason; } -export function isPainlessError(err: Error) { - const rootCause = getRootCause(err); +export function isPainlessError(err: Error | EsError) { + if (!isEsError(err)) return false; + + const rootCause = getRootCause(err as EsError); if (!rootCause) return false; - const [{ lang }] = rootCause; + const { lang } = rootCause; return lang === 'painless'; } diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index e1e1114c4f171..532c48a82b962 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButton, EuiSpacer } from '@elastic/eui'; +import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; import { ApplicationStart } from 'kibana/public'; import { KbnError } from '../../../../kibana_utils/common'; @@ -98,11 +98,11 @@ export class SearchTimeoutError extends KbnError { {actionText && ( <> -
+ this.onClick(application)} size="s"> {actionText} -
+ )} diff --git a/src/plugins/data/public/search/errors/types.ts b/src/plugins/data/public/search/errors/types.ts new file mode 100644 index 0000000000000..09cd54e0d481d --- /dev/null +++ b/src/plugins/data/public/search/errors/types.ts @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +interface FailedShard { + shard: number; + index: string; + node: string; + reason: { + type: string; + reason: string; + script_stack: string[]; + script: string; + lang: string; + position: { + offset: number; + start: number; + end: number; + }; + caused_by: { + type: string; + reason: string; + }; + }; +} + +export interface EsError { + body: { + statusCode: number; + error: string; + message: string; + attributes?: { + error?: { + root_cause?: [ + { + lang: string; + script: string; + } + ]; + type: string; + reason: string; + failed_shards: FailedShard[]; + caused_by: { + type: string; + reason: string; + phase: string; + grouped: boolean; + failed_shards: FailedShard[]; + script_stack: string[]; + }; + }; + }; + }; +} + +export function isEsError(e: any): e is EsError { + return !!e.body.attributes; +} diff --git a/test/functional/apps/discover/_errors.ts b/test/functional/apps/discover/_errors.ts index 521a05f29bdbd..7f1552b90668b 100644 --- a/test/functional/apps/discover/_errors.ts +++ b/test/functional/apps/discover/_errors.ts @@ -40,7 +40,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('invalid scripted field error', () => { it('is rendered', async () => { const toast = await toasts.getToastElement(1); - expect(toast).not.to.be(undefined); + const painlessStackTrace = await toast.findByTestSubject('painlessStackTrace'); + expect(painlessStackTrace).not.to.be(undefined); }); }); }); From 848eb0c8d52b002ea3e639be1d719b36af25bca2 Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 22 Sep 2020 15:14:03 +0300 Subject: [PATCH 18/27] doc --- ...plugins-data-public.painlesserror._constructor_.md | 4 ++-- ...kibana-plugin-plugins-data-public.painlesserror.md | 6 ++++++ ...plugins-data-public.painlesserror.painlessstack.md | 11 +++++++++++ src/plugins/data/public/public.api.md | 5 ++++- 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md index e2260f2e4f1d9..a57aa6c22479c 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md @@ -9,13 +9,13 @@ Constructs a new instance of the `PainlessError` class Signature: ```typescript -constructor(err: Error, request: IEsSearchRequest); +constructor(err: EsError, request: IEsSearchRequest); ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| err | Error | | +| err | EsError | | | request | IEsSearchRequest | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md index 02b51c7881b82..bb898115bb0d2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md @@ -16,6 +16,12 @@ export declare class PainlessError extends KbnError | --- | --- | --- | | [(constructor)(err, request)](./kibana-plugin-plugins-data-public.painlesserror._constructor_.md) | | Constructs a new instance of the PainlessError class | +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [painlessStack](./kibana-plugin-plugins-data-public.painlesserror.painlessstack.md) | | any | | + ## Methods | Method | Modifiers | Description | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md new file mode 100644 index 0000000000000..8f0019d9e1386 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) > [painlessStack](./kibana-plugin-plugins-data-public.painlesserror.painlessstack.md) + +## PainlessError.painlessStack property + +Signature: + +```typescript +painlessStack: any; +``` diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 2d9b689c08b36..b0299c000043d 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1588,9 +1588,12 @@ export interface OptionedValueProp { // // @public (undocumented) export class PainlessError extends KbnError { - constructor(err: Error, request: IEsSearchRequest); + // Warning: (ae-forgotten-export) The symbol "EsError" needs to be exported by the entry point index.d.ts + constructor(err: EsError, request: IEsSearchRequest); // (undocumented) getErrorMessage(application: ApplicationStart): JSX.Element; + // (undocumented) + painlessStack: any; } // Warning: (ae-forgotten-export) The symbol "parseEsInterval" needs to be exported by the entry point index.d.ts From 4e6f47d8351b1ad725fd5f8d5d2b4ff0d7efdc03 Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 22 Sep 2020 15:19:20 +0300 Subject: [PATCH 19/27] doc fix --- ...ibana-plugin-plugins-data-public.painlesserror.md | 2 +- ...lugins-data-public.painlesserror.painlessstack.md | 2 +- src/plugins/data/public/public.api.md | 2 +- .../data/public/search/errors/painless_error.tsx | 12 +++++++----- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md index bb898115bb0d2..306211cd60259 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md @@ -20,7 +20,7 @@ export declare class PainlessError extends KbnError | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [painlessStack](./kibana-plugin-plugins-data-public.painlesserror.painlessstack.md) | | any | | +| [painlessStack](./kibana-plugin-plugins-data-public.painlesserror.painlessstack.md) | | string | | ## Methods diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md index 8f0019d9e1386..a7e6920b2ae21 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md @@ -7,5 +7,5 @@ Signature: ```typescript -painlessStack: any; +painlessStack?: string; ``` diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index b0299c000043d..a91e000b4ab72 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1593,7 +1593,7 @@ export class PainlessError extends KbnError { // (undocumented) getErrorMessage(application: ApplicationStart): JSX.Element; // (undocumented) - painlessStack: any; + painlessStack?: string; } // Warning: (ae-forgotten-export) The symbol "parseEsInterval" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx index 2fd053430e924..0b8af6985b270 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -27,7 +27,7 @@ import { EsError, isEsError } from './types'; import { IEsSearchRequest } from '..'; export class PainlessError extends KbnError { - painlessStack: any; + painlessStack?: string; constructor(err: EsError, request: IEsSearchRequest) { const rootCause = getRootCause(err as EsError); @@ -37,7 +37,7 @@ export class PainlessError extends KbnError { values: { script: rootCause?.script }, }) ); - this.painlessStack = rootCause?.script_stack.join('\n'); + this.painlessStack = rootCause?.script_stack ? rootCause?.script_stack.join('\n') : undefined; } public getErrorMessage(application: ApplicationStart) { @@ -52,9 +52,11 @@ export class PainlessError extends KbnError { {this.message} - - {this.painlessStack} - + {this.painlessStack ? ( + + {this.painlessStack} + + ) : null} From 741e8cd907db1a5062d08b43396cf7d332cdeb05 Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 22 Sep 2020 15:48:48 +0300 Subject: [PATCH 20/27] visualization type fix --- .../public/application/utils/get_visualization_instance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts index 62cc26cb17778..c5cfa5a4c639b 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts @@ -45,7 +45,7 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( embeddableHandler.getOutput$().subscribe((output) => { if (output.error) { data.search.showError( - ((output.error as unknown) as ExpressionValueError).error?.original || output.error + ((output.error as unknown) as ExpressionValueError['error']).original || output.error ); } }); From 40c9411e07dd6cd74f3b4ac1f0b3e515b3642b22 Mon Sep 17 00:00:00 2001 From: Liza K Date: Tue, 22 Sep 2020 16:08:01 +0300 Subject: [PATCH 21/27] fix jest --- src/plugins/data/public/search/errors/types.ts | 2 +- .../data/public/search/search_interceptor.test.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/plugins/data/public/search/errors/types.ts b/src/plugins/data/public/search/errors/types.ts index 09cd54e0d481d..4182209eb68a5 100644 --- a/src/plugins/data/public/search/errors/types.ts +++ b/src/plugins/data/public/search/errors/types.ts @@ -69,5 +69,5 @@ export interface EsError { } export function isEsError(e: any): e is EsError { - return !!e.body.attributes; + return !!e.body?.attributes; } diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index 8ff80efb7adec..87f76cdc6f923 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -115,13 +115,21 @@ describe('SearchInterceptor', () => { } }); - test('Should throw Painless error on server error', async (done) => { + test('Should throw Painless error on server error with OSS format', async (done) => { const mockResponse: any = { result: 500, body: { attributes: { error: { - root_cause: [{ lang: 'painless' }], + failed_shards: [ + { + reason: { + lang: 'painless', + script_stack: ['a', 'b'], + reason: 'banana', + }, + }, + ], }, }, }, From 2c3d5943ceaac2ca261b8970f7d49f6166586db0 Mon Sep 17 00:00:00 2001 From: Liza K Date: Wed, 23 Sep 2020 10:35:04 +0300 Subject: [PATCH 22/27] Fix xpack functional test --- x-pack/test/functional/apps/discover/error_handling.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/discover/error_handling.ts b/x-pack/test/functional/apps/discover/error_handling.ts index 515e5e293ae28..a76ea8959fb61 100644 --- a/x-pack/test/functional/apps/discover/error_handling.ts +++ b/x-pack/test/functional/apps/discover/error_handling.ts @@ -23,11 +23,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async function () { await esArchiver.unload('invalid_scripted_field'); }); + // this is the same test as in OSS but it catches different error message issue in different licences describe('invalid scripted field error', () => { it('is rendered', async () => { - const isFetchErrorVisible = await testSubjects.exists('discoverFetchError'); - expect(isFetchErrorVisible).to.be(true); + const toast = await toasts.getToastElement(1); + const painlessStackTrace = await toast.findByTestSubject('painlessStackTrace'); + expect(painlessStackTrace).not.to.be(undefined); }); }); }); From 9c6972b2d8aa887620b404624472031c9ced6644 Mon Sep 17 00:00:00 2001 From: Liza K Date: Wed, 23 Sep 2020 13:53:22 +0300 Subject: [PATCH 23/27] fix xpack test --- x-pack/test/functional/apps/discover/error_handling.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/discover/error_handling.ts b/x-pack/test/functional/apps/discover/error_handling.ts index a76ea8959fb61..40aa8cd5c0606 100644 --- a/x-pack/test/functional/apps/discover/error_handling.ts +++ b/x-pack/test/functional/apps/discover/error_handling.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const testSubjects = getService('testSubjects'); + const toasts = getService('toasts'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); describe('errors', function describeIndexTests() { From ecd5ccecee3707cd7ae7cd64e78234c26e10eddd Mon Sep 17 00:00:00 2001 From: Liza K Date: Wed, 23 Sep 2020 22:21:28 +0300 Subject: [PATCH 24/27] code review --- .../public/search/errors/painless_error.tsx | 2 +- .../public/search/search_interceptor.test.ts | 2 +- .../data/public/search/search_interceptor.ts | 18 +++++++++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx index 0b8af6985b270..89287d1384dbe 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -33,7 +33,7 @@ export class PainlessError extends KbnError { super( i18n.translate('data.painlessError.painlessScriptedFieldErrorMessage', { - defaultMessage: "Error with Painless scripted field '{script}'.", + defaultMessage: "Error executing Painless script: '{script}'.", values: { script: rootCause?.script }, }) ); diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index 87f76cdc6f923..ade15adc1c3a3 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -91,7 +91,7 @@ describe('SearchInterceptor', () => { } }); - test('Search error should be throttled', async (done) => { + test('Search error should be debounced', async (done) => { const mockResponse: any = { result: 500, body: { diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index a209d7d2147cf..4cf4927136a36 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -17,7 +17,7 @@ * under the License. */ -import { get, trimEnd, throttle } from 'lodash'; +import { get, trimEnd, debounce } from 'lodash'; import { BehaviorSubject, throwError, timer, defer, from, Observable, NEVER } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; @@ -173,12 +173,16 @@ export class SearchInterceptor { * error notification per session. * @internal */ - private showTimeoutError = throttle((e: SearchTimeoutError) => { - this.deps.toasts.addDanger({ - title: 'Timed out', - text: toMountPoint(e.getErrorMessage(this.application)), - }); - }, 30000); + private showTimeoutError = debounce( + (e: SearchTimeoutError) => { + this.deps.toasts.addDanger({ + title: 'Timed out', + text: toMountPoint(e.getErrorMessage(this.application)), + }); + }, + 30000, + { leading: true, trailing: false } + ); /** * Searches using the given `search` method. Overrides the `AbortSignal` with one that will abort From 1fbba19c37c197ac382f7d07b5b55530df190b54 Mon Sep 17 00:00:00 2001 From: Liza K Date: Thu, 24 Sep 2020 10:19:55 +0300 Subject: [PATCH 25/27] delete debubg flag --- examples/search_examples/server/my_strategy.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts index a1116ddbd759b..2887ff6c9a53b 100644 --- a/examples/search_examples/server/my_strategy.ts +++ b/examples/search_examples/server/my_strategy.ts @@ -24,7 +24,6 @@ export const mySearchStrategyProvider = (data: PluginStart): ISearchStrategy => const es = data.search.getSearchStrategy('es'); return { search: async (context, request, options): Promise => { - request.debug = true; const esSearchRes = await es.search(context, request, options); return { ...esSearchRes, From 624f30fa1c9b91d96115345744766073f811b53c Mon Sep 17 00:00:00 2001 From: Liza K Date: Thu, 24 Sep 2020 20:58:54 +0300 Subject: [PATCH 26/27] Update texts by @gchaps --- .../data/public/search/errors/timeout_error.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index 532c48a82b962..56aecb42f5326 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -45,17 +45,17 @@ export class SearchTimeoutError extends KbnError { case TimeoutErrorMode.UPGRADE: return i18n.translate('data.search.upgradeLicense', { defaultMessage: - 'One or more queries timed out. With our free Basic tier, your queries never time out.', + 'Your query has timed out. With our free Basic tier, your queries never time out.', }); case TimeoutErrorMode.CONTACT: return i18n.translate('data.search.timeoutContactAdmin', { defaultMessage: - 'One or more queries timed out. Contact your system administrator to increase the run time.', + 'Your query has timed out. Contact your system administrator to increase the run time.', }); case TimeoutErrorMode.CHANGE: return i18n.translate('data.search.timeoutIncreaseSetting', { defaultMessage: - 'One or more queries timed out. Increase run time with the search timeout advanced setting.', + 'Your query has timed out. Increase run time with the search timeout advanced setting.', }); } } @@ -64,12 +64,12 @@ export class SearchTimeoutError extends KbnError { switch (this.mode) { case TimeoutErrorMode.UPGRADE: return i18n.translate('data.search.upgradeLicenseActionText', { - defaultMessage: 'Upgrade', + defaultMessage: 'Upgrade now', }); break; case TimeoutErrorMode.CHANGE: return i18n.translate('data.search.timeoutIncreaseSettingActionText', { - defaultMessage: 'Go to Advanced Settings', + defaultMessage: 'Edit setting', }); break; } From f3b5e6247d446e1813eac36962f3781c36e6a1d6 Mon Sep 17 00:00:00 2001 From: Liza K Date: Thu, 24 Sep 2020 22:51:43 +0300 Subject: [PATCH 27/27] docs and ts --- .../kibana-plugin-plugins-data-public.isearchstart.md | 2 +- ...lugin-plugins-data-public.isearchstart.showerror.md | 2 +- ...-plugins-data-public.painlesserror._constructor_.md | 4 ++-- ...-data-public.searchinterceptor.handlesearcherror.md | 4 ++-- ...gin-plugins-data-public.searchinterceptor.search.md | 4 ++-- src/plugins/data/public/public.api.md | 10 +++++----- .../components/alerts_table/actions.test.tsx | 1 + 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md index 39f10837c28d8..5defe4a647614 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md @@ -19,5 +19,5 @@ export interface ISearchStart | [aggs](./kibana-plugin-plugins-data-public.isearchstart.aggs.md) | AggsStart | agg config sub service [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) | | [search](./kibana-plugin-plugins-data-public.isearchstart.search.md) | ISearchGeneric | low level search [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | [searchSource](./kibana-plugin-plugins-data-public.isearchstart.searchsource.md) | ISearchStartSearchSource | high level search [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | -| [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) | (e: any) => void | | +| [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) | (e: Error) => void | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md index e4ba77a32c54c..fb14057d83d5c 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md @@ -7,5 +7,5 @@ Signature: ```typescript -showError: (e: any) => void; +showError: (e: Error) => void; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md index a57aa6c22479c..f8966572afbb6 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `PainlessError` class Signature: ```typescript -constructor(err: EsError, request: IEsSearchRequest); +constructor(err: EsError, request: IKibanaSearchRequest); ``` ## Parameters @@ -17,5 +17,5 @@ constructor(err: EsError, request: IEsSearchRequest); | Parameter | Type | Description | | --- | --- | --- | | err | EsError | | -| request | IEsSearchRequest | | +| request | IKibanaSearchRequest | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md index 1a849ecb1a03b..02db74b1a9e91 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md @@ -7,7 +7,7 @@ Signature: ```typescript -protected handleSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; +protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; ``` ## Parameters @@ -15,7 +15,7 @@ protected handleSearchError(e: any, request: IEsSearchRequest, timeoutSignal: Ab | Parameter | Type | Description | | --- | --- | --- | | e | any | | -| request | IEsSearchRequest | | +| request | IKibanaSearchRequest | | | timeoutSignal | AbortSignal | | | appAbortSignal | AbortSignal | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md index 7f19e2704ac85..672ff5065c456 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md @@ -9,14 +9,14 @@ Searches using the given `search` method. Overrides the `AbortSignal` with one t Signature: ```typescript -search(request: IEsSearchRequest, options?: ISearchOptions): Observable; +search(request: IKibanaSearchRequest, options?: ISearchOptions): Observable; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| request | IEsSearchRequest | | +| request | IKibanaSearchRequest | | | options | ISearchOptions | | Returns: diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 45520a914905a..48f2f8a06328b 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1465,7 +1465,7 @@ export interface ISearchStart { search: ISearchGeneric; searchSource: ISearchStartSearchSource; // (undocumented) - showError: (e: any) => void; + showError: (e: Error) => void; } // @public @@ -1638,7 +1638,7 @@ export interface OptionedValueProp { // @public (undocumented) export class PainlessError extends KbnError { // Warning: (ae-forgotten-export) The symbol "EsError" needs to be exported by the entry point index.d.ts - constructor(err: EsError, request: IEsSearchRequest); + constructor(err: EsError, request: IKibanaSearchRequest); // (undocumented) getErrorMessage(application: ApplicationStart): JSX.Element; // (undocumented) @@ -2036,12 +2036,12 @@ export class SearchInterceptor { // (undocumented) protected getTimeoutMode(): TimeoutErrorMode; // (undocumented) - protected handleSearchError(e: any, request: IEsSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; + protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) - protected runSearch(request: IEsSearchRequest, signal: AbortSignal, strategy?: string): Observable; - search(request: IEsSearchRequest, options?: ISearchOptions): Observable; + protected runSearch(request: IKibanaSearchRequest, signal: AbortSignal, strategy?: string): Observable; + search(request: IKibanaSearchRequest, options?: ISearchOptions): Observable; // @internal (undocumented) protected setupAbortSignal({ abortSignal, timeout, }: { abortSignal?: AbortSignal; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index f326d5ad54ef2..47da1e93cf004 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -44,6 +44,7 @@ describe('alert actions', () => { updateTimelineIsLoading = jest.fn() as jest.Mocked; searchStrategyClient = { aggs: {} as ISearchStart['aggs'], + showError: jest.fn(), search: jest.fn().mockResolvedValue({ data: mockTimelineDetails }), searchSource: {} as ISearchStart['searchSource'], };