From 89c103a8962d95b532ce3a7c71f0274b5b893e85 Mon Sep 17 00:00:00 2001
From: Dario Gieselaar
Date: Tue, 23 Feb 2021 10:31:48 +0100
Subject: [PATCH 01/50] [APM] Guard searches with range/additional queries
(#92112)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../__snapshots__/queries.test.ts.snap | 9 +++++
.../__fixtures__/versions_first_seen.json | 16 ++++-----
.../get_derived_service_annotations.ts | 18 +++++-----
.../lib/services/annotations/index.test.ts | 4 +--
.../get_services/get_legacy_data_status.ts | 12 ++++---
.../__snapshots__/queries.test.ts.snap | 34 +++++++++++++++++++
.../lib/transactions/breakdown/index.ts | 9 +++++
7 files changed, 78 insertions(+), 24 deletions(-)
diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
index 3e68831ee7cba7..0521ff7d9554d6 100644
--- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
@@ -34,6 +34,15 @@ Object {
},
},
},
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json b/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json
index c53b28c8bf5943..8a277797825a45 100644
--- a/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json
+++ b/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json
@@ -9,16 +9,16 @@
},
"hits": {
"total": {
- "value": 10000,
+ "value": 1,
"relation": "gte"
},
"max_score": null,
- "hits": []
- },
- "aggregations": {
- "first_seen": {
- "value": 1.5281138E12,
- "value_as_string": "2018-06-04T12:00:00.000Z"
- }
+ "hits": [
+ {
+ "_source": {
+ "@timestamp": "2018-06-04T12:00:00.000Z"
+ }
+ }
+ ]
}
}
diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts
index 67aa9d7fcd8707..25c42f403da2e5 100644
--- a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts
+++ b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { isNumber } from 'lodash';
+import { isFiniteNumber } from '../../../../common/utils/is_finite_number';
import { ESFilter } from '../../../../../../typings/elasticsearch';
import { Annotation, AnnotationType } from '../../../../common/annotations';
import {
@@ -85,25 +85,23 @@ export async function getDerivedServiceAnnotations({
],
},
body: {
- size: 0,
+ size: 1,
query: {
bool: {
filter: [...filter, { term: { [SERVICE_VERSION]: version } }],
},
},
- aggs: {
- first_seen: {
- min: {
- field: '@timestamp',
- },
- },
+ sort: {
+ '@timestamp': 'desc',
},
},
});
- const firstSeen = response.aggregations?.first_seen.value;
+ const firstSeen = new Date(
+ response.hits.hits[0]._source['@timestamp']
+ ).getTime();
- if (!isNumber(firstSeen)) {
+ if (!isFiniteNumber(firstSeen)) {
throw new Error(
'First seen for version was unexpectedly undefined or null.'
);
diff --git a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
index 206e04d49cc033..d86016ed9d505b 100644
--- a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
+++ b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
@@ -111,13 +111,13 @@ describe('getServiceAnnotations', () => {
{
id: '8.0.0',
text: '8.0.0',
- '@timestamp': 1.5281138e12,
+ '@timestamp': new Date('2018-06-04T12:00:00.000Z').getTime(),
type: 'version',
},
{
id: '7.5.0',
text: '7.5.0',
- '@timestamp': 1.5281138e12,
+ '@timestamp': new Date('2018-06-04T12:00:00.000Z').getTime(),
type: 'version',
},
]);
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts
index 87f3c0a5d1b389..a3adca0d306aa7 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts
@@ -5,15 +5,16 @@
* 2.0.
*/
+import { rangeQuery } from '../../../../common/utils/queries';
import { ProcessorEvent } from '../../../../common/processor_event';
import { OBSERVER_VERSION_MAJOR } from '../../../../common/elasticsearch_fieldnames';
-import { Setup } from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { withApmSpan } from '../../../utils/with_apm_span';
// returns true if 6.x data is found
-export async function getLegacyDataStatus(setup: Setup) {
+export async function getLegacyDataStatus(setup: Setup & SetupTimeRange) {
return withApmSpan('get_legacy_data_status', async () => {
- const { apmEventClient } = setup;
+ const { apmEventClient, start, end } = setup;
const params = {
terminateAfter: 1,
@@ -24,7 +25,10 @@ export async function getLegacyDataStatus(setup: Setup) {
size: 0,
query: {
bool: {
- filter: [{ range: { [OBSERVER_VERSION_MAJOR]: { lt: 7 } } }],
+ filter: [
+ { range: { [OBSERVER_VERSION_MAJOR]: { lt: 7 } } },
+ ...rangeQuery(start, end),
+ ],
},
},
},
diff --git a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
index 5d6a92a8741119..62050563497e94 100644
--- a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
@@ -164,6 +164,23 @@ Object {
"service.environment": "test",
},
},
+ Object {
+ "bool": Object {
+ "minimum_should_match": 1,
+ "should": Array [
+ Object {
+ "exists": Object {
+ "field": "span.self_time.sum.us",
+ },
+ },
+ Object {
+ "exists": Object {
+ "field": "transaction.breakdown.count",
+ },
+ },
+ ],
+ },
+ },
],
},
},
@@ -298,6 +315,23 @@ Object {
"service.environment": "test",
},
},
+ Object {
+ "bool": Object {
+ "minimum_should_match": 1,
+ "should": Array [
+ Object {
+ "exists": Object {
+ "field": "span.self_time.sum.us",
+ },
+ },
+ Object {
+ "exists": Object {
+ "field": "transaction.breakdown.count",
+ },
+ },
+ ],
+ },
+ },
Object {
"term": Object {
"transaction.name": "baz",
diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
index c3741184c807db..f1e202df312c27 100644
--- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
@@ -87,6 +87,15 @@ export function getTransactionBreakdown({
...rangeQuery(start, end),
...environmentQuery(environment),
...esFilter,
+ {
+ bool: {
+ should: [
+ { exists: { field: SPAN_SELF_TIME_SUM } },
+ { exists: { field: TRANSACTION_BREAKDOWN_COUNT } },
+ ],
+ minimum_should_match: 1,
+ },
+ },
];
if (transactionName) {
From 2ce344019a8679c36fa2e28ffeec135a3304e279 Mon Sep 17 00:00:00 2001
From: Shahzad
Date: Tue, 23 Feb 2021 10:37:56 +0100
Subject: [PATCH 02/50] Add text search mode in global kuery bar (#91814)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
...a-public.querystringinputprops.icontype.md | 2 +-
...ublic.querystringinputprops.isclearable.md | 11 +++
...ugins-data-public.querystringinputprops.md | 5 +-
...public.querystringinputprops.nonkqlmode.md | 11 +++
...uerystringinputprops.nonkqlmodehelptext.md | 11 +++
...na-plugin-plugins-data-public.searchbar.md | 4 +-
src/plugins/data/public/public.api.md | 13 ++-
.../language_switcher.test.tsx | 93 ++++++++++++++++++-
.../query_string_input/language_switcher.tsx | 47 ++++++++--
.../query_string_input/query_bar_top_row.tsx | 11 +++
.../query_string_input/query_string_input.tsx | 31 ++++++-
.../ui/search_bar/create_search_bar.tsx | 6 ++
.../data/public/ui/search_bar/search_bar.tsx | 12 +++
.../translations/translations/ja-JP.json | 1 -
.../translations/translations/zh-CN.json | 1 -
15 files changed, 236 insertions(+), 23 deletions(-)
create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md
create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md
create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.icontype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.icontype.md
index 4b1c5b84557e7b..3de186cf775149 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.icontype.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.icontype.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-iconType?: string;
+iconType?: EuiIconProps['type'];
```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md
new file mode 100644
index 00000000000000..738041c2d57506
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [isClearable](./kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md)
+
+## QueryStringInputProps.isClearable property
+
+Signature:
+
+```typescript
+isClearable?: boolean;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md
index 90c604d131800f..a3089cc5d4da94 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md
@@ -19,10 +19,13 @@ export interface QueryStringInputProps
| [dataTestSubj](./kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md) | string
| |
| [disableAutoFocus](./kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md) | boolean
| |
| [disableLanguageSwitcher](./kibana-plugin-plugins-data-public.querystringinputprops.disablelanguageswitcher.md) | boolean
| |
-| [iconType](./kibana-plugin-plugins-data-public.querystringinputprops.icontype.md) | string
| |
+| [iconType](./kibana-plugin-plugins-data-public.querystringinputprops.icontype.md) | EuiIconProps['type']
| |
| [indexPatterns](./kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md) | Array<IIndexPattern | string>
| |
+| [isClearable](./kibana-plugin-plugins-data-public.querystringinputprops.isclearable.md) | boolean
| |
| [isInvalid](./kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md) | boolean
| |
| [languageSwitcherPopoverAnchorPosition](./kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md) | PopoverAnchorPosition
| |
+| [nonKqlMode](./kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md) | 'lucene' | 'text'
| |
+| [nonKqlModeHelpText](./kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md) | string
| |
| [onBlur](./kibana-plugin-plugins-data-public.querystringinputprops.onblur.md) | () => void
| |
| [onChange](./kibana-plugin-plugins-data-public.querystringinputprops.onchange.md) | (query: Query) => void
| |
| [onChangeQueryInputFocus](./kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md) | (isFocused: boolean) => void
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md
new file mode 100644
index 00000000000000..809bf0bb56b289
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [nonKqlMode](./kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmode.md)
+
+## QueryStringInputProps.nonKqlMode property
+
+Signature:
+
+```typescript
+nonKqlMode?: 'lucene' | 'text';
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md
new file mode 100644
index 00000000000000..8caf492bebeb18
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [nonKqlModeHelpText](./kibana-plugin-plugins-data-public.querystringinputprops.nonkqlmodehelptext.md)
+
+## QueryStringInputProps.nonKqlModeHelpText property
+
+Signature:
+
+```typescript
+nonKqlModeHelpText?: string;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md
index 786ac4f9d61a90..193a2e5a24f3f4 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md
@@ -7,7 +7,7 @@
Signature:
```typescript
-SearchBar: React.ComponentClass, "query" | "isLoading" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
- WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>;
+SearchBar: React.ComponentClass, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "isClearable" | "refreshInterval" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
+ WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>;
}
```
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 664f4e16fa6d7d..63b766a82e8e62 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -35,6 +35,7 @@ import { EuiComboBoxProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
import { EuiFlyoutSize } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
+import { EuiIconProps } from '@elastic/eui';
import { EventEmitter } from 'events';
import { ExecutionContext } from 'src/plugins/expressions/common';
import { ExpressionAstExpression } from 'src/plugins/expressions/common';
@@ -1998,14 +1999,20 @@ export interface QueryStringInputProps {
// (undocumented)
disableLanguageSwitcher?: boolean;
// (undocumented)
- iconType?: string;
+ iconType?: EuiIconProps['type'];
// (undocumented)
indexPatterns: Array;
// (undocumented)
+ isClearable?: boolean;
+ // (undocumented)
isInvalid?: boolean;
// (undocumented)
languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition;
// (undocumented)
+ nonKqlMode?: 'lucene' | 'text';
+ // (undocumented)
+ nonKqlModeHelpText?: string;
+ // (undocumented)
onBlur?: () => void;
// (undocumented)
onChange?: (query: Query) => void;
@@ -2254,8 +2261,8 @@ export const SEARCH_SESSIONS_MANAGEMENT_ID = "search_sessions";
// Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const SearchBar: React.ComponentClass, "query" | "isLoading" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
- WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>;
+export const SearchBar: React.ComponentClass, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "isClearable" | "refreshInterval" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
+ WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>;
};
// Warning: (ae-forgotten-export) The symbol "SearchBarOwnProps" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/data/public/ui/query_string_input/language_switcher.test.tsx b/src/plugins/data/public/ui/query_string_input/language_switcher.test.tsx
index cc048b5ed9cacf..acbd48718d92ea 100644
--- a/src/plugins/data/public/ui/query_string_input/language_switcher.test.tsx
+++ b/src/plugins/data/public/ui/query_string_input/language_switcher.test.tsx
@@ -7,15 +7,15 @@
*/
import React from 'react';
-import { QueryLanguageSwitcher } from './language_switcher';
+import { QueryLanguageSwitcher, QueryLanguageSwitcherProps } from './language_switcher';
import { KibanaContextProvider } from 'src/plugins/kibana_react/public';
import { coreMock } from '../../../../../core/public/mocks';
import { mountWithIntl } from '@kbn/test/jest';
-import { EuiButtonEmpty, EuiPopover } from '@elastic/eui';
+import { EuiButtonEmpty, EuiIcon, EuiPopover } from '@elastic/eui';
const startMock = coreMock.createStart();
describe('LanguageSwitcher', () => {
- function wrapInContext(testProps: any) {
+ function wrapInContext(testProps: QueryLanguageSwitcherProps) {
const services = {
uiSettings: startMock.uiSettings,
docLinks: startMock.docLinks,
@@ -55,4 +55,91 @@ describe('LanguageSwitcher', () => {
expect(component.find(EuiPopover).prop('isOpen')).toBe(true);
expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeTruthy();
});
+
+ it('should toggle off if language is text', () => {
+ const component = mountWithIntl(
+ wrapInContext({
+ language: 'text',
+ onSelectLanguage: () => {
+ return;
+ },
+ })
+ );
+ component.find(EuiButtonEmpty).simulate('click');
+ expect(component.find(EuiPopover).prop('isOpen')).toBe(true);
+ expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeFalsy();
+ });
+ it('it set language on nonKql mode text', () => {
+ const onSelectLanguage = jest.fn();
+
+ const component = mountWithIntl(
+ wrapInContext({
+ language: 'kuery',
+ nonKqlMode: 'text',
+ onSelectLanguage,
+ })
+ );
+ component.find(EuiButtonEmpty).simulate('click');
+ expect(component.find(EuiPopover).prop('isOpen')).toBe(true);
+ expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeTruthy();
+
+ component.find('[data-test-subj="languageToggle"]').at(1).simulate('click');
+
+ expect(onSelectLanguage).toHaveBeenCalledWith('text');
+ });
+ it('it set language on nonKql mode lucene', () => {
+ const onSelectLanguage = jest.fn();
+
+ const component = mountWithIntl(
+ wrapInContext({
+ language: 'kuery',
+ nonKqlMode: 'lucene',
+ onSelectLanguage,
+ })
+ );
+ component.find(EuiButtonEmpty).simulate('click');
+ component.find('[data-test-subj="languageToggle"]').at(1).simulate('click');
+
+ expect(onSelectLanguage).toHaveBeenCalledWith('lucene');
+ });
+
+ it('it set language on kuery mode with nonKqlMode text', () => {
+ const onSelectLanguage = jest.fn();
+
+ const component = mountWithIntl(
+ wrapInContext({
+ language: 'text',
+ nonKqlMode: 'text',
+ onSelectLanguage,
+ })
+ );
+
+ expect(component.find(EuiIcon).prop('type')).toBe('boxesVertical');
+
+ component.find(EuiButtonEmpty).simulate('click');
+ component.find('[data-test-subj="languageToggle"]').at(1).simulate('click');
+
+ expect(onSelectLanguage).toHaveBeenCalledWith('kuery');
+ });
+
+ it('it set language on kuery mode with nonKqlMode lucene', () => {
+ const onSelectLanguage = jest.fn();
+
+ const component = mountWithIntl(
+ wrapInContext({
+ language: 'lucene',
+ nonKqlMode: 'lucene',
+ onSelectLanguage,
+ })
+ );
+
+ expect(component.find('[data-test-subj="switchQueryLanguageButton"]').at(0).text()).toBe(
+ 'Lucene'
+ );
+
+ component.find(EuiButtonEmpty).simulate('click');
+ component.find('[data-test-subj="languageToggle"]').at(1).simulate('click');
+
+ expect(onSelectLanguage).toHaveBeenCalledWith('kuery');
+ });
});
diff --git a/src/plugins/data/public/ui/query_string_input/language_switcher.tsx b/src/plugins/data/public/ui/query_string_input/language_switcher.tsx
index c4afc2e05808a4..0c3659c079913c 100644
--- a/src/plugins/data/public/ui/query_string_input/language_switcher.tsx
+++ b/src/plugins/data/public/ui/query_string_input/language_switcher.tsx
@@ -10,6 +10,7 @@ import {
EuiButtonEmpty,
EuiForm,
EuiFormRow,
+ EuiIcon,
EuiLink,
EuiPopover,
EuiPopoverTitle,
@@ -19,16 +20,25 @@ import {
PopoverAnchorPosition,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
+import { i18n } from '@kbn/i18n';
import React, { useState } from 'react';
import { useKibana } from '../../../../kibana_react/public';
-interface Props {
+export interface QueryLanguageSwitcherProps {
language: string;
onSelectLanguage: (newLanguage: string) => void;
anchorPosition?: PopoverAnchorPosition;
+ nonKqlMode?: 'lucene' | 'text';
+ nonKqlModeHelpText?: string;
}
-export function QueryLanguageSwitcher(props: Props) {
+export function QueryLanguageSwitcher({
+ language,
+ anchorPosition,
+ onSelectLanguage,
+ nonKqlMode = 'lucene',
+ nonKqlModeHelpText,
+}: QueryLanguageSwitcherProps) {
const kibana = useKibana();
const kueryQuerySyntaxDocs = kibana.services.docLinks!.links.query.kueryQuerySyntax;
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
@@ -38,6 +48,7 @@ export function QueryLanguageSwitcher(props: Props) {
const kqlLabel = (
);
+
const kqlFullName = (
);
+ const kqlModeTitle = i18n.translate('data.query.queryBar.languageSwitcher.toText', {
+ defaultMessage: 'Switch to Kibana Query Language for search',
+ });
+
const button = (
- {props.language === 'lucene' ? luceneLabel : kqlLabel}
+ {language === 'kuery' ? (
+ kqlLabel
+ ) : nonKqlMode === 'lucene' ? (
+ luceneLabel
+ ) : (
+
+ )}
);
@@ -60,7 +81,7 @@ export function QueryLanguageSwitcher(props: Props) {
setIsPopoverOpen(false)}
@@ -79,13 +100,21 @@ export function QueryLanguageSwitcher(props: Props) {
id="data.query.queryBar.syntaxOptionsDescription"
defaultMessage="The {docsLink} (KQL) offers a simplified query
syntax and support for scripted fields. KQL also provides autocomplete if you have
- a Basic license or above. If you turn off KQL, Kibana uses Lucene."
+ a Basic license or above. If you turn off KQL, {nonKqlModeHelpText}"
values={{
docsLink: (
{kqlFullName}
),
+ nonKqlModeHelpText:
+ nonKqlModeHelpText ||
+ i18n.translate(
+ 'data.query.queryBar.syntaxOptionsDescription.nonKqlModeHelpText',
+ {
+ defaultMessage: 'Kibana uses Lucene.',
+ }
+ ),
}}
/>
@@ -99,16 +128,16 @@ export function QueryLanguageSwitcher(props: Props) {
id="queryEnhancementOptIn"
name="popswitch"
label={
- props.language === 'kuery' ? (
+ language === 'kuery' ? (
) : (
)
}
- checked={props.language === 'kuery'}
+ checked={language === 'kuery'}
onChange={() => {
- const newLanguage = props.language === 'lucene' ? 'kuery' : 'lucene';
- props.onSelectLanguage(newLanguage);
+ const newLanguage = language === 'kuery' ? nonKqlMode : 'kuery';
+ onSelectLanguage(newLanguage);
}}
data-test-subj="languageToggle"
/>
diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx
index 61e894410437ec..4142bccc09f406 100644
--- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx
+++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx
@@ -19,6 +19,7 @@ import {
EuiSuperDatePicker,
EuiFieldText,
prettyDuration,
+ EuiIconProps,
} from '@elastic/eui';
// @ts-ignore
import { EuiSuperUpdateButton, OnRefreshProps } from '@elastic/eui';
@@ -57,6 +58,11 @@ export interface QueryBarTopRowProps {
isDirty: boolean;
timeHistory?: TimeHistoryContract;
indicateNoData?: boolean;
+ iconType?: EuiIconProps['type'];
+ placeholder?: string;
+ isClearable?: boolean;
+ nonKqlMode?: 'lucene' | 'text';
+ nonKqlModeHelpText?: string;
}
// Needed for React.lazy
@@ -185,6 +191,11 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) {
onSubmit={onInputSubmit}
persistedLog={persistedLog}
dataTestSubj={props.dataTestSubj}
+ placeholder={props.placeholder}
+ isClearable={props.isClearable}
+ iconType={props.iconType}
+ nonKqlMode={props.nonKqlMode}
+ nonKqlModeHelpText={props.nonKqlModeHelpText}
/>
);
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 aa2fc9e6314361..42ebfed42eb253 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
@@ -21,6 +21,7 @@ import {
htmlIdGenerator,
EuiPortal,
EuiIcon,
+ EuiIconProps,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -56,7 +57,15 @@ export interface QueryStringInputProps {
size?: SuggestionsListSize;
className?: string;
isInvalid?: boolean;
- iconType?: string;
+ isClearable?: boolean;
+ iconType?: EuiIconProps['type'];
+
+ /**
+ * @param nonKqlMode by default if language switch is enabled, user can switch between kql and lucene syntax mode
+ * this params add another option text, which is just a simple keyword search mode, the way a simple search box works
+ */
+ nonKqlMode?: 'lucene' | 'text';
+ nonKqlModeHelpText?: string;
}
interface Props extends QueryStringInputProps {
@@ -698,10 +707,26 @@ export default class QueryStringInputUI extends Component {
) : null}
+ {this.props.isClearable && this.props.query.query ? (
+
+ {
+ this.onQueryStringChange('');
+ }}
+ >
+
+
+
+ ) : null}
{
language={this.props.query.language}
anchorPosition={this.props.languageSwitcherPopoverAnchorPosition}
onSelectLanguage={this.onSelectLanguage}
+ nonKqlMode={this.props.nonKqlMode}
+ nonKqlModeHelpText={this.props.nonKqlModeHelpText}
/>
)}
diff --git a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx
index 4aba59442d204f..7b7538441c38f5 100644
--- a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx
+++ b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx
@@ -189,6 +189,12 @@ export function createSearchBar({ core, storage, data, usageCollection }: Statef
onClearSavedQuery={defaultOnClearSavedQuery(props, clearSavedQuery)}
onSavedQueryUpdated={defaultOnSavedQueryUpdated(props, setSavedQuery)}
onSaved={defaultOnSavedQueryUpdated(props, setSavedQuery)}
+ iconType={props.iconType}
+ nonKqlMode={props.nonKqlMode}
+ nonKqlModeHelpText={props.nonKqlModeHelpText}
+ customSubmitButton={props.customSubmitButton}
+ isClearable={props.isClearable}
+ placeholder={props.placeholder}
{...overrideDefaultBehaviors(props)}
/>
diff --git a/src/plugins/data/public/ui/search_bar/search_bar.tsx b/src/plugins/data/public/ui/search_bar/search_bar.tsx
index fe8165a12714a6..9c84b9b73d5e50 100644
--- a/src/plugins/data/public/ui/search_bar/search_bar.tsx
+++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx
@@ -12,6 +12,7 @@ import classNames from 'classnames';
import React, { Component } from 'react';
import ResizeObserver from 'resize-observer-polyfill';
import { get, isEqual } from 'lodash';
+import { EuiIconProps } from '@elastic/eui';
import { METRIC_TYPE } from '@kbn/analytics';
import { withKibana, KibanaReactContextValue } from '../../../../kibana_react/public';
@@ -68,6 +69,12 @@ export interface SearchBarOwnProps {
onRefresh?: (payload: { dateRange: TimeRange }) => void;
indicateNoData?: boolean;
+
+ placeholder?: string;
+ isClearable?: boolean;
+ iconType?: EuiIconProps['type'];
+ nonKqlMode?: 'lucene' | 'text';
+ nonKqlModeHelpText?: string;
}
export type SearchBarProps = SearchBarOwnProps & SearchBarInjectedDeps;
@@ -399,6 +406,11 @@ class SearchBarUI extends Component {
}
dataTestSubj={this.props.dataTestSubj}
indicateNoData={this.props.indicateNoData}
+ placeholder={this.props.placeholder}
+ isClearable={this.props.isClearable}
+ iconType={this.props.iconType}
+ nonKqlMode={this.props.nonKqlMode}
+ nonKqlModeHelpText={this.props.nonKqlModeHelpText}
/>
);
}
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index de21881a4bade6..8b3912bb93595e 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -906,7 +906,6 @@
"data.query.queryBar.luceneSyntaxWarningTitle": "Lucene 構文警告",
"data.query.queryBar.searchInputAriaLabel": "{pageType} ページの検索とフィルタリングを行うには入力を開始してください",
"data.query.queryBar.searchInputPlaceholder": "検索",
- "data.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) は、シンプルなクエリ構文とスクリプトフィールドのサポートを提供します。また、KQL はベーシックライセンス以上をご利用の場合、自動入力も提供します。KQL をオフにすると、Kibana は Lucene を使用します。",
"data.query.queryBar.syntaxOptionsDescription.docsLinkText": "こちら",
"data.query.queryBar.syntaxOptionsTitle": "構文オプション",
"data.search.aggs.aggGroups.bucketsText": "バケット",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index e7abb69b25a221..2561d76bca1aa7 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -906,7 +906,6 @@
"data.query.queryBar.luceneSyntaxWarningTitle": "Lucene 语法警告",
"data.query.queryBar.searchInputAriaLabel": "开始键入内容,以搜索并筛选 {pageType} 页面",
"data.query.queryBar.searchInputPlaceholder": "搜索",
- "data.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) 提供简化查询语法并支持脚本字段。如果您具有基本级或更高级别的许可,KQL 还提供自动填充功能。如果关闭 KQL,Kibana 将使用 Lucene。",
"data.query.queryBar.syntaxOptionsDescription.docsLinkText": "此处",
"data.query.queryBar.syntaxOptionsTitle": "语法选项",
"data.search.aggs.aggGroups.bucketsText": "存储桶",
From ee811105167f501aa30d84baf81bcee52f30b3d2 Mon Sep 17 00:00:00 2001
From: Gidi Meir Morris
Date: Tue, 23 Feb 2021 10:00:17 +0000
Subject: [PATCH 03/50] renamed reloadAlerts to onSave wit hdeprecation
(#91997)
The `reloadAlerts` properties in `AlertAdd` and `AlertEdit` are confusing property names.
In this PR we deprecate those and replace them with `onSave`.
---
x-pack/plugins/ml/public/alerting/ml_alerting_flyout.tsx | 2 +-
x-pack/plugins/triggers_actions_ui/README.md | 6 +++---
.../sections/alert_details/components/alert_details.tsx | 2 +-
.../application/sections/alert_form/alert_add.test.tsx | 2 +-
.../public/application/sections/alert_form/alert_add.tsx | 8 ++++++--
.../application/sections/alert_form/alert_edit.test.tsx | 2 +-
.../public/application/sections/alert_form/alert_edit.tsx | 8 ++++++--
.../sections/alerts_list/components/alerts_list.tsx | 2 +-
8 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/x-pack/plugins/ml/public/alerting/ml_alerting_flyout.tsx b/x-pack/plugins/ml/public/alerting/ml_alerting_flyout.tsx
index ba573fe42f5f23..989cecf1da19c2 100644
--- a/x-pack/plugins/ml/public/alerting/ml_alerting_flyout.tsx
+++ b/x-pack/plugins/ml/public/alerting/ml_alerting_flyout.tsx
@@ -41,7 +41,7 @@ export const MlAnomalyAlertFlyout: FC = ({
onCloseFlyout();
},
// Callback for successful save
- reloadAlerts: async () => {
+ onSave: async () => {
if (onSave) {
onSave();
}
diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md
index 37f605b0d500fb..61cad0fc7fd7b5 100644
--- a/x-pack/plugins/triggers_actions_ui/README.md
+++ b/x-pack/plugins/triggers_actions_ui/README.md
@@ -833,7 +833,7 @@ interface AlertAddProps {
alertTypeId?: string;
canChangeTrigger?: boolean;
initialValues?: Partial;
- reloadAlerts?: () => Promise;
+ onSave?: () => Promise;
metadata?: MetaData;
}
```
@@ -845,7 +845,7 @@ interface AlertAddProps {
|setAddFlyoutVisibility|Function for changing visibility state of the Create Alert flyout.|
|alertTypeId|Optional property to preselect alert type.|
|canChangeTrigger|Optional property, that hides change alert type possibility.|
-|reloadAlerts|Optional function, which will be executed if alert was saved sucsessfuly.|
+|onSave|Optional function, which will be executed if alert was saved sucsessfuly.|
|initialValues|Default values for Alert properties.|
|metadata|Optional generic property, which allows to define component specific metadata. This metadata can be used for passing down preloaded data for Alert type expression component.|
@@ -1470,7 +1470,7 @@ interface ActionAccordionFormProps {
|Property|Description|
|---|---|
-|reloadAlerts|Optional function, which will be executed if alert was saved sucsessfuly.|
+|onSave|Optional function, which will be executed if alert was saved sucsessfuly.|
|http|HttpSetup needed for executing API calls.|
|alertTypeRegistry|Registry for alert types.|
|actionTypeRegistry|Registry for action types.|
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx
index d7001aade286af..4a863fdad67144 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx
@@ -158,7 +158,7 @@ export const AlertDetails: React.FunctionComponent = ({
}}
actionTypeRegistry={actionTypeRegistry}
alertTypeRegistry={alertTypeRegistry}
- reloadAlerts={setAlert}
+ onSave={setAlert}
/>
)}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
index 12bbcb1f538104..bf94b0a2f3f0ad 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
@@ -156,7 +156,7 @@ describe('alert_add', () => {
consumer={ALERTS_FEATURE_ID}
onClose={onClose}
initialValues={initialValues}
- reloadAlerts={() => {
+ onSave={() => {
return new Promise(() => {});
}}
actionTypeRegistry={actionTypeRegistry}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx
index 09ff13c9855c2b..90fb96b81a7754 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx
@@ -39,7 +39,9 @@ export interface AlertAddProps> {
alertTypeId?: string;
canChangeTrigger?: boolean;
initialValues?: Partial;
+ /** @deprecated use `onSave` as a callback after an alert is saved*/
reloadAlerts?: () => Promise;
+ onSave?: () => Promise;
metadata?: MetaData;
}
@@ -52,8 +54,10 @@ const AlertAdd = ({
alertTypeId,
initialValues,
reloadAlerts,
+ onSave,
metadata,
}: AlertAddProps) => {
+ const onSaveHandler = onSave ?? reloadAlerts;
const initialAlert: InitialAlert = useMemo(
() => ({
params: {},
@@ -129,8 +133,8 @@ const AlertAdd = ({
setIsSaving(false);
if (savedAlert) {
onClose(AlertFlyoutCloseReason.SAVED);
- if (reloadAlerts) {
- reloadAlerts();
+ if (onSaveHandler) {
+ onSaveHandler();
}
}
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx
index 9afdd00074a692..e5b9f6f76a3c9c 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx
@@ -167,7 +167,7 @@ describe('alert_edit', () => {
{}}
initialAlert={alert}
- reloadAlerts={() => {
+ onSave={() => {
return new Promise(() => {});
}}
actionTypeRegistry={actionTypeRegistry}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx
index 267aa7a251324b..20761c5760be3d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx
@@ -44,7 +44,9 @@ export interface AlertEditProps> {
alertTypeRegistry: AlertTypeRegistryContract;
actionTypeRegistry: ActionTypeRegistryContract;
onClose: (reason: AlertFlyoutCloseReason) => void;
+ /** @deprecated use `onSave` as a callback after an alert is saved*/
reloadAlerts?: () => Promise;
+ onSave?: () => Promise;
metadata?: MetaData;
}
@@ -52,10 +54,12 @@ export const AlertEdit = ({
initialAlert,
onClose,
reloadAlerts,
+ onSave,
alertTypeRegistry,
actionTypeRegistry,
metadata,
}: AlertEditProps) => {
+ const onSaveHandler = onSave ?? reloadAlerts;
const [{ alert }, dispatch] = useReducer(alertReducer as ConcreteAlertReducer, {
alert: cloneDeep(initialAlert),
});
@@ -203,8 +207,8 @@ export const AlertEdit = ({
setIsSaving(false);
if (savedAlert) {
onClose(AlertFlyoutCloseReason.SAVED);
- if (reloadAlerts) {
- reloadAlerts();
+ if (onSaveHandler) {
+ onSaveHandler();
}
}
}}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
index 11761cec7cdbb9..ac85557c2c163e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
@@ -757,7 +757,7 @@ export const AlertsList: React.FunctionComponent = () => {
}}
actionTypeRegistry={actionTypeRegistry}
alertTypeRegistry={alertTypeRegistry}
- reloadAlerts={loadAlertsData}
+ onSave={loadAlertsData}
/>
)}
From 004238ac16ec3706b00ccad7840e2bfae1093cc1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?=
Date: Tue, 23 Feb 2021 11:23:26 +0100
Subject: [PATCH 04/50] [Logs UI] Add default panel title to log stream
embeddable (#91817)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../log_stream/log_stream_embeddable_factory.ts | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable_factory.ts b/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable_factory.ts
index d621cae3e628cb..4b9b2f99215b7a 100644
--- a/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable_factory.ts
+++ b/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable_factory.ts
@@ -6,6 +6,7 @@
*/
import { StartServicesAccessor } from 'kibana/public';
+import { i18n } from '@kbn/i18n';
import {
EmbeddableFactoryDefinition,
IContainer,
@@ -34,6 +35,16 @@ export class LogStreamEmbeddableFactoryDefinition
}
public getDisplayName() {
- return 'Log stream';
+ return i18n.translate('xpack.infra.logStreamEmbeddable.displayName', {
+ defaultMessage: 'Log stream',
+ });
+ }
+
+ public async getExplicitInput() {
+ return {
+ title: i18n.translate('xpack.infra.logStreamEmbeddable.title', {
+ defaultMessage: 'Log stream',
+ }),
+ };
}
}
From 3b1ca526a79dafcfd092e55f8f10b57cf365792a Mon Sep 17 00:00:00 2001
From: Rudolf Meijering
Date: Tue, 23 Feb 2021 11:44:43 +0100
Subject: [PATCH 05/50] v1 migrations: drop fleet-agent-events during a
migration (#92188)
* v1 migrations: drop fleet-agent-events during a migration
* Add TODO to fleet to make it clear that fleet-agent-events should not be used
* Fix test
---
.../migrations/core/elastic_index.test.ts | 13 ++-
.../migrations/core/elastic_index.ts | 19 ++++-
.../apis/saved_objects/migrations.ts | 80 +++++++++++++++++++
.../plugins/fleet/common/constants/agent.ts | 2 +
.../fleet/server/saved_objects/index.ts | 2 +
5 files changed, 114 insertions(+), 2 deletions(-)
diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.test.ts b/src/core/server/saved_objects/migrations/core/elastic_index.test.ts
index b493501e09c6ec..bfa686ac0cc47e 100644
--- a/src/core/server/saved_objects/migrations/core/elastic_index.test.ts
+++ b/src/core/server/saved_objects/migrations/core/elastic_index.test.ts
@@ -432,7 +432,18 @@ describe('ElasticIndex', () => {
expect(await read()).toEqual([]);
expect(client.search).toHaveBeenCalledWith({
- body: { size: 100 },
+ body: {
+ size: 100,
+ query: {
+ bool: {
+ must_not: {
+ term: {
+ type: 'fleet-agent-events',
+ },
+ },
+ },
+ },
+ },
index,
scroll: '5m',
});
diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.ts b/src/core/server/saved_objects/migrations/core/elastic_index.ts
index cb1120eb2ff457..e42643565eb4fe 100644
--- a/src/core/server/saved_objects/migrations/core/elastic_index.ts
+++ b/src/core/server/saved_objects/migrations/core/elastic_index.ts
@@ -67,6 +67,20 @@ export function reader(
const scroll = scrollDuration;
let scrollId: string | undefined;
+ // When migrating from the outdated index we use a read query which excludes
+ // saved objects which are no longer used. These saved objects will still be
+ // kept in the outdated index for backup purposes, but won't be availble in
+ // the upgraded index.
+ const excludeUnusedTypes = {
+ bool: {
+ must_not: {
+ term: {
+ type: 'fleet-agent-events', // https://github.com/elastic/kibana/issues/91869
+ },
+ },
+ },
+ };
+
const nextBatch = () =>
scrollId !== undefined
? client.scroll>({
@@ -74,7 +88,10 @@ export function reader(
scroll_id: scrollId,
})
: client.search>({
- body: { size: batchSize },
+ body: {
+ size: batchSize,
+ query: excludeUnusedTypes,
+ },
index,
scroll,
});
diff --git a/test/api_integration/apis/saved_objects/migrations.ts b/test/api_integration/apis/saved_objects/migrations.ts
index 5a5158825a2248..1f1f1a5c98cd64 100644
--- a/test/api_integration/apis/saved_objects/migrations.ts
+++ b/test/api_integration/apis/saved_objects/migrations.ts
@@ -48,6 +48,12 @@ const BAZ_TYPE: SavedObjectsType = {
namespaceType: 'single',
mappings: { properties: {} },
};
+const FLEET_AGENT_EVENT_TYPE: SavedObjectsType = {
+ name: 'fleet-agent-event',
+ hidden: false,
+ namespaceType: 'single',
+ mappings: { properties: {} },
+};
function getLogMock() {
return {
@@ -331,6 +337,80 @@ export default ({ getService }: FtrProviderContext) => {
]);
});
+ it('drops fleet-agent-event saved object types when doing a migration', async () => {
+ const index = '.migration-b';
+ const originalDocs = [
+ {
+ id: 'fleet-agent-event:a',
+ type: 'fleet-agent-event',
+ 'fleet-agent-event': { name: 'Foo A' },
+ },
+ {
+ id: 'fleet-agent-event:e',
+ type: 'fleet-agent-event',
+ 'fleet-agent-event': { name: 'Fooey' },
+ },
+ { id: 'bar:i', type: 'bar', bar: { nomnom: 33 } },
+ { id: 'bar:o', type: 'bar', bar: { nomnom: 2 } },
+ ];
+
+ const mappingProperties = {
+ 'fleet-agent-event': { properties: { name: { type: 'text' } } },
+ bar: { properties: { mynum: { type: 'integer' } } },
+ };
+
+ let savedObjectTypes: SavedObjectsType[] = [
+ FLEET_AGENT_EVENT_TYPE,
+ {
+ ...BAR_TYPE,
+ migrations: {
+ '1.0.0': (doc) => set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1),
+ '1.3.0': (doc) => set(doc, 'attributes', { mynum: doc.attributes.nomnom }),
+ '1.9.0': (doc) => set(doc, 'attributes.mynum', doc.attributes.mynum * 2),
+ },
+ },
+ ];
+
+ await createIndex({ esClient, index, esDeleteAllIndices });
+ await createDocs({ esClient, index, docs: originalDocs });
+
+ await migrateIndex({ esClient, index, savedObjectTypes, mappingProperties });
+
+ // @ts-expect-error name doesn't exist on mynum type
+ mappingProperties.bar.properties.name = { type: 'keyword' };
+ savedObjectTypes = [
+ FLEET_AGENT_EVENT_TYPE,
+ {
+ ...BAR_TYPE,
+ migrations: {
+ '2.3.4': (doc) => set(doc, 'attributes.name', `NAME ${doc.id}`),
+ },
+ },
+ ];
+
+ await migrateIndex({ esClient, index, savedObjectTypes, mappingProperties });
+
+ // Assert that fleet-agent-events were dropped
+ expect(await fetchDocs(esClient, index)).to.eql([
+ {
+ id: 'bar:i',
+ type: 'bar',
+ migrationVersion: { bar: '2.3.4' },
+ bar: { mynum: 68, name: 'NAME i' },
+ references: [],
+ coreMigrationVersion: KIBANA_VERSION,
+ },
+ {
+ id: 'bar:o',
+ type: 'bar',
+ migrationVersion: { bar: '2.3.4' },
+ bar: { mynum: 6, name: 'NAME o' },
+ references: [],
+ coreMigrationVersion: KIBANA_VERSION,
+ },
+ ]);
+ });
+
it('Coordinates migrations across the Kibana cluster', async () => {
const index = '.migration-c';
const originalDocs = [{ id: 'foo:lotr', type: 'foo', foo: { name: 'Lord of the Rings' } }];
diff --git a/x-pack/plugins/fleet/common/constants/agent.ts b/x-pack/plugins/fleet/common/constants/agent.ts
index d3467973fad9f7..92e24256c7a2b4 100644
--- a/x-pack/plugins/fleet/common/constants/agent.ts
+++ b/x-pack/plugins/fleet/common/constants/agent.ts
@@ -6,6 +6,8 @@
*/
export const AGENT_SAVED_OBJECT_TYPE = 'fleet-agents';
+// TODO: Remove this saved object type. Core will drop any saved objects of
+// this type during migrations. See https://github.com/elastic/kibana/issues/91869
export const AGENT_EVENT_SAVED_OBJECT_TYPE = 'fleet-agent-events';
export const AGENT_ACTION_SAVED_OBJECT_TYPE = 'fleet-agent-actions';
diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts
index 5b851c692ad3f6..d6e3cba9989077 100644
--- a/x-pack/plugins/fleet/server/saved_objects/index.ts
+++ b/x-pack/plugins/fleet/server/saved_objects/index.ts
@@ -124,6 +124,8 @@ const getSavedObjectTypes = (
'7.10.0': migrateAgentActionToV7100(encryptedSavedObjects),
},
},
+ // TODO: Remove this saved object type. Core will drop any saved objects of
+ // this type during migrations. See https://github.com/elastic/kibana/issues/91869
[AGENT_EVENT_SAVED_OBJECT_TYPE]: {
name: AGENT_EVENT_SAVED_OBJECT_TYPE,
hidden: false,
From bfed81aaf999e9d920f034eecdc6c501d7ef3cf1 Mon Sep 17 00:00:00 2001
From: Uladzislau Lasitsa
Date: Tue, 23 Feb 2021 15:12:11 +0300
Subject: [PATCH 06/50] [TSVB] Markdown variables not working for empty labels
(#91838)
* Fix markdown for empty values
* Fix ci
* Fix eslint
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../vis_type_timeseries/common/empty_label.ts | 13 +++++++++++++
.../components/lib/convert_series_to_vars.js | 10 +++++-----
.../application/components/lib/replace_vars.js | 7 ++++++-
.../components/vis_types/table/vis.js | 9 ++-------
.../application/components/vis_with_splits.js | 9 ++-------
.../visualizations/views/timeseries/index.js | 16 +++-------------
.../application/visualizations/views/top_n.js | 7 ++-----
7 files changed, 33 insertions(+), 38 deletions(-)
create mode 100644 src/plugins/vis_type_timeseries/common/empty_label.ts
diff --git a/src/plugins/vis_type_timeseries/common/empty_label.ts b/src/plugins/vis_type_timeseries/common/empty_label.ts
new file mode 100644
index 00000000000000..d55a58f17dbf3e
--- /dev/null
+++ b/src/plugins/vis_type_timeseries/common/empty_label.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const emptyLabel = i18n.translate('visTypeTimeseries.emptyTextValue', {
+ defaultMessage: '(empty)',
+});
diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js b/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js
index 3e5de5ea0f14c1..478b41810719c7 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js
@@ -9,6 +9,7 @@
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash';
import { getLastValue } from '../../../../common/get_last_value';
+import { emptyLabel } from '../../../../common/empty_label';
import { createTickFormatter } from './tick_formatter';
import { labelDateFormatter } from './label_date_formatter';
import moment from 'moment';
@@ -19,9 +20,8 @@ export const convertSeriesToVars = (series, model, dateFormat = 'lll', getConfig
series
.filter((row) => _.startsWith(row.id, seriesModel.id))
.forEach((row) => {
- const varName = [_.snakeCase(row.label), _.snakeCase(seriesModel.var_name)]
- .filter((v) => v)
- .join('.');
+ const label = row.label ? _.snakeCase(row.label) : emptyLabel;
+ const varName = [label, _.snakeCase(seriesModel.var_name)].filter((v) => v).join('.');
const formatter = createTickFormatter(
seriesModel.formatter,
@@ -43,7 +43,7 @@ export const convertSeriesToVars = (series, model, dateFormat = 'lll', getConfig
},
};
set(variables, varName, data);
- set(variables, `${_.snakeCase(row.label)}.label`, row.label);
+ set(variables, `${label}.label`, row.label);
/**
* Handle the case when a field has "key_as_string" value.
@@ -54,7 +54,7 @@ export const convertSeriesToVars = (series, model, dateFormat = 'lll', getConfig
*/
if (row.labelFormatted) {
const val = labelDateFormatter(row.labelFormatted, dateFormat);
- set(variables, `${_.snakeCase(row.label)}.formatted`, val);
+ set(variables, `${label}.formatted`, val);
}
});
});
diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.js b/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.js
index 50cf135bbc8e95..6e5e423d868075 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.js
@@ -8,11 +8,16 @@
import _ from 'lodash';
import handlebars from 'handlebars/dist/handlebars';
+import { emptyLabel } from '../../../../common/empty_label';
import { i18n } from '@kbn/i18n';
export function replaceVars(str, args = {}, vars = {}) {
try {
- const template = handlebars.compile(str, { strict: true, knownHelpersOnly: true });
+ // we need add '[]' for emptyLabel because this value contains special characters. (https://handlebarsjs.com/guide/expressions.html#literal-segments)
+ const template = handlebars.compile(str.replace(emptyLabel, `[${emptyLabel}]`), {
+ strict: true,
+ knownHelpersOnly: true,
+ });
const string = template(_.assign({}, vars, { args }));
diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js
index 24d0ca1b588f73..faf6fef0aa5494 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js
@@ -8,7 +8,6 @@
import _, { isArray, last, get } from 'lodash';
import React, { Component } from 'react';
-import { i18n } from '@kbn/i18n';
import PropTypes from 'prop-types';
import { RedirectAppLinks } from '../../../../../../kibana_react/public';
import { createTickFormatter } from '../../lib/tick_formatter';
@@ -18,6 +17,7 @@ import { replaceVars } from '../../lib/replace_vars';
import { fieldFormats } from '../../../../../../../plugins/data/public';
import { FormattedMessage } from '@kbn/i18n/react';
import { getFieldFormats, getCoreStart } from '../../../../services';
+import { emptyLabel } from '../../../../../common/empty_label';
function getColor(rules, colorKey, value) {
let color;
@@ -89,12 +89,7 @@ class TableVis extends Component {
});
return (
-
- {rowDisplay ||
- i18n.translate('visTypeTimeseries.emptyTextValue', {
- defaultMessage: '(empty)',
- })}
-
+ {rowDisplay || emptyLabel}
{columns}
);
diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js b/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js
index fbec412c30f48d..59f4724c1394cb 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js
@@ -7,10 +7,10 @@
*/
import React from 'react';
-import { i18n } from '@kbn/i18n';
import { getDisplayName } from './lib/get_display_name';
import { labelDateFormatter } from './lib/label_date_formatter';
import { findIndex, first } from 'lodash';
+import { emptyLabel } from '../../../common/empty_label';
export function visWithSplits(WrappedComponent) {
function SplitVisComponent(props) {
@@ -81,12 +81,7 @@ export function visWithSplits(WrappedComponent) {
model={model}
visData={newVisData}
onBrush={props.onBrush}
- additionalLabel={
- additionalLabel ||
- i18n.translate('visTypeTimeseries.emptyTextValue', {
- defaultMessage: '(empty)',
- })
- }
+ additionalLabel={additionalLabel || emptyLabel}
backgroundColor={props.backgroundColor}
getConfig={props.getConfig}
/>
diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js
index 4ec60661ffed29..d306704463c2ed 100644
--- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js
+++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js
@@ -9,7 +9,6 @@
import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-import { i18n } from '@kbn/i18n';
import { labelDateFormatter } from '../../../components/lib/label_date_formatter';
import {
@@ -31,6 +30,7 @@ import { AreaSeriesDecorator } from './decorators/area_decorator';
import { BarSeriesDecorator } from './decorators/bar_decorator';
import { getStackAccessors } from './utils/stack_format';
import { getBaseTheme, getChartClasses } from './utils/theme';
+import { emptyLabel } from '../../../../../common/empty_label';
const generateAnnotationData = (values, formatter) =>
values.map(({ key, docs }) => ({
@@ -189,12 +189,7 @@ export const TimeSeries = ({
key={key}
seriesId={id}
seriesGroupId={groupId}
- name={
- seriesName ||
- i18n.translate('visTypeTimeseries.emptyTextValue', {
- defaultMessage: '(empty)',
- })
- }
+ name={seriesName || emptyLabel}
data={data}
hideInLegend={hideInLegend}
bars={bars}
@@ -219,12 +214,7 @@ export const TimeSeries = ({
key={key}
seriesId={id}
seriesGroupId={groupId}
- name={
- seriesName ||
- i18n.translate('visTypeTimeseries.emptyTextValue', {
- defaultMessage: '(empty)',
- })
- }
+ name={seriesName || emptyLabel}
data={data}
hideInLegend={hideInLegend}
lines={lines}
diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js
index 542ee0871fdcb7..2559ed543e543d 100644
--- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js
+++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js
@@ -8,9 +8,9 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
-import { i18n } from '@kbn/i18n';
import { getLastValue } from '../../../../common/get_last_value';
import { labelDateFormatter } from '../../components/lib/label_date_formatter';
+import { emptyLabel } from '../../../../common/empty_label';
import reactcss from 'reactcss';
const RENDER_MODES = {
@@ -130,10 +130,7 @@ export class TopN extends Component {
return (
- {label ||
- i18n.translate('visTypeTimeseries.emptyTextValue', {
- defaultMessage: '(empty)',
- })}
+ {label || emptyLabel}
From f246a053f3d0f323b78432d889b5f0fe73e74fae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?=
Date: Tue, 23 Feb 2021 07:23:34 -0500
Subject: [PATCH 07/50] Update docs on filtering alerts by string params
(#92239)
---
docs/api/alerts/find.asciidoc | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/api/alerts/find.asciidoc b/docs/api/alerts/find.asciidoc
index ebd1c7841206c0..5af01efbc77873 100644
--- a/docs/api/alerts/find.asciidoc
+++ b/docs/api/alerts/find.asciidoc
@@ -6,6 +6,9 @@
Retrieve a paginated set of alerts based on condition.
+NOTE: As alerts change in {kib}, the results on each page of the response also
+change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.
+
[[alerts-api-find-request]]
==== Request
@@ -14,6 +17,8 @@ Retrieve a paginated set of alerts based on condition.
[[alerts-api-find-query-params]]
==== Query Parameters
+NOTE: Alert `params` are stored as a {ref}/flattened.html[flattened field type] and analyzed as keywords.
+
`per_page`::
(Optional, number) The number of alerts to return per page.
@@ -46,11 +51,6 @@ Retrieve a paginated set of alerts based on condition.
It should look like savedObjectType.attributes.title: "myTitle". However, If you used a direct attribute of a saved object, such as `updatedAt`,
you will have to define your filter, for example, savedObjectType.updatedAt > 2018-12-22.
-NOTE: As alerts change in {kib}, the results on each page of the response also
-change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.
-
-NOTE: Alert `params` are stored as {ref}/flattened.html[flattened] and analyzed as `keyword`.
-
[[alerts-api-find-request-codes]]
==== Response code
From 4c82ffc25ff468c2dc73e3902a865d7574480cbe Mon Sep 17 00:00:00 2001
From: Gidi Meir Morris
Date: Tue, 23 Feb 2021 12:54:55 +0000
Subject: [PATCH 08/50] [Docs][Alerting] updates images in Alerts Management
and adds docs for the Run When field (#92225)
Updates images in Alerts Management Docs and usage of the Run When field
---
docs/user/alerting/defining-alerts.asciidoc | 7 ++++++-
.../images/alert-flyout-action-details.png | Bin 95857 -> 204041 bytes
.../images/alert-flyout-action-variables.png | Bin 138446 -> 271027 bytes
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc
index d0a6b7f1c2c03f..528189b8a1216a 100644
--- a/docs/user/alerting/defining-alerts.asciidoc
+++ b/docs/user/alerting/defining-alerts.asciidoc
@@ -56,7 +56,12 @@ To add an action to an alert, you first select the type of action:
[role="screenshot"]
image::images/alert-flyout-action-type-selection.png[UI for selecting an action type]
-Each action must specify a <> instance. If no connectors exist for that action type, click "Add new" to create one.
+When an alert instance matches a condition, the alert is marked as _Active_ and assigned an action group. The actions in that group are triggered.
+When the condition is no longer detected, the alert is assigned to the _Recovered_ action group, which triggers any actions assigned to that group.
+
+**Run When** allows you to assign an action to an _action group_. This will trigger the action in accordance with your **Notify every** setting.
+
+Each action must specify a <> instance. If no connectors exist for that action type, click *Add action* to create one.
Each action type exposes different properties. For example an email action allows you to set the recipients, the subject, and a message body in markdown format. See <> for details on the types of actions provided by {kib} and their properties.
diff --git a/docs/user/alerting/images/alert-flyout-action-details.png b/docs/user/alerting/images/alert-flyout-action-details.png
index 06287ed1837d2d6fe0da2a635d08aa44fcbc3ab7..7547fc819f539035bf8f2a541f675eb788be793a 100644
GIT binary patch
literal 204041
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