({
+ type: 'localization',
isReady: () => true,
fetch: createCollectorFetch(helpers),
+ schema: {
+ locale: { type: 'keyword' },
+ integrities: { DYNAMIC_KEY: { type: 'text' } },
+ labelsCount: { type: 'long' },
+ },
});
usageCollection.registerCollector(collector);
diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
index 794168132abb2..e9fa2833c3db5 100644
--- a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
+++ b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
@@ -22,6 +22,7 @@ import classNames from 'classnames';
import 'brace/theme/textmate';
import 'brace/mode/markdown';
+import 'brace/mode/json';
import {
EuiBadge,
diff --git a/src/plugins/dashboard/public/application/actions/library_notification_popover.tsx b/src/plugins/dashboard/public/application/actions/library_notification_popover.tsx
index 8bc81b3296c3d..6ec5b0d637556 100644
--- a/src/plugins/dashboard/public/application/actions/library_notification_popover.tsx
+++ b/src/plugins/dashboard/public/application/actions/library_notification_popover.tsx
@@ -72,7 +72,7 @@ export function LibraryNotificationPopover({
{i18n.translate('dashboard.panel.libraryNotification.toolTip', {
defaultMessage:
- 'This panel is linked to a library item. Editing the panel might affect other dashboards.',
+ 'Editing this panel might affect other dashboards. To change to this panel only, unlink it from the library.',
})}
diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts
index 28646c092c01c..826e402b14682 100644
--- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts
+++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts
@@ -79,6 +79,33 @@ describe('getFormatWithAggs', () => {
expect(getFormat).toHaveBeenCalledTimes(1);
});
+ test('creates alternative format for range using the template parameter', () => {
+ const mapping = { id: 'range', params: { template: 'arrow_right' } };
+ const getFieldFormat = getFormatWithAggs(getFormat);
+ const format = getFieldFormat(mapping);
+
+ expect(format.convert({ gte: 1, lt: 20 })).toBe('1 → 20');
+ expect(getFormat).toHaveBeenCalledTimes(1);
+ });
+
+ test('handles Infinity values internally when no nestedFormatter is passed', () => {
+ const mapping = { id: 'range', params: { replaceInfinity: true } };
+ const getFieldFormat = getFormatWithAggs(getFormat);
+ const format = getFieldFormat(mapping);
+
+ expect(format.convert({ gte: -Infinity, lt: Infinity })).toBe('≥ −∞ and < +∞');
+ expect(getFormat).toHaveBeenCalledTimes(1);
+ });
+
+ test('lets Infinity values handling to nestedFormatter even when flag is on', () => {
+ const mapping = { id: 'range', params: { replaceInfinity: true, id: 'any' } };
+ const getFieldFormat = getFormatWithAggs(getFormat);
+ const format = getFieldFormat(mapping);
+
+ expect(format.convert({ gte: -Infinity, lt: Infinity })).toBe('≥ -Infinity and < Infinity');
+ expect(getFormat).toHaveBeenCalledTimes(1);
+ });
+
test('returns custom label for range if provided', () => {
const mapping = { id: 'range', params: {} };
const getFieldFormat = getFormatWithAggs(getFormat);
diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts
index a8134619fec0d..6b03dc5f70edc 100644
--- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts
+++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts
@@ -56,15 +56,35 @@ export function getFormatWithAggs(getFieldFormat: GetFieldFormat): GetFieldForma
id: nestedFormatter.id,
params: nestedFormatter.params,
});
+
const gte = '\u2265';
const lt = '\u003c';
+ let fromValue = format.convert(range.gte);
+ let toValue = format.convert(range.lt);
+ // In case of identity formatter and a specific flag, replace Infinity values by specific strings
+ if (params.replaceInfinity && nestedFormatter.id == null) {
+ const FROM_PLACEHOLDER = '\u2212\u221E';
+ const TO_PLACEHOLDER = '+\u221E';
+ fromValue = isFinite(range.gte) ? fromValue : FROM_PLACEHOLDER;
+ toValue = isFinite(range.lt) ? toValue : TO_PLACEHOLDER;
+ }
+
+ if (params.template === 'arrow_right') {
+ return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessageArrowRight', {
+ defaultMessage: '{from} → {to}',
+ values: {
+ from: fromValue,
+ to: toValue,
+ },
+ });
+ }
return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessage', {
defaultMessage: '{gte} {from} and {lt} {to}',
values: {
gte,
- from: format.convert(range.gte),
+ from: fromValue,
lt,
- to: format.convert(range.lt),
+ to: toValue,
},
});
});
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index d2439e3f1573c..81fa6d4ba20db 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -1598,13 +1598,13 @@ 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-forgotten-export) The symbol "EsError" 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 {
- // Warning: (ae-forgotten-export) The symbol "EsError" needs to be exported by the entry point index.d.ts
- constructor(err: EsError, request: IKibanaSearchRequest);
+export class PainlessError extends EsError {
+ // Warning: (ae-forgotten-export) The symbol "IEsError" needs to be exported by the entry point index.d.ts
+ constructor(err: IEsError, request: IKibanaSearchRequest);
// (undocumented)
getErrorMessage(application: ApplicationStart): JSX.Element;
// (undocumented)
@@ -2134,6 +2134,7 @@ export interface SearchSourceFields {
version?: boolean;
}
+// Warning: (ae-forgotten-export) The symbol "KbnError" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "SearchTimeoutError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
diff --git a/src/plugins/data/public/search/errors/es_error.tsx b/src/plugins/data/public/search/errors/es_error.tsx
new file mode 100644
index 0000000000000..53d00159b836b
--- /dev/null
+++ b/src/plugins/data/public/search/errors/es_error.tsx
@@ -0,0 +1,46 @@
+/*
+ * 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 { EuiCodeBlock, EuiSpacer } from '@elastic/eui';
+import { ApplicationStart } from 'kibana/public';
+import { KbnError } from '../../../../kibana_utils/common';
+import { IEsError } from './types';
+import { getRootCause } from './utils';
+
+export class EsError extends KbnError {
+ constructor(protected readonly err: IEsError) {
+ super('EsError');
+ }
+
+ public getErrorMessage(application: ApplicationStart) {
+ const rootCause = getRootCause(this.err)?.reason;
+
+ return (
+ <>
+
+ {rootCause ? (
+
+ {rootCause}
+
+ ) : null}
+ >
+ );
+ }
+}
diff --git a/src/plugins/data/public/search/errors/http_error.tsx b/src/plugins/data/public/search/errors/http_error.tsx
new file mode 100644
index 0000000000000..58ae3148804a2
--- /dev/null
+++ b/src/plugins/data/public/search/errors/http_error.tsx
@@ -0,0 +1,38 @@
+/*
+ * 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 { EuiCodeBlock, EuiSpacer } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+
+export function getHttpError(message: string) {
+ return (
+ <>
+ {i18n.translate('data.errors.fetchError', {
+ defaultMessage:
+ 'Check your network and proxy configuration. If the problem persists, contact your network administrator.',
+ })}
+
+
+
+ {message}
+
+ >
+ );
+}
diff --git a/src/plugins/data/public/search/errors/index.ts b/src/plugins/data/public/search/errors/index.ts
index 6082e758a8bad..01357d25334a3 100644
--- a/src/plugins/data/public/search/errors/index.ts
+++ b/src/plugins/data/public/search/errors/index.ts
@@ -17,5 +17,9 @@
* under the License.
*/
+export * from './es_error';
export * from './painless_error';
export * from './timeout_error';
+export * from './utils';
+export * from './types';
+export * from './http_error';
diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx
index 244f205469a2f..282a602d358c7 100644
--- a/src/plugins/data/public/search/errors/painless_error.tsx
+++ b/src/plugins/data/public/search/errors/painless_error.tsx
@@ -22,22 +22,15 @@ import { i18n } from '@kbn/i18n';
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 { IEsError, isEsError } from './types';
+import { EsError } from './es_error';
+import { getRootCause } from './utils';
import { IKibanaSearchRequest } from '..';
-export class PainlessError extends KbnError {
+export class PainlessError extends EsError {
painlessStack?: string;
- constructor(err: EsError, request: IKibanaSearchRequest) {
- const rootCause = getRootCause(err as EsError);
-
- super(
- i18n.translate('data.painlessError.painlessScriptedFieldErrorMessage', {
- defaultMessage: "Error executing Painless script: '{script}'.",
- values: { script: rootCause?.script },
- })
- );
- this.painlessStack = rootCause?.script_stack ? rootCause?.script_stack.join('\n') : undefined;
+ constructor(err: IEsError, request: IKibanaSearchRequest) {
+ super(err);
}
public getErrorMessage(application: ApplicationStart) {
@@ -47,14 +40,20 @@ export class PainlessError extends KbnError {
});
}
+ const rootCause = getRootCause(this.err);
+ const painlessStack = rootCause?.script_stack ? rootCause?.script_stack.join('\n') : undefined;
+
return (
<>
- {this.message}
+ {i18n.translate('data.painlessError.painlessScriptedFieldErrorMessage', {
+ defaultMessage: "Error executing Painless script: '{script}'.",
+ values: { script: rootCause?.script },
+ })}
- {this.painlessStack ? (
+ {painlessStack ? (
- {this.painlessStack}
+ {painlessStack}
) : null}
@@ -67,21 +66,10 @@ export class PainlessError extends KbnError {
}
}
-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 | EsError) {
+export function isPainlessError(err: Error | IEsError) {
if (!isEsError(err)) return false;
- const rootCause = getRootCause(err as EsError);
+ const rootCause = getRootCause(err as IEsError);
if (!rootCause) return false;
const { lang } = rootCause;
diff --git a/src/plugins/data/public/search/errors/types.ts b/src/plugins/data/public/search/errors/types.ts
index 4182209eb68a5..011af015318a7 100644
--- a/src/plugins/data/public/search/errors/types.ts
+++ b/src/plugins/data/public/search/errors/types.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-interface FailedShard {
+export interface FailedShard {
shard: number;
index: string;
node: string;
@@ -39,7 +39,7 @@ interface FailedShard {
};
}
-export interface EsError {
+export interface IEsError {
body: {
statusCode: number;
error: string;
@@ -68,6 +68,6 @@ export interface EsError {
};
}
-export function isEsError(e: any): e is EsError {
+export function isEsError(e: any): e is IEsError {
return !!e.body?.attributes;
}
diff --git a/src/plugins/data/public/search/errors/utils.ts b/src/plugins/data/public/search/errors/utils.ts
new file mode 100644
index 0000000000000..d07d9b05e91e9
--- /dev/null
+++ b/src/plugins/data/public/search/errors/utils.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 { IEsError } from './types';
+
+export function getFailedShards(err: IEsError) {
+ const failedShards =
+ err.body?.attributes?.error?.failed_shards ||
+ err.body?.attributes?.error?.caused_by?.failed_shards;
+ return failedShards ? failedShards[0] : undefined;
+}
+
+export function getRootCause(err: IEsError) {
+ return getFailedShards(err)?.reason;
+}
diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts
index 2275397ad41c8..087ca9e4f5c47 100644
--- a/src/plugins/data/public/search/search_interceptor.ts
+++ b/src/plugins/data/public/search/search_interceptor.ts
@@ -22,6 +22,7 @@ import { BehaviorSubject, throwError, timer, defer, from, Observable, NEVER } fr
import { catchError, finalize } from 'rxjs/operators';
import { PublicMethodsOf } from '@kbn/utility-types';
import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public';
+import { i18n } from '@kbn/i18n';
import {
getCombinedSignal,
AbortError,
@@ -32,7 +33,15 @@ import {
ISessionService,
} from '../../common';
import { SearchUsageCollector } from './collectors';
-import { SearchTimeoutError, PainlessError, isPainlessError, TimeoutErrorMode } from './errors';
+import {
+ SearchTimeoutError,
+ PainlessError,
+ isPainlessError,
+ TimeoutErrorMode,
+ isEsError,
+ EsError,
+ getHttpError,
+} from './errors';
import { toMountPoint } from '../../../kibana_react/public';
export interface SearchInterceptorDeps {
@@ -102,8 +111,12 @@ export class SearchInterceptor {
} else if (options?.abortSignal?.aborted) {
// In the case an application initiated abort, throw the existing AbortError.
return e;
- } else if (isPainlessError(e)) {
- return new PainlessError(e, request);
+ } else if (isEsError(e)) {
+ if (isPainlessError(e)) {
+ return new PainlessError(e, request);
+ } else {
+ return new EsError(e);
+ }
} else {
return e;
}
@@ -237,24 +250,28 @@ export class SearchInterceptor {
*
*/
public showError(e: Error) {
- if (e instanceof AbortError) return;
-
- if (e instanceof SearchTimeoutError) {
+ if (e instanceof AbortError || 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) {
+ } else if (e instanceof EsError) {
this.deps.toasts.addDanger({
- title: 'Search Error',
+ title: i18n.translate('data.search.esErrorTitle', {
+ defaultMessage: 'Cannot retrieve search results',
+ }),
text: toMountPoint(e.getErrorMessage(this.application)),
});
- return;
+ } else if (e.constructor.name === 'HttpFetchError') {
+ this.deps.toasts.addDanger({
+ title: i18n.translate('data.search.httpErrorTitle', {
+ defaultMessage: 'Cannot retrieve your data',
+ }),
+ text: toMountPoint(getHttpError(e.message)),
+ });
+ } else {
+ this.deps.toasts.addError(e, {
+ title: 'Search Error',
+ });
}
-
- this.deps.toasts.addError(e, {
- title: 'Search Error',
- });
}
}
diff --git a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx
index c17872028ea8d..bc9e2ed6a83ce 100644
--- a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx
+++ b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx
@@ -111,6 +111,7 @@ export default class QueryStringInputUI extends Component {
private persistedLog: PersistedLog | undefined;
private abortController?: AbortController;
+ private fetchIndexPatternsAbortController?: AbortController;
private services = this.props.kibana.services;
private componentIsUnmounting = false;
private queryBarInputDivRefInstance: RefObject = createRef();
@@ -119,7 +120,7 @@ export default class QueryStringInputUI extends Component {
return toUser(this.props.query.query);
};
- private fetchIndexPatterns = async () => {
+ private fetchIndexPatterns = debounce(async () => {
const stringPatterns = this.props.indexPatterns.filter(
(indexPattern) => typeof indexPattern === 'string'
) as string[];
@@ -127,16 +128,26 @@ export default class QueryStringInputUI extends Component {
(indexPattern) => typeof indexPattern !== 'string'
) as IIndexPattern[];
+ // abort the previous fetch to avoid overriding with outdated data
+ // issue https://github.com/elastic/kibana/issues/80831
+ if (this.fetchIndexPatternsAbortController) this.fetchIndexPatternsAbortController.abort();
+ this.fetchIndexPatternsAbortController = new AbortController();
+ const currentAbortController = this.fetchIndexPatternsAbortController;
+
const objectPatternsFromStrings = (await fetchIndexPatterns(
this.services.savedObjects!.client,
stringPatterns,
this.services.uiSettings!
)) as IIndexPattern[];
- this.setState({
- indexPatterns: [...objectPatterns, ...objectPatternsFromStrings],
- });
- };
+ if (!currentAbortController.signal.aborted) {
+ this.setState({
+ indexPatterns: [...objectPatterns, ...objectPatternsFromStrings],
+ });
+
+ this.updateSuggestions();
+ }
+ }, 200);
private getSuggestions = async () => {
if (!this.inputRef) {
@@ -506,7 +517,7 @@ export default class QueryStringInputUI extends Component {
}
this.initPersistedLog();
- this.fetchIndexPatterns().then(this.updateSuggestions);
+ this.fetchIndexPatterns();
this.handleListUpdate();
window.addEventListener('resize', this.handleAutoHeight);
@@ -525,7 +536,7 @@ export default class QueryStringInputUI extends Component {
this.initPersistedLog();
if (!isEqual(prevProps.indexPatterns, this.props.indexPatterns)) {
- this.fetchIndexPatterns().then(this.updateSuggestions);
+ this.fetchIndexPatterns();
} else if (!isEqual(prevProps.query, this.props.query)) {
this.updateSuggestions();
}
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index c001356e1979d..e5882a6cff809 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -26,6 +26,7 @@ import { ISearchSource } from 'src/plugins/data/public';
import { KibanaRequest } from 'src/core/server';
import { LegacyAPICaller } from 'kibana/server';
import { Logger } from 'kibana/server';
+import { Logger as Logger_2 } from 'src/core/server';
import { LoggerFactory } from '@kbn/logging';
import { Moment } from 'moment';
import moment from 'moment';
diff --git a/src/plugins/discover/public/application/angular/context_app.html b/src/plugins/discover/public/application/angular/context_app.html
index d609a497c4ba1..ef7bc09206176 100644
--- a/src/plugins/discover/public/application/angular/context_app.html
+++ b/src/plugins/discover/public/application/angular/context_app.html
@@ -10,7 +10,6 @@
>
-
-
-
-
-
-
-
+ predecessor-count="contextApp.state.queryParameters.predecessorCount"
+ predecessor-available="contextApp.state.rows.predecessors.length"
+ predecessor-status="contextApp.state.loadingStatus.predecessors.status"
+ on-change-predecessor-count="contextApp.actions.fetchGivenPredecessorRows"
+ successor-count="contextApp.state.queryParameters.successorCount"
+ successor-available="contextApp.state.rows.successors.length"
+ successor-status="contextApp.state.loadingStatus.successors.status"
+ on-change-successor-count="contextApp.actions.fetchGivenSuccessorRows"
+ >
diff --git a/src/plugins/discover/public/application/components/context_app/__snapshots__/context_app_legacy.test.tsx.snap b/src/plugins/discover/public/application/components/context_app/__snapshots__/context_app_legacy.test.tsx.snap
deleted file mode 100644
index 58305ee23cb21..0000000000000
--- a/src/plugins/discover/public/application/components/context_app/__snapshots__/context_app_legacy.test.tsx.snap
+++ /dev/null
@@ -1,741 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ContextAppLegacy test renders correctly 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`ContextAppLegacy test renders loading indicator 1`] = `
-
-
-
-
-
-
-
-
-
-
-
- Loading...
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx
index 16d8cd78004f9..25576a9072944 100644
--- a/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx
+++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx
@@ -23,6 +23,7 @@ import { IIndexPattern } from '../../../../../data/common/index_patterns';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { DocTableLegacy } from '../../angular/doc_table/create_doc_table_react';
import { findTestSubject } from '@elastic/eui/lib/test';
+import { ActionBar } from '../../angular/context/components/action_bar/action_bar';
describe('ContextAppLegacy test', () => {
const hit = {
@@ -48,28 +49,36 @@ describe('ContextAppLegacy test', () => {
columns: ['_source'],
filter: () => {},
hits: [hit],
- infiniteScroll: true,
sorting: ['order_date', 'desc'],
minimumVisibleRows: 5,
indexPattern,
status: 'loaded',
+ defaultStepSize: 5,
+ predecessorCount: 10,
+ successorCount: 10,
+ predecessorAvailable: 10,
+ successorAvailable: 10,
+ onChangePredecessorCount: jest.fn(),
+ onChangeSuccessorCount: jest.fn(),
+ predecessorStatus: 'loaded',
+ successorStatus: 'loaded',
};
it('renders correctly', () => {
const component = mountWithIntl();
- expect(component).toMatchSnapshot();
expect(component.find(DocTableLegacy).length).toBe(1);
const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator');
expect(loadingIndicator.length).toBe(0);
+ expect(component.find(ActionBar).length).toBe(2);
});
it('renders loading indicator', () => {
const props = { ...defaultProps };
props.status = 'loading';
const component = mountWithIntl();
- expect(component).toMatchSnapshot();
expect(component.find('DocTableLegacy').length).toBe(0);
const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator');
expect(loadingIndicator.length).toBe(1);
+ expect(component.find(ActionBar).length).toBe(2);
});
});
diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx
index ee8b2f590f71c..afb4a9a981e21 100644
--- a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx
+++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx
@@ -17,15 +17,15 @@
* under the License.
*/
import React from 'react';
-import { FormattedMessage } from '@kbn/i18n/react';
+import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { EuiPanel, EuiText } from '@elastic/eui';
-import { I18nProvider } from '@kbn/i18n/react';
import {
DocTableLegacy,
DocTableLegacyProps,
} from '../../angular/doc_table/create_doc_table_react';
import { IIndexPattern, IndexPatternField } from '../../../../../data/common/index_patterns';
import { LOADING_STATUS } from './constants';
+import { ActionBar, ActionBarProps } from '../../angular/context/components/action_bar/action_bar';
export interface ContextAppProps {
columns: string[];
@@ -35,15 +35,64 @@ export interface ContextAppProps {
minimumVisibleRows: number;
sorting: string[];
status: string;
+ defaultStepSize: number;
+ predecessorCount: number;
+ successorCount: number;
+ predecessorAvailable: number;
+ successorAvailable: number;
+ onChangePredecessorCount: (count: number) => void;
+ onChangeSuccessorCount: (count: number) => void;
+ predecessorStatus: string;
+ successorStatus: string;
+}
+
+const PREDECESSOR_TYPE = 'predecessors';
+const SUCCESSOR_TYPE = 'successors';
+
+function isLoading(status: string) {
+ return status !== LOADING_STATUS.LOADED && status !== LOADING_STATUS.FAILED;
}
export function ContextAppLegacy(renderProps: ContextAppProps) {
- const { hits, filter, sorting, status } = renderProps;
- const props = ({ ...renderProps } as unknown) as DocTableLegacyProps;
- props.rows = hits;
- props.onFilter = filter;
- props.sort = sorting.map((el) => [el]);
+ const status = renderProps.status;
const isLoaded = status === LOADING_STATUS.LOADED;
+
+ const actionBarProps = (type: string) => {
+ const {
+ defaultStepSize,
+ successorCount,
+ predecessorCount,
+ predecessorAvailable,
+ successorAvailable,
+ predecessorStatus,
+ successorStatus,
+ onChangePredecessorCount,
+ onChangeSuccessorCount,
+ } = renderProps;
+ const isPredecessorType = type === PREDECESSOR_TYPE;
+ return {
+ defaultStepSize,
+ docCount: isPredecessorType ? predecessorCount : successorCount,
+ docCountAvailable: isPredecessorType ? predecessorAvailable : successorAvailable,
+ onChangeCount: isPredecessorType ? onChangePredecessorCount : onChangeSuccessorCount,
+ isLoading: isPredecessorType ? isLoading(predecessorStatus) : isLoading(successorStatus),
+ type,
+ isDisabled: !isLoaded,
+ } as ActionBarProps;
+ };
+
+ const docTableProps = () => {
+ const { hits, filter, sorting, columns, indexPattern, minimumVisibleRows } = renderProps;
+ return {
+ columns,
+ indexPattern,
+ minimumVisibleRows,
+ rows: hits,
+ onFilter: filter,
+ sort: sorting.map((el) => [el]),
+ } as DocTableLegacyProps;
+ };
+
const loadingFeedback = () => {
if (status === LOADING_STATUS.UNINITIALIZED || status === LOADING_STATUS.LOADING) {
return (
@@ -59,17 +108,20 @@ export function ContextAppLegacy(renderProps: ContextAppProps) {
}
return null;
};
+
return (
+
{loadingFeedback()}
{isLoaded ? (
-
+
) : null}
+
);
diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts
index af94c5537da28..4a315be513a0d 100644
--- a/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts
+++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts
@@ -25,8 +25,16 @@ export function createContextAppLegacy(reactDirective: any) {
['indexPattern', { watchDepth: 'reference' }],
['sorting', { watchDepth: 'reference' }],
['columns', { watchDepth: 'collection' }],
- ['infiniteScroll', { watchDepth: 'reference' }],
['minimumVisibleRows', { watchDepth: 'reference' }],
['status', { watchDepth: 'reference' }],
+ ['defaultStepSize', { watchDepth: 'reference' }],
+ ['predecessorCount', { watchDepth: 'reference' }],
+ ['predecessorAvailable', { watchDepth: 'reference' }],
+ ['predecessorStatus', { watchDepth: 'reference' }],
+ ['onChangePredecessorCount', { watchDepth: 'reference' }],
+ ['successorCount', { watchDepth: 'reference' }],
+ ['successorAvailable', { watchDepth: 'reference' }],
+ ['successorStatus', { watchDepth: 'reference' }],
+ ['onChangeSuccessorCount', { watchDepth: 'reference' }],
]);
}
diff --git a/src/plugins/expressions/common/execution/execution.abortion.test.ts b/src/plugins/expressions/common/execution/execution.abortion.test.ts
index ecbf94eceae64..9fa7d92875b9a 100644
--- a/src/plugins/expressions/common/execution/execution.abortion.test.ts
+++ b/src/plugins/expressions/common/execution/execution.abortion.test.ts
@@ -36,8 +36,7 @@ const createExecution = (
const execution = new Execution({
executor,
ast: parseExpression(expression),
- context,
- debug,
+ params: { ...context, debug },
});
return execution;
};
diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts
index ff331d7c5ddaf..3bd29632f0902 100644
--- a/src/plugins/expressions/common/execution/execution.test.ts
+++ b/src/plugins/expressions/common/execution/execution.test.ts
@@ -38,8 +38,10 @@ const createExecution = (
const execution = new Execution({
executor,
ast: parseExpression(expression),
- context,
- debug,
+ params: {
+ ...context,
+ debug,
+ },
});
return execution;
};
@@ -68,7 +70,7 @@ describe('Execution', () => {
test('creates default ExecutionContext', () => {
const execution = createExecution();
expect(execution.context).toMatchObject({
- getInitialInput: expect.any(Function),
+ getSearchContext: expect.any(Function),
variables: expect.any(Object),
types: expect.any(Object),
});
@@ -143,6 +145,7 @@ describe('Execution', () => {
const execution = new Execution({
executor,
expression,
+ params: {},
});
expect(execution.expression).toBe(expression);
});
@@ -153,6 +156,7 @@ describe('Execution', () => {
const execution = new Execution({
ast: parseExpression(expression),
executor,
+ params: {},
});
expect(execution.expression).toBe(expression);
});
@@ -619,7 +623,7 @@ describe('Execution', () => {
const execution = new Execution({
executor,
ast: parseExpression('add val=1 | throws | add val=3'),
- debug: true,
+ params: { debug: true },
});
execution.start(0);
await execution.result;
@@ -637,7 +641,7 @@ describe('Execution', () => {
const execution = new Execution({
executor,
ast: parseExpression('add val=1 | throws | add val=3'),
- debug: true,
+ params: { debug: true },
});
execution.start(0);
await execution.result;
@@ -658,7 +662,7 @@ describe('Execution', () => {
const execution = new Execution({
executor,
ast: parseExpression('add val=1 | throws | add val=3'),
- debug: true,
+ params: { debug: true },
});
execution.start(0);
await execution.result;
diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts
index 69140453f486d..2bcf441b14203 100644
--- a/src/plugins/expressions/common/execution/execution.ts
+++ b/src/plugins/expressions/common/execution/execution.ts
@@ -19,7 +19,7 @@
import { i18n } from '@kbn/i18n';
import { keys, last, mapValues, reduce, zipObject } from 'lodash';
-import { Executor, ExpressionExecOptions } from '../executor';
+import { Executor } from '../executor';
import { createExecutionContainer, ExecutionContainer } from './container';
import { createError } from '../util';
import { Defer, now } from '../../../kibana_utils/common';
@@ -39,6 +39,7 @@ import { getType, ExpressionValue } from '../expression_types';
import { ArgumentType, ExpressionFunction } from '../expression_functions';
import { getByAlias } from '../util/get_by_alias';
import { ExecutionContract } from './execution_contract';
+import { ExpressionExecutionParams } from '../service';
const createAbortErrorValue = () =>
createError({
@@ -46,20 +47,11 @@ const createAbortErrorValue = () =>
name: 'AbortError',
});
-export interface ExecutionParams<
- ExtraContext extends Record = Record
-> {
+export interface ExecutionParams {
executor: Executor;
ast?: ExpressionAstExpression;
expression?: string;
- context?: ExtraContext;
-
- /**
- * Whether to execute expression in *debug mode*. In *debug mode* inputs and
- * outputs as well as all resolved arguments and time it took to execute each
- * function are saved and are available for introspection.
- */
- debug?: boolean;
+ params: ExpressionExecutionParams;
}
const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
@@ -68,11 +60,10 @@ const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
});
export class Execution<
- ExtraContext extends Record = Record,
Input = unknown,
Output = unknown,
- InspectorAdapters extends Adapters = ExtraContext['inspectorAdapters'] extends object
- ? ExtraContext['inspectorAdapters']
+ InspectorAdapters extends Adapters = ExpressionExecutionParams['inspectorAdapters'] extends object
+ ? ExpressionExecutionParams['inspectorAdapters']
: DefaultInspectorAdapters
> {
/**
@@ -92,7 +83,7 @@ export class Execution<
* Execution context - object that allows to do side-effects. Context is passed
* to every function.
*/
- public readonly context: ExecutionContext & ExtraContext;
+ public readonly context: ExecutionContext;
/**
* AbortController to cancel this Execution.
@@ -126,11 +117,10 @@ export class Execution<
* can return to other plugins for their consumption.
*/
public readonly contract: ExecutionContract<
- ExtraContext,
Input,
Output,
InspectorAdapters
- > = new ExecutionContract(this);
+ > = new ExecutionContract(this);
public readonly expression: string;
@@ -142,17 +132,17 @@ export class Execution<
return this.context.inspectorAdapters;
}
- constructor(public readonly params: ExecutionParams) {
- const { executor } = params;
+ constructor(public readonly execution: ExecutionParams) {
+ const { executor } = execution;
- if (!params.ast && !params.expression) {
+ if (!execution.ast && !execution.expression) {
throw new TypeError('Execution params should contain at least .ast or .expression key.');
- } else if (params.ast && params.expression) {
+ } else if (execution.ast && execution.expression) {
throw new TypeError('Execution params cannot contain both .ast and .expression key.');
}
- this.expression = params.expression || formatExpression(params.ast!);
- const ast = params.ast || parseExpression(this.expression);
+ this.expression = execution.expression || formatExpression(execution.ast!);
+ const ast = execution.ast || parseExpression(this.expression);
this.state = createExecutionContainer