From f0be78d7fc2278177f3409ad09ec73dad2e8ad84 Mon Sep 17 00:00:00 2001
From: Vadim Yakhin
Date: Wed, 17 Feb 2021 11:26:47 -0400
Subject: [PATCH 01/79] Personal dashboard tests (#91555)
* Add unit tests for PrivateSourcesLayout
* Add unit tests for PrivateSources
* Extract groups sentence into separate utility function
* Add test for toSentenceSerial
* Move 'and' constant to WS-level constants file
* Use strings to find components in tests
* Revert "Use strings to find components in tests"
This reverts commit 50caf46b3a08cf7270b5434ae4d56a407b2611fd.
* Use imports to find components in tests
* Return only the first array element in toSentenceSerial
Convering the entire array with one element to string also works,
but it's less clear.
---
.../workplace_search/constants.ts | 4 +
.../workplace_search/utils/index.ts | 8 ++
.../utils/to_sentence_serial.test.ts | 22 ++++
.../utils/to_sentence_serial.ts | 15 +++
.../views/content_sources/constants.ts | 4 -
.../content_sources/private_sources.test.tsx | 111 ++++++++++++++++++
.../views/content_sources/private_sources.tsx | 15 +--
.../private_sources_layout.test.tsx | 69 +++++++++++
8 files changed, 235 insertions(+), 13 deletions(-)
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/to_sentence_serial.test.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/to_sentence_serial.ts
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.test.tsx
create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
index 4fd4d480108def..cdfd07b07de912 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts
@@ -679,6 +679,10 @@ export const DESCRIPTION_LABEL = i18n.translate(
}
);
+export const AND = i18n.translate('xpack.enterpriseSearch.workplaceSearch.and', {
+ defaultMessage: 'and',
+});
+
export const UPDATE_LABEL = i18n.translate('xpack.enterpriseSearch.workplaceSearch.update.label', {
defaultMessage: 'Update',
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts
new file mode 100644
index 00000000000000..37228cf9e7025d
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export { toSentenceSerial } from './to_sentence_serial';
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/to_sentence_serial.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/to_sentence_serial.test.ts
new file mode 100644
index 00000000000000..fb6501d3cb943a
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/to_sentence_serial.test.ts
@@ -0,0 +1,22 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { toSentenceSerial } from './to_sentence_serial';
+
+describe('toSentenceSerial', () => {
+ it('works correctly for 1 word', () => {
+ expect(toSentenceSerial(['One'])).toEqual('One');
+ });
+
+ it('works correctly for 2 words', () => {
+ expect(toSentenceSerial(['One', 'Two'])).toEqual('One and Two');
+ });
+
+ it('works correctly for 3+ words', () => {
+ expect(toSentenceSerial(['One', 'Two', 'Three'])).toEqual('One, Two, and Three');
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/to_sentence_serial.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/to_sentence_serial.ts
new file mode 100644
index 00000000000000..ad6383a76adc22
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/to_sentence_serial.ts
@@ -0,0 +1,15 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { AND } from '../constants';
+
+export const toSentenceSerial = (array: string[]) =>
+ array.length === 1
+ ? array[0]
+ : `${array.slice(0, array.length - 1).join(', ')}${
+ array.length === 2 ? '' : ','
+ } ${AND} ${array.slice(-1)}`;
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts
index 8efa61c1ed524f..3e1290292704e3 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts
@@ -413,10 +413,6 @@ export const SHARED_EMPTY_DESCRIPTION = i18n.translate(
}
);
-export const AND = i18n.translate('xpack.enterpriseSearch.workplaceSearch.and', {
- defaultMessage: 'and',
-});
-
export const LICENSE_CALLOUT_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.sources.licenseCallout.title',
{
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.test.tsx
new file mode 100644
index 00000000000000..e6f19ff13b3ccf
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.test.tsx
@@ -0,0 +1,111 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import '../../../__mocks__/shallow_useeffect.mock';
+
+import { setMockValues, setMockActions } from '../../../__mocks__';
+
+import React from 'react';
+
+import { shallow } from 'enzyme';
+
+import { EuiCallOut, EuiEmptyPrompt } from '@elastic/eui';
+
+import { Loading } from '../../../shared/loading';
+import { ContentSection } from '../../components/shared/content_section';
+import { SourcesTable } from '../../components/shared/sources_table';
+
+import { PrivateSources } from './private_sources';
+import { SourcesView } from './sources_view';
+
+describe('PrivateSources', () => {
+ const mockValues = {
+ account: { canCreatePersonalSources: false, groups: [] },
+ dataLoading: false,
+ contentSources: [],
+ privateContentSources: [],
+ serviceTypes: [],
+ hasPlatinumLicense: true,
+ };
+
+ beforeEach(() => {
+ setMockActions({ initializeSources: jest.fn() });
+ setMockValues({ ...mockValues });
+ });
+
+ it('renders', () => {
+ const wrapper = shallow( );
+
+ expect(wrapper.find(SourcesView)).toHaveLength(1);
+ });
+
+ it('renders Loading when loading', () => {
+ setMockValues({ ...mockValues, dataLoading: true });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(Loading)).toHaveLength(1);
+ });
+
+ it('renders only shared sources section when canCreatePersonalSources is false', () => {
+ setMockValues({ ...mockValues });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(ContentSection)).toHaveLength(1);
+ });
+
+ it('renders both shared and private sources sections when canCreatePersonalSources is true', () => {
+ setMockValues({ ...mockValues, account: { canCreatePersonalSources: true, groups: [] } });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(ContentSection)).toHaveLength(2);
+ });
+
+ it('renders license callout when has private sources with non-Platinum license', () => {
+ setMockValues({
+ ...mockValues,
+ privateContentSources: ['source1', 'source2'],
+ hasPlatinumLicense: false,
+ account: { canCreatePersonalSources: true, groups: [] },
+ });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(EuiCallOut)).toHaveLength(1);
+ });
+
+ it('renders an action button when user can add private sources', () => {
+ setMockValues({
+ ...mockValues,
+ account: { canCreatePersonalSources: true, groups: [] },
+ serviceTypes: [{ configured: true }],
+ });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(ContentSection).first().prop('action')).toBeTruthy();
+ });
+
+ it('renders empty prompts if no sources are available', () => {
+ setMockValues({
+ ...mockValues,
+ account: { canCreatePersonalSources: true, groups: [] },
+ });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(2);
+ });
+
+ it('renders SourcesTable if sources are available', () => {
+ setMockValues({
+ ...mockValues,
+ account: { canCreatePersonalSources: true, groups: [] },
+ contentSources: ['1', '2'],
+ privateContentSources: ['1', '2'],
+ });
+ const wrapper = shallow( );
+
+ expect(wrapper.find(SourcesTable)).toHaveLength(2);
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx
index 3ff14602b979d6..114df3cf41e399 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx
@@ -20,9 +20,9 @@ import noSharedSourcesIcon from '../../assets/share_circle.svg';
import { ContentSection } from '../../components/shared/content_section';
import { SourcesTable } from '../../components/shared/sources_table';
import { ADD_SOURCE_PATH, getSourcesPath } from '../../routes';
+import { toSentenceSerial } from '../../utils';
import {
- AND,
PRIVATE_LINK_TITLE,
PRIVATE_HEADER_TITLE,
PRIVATE_HEADER_DESCRIPTION,
@@ -122,13 +122,6 @@ export const PrivateSources: React.FC = () => {
);
- const groupsSentence =
- groups.length === 1
- ? `${groups}`
- : `${groups.slice(0, groups.length - 1).join(', ')}${
- groups.length === 2 ? '' : ','
- } ${AND} ${groups.slice(-1)}`;
-
const sharedSourcesSection = (
{
}}
+ values={{
+ groups: groups.length,
+ groupsSentence: toSentenceSerial(groups),
+ newline: ,
+ }}
/>
)
}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx
new file mode 100644
index 00000000000000..488eb4b49853bd
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx
@@ -0,0 +1,69 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import '../../../__mocks__/shallow_useeffect.mock';
+
+import { setMockValues } from '../../../__mocks__';
+
+import React from 'react';
+
+import { shallow } from 'enzyme';
+
+import { EuiCallOut } from '@elastic/eui';
+
+import { ViewContentHeader } from '../../components/shared/view_content_header';
+
+import {
+ PRIVATE_CAN_CREATE_PAGE_TITLE,
+ PRIVATE_VIEW_ONLY_PAGE_TITLE,
+ PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION,
+ PRIVATE_CAN_CREATE_PAGE_DESCRIPTION,
+} from './constants';
+import { PrivateSourcesLayout } from './private_sources_layout';
+
+describe('PrivateSourcesLayout', () => {
+ const mockValues = {
+ account: { canCreatePersonalSources: true },
+ };
+
+ const children = test
;
+
+ beforeEach(() => {
+ setMockValues({ ...mockValues });
+ });
+
+ it('renders', () => {
+ const wrapper = shallow({children} );
+
+ expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1);
+ });
+
+ it('uses correct title and description when private sources are enabled', () => {
+ const wrapper = shallow({children} );
+
+ expect(wrapper.find(ViewContentHeader).prop('title')).toEqual(PRIVATE_CAN_CREATE_PAGE_TITLE);
+ expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
+ PRIVATE_CAN_CREATE_PAGE_DESCRIPTION
+ );
+ });
+
+ it('uses correct title and description when private sources are disabled', () => {
+ setMockValues({ account: { canCreatePersonalSources: false } });
+ const wrapper = shallow({children} );
+
+ expect(wrapper.find(ViewContentHeader).prop('title')).toEqual(PRIVATE_VIEW_ONLY_PAGE_TITLE);
+ expect(wrapper.find(ViewContentHeader).prop('description')).toEqual(
+ PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION
+ );
+ });
+
+ it('renders callout when in read-only mode', () => {
+ const wrapper = shallow({children} );
+
+ expect(wrapper.find(EuiCallOut)).toHaveLength(1);
+ });
+});
From bd623d3ca906e261dad530bc4cc8ebcbbf8894be Mon Sep 17 00:00:00 2001
From: Marta Bondyra
Date: Wed, 17 Feb 2021 16:33:11 +0100
Subject: [PATCH 02/79] [Lens] remove test warnings from console (#91632)
---
.../dimension_panel/dimension_panel.test.tsx | 13 ++++++++++---
.../operations/definitions/ranges/ranges.test.tsx | 13 +++++++++++++
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
index c26d35c4d9a5d1..5eaa798f459e33 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
@@ -8,7 +8,14 @@
import { ReactWrapper, ShallowWrapper } from 'enzyme';
import React, { ChangeEvent, MouseEvent } from 'react';
import { act } from 'react-dom/test-utils';
-import { EuiComboBox, EuiListGroupItemProps, EuiListGroup, EuiRange } from '@elastic/eui';
+import {
+ EuiComboBox,
+ EuiListGroupItemProps,
+ EuiListGroup,
+ EuiRange,
+ EuiSelect,
+ EuiButtonIcon,
+} from '@elastic/eui';
import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public';
import {
IndexPatternDimensionEditorComponent,
@@ -24,8 +31,6 @@ import { OperationMetadata } from '../../types';
import { DateHistogramIndexPatternColumn } from '../operations/definitions/date_histogram';
import { getFieldByNameFactory } from '../pure_helpers';
import { TimeScaling } from './time_scaling';
-import { EuiSelect } from '@elastic/eui';
-import { EuiButtonIcon } from '@elastic/eui';
import { DimensionEditor } from './dimension_editor';
jest.mock('../loader');
@@ -742,6 +747,8 @@ describe('IndexPatternDimensionEditorPanel', () => {
});
it('should leave error state when switching from incomplete state to fieldless operation', () => {
+ // @ts-expect-error
+ window['__react-beautiful-dnd-disable-dev-warnings'] = true; // issue with enzyme & react-beautiful-dnd throwing errors: https://github.com/atlassian/react-beautiful-dnd/issues/1593
wrapper = mount( );
wrapper
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx
index d8c4c5fd8ca89c..62c729aa2b3f19 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx
@@ -33,6 +33,19 @@ import { RangePopover } from './advanced_editor';
import { DragDropBuckets } from '../shared_components';
import { getFieldByNameFactory } from '../../../pure_helpers';
+// mocking random id generator function
+jest.mock('@elastic/eui', () => {
+ const original = jest.requireActual('@elastic/eui');
+
+ return {
+ ...original,
+ htmlIdGenerator: (fn: unknown) => {
+ let counter = 0;
+ return () => counter++;
+ },
+ };
+});
+
const dataPluginMockValue = dataPluginMock.createStartContract();
// need to overwrite the formatter field first
dataPluginMockValue.fieldFormats.deserialize = jest.fn().mockImplementation(({ params }) => {
From 9eb69cf71acd50a890ad75154ab8e082e4d67f56 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez
Date: Wed, 17 Feb 2021 10:39:39 -0500
Subject: [PATCH 03/79] move randomize seed to advanced config (#91535)
---
.../advanced_step/advanced_step_details.tsx | 74 +++++++++----------
.../advanced_step/advanced_step_form.tsx | 28 +++++++
.../advanced_step/hyper_parameters.tsx | 29 --------
3 files changed, 65 insertions(+), 66 deletions(-)
diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_details.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_details.tsx
index 469327323bee98..fc8798a3af0f57 100644
--- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_details.tsx
+++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_details.tsx
@@ -131,20 +131,6 @@ export const AdvancedStepDetails: FC<{ setCurrentStep: any; state: State }> = ({
}
if (isRegOrClassJob) {
- if (jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION) {
- advancedFirstCol.push({
- title: i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.numTopClasses', {
- defaultMessage: 'Top classes',
- }),
- description:
- numTopClasses === -1
- ? i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.allClasses', {
- defaultMessage: 'All classes',
- })
- : getStringValue(numTopClasses),
- });
- }
-
advancedFirstCol.push({
title: i18n.translate(
'xpack.ml.dataframe.analytics.create.configDetails.numTopFeatureImportanceValues',
@@ -170,15 +156,23 @@ export const AdvancedStepDetails: FC<{ setCurrentStep: any; state: State }> = ({
}
);
- advancedSecondCol.push({
- title: i18n.translate(
- 'xpack.ml.dataframe.analytics.create.configDetails.predictionFieldName',
- {
- defaultMessage: 'Prediction field name',
- }
- ),
- description: predictionFieldName ? predictionFieldName : `${dependentVariable}_prediction`,
- });
+ advancedSecondCol.push(
+ {
+ title: i18n.translate(
+ 'xpack.ml.dataframe.analytics.create.configDetails.predictionFieldName',
+ {
+ defaultMessage: 'Prediction field name',
+ }
+ ),
+ description: predictionFieldName ? predictionFieldName : `${dependentVariable}_prediction`,
+ },
+ {
+ title: i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.randomizedSeed', {
+ defaultMessage: 'Randomized seed',
+ }),
+ description: getStringValue(randomizeSeed),
+ }
+ );
hyperSecondCol.push(
{
@@ -205,20 +199,26 @@ export const AdvancedStepDetails: FC<{ setCurrentStep: any; state: State }> = ({
description: `${modelMemoryLimit}`,
});
- hyperThirdCol.push(
- {
- title: i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.gamma', {
- defaultMessage: 'Gamma',
- }),
- description: getStringValue(gamma),
- },
- {
- title: i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.randomizedSeed', {
- defaultMessage: 'Randomized seed',
- }),
- description: getStringValue(randomizeSeed),
- }
- );
+ hyperThirdCol.push({
+ title: i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.gamma', {
+ defaultMessage: 'Gamma',
+ }),
+ description: getStringValue(gamma),
+ });
+ }
+
+ if (jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION) {
+ advancedThirdCol.push({
+ title: i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.numTopClasses', {
+ defaultMessage: 'Top classes',
+ }),
+ description:
+ numTopClasses === -1
+ ? i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.allClasses', {
+ defaultMessage: 'All classes',
+ })
+ : getStringValue(numTopClasses),
+ });
}
if (maxNumThreads !== undefined) {
diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx
index 8e25fc961c7c28..71770dcf952d97 100644
--- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx
+++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx
@@ -409,6 +409,34 @@ export const AdvancedStepForm: FC = ({
/>
+
+
+
+ setFormState({ randomizeSeed: e.target.value === '' ? undefined : +e.target.value })
+ }
+ isInvalid={randomizeSeed !== undefined && typeof randomizeSeed !== 'number'}
+ value={getNumberValue(randomizeSeed)}
+ step={1}
+ />
+
+
);
diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/hyper_parameters.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/hyper_parameters.tsx
index cd4dd44edfa507..704b2cc77a7f93 100644
--- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/hyper_parameters.tsx
+++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/hyper_parameters.tsx
@@ -31,7 +31,6 @@ export const HyperParameters: FC = ({ actions, state, advancedParamErrors
lambda,
maxOptimizationRoundsPerHyperparameter,
maxTrees,
- randomizeSeed,
softTreeDepthLimit,
softTreeDepthTolerance,
} = state.form;
@@ -184,34 +183,6 @@ export const HyperParameters: FC = ({ actions, state, advancedParamErrors
/>
-
-
-
- setFormState({ randomizeSeed: e.target.value === '' ? undefined : +e.target.value })
- }
- isInvalid={randomizeSeed !== undefined && typeof randomizeSeed !== 'number'}
- value={getNumberValue(randomizeSeed)}
- step={1}
- />
-
-
Date: Wed, 17 Feb 2021 09:40:49 -0600
Subject: [PATCH 04/79] [ML] Fix runtime mappings for runtimeField with
optional script field (#91634)
---
x-pack/plugins/ml/common/types/fields.ts | 2 +-
.../transform/common/api_schemas/transforms.ts | 14 ++++++++------
.../components/step_define/common/types.ts | 2 +-
3 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/ml/common/types/fields.ts b/x-pack/plugins/ml/common/types/fields.ts
index 581ce861e8331c..047852534965c2 100644
--- a/x-pack/plugins/ml/common/types/fields.ts
+++ b/x-pack/plugins/ml/common/types/fields.ts
@@ -113,7 +113,7 @@ type RuntimeType = typeof RUNTIME_FIELD_TYPES[number];
export interface RuntimeField {
type: RuntimeType;
- script:
+ script?:
| string
| {
source: string;
diff --git a/x-pack/plugins/transform/common/api_schemas/transforms.ts b/x-pack/plugins/transform/common/api_schemas/transforms.ts
index 3d8d7ef4d8ae3a..d86df3af3e1d0a 100644
--- a/x-pack/plugins/transform/common/api_schemas/transforms.ts
+++ b/x-pack/plugins/transform/common/api_schemas/transforms.ts
@@ -76,12 +76,14 @@ export const runtimeMappingsSchema = schema.maybe(
schema.literal('ip'),
schema.literal('boolean'),
]),
- script: schema.oneOf([
- schema.string(),
- schema.object({
- source: schema.string(),
- }),
- ]),
+ script: schema.maybe(
+ schema.oneOf([
+ schema.string(),
+ schema.object({
+ source: schema.string(),
+ }),
+ ])
+ ),
})
)
);
diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts
index cdba7a3f5482c9..6c9293a6d13cf6 100644
--- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts
+++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts
@@ -40,7 +40,7 @@ type RuntimeType = typeof RUNTIME_FIELD_TYPES[number];
export interface RuntimeField {
type: RuntimeType;
- script:
+ script?:
| string
| {
source: string;
From 31cbfbb8d52c9a7e2bdad101889d7fa127e6d5e3 Mon Sep 17 00:00:00 2001
From: Wylie Conlon
Date: Wed, 17 Feb 2021 10:46:14 -0500
Subject: [PATCH 05/79] [TSVB] Support min and max with histogram data types
(#91581)
---
.../common/metric_types.ts | 2 +
.../components/aggs/agg_select.test.tsx | 8 +++
.../components/aggs/agg_select.tsx | 2 +-
.../components/aggs/filter_ratio.test.js | 53 +++++++------------
.../components/aggs/histogram_support.test.js | 4 +-
.../get_supported_fields_by_metric_type.js | 2 +
...et_supported_fields_by_metric_type.test.js | 4 +-
7 files changed, 35 insertions(+), 40 deletions(-)
diff --git a/src/plugins/vis_type_timeseries/common/metric_types.ts b/src/plugins/vis_type_timeseries/common/metric_types.ts
index 52b31b67743715..17352f0f9da258 100644
--- a/src/plugins/vis_type_timeseries/common/metric_types.ts
+++ b/src/plugins/vis_type_timeseries/common/metric_types.ts
@@ -20,6 +20,8 @@ export enum METRIC_TYPES {
VALUE_COUNT = 'value_count',
AVERAGE = 'avg',
SUM = 'sum',
+ MIN = 'min',
+ MAX = 'max',
}
// We should probably use BUCKET_TYPES from data plugin in future.
diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.test.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.test.tsx
index d82a16f1186219..579205ae048444 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.test.tsx
+++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.test.tsx
@@ -87,6 +87,14 @@ describe('TSVB AggSelect', () => {
"label": "Count",
"value": "count",
},
+ Object {
+ "label": "Max",
+ "value": "max",
+ },
+ Object {
+ "label": "Min",
+ "value": "min",
+ },
Object {
"label": "Sum",
"value": "sum",
diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.tsx
index 0b8fad4d780f64..00d088025bf253 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.tsx
+++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.tsx
@@ -225,7 +225,7 @@ const FILTER_RATIO_AGGS = [
'value_count',
];
-const HISTOGRAM_AGGS = ['avg', 'count', 'sum', 'value_count'];
+const HISTOGRAM_AGGS = ['avg', 'count', 'sum', 'min', 'max', 'value_count'];
const allAggOptions = [...metricAggs, ...pipelineAggs, ...siblingAggs, ...specialAggs];
diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js
index dd85e2490d70a7..5648a8d3e7133a 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js
@@ -51,26 +51,13 @@ describe('TSVB Filter Ratio', () => {
field: 'histogram_value',
};
const wrapper = setup(metric);
- expect(wrapper.find(EuiComboBox).at(1).props().options).toMatchInlineSnapshot(`
- Array [
- Object {
- "label": "Average",
- "value": "avg",
- },
- Object {
- "label": "Count",
- "value": "count",
- },
- Object {
- "label": "Sum",
- "value": "sum",
- },
- Object {
- "label": "Value Count",
- "value": "value_count",
- },
- ]
- `);
+ expect(
+ wrapper
+ .find(EuiComboBox)
+ .at(1)
+ .props()
+ .options.map(({ value }) => value)
+ ).toEqual(['avg', 'count', 'max', 'min', 'sum', 'value_count']);
});
const shouldNotHaveHistogramField = (agg) => {
it(`should not have histogram fields for ${agg}`, () => {
@@ -80,23 +67,19 @@ describe('TSVB Filter Ratio', () => {
field: '',
};
const wrapper = setup(metric);
- expect(wrapper.find(EuiComboBox).at(2).props().options).toMatchInlineSnapshot(`
- Array [
- Object {
- "label": "number",
- "options": Array [
- Object {
- "label": "system.cpu.user.pct",
- "value": "system.cpu.user.pct",
- },
- ],
- },
- ]
- `);
+ expect(wrapper.find(EuiComboBox).at(2).props().options).toEqual([
+ {
+ label: 'number',
+ options: [
+ {
+ label: 'system.cpu.user.pct',
+ value: 'system.cpu.user.pct',
+ },
+ ],
+ },
+ ]);
});
};
- shouldNotHaveHistogramField('max');
- shouldNotHaveHistogramField('min');
shouldNotHaveHistogramField('positive_rate');
it(`should not have histogram fields for cardinality`, () => {
diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js
index 5a5f37db2b7b5e..c536856327f283 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js
@@ -65,6 +65,8 @@ describe('Histogram Types', () => {
};
shouldHaveHistogramSupport('avg');
shouldHaveHistogramSupport('sum');
+ shouldHaveHistogramSupport('min');
+ shouldHaveHistogramSupport('max');
shouldHaveHistogramSupport('value_count');
shouldHaveHistogramSupport('percentile');
shouldHaveHistogramSupport('percentile_rank');
@@ -81,8 +83,6 @@ describe('Histogram Types', () => {
);
};
shouldNotHaveHistogramSupport('cardinality');
- shouldNotHaveHistogramSupport('max');
- shouldNotHaveHistogramSupport('min');
shouldNotHaveHistogramSupport('variance');
shouldNotHaveHistogramSupport('sum_of_squares');
shouldNotHaveHistogramSupport('std_deviation');
diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js b/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js
index ba3b330ed8eef2..61226e4e8dcbe5 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js
@@ -17,6 +17,8 @@ export function getSupportedFieldsByMetricType(type) {
return Object.values(KBN_FIELD_TYPES);
case METRIC_TYPES.AVERAGE:
case METRIC_TYPES.SUM:
+ case METRIC_TYPES.MIN:
+ case METRIC_TYPES.MAX:
return [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM];
default:
return [KBN_FIELD_TYPES.NUMBER];
diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js b/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js
index 10258effbb8836..c009146abb7bd1 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js
@@ -26,11 +26,11 @@ describe('getSupportedFieldsByMetricType', () => {
shouldSupportAllFieldTypes('value_count');
shouldHaveHistogramAndNumbers('avg');
shouldHaveHistogramAndNumbers('sum');
+ shouldHaveHistogramAndNumbers('min');
+ shouldHaveHistogramAndNumbers('max');
shouldHaveOnlyNumbers('positive_rate');
shouldHaveOnlyNumbers('std_deviation');
- shouldHaveOnlyNumbers('max');
- shouldHaveOnlyNumbers('min');
it(`should return everything but histogram for cardinality`, () => {
expect(getSupportedFieldsByMetricType('cardinality')).not.toContain('histogram');
From 351990068dca81fa27f1bb03ec30da84fffe6e84 Mon Sep 17 00:00:00 2001
From: Marta Bondyra
Date: Wed, 17 Feb 2021 16:48:26 +0100
Subject: [PATCH 06/79] [Lens] Drag and drop accessibility messages (#91494)
* [Lens] copy dnd
* Update x-pack/plugins/lens/public/drag_drop/providers.tsx
Co-authored-by: Wylie Conlon
Co-authored-by: Wylie Conlon
---
.../lens/public/drag_drop/announcements.tsx | 143 ++++++++++++++----
.../lens/public/drag_drop/drag_drop.test.tsx | 50 +++---
.../lens/public/drag_drop/drag_drop.tsx | 2 +-
.../lens/public/drag_drop/providers.tsx | 4 +-
.../plugins/lens/public/drag_drop/readme.md | 2 +-
.../draggable_dimension_button.tsx | 8 +-
.../config_panel/empty_dimension_button.tsx | 8 +-
.../config_panel/layer_panel.test.tsx | 20 ++-
.../public/editor_frame_service/mocks.tsx | 2 +-
.../dimension_panel/droppable.test.ts | 30 ++--
.../dimension_panel/droppable.ts | 76 +++++++---
.../indexpattern_datasource/indexpattern.tsx | 4 +-
x-pack/plugins/lens/public/types.ts | 4 +-
13 files changed, 245 insertions(+), 108 deletions(-)
diff --git a/x-pack/plugins/lens/public/drag_drop/announcements.tsx b/x-pack/plugins/lens/public/drag_drop/announcements.tsx
index 61785310bdcf37..3c65008f8f38b7 100644
--- a/x-pack/plugins/lens/public/drag_drop/announcements.tsx
+++ b/x-pack/plugins/lens/public/drag_drop/announcements.tsx
@@ -11,6 +11,7 @@ export interface HumanData {
label: string;
groupLabel?: string;
position?: number;
+ nextLabel?: string;
}
type AnnouncementFunction = (draggedElement: HumanData, dropElement: HumanData) => string;
@@ -25,7 +26,7 @@ const selectedTargetReplace = (
{ label: dropLabel, groupLabel, position }: HumanData
) =>
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replace', {
- defaultMessage: `Selected {dropLabel} in {groupLabel} group at position {position}. Press space or enter to replace {dropLabel} with {label}.`,
+ defaultMessage: `Replace {dropLabel} in {groupLabel} group at position {position} with {label}. Press space or enter to replace`,
values: {
label,
dropLabel,
@@ -34,65 +35,103 @@ const selectedTargetReplace = (
},
});
-const droppedReplace = ({ label }: HumanData, { label: dropLabel, groupLabel }: HumanData) =>
+const droppedReplace = (
+ { label }: HumanData,
+ { label: dropLabel, groupLabel, position }: HumanData
+) =>
i18n.translate('xpack.lens.dragDrop.announce.duplicated.replace', {
- defaultMessage:
- 'You have dropped the item. You have replaced {dropLabel} with {label} in {groupLabel} group.',
+ defaultMessage: 'Replaced {dropLabel} with {label} in {groupLabel} at position {position}',
values: {
label,
dropLabel,
groupLabel,
+ position,
},
});
export const announcements: CustomAnnouncementsType = {
selectedTarget: {
- reorder: ({ label, position: prevPosition }, { position }) =>
+ reorder: ({ label, groupLabel, position: prevPosition }, { position }) =>
prevPosition === position
? i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.reorderedBack', {
- defaultMessage: `You have moved the item {label} back to position {prevPosition}`,
+ defaultMessage: `{label} returned to its initial position {prevPosition}`,
values: {
label,
prevPosition,
},
})
: i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.reordered', {
- defaultMessage: `You have moved the item {label} from position {prevPosition} to position {position}`,
+ defaultMessage: `Reorder {label} in {groupLabel} group from position {prevPosition} to position {position}. Press space or enter to reorder`,
values: {
+ groupLabel,
label,
position,
prevPosition,
},
}),
- duplicate_in_group: ({ label }, { label: dropLabel, groupLabel, position }) =>
+ duplicate_in_group: ({ label }, { groupLabel, position }) =>
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.duplicated', {
- defaultMessage: `Selected {dropLabel} in {groupLabel} group at position {position}. Press space or enter to duplicate {label}.`,
+ defaultMessage: `Duplicate {label} to {groupLabel} group at position {position}. Press space or enter to duplicate`,
values: {
label,
- dropLabel,
groupLabel,
position,
},
}),
field_replace: selectedTargetReplace,
replace_compatible: selectedTargetReplace,
- replace_incompatible: selectedTargetReplace,
+ replace_incompatible: (
+ { label }: HumanData,
+ { label: dropLabel, groupLabel, position, nextLabel }: HumanData
+ ) =>
+ i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatible', {
+ defaultMessage: `Convert {label} to {nextLabel} and replace {dropLabel} in {groupLabel} group at position {position}. Press space or enter to replace`,
+ values: {
+ label,
+ nextLabel,
+ dropLabel,
+ groupLabel,
+ position,
+ },
+ }),
+ move_incompatible: (
+ { label }: HumanData,
+ { label: groupLabel, position, nextLabel }: HumanData
+ ) =>
+ i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.moveIncompatible', {
+ defaultMessage: `Convert {label} to {nextLabel} and move to {groupLabel} group at position {position}. Press space or enter to move`,
+ values: {
+ label,
+ nextLabel,
+ groupLabel,
+ position,
+ },
+ }),
+ move_compatible: ({ label }: HumanData, { groupLabel, position }: HumanData) =>
+ i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.moveCompatible', {
+ defaultMessage: `Move {label} to {groupLabel} group at position {position}. Press space or enter to move`,
+ values: {
+ label,
+ groupLabel,
+ position,
+ },
+ }),
},
dropped: {
- reorder: ({ label, position: prevPosition }, { position }) =>
+ reorder: ({ label, groupLabel, position: prevPosition }, { position }) =>
i18n.translate('xpack.lens.dragDrop.announce.dropped.reordered', {
defaultMessage:
- 'You have dropped the item {label}. You have moved the item from position {prevPosition} to positon {position}',
+ 'Reordered {label} in {groupLabel} group from position {prevPosition} to positon {position}',
values: {
label,
+ groupLabel,
position,
prevPosition,
},
}),
duplicate_in_group: ({ label }, { groupLabel, position }) =>
i18n.translate('xpack.lens.dragDrop.announce.dropped.duplicated', {
- defaultMessage:
- 'You have dropped the item. You have duplicated {label} in {groupLabel} group at position {position}',
+ defaultMessage: 'Duplicated {label} in {groupLabel} group at position {position}',
values: {
label,
groupLabel,
@@ -101,7 +140,42 @@ export const announcements: CustomAnnouncementsType = {
}),
field_replace: droppedReplace,
replace_compatible: droppedReplace,
- replace_incompatible: droppedReplace,
+ replace_incompatible: (
+ { label }: HumanData,
+ { label: dropLabel, groupLabel, position, nextLabel }: HumanData
+ ) =>
+ i18n.translate('xpack.lens.dragDrop.announce.dropped.replaceIncompatible', {
+ defaultMessage:
+ 'Converted {label} to {nextLabel} and replaced {dropLabel} in {groupLabel} group at position {position}',
+ values: {
+ label,
+ nextLabel,
+ dropLabel,
+ groupLabel,
+ position,
+ },
+ }),
+ move_incompatible: ({ label }: HumanData, { groupLabel, position, nextLabel }: HumanData) =>
+ i18n.translate('xpack.lens.dragDrop.announce.dropped.moveIncompatible', {
+ defaultMessage:
+ 'Converted {label} to {nextLabel} and moved to {groupLabel} group at position {position}',
+ values: {
+ label,
+ nextLabel,
+ groupLabel,
+ position,
+ },
+ }),
+
+ move_compatible: ({ label }: HumanData, { groupLabel, position }: HumanData) =>
+ i18n.translate('xpack.lens.dragDrop.announce.dropped.moveCompatible', {
+ defaultMessage: 'Moved {label} to {groupLabel} group at position {position}',
+ values: {
+ label,
+ groupLabel,
+ position,
+ },
+ }),
},
};
@@ -113,13 +187,29 @@ const defaultAnnouncements = {
label,
},
}),
- cancelled: () =>
- i18n.translate('xpack.lens.dragDrop.announce.cancelled', {
- defaultMessage: 'Movement cancelled',
- }),
+ cancelled: ({ label, groupLabel, position }: HumanData) => {
+ if (!groupLabel || !position) {
+ return i18n.translate('xpack.lens.dragDrop.announce.cancelled', {
+ defaultMessage: 'Movement cancelled. {label} returned to its initial position',
+ values: {
+ label,
+ },
+ });
+ }
+ return i18n.translate('xpack.lens.dragDrop.announce.cancelledItem', {
+ defaultMessage:
+ 'Movement cancelled. {label} returned to {groupLabel} group at position {position}',
+ values: {
+ label,
+ groupLabel,
+ position,
+ },
+ });
+ },
+
noTarget: () => {
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.noSelected', {
- defaultMessage: `No target selected. Use arrow keys to select a target.`,
+ defaultMessage: `No target selected. Use arrow keys to select a target`,
});
},
@@ -129,17 +219,15 @@ const defaultAnnouncements = {
) =>
dropGroupLabel && position
? i18n.translate('xpack.lens.dragDrop.announce.droppedDefault', {
- defaultMessage:
- 'You have dropped {label} to {dropLabel} in {dropGroupLabel} group at position {position}',
+ defaultMessage: 'Added {label} in {dropGroupLabel} group at position {position}',
values: {
label,
dropGroupLabel,
position,
- dropLabel,
},
})
: i18n.translate('xpack.lens.dragDrop.announce.droppedNoPosition', {
- defaultMessage: 'You have dropped {label} to {dropLabel}',
+ defaultMessage: 'Added {label} to {dropLabel}',
values: {
label,
dropLabel,
@@ -151,16 +239,15 @@ const defaultAnnouncements = {
) => {
return dropGroupLabel && position
? i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.default', {
- defaultMessage: `Selected {dropLabel} in {dropGroupLabel} group at position {position}. Press space or enter to drop {label}`,
+ defaultMessage: `Add {label} to {dropGroupLabel} group at position {position}. Press space or enter to add`,
values: {
- dropLabel,
label,
dropGroupLabel,
position,
},
})
: i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.defaultNoPosition', {
- defaultMessage: `Selected {dropLabel}. Press space or enter to drop {label}`,
+ defaultMessage: `Add {label} to {dropLabel}. Press space or enter to add`,
values: {
dropLabel,
label,
diff --git a/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx b/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx
index f9bae547a563bf..f2a2fda730388d 100644
--- a/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx
+++ b/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx
@@ -110,7 +110,7 @@ describe('DragDrop', () => {
const component = mount(
@@ -126,7 +126,7 @@ describe('DragDrop', () => {
expect(preventDefault).toBeCalled();
expect(stopPropagation).toBeCalled();
expect(setDragging).toBeCalledWith(undefined);
- expect(onDrop).toBeCalledWith({ id: '2', humanData: { label: 'label1' } }, 'field_add');
+ expect(onDrop).toBeCalledWith({ id: '2', humanData: { label: 'Label1' } }, 'field_add');
});
test('drop function is not called on dropType undefined', async () => {
@@ -138,7 +138,7 @@ describe('DragDrop', () => {
const component = mount(
@@ -195,7 +195,7 @@ describe('DragDrop', () => {
});
test('additional styles are reflected in the className until drop', () => {
- let dragging: { id: '1'; humanData: { label: 'label1' } } | undefined;
+ let dragging: { id: '1'; humanData: { label: 'Label1' } } | undefined;
const getAdditionalClassesOnEnter = jest.fn().mockReturnValue('additional');
const getAdditionalClassesOnDroppable = jest.fn().mockReturnValue('droppable');
const setA11yMessage = jest.fn();
@@ -206,7 +206,7 @@ describe('DragDrop', () => {
dragging={dragging}
setA11yMessage={setA11yMessage}
setDragging={() => {
- dragging = { id: '1', humanData: { label: 'label1' } };
+ dragging = { id: '1', humanData: { label: 'Label1' } };
}}
>
{
});
test('additional enter styles are reflected in the className until dragleave', () => {
- let dragging: { id: '1'; humanData: { label: 'label1' } } | undefined;
+ let dragging: { id: '1'; humanData: { label: 'Label1' } } | undefined;
const getAdditionalClasses = jest.fn().mockReturnValue('additional');
const getAdditionalClassesOnDroppable = jest.fn().mockReturnValue('droppable');
const setActiveDropTarget = jest.fn();
@@ -252,7 +252,7 @@ describe('DragDrop', () => {
setA11yMessage={jest.fn()}
dragging={dragging}
setDragging={() => {
- dragging = { id: '1', humanData: { label: 'label1' } };
+ dragging = { id: '1', humanData: { label: 'Label1' } };
}}
setActiveDropTarget={setActiveDropTarget}
activeDropTarget={
@@ -303,7 +303,7 @@ describe('DragDrop', () => {
draggable: true,
value: {
id: '1',
- humanData: { label: 'label1', position: 1 },
+ humanData: { label: 'Label1', position: 1 },
},
children: '1',
order: [2, 0, 0, 0],
@@ -326,7 +326,7 @@ describe('DragDrop', () => {
dragType: 'move' as 'copy' | 'move',
value: {
id: '3',
- humanData: { label: 'label3', position: 1 },
+ humanData: { label: 'label3', position: 1, groupLabel: 'Y' },
},
onDrop,
dropType: 'replace_compatible' as DropType,
@@ -337,7 +337,7 @@ describe('DragDrop', () => {
dragType: 'move' as 'copy' | 'move',
value: {
id: '4',
- humanData: { label: 'label4', position: 2 },
+ humanData: { label: 'label4', position: 2, groupLabel: 'Y' },
},
order: [2, 0, 2, 1],
},
@@ -380,11 +380,11 @@ describe('DragDrop', () => {
});
keyboardHandler.simulate('keydown', { key: 'Enter' });
expect(setA11yMessage).toBeCalledWith(
- 'Selected label3 in group at position 1. Press space or enter to replace label3 with label1.'
+ 'Replace label3 in Y group at position 1 with Label1. Press space or enter to replace'
);
expect(setActiveDropTarget).toBeCalledWith(undefined);
expect(onDrop).toBeCalledWith(
- { humanData: { label: 'label1', position: 1 }, id: '1' },
+ { humanData: { label: 'Label1', position: 1 }, id: '1' },
'move_compatible'
);
});
@@ -437,7 +437,7 @@ describe('DragDrop', () => {
draggable: true,
value: {
id: '1',
- humanData: { label: 'label1', position: 1 },
+ humanData: { label: 'Label1', position: 1 },
},
children: '1',
order: [2, 0, 0, 0],
@@ -488,19 +488,19 @@ describe('DragDrop', () => {
const items = [
{
id: '1',
- humanData: { label: 'label1', position: 1 },
+ humanData: { label: 'Label1', position: 1, groupLabel: 'X' },
onDrop,
dropType: 'reorder' as DropType,
},
{
id: '2',
- humanData: { label: 'label2', position: 2 },
+ humanData: { label: 'label2', position: 2, groupLabel: 'X' },
onDrop,
dropType: 'reorder' as DropType,
},
{
id: '3',
- humanData: { label: 'label3', position: 3 },
+ humanData: { label: 'label3', position: 3, groupLabel: 'X' },
onDrop,
dropType: 'reorder' as DropType,
},
@@ -583,7 +583,7 @@ describe('DragDrop', () => {
});
expect(setDragging).toBeCalledWith({ ...items[0] });
- expect(setA11yMessage).toBeCalledWith('Lifted label1');
+ expect(setA11yMessage).toBeCalledWith('Lifted Label1');
expect(
component
.find('[data-test-subj="lnsDragDrop-reorderableGroup"]')
@@ -652,7 +652,7 @@ describe('DragDrop', () => {
jest.runAllTimers();
expect(setA11yMessage).toBeCalledWith(
- 'You have dropped the item label1. You have moved the item from position 1 to positon 3'
+ 'Reordered Label1 in X group from position 1 to positon 3'
);
expect(preventDefault).toBeCalled();
expect(stopPropagation).toBeCalled();
@@ -687,7 +687,7 @@ describe('DragDrop', () => {
expect(setActiveDropTarget).toBeCalledWith(items[1]);
expect(setA11yMessage).toBeCalledWith(
- 'You have moved the item label1 from position 1 to position 2'
+ 'Reorder Label1 in X group from position 1 to position 2. Press space or enter to reorder'
);
});
test(`Keyboard navigation: user can drop element to an activeDropTarget`, () => {
@@ -729,13 +729,17 @@ describe('DragDrop', () => {
jest.runAllTimers();
expect(onDropHandler).not.toHaveBeenCalled();
- expect(setA11yMessage).toBeCalledWith('Movement cancelled');
+ expect(setA11yMessage).toBeCalledWith(
+ 'Movement cancelled. Label1 returned to X group at position 1'
+ );
keyboardHandler.simulate('keydown', { key: 'Space' });
keyboardHandler.simulate('keydown', { key: 'ArrowDown' });
keyboardHandler.simulate('blur');
expect(onDropHandler).not.toHaveBeenCalled();
- expect(setA11yMessage).toBeCalledWith('Movement cancelled');
+ expect(setA11yMessage).toBeCalledWith(
+ 'Movement cancelled. Label1 returned to X group at position 1'
+ );
});
test(`Keyboard Navigation: Reordered elements get extra styles to show the reorder effect`, () => {
@@ -772,7 +776,7 @@ describe('DragDrop', () => {
component.find('[data-test-subj="lnsDragDrop-translatableDrop"]').at(1).prop('style')
).toEqual(undefined);
expect(setA11yMessage).toBeCalledWith(
- 'You have moved the item label1 from position 1 to position 2'
+ 'Reorder Label1 in X group from position 1 to position 2. Press space or enter to reorder'
);
component
@@ -837,7 +841,7 @@ describe('DragDrop', () => {
keyboardHandler.simulate('keydown', { key: 'Space' });
keyboardHandler.simulate('keydown', { key: 'ArrowUp' });
expect(setActiveDropTarget).toBeCalledWith(undefined);
- expect(setA11yMessage).toBeCalledWith('You have moved the item label1 back to position 1');
+ expect(setA11yMessage).toBeCalledWith('Label1 returned to its initial position 1');
});
});
});
diff --git a/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx b/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx
index 4b250643203279..6c6a65ab421b33 100644
--- a/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx
+++ b/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx
@@ -267,7 +267,7 @@ const DragInner = memo(function DragInner({
setDragging(undefined);
setActiveDropTarget(undefined);
setKeyboardMode(false);
- setA11yMessage(announce.cancelled());
+ setA11yMessage(announce.cancelled(value.humanData));
if (onDragEnd) {
onDragEnd();
}
diff --git a/x-pack/plugins/lens/public/drag_drop/providers.tsx b/x-pack/plugins/lens/public/drag_drop/providers.tsx
index 1a056902a474d6..deb9bf6cb17aec 100644
--- a/x-pack/plugins/lens/public/drag_drop/providers.tsx
+++ b/x-pack/plugins/lens/public/drag_drop/providers.tsx
@@ -204,12 +204,12 @@ export function RootDragDropProvider({ children }: { children: React.ReactNode }
{i18n.translate('xpack.lens.dragDrop.keyboardInstructionsReorder', {
- defaultMessage: `Press enter or space to dragging. When dragging, use the up/down arrow keys to reorder items in the group and left/right arrow keys to choose drop targets outside of the group. Press enter or space again to finish.`,
+ defaultMessage: `Press space or enter to start dragging. When dragging, use the up/down arrow keys to reorder items in the group and left/right arrow keys to choose drop targets outside of the group. Press space or enter again to finish.`,
})}
{i18n.translate('xpack.lens.dragDrop.keyboardInstructions', {
- defaultMessage: `Press enter or space to start dragging. When dragging, use the left/right arrow keys to move between drop targets. Press enter or space again to finish.`,
+ defaultMessage: `Press space or enter to start dragging. When dragging, use the left/right arrow keys to move between drop targets. Press space or enter again to finish.`,
})}
diff --git a/x-pack/plugins/lens/public/drag_drop/readme.md b/x-pack/plugins/lens/public/drag_drop/readme.md
index 55a9e3157c2471..01cc4c7bc85a52 100644
--- a/x-pack/plugins/lens/public/drag_drop/readme.md
+++ b/x-pack/plugins/lens/public/drag_drop/readme.md
@@ -56,7 +56,7 @@ const { dragging } = useContext(DragContext);
return (
onChange([...items, item])}
>
{items.map((x) => (
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx
index f493000aa587a9..1cbd41fff2a8fb 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx
@@ -64,13 +64,16 @@ export function DraggableDimensionButton({
columnId: string;
registerNewButtonRef: (id: string, instance: HTMLDivElement | null) => void;
}) {
- const dropType = layerDatasource.getDropTypes({
+ const dropProps = layerDatasource.getDropProps({
...layerDatasourceDropProps,
columnId,
filterOperations: group.filterOperations,
groupId: group.groupId,
});
+ const dropType = dropProps?.dropType;
+ const nextLabel = dropProps?.nextLabel;
+
const value = useMemo(
() => ({
columnId,
@@ -82,9 +85,10 @@ export function DraggableDimensionButton({
label,
groupLabel: group.groupLabel,
position: accessorIndex + 1,
+ nextLabel: nextLabel || '',
},
}),
- [columnId, group.groupId, accessorIndex, layerId, dropType, label, group.groupLabel]
+ [columnId, group.groupId, accessorIndex, layerId, dropType, label, group.groupLabel, nextLabel]
);
// todo: simplify by id and use drop targets?
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx
index a83d4bde0383c6..c9d0a7b002870c 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx
@@ -54,13 +54,16 @@ export function EmptyDimensionButton({
setNewColumnId(generateId());
}, [itemIndex]);
- const dropType = layerDatasource.getDropTypes({
+ const dropProps = layerDatasource.getDropProps({
...layerDatasourceDropProps,
columnId: newColumnId,
filterOperations: group.filterOperations,
groupId: group.groupId,
});
+ const dropType = dropProps?.dropType;
+ const nextLabel = dropProps?.nextLabel;
+
const value = useMemo(
() => ({
columnId: newColumnId,
@@ -72,9 +75,10 @@ export function EmptyDimensionButton({
label,
groupLabel: group.groupLabel,
position: itemIndex + 1,
+ nextLabel: nextLabel || '',
},
}),
- [dropType, newColumnId, group.groupId, layerId, group.groupLabel, itemIndex]
+ [dropType, newColumnId, group.groupId, layerId, group.groupLabel, itemIndex, nextLabel]
);
return (
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
index 1f97399fdd2929..619147987cdd55 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
@@ -440,7 +440,10 @@ describe('LayerPanel', () => {
],
});
- mockDatasource.getDropTypes.mockReturnValue('field_add');
+ mockDatasource.getDropProps.mockReturnValue({
+ dropType: 'field_add',
+ nextLabel: '',
+ });
const draggingField = {
field: { name: 'dragged' },
@@ -459,7 +462,7 @@ describe('LayerPanel', () => {
);
- expect(mockDatasource.getDropTypes).toHaveBeenCalledWith(
+ expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
expect.objectContaining({
dragDropContext: expect.objectContaining({
dragging: draggingField,
@@ -492,8 +495,8 @@ describe('LayerPanel', () => {
],
});
- mockDatasource.getDropTypes.mockImplementation(({ columnId }) =>
- columnId !== 'a' ? 'field_replace' : undefined
+ mockDatasource.getDropProps.mockImplementation(({ columnId }) =>
+ columnId !== 'a' ? { dropType: 'field_replace', nextLabel: '' } : undefined
);
const draggingField = {
@@ -513,7 +516,7 @@ describe('LayerPanel', () => {
);
- expect(mockDatasource.getDropTypes).toHaveBeenCalledWith(
+ expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
expect.objectContaining({ columnId: 'a' })
);
@@ -554,7 +557,10 @@ describe('LayerPanel', () => {
],
});
- mockDatasource.getDropTypes.mockReturnValue('replace_compatible');
+ mockDatasource.getDropProps.mockReturnValue({
+ dropType: 'replace_compatible',
+ nextLabel: '',
+ });
const draggingOperation = {
layerId: 'first',
@@ -574,7 +580,7 @@ describe('LayerPanel', () => {
);
- expect(mockDatasource.getDropTypes).toHaveBeenCalledWith(
+ expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
expect.objectContaining({
dragDropContext: expect.objectContaining({
dragging: draggingOperation,
diff --git a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
index 36c2d7128460cd..db3b29bb74d317 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
@@ -88,7 +88,7 @@ export function createMockDatasource(id: string): DatasourceMock {
uniqueLabels: jest.fn((_state) => ({})),
renderDimensionTrigger: jest.fn(),
renderDimensionEditor: jest.fn(),
- getDropTypes: jest.fn(),
+ getDropProps: jest.fn(),
onDrop: jest.fn(),
// this is an additional property which doesn't exist on real datasources
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts
index 1f0381d92ce640..17f069b8831e7b 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts
@@ -6,7 +6,7 @@
*/
import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public';
import { IndexPatternDimensionEditorProps } from './dimension_panel';
-import { onDrop, getDropTypes } from './droppable';
+import { onDrop, getDropProps } from './droppable';
import { DragContextState } from '../../drag_drop';
import { createMockedDragDropContext } from '../mocks';
import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup, CoreSetup } from 'kibana/public';
@@ -91,7 +91,7 @@ const draggingField = {
* - Dimension trigger: Not tested here
* - Dimension editor component: First half of the tests
*
- * - getDropTypes: Returns drop types that are possible for the current dragging field or other dimension
+ * - getDropProps: Returns drop types that are possible for the current dragging field or other dimension
* - onDrop: Correct application of drop logic
*/
describe('IndexPatternDimensionEditorPanel', () => {
@@ -174,14 +174,14 @@ describe('IndexPatternDimensionEditorPanel', () => {
});
const groupId = 'a';
- describe('getDropTypes', () => {
+ describe('getDropProps', () => {
it('returns undefined if no drag is happening', () => {
- expect(getDropTypes({ ...defaultProps, groupId, dragDropContext })).toBe(undefined);
+ expect(getDropProps({ ...defaultProps, groupId, dragDropContext })).toBe(undefined);
});
it('returns undefined if the dragged item has no field', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -198,7 +198,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('returns undefined if field is not supported by filterOperations', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -217,7 +217,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('returns remove_add if the field is supported by filterOperations and the dropTarget is an existing column', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -226,12 +226,12 @@ describe('IndexPatternDimensionEditorPanel', () => {
},
filterOperations: (op: OperationMetadata) => op.dataType === 'number',
})
- ).toBe('field_replace');
+ ).toEqual({ dropType: 'field_replace', nextLabel: 'Intervals' });
});
it('returns undefined if the field belongs to another index pattern', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -250,7 +250,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('returns undefined if the dragged field is already in use by this operation', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -275,7 +275,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('returns move if the dragged column is compatible', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -290,7 +290,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
},
columnId: 'col2',
})
- ).toBe('move_compatible');
+ ).toEqual({ dropType: 'move_compatible' });
});
it('returns undefined if the dragged column from different group uses the same field as the dropTarget', () => {
@@ -318,7 +318,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
};
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -357,7 +357,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
};
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -373,7 +373,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
columnId: 'col2',
filterOperations: (op: OperationMetadata) => op.isBucketed === false,
})
- ).toEqual('replace_incompatible');
+ ).toEqual({ dropType: 'replace_incompatible', nextLabel: 'Unique count' });
});
});
describe('onDrop', () => {
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts
index 69c7e8c3c2ae61..be791b3c7f7cec 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts
@@ -10,21 +10,29 @@ import {
DatasourceDimensionDropHandlerProps,
isDraggedOperation,
DraggedOperation,
+ DropType,
} from '../../types';
import { IndexPatternColumn } from '../indexpattern';
-import { insertOrReplaceColumn, deleteColumn, getOperationTypesForField } from '../operations';
+import {
+ insertOrReplaceColumn,
+ deleteColumn,
+ getOperationTypesForField,
+ getOperationDisplay,
+} from '../operations';
import { mergeLayer } from '../state_helpers';
import { hasField, isDraggedField } from '../utils';
-import { IndexPatternPrivateState, IndexPatternField, DraggedField } from '../types';
+import { IndexPatternPrivateState, DraggedField } from '../types';
import { trackUiEvent } from '../../lens_ui_telemetry';
type DropHandlerProps = DatasourceDimensionDropHandlerProps & {
droppedItem: T;
};
-export function getDropTypes(
+const operationLabels = getOperationDisplay();
+
+export function getDropProps(
props: DatasourceDimensionDropProps & { groupId: string }
-) {
+): { dropType: DropType; nextLabel?: string } | undefined {
const { dragging } = props.dragDropContext;
if (!dragging) {
return;
@@ -32,23 +40,27 @@ export function getDropTypes(
const layerIndexPatternId = props.state.layers[props.layerId].indexPatternId;
- function hasOperationForField(field: IndexPatternField) {
- const operationsForNewField = getOperationTypesForField(field, props.filterOperations);
- return !!operationsForNewField.length;
- }
-
const currentColumn = props.state.layers[props.layerId].columns[props.columnId];
if (isDraggedField(dragging)) {
- if (
- !!(layerIndexPatternId === dragging.indexPatternId && hasOperationForField(dragging.field))
- ) {
+ const operationsForNewField = getOperationTypesForField(dragging.field, props.filterOperations);
+
+ if (!!(layerIndexPatternId === dragging.indexPatternId && operationsForNewField.length)) {
+ const highestPriorityOperationLabel = operationLabels[operationsForNewField[0]].displayName;
if (!currentColumn) {
- return 'field_add';
+ return { dropType: 'field_add', nextLabel: highestPriorityOperationLabel };
} else if (
(hasField(currentColumn) && currentColumn.sourceField !== dragging.field.name) ||
!hasField(currentColumn)
) {
- return 'field_replace';
+ const persistingOperationLabel =
+ currentColumn &&
+ operationsForNewField.includes(currentColumn.operationType) &&
+ operationLabels[currentColumn.operationType].displayName;
+
+ return {
+ dropType: 'field_replace',
+ nextLabel: persistingOperationLabel || highestPriorityOperationLabel,
+ };
}
}
return;
@@ -62,9 +74,9 @@ export function getDropTypes(
// same group
if (props.groupId === dragging.groupId) {
if (currentColumn) {
- return 'reorder';
+ return { dropType: 'reorder' };
}
- return 'duplicate_in_group';
+ return { dropType: 'duplicate_in_group' };
}
// compatible group
@@ -80,20 +92,34 @@ export function getDropTypes(
}
if (props.filterOperations(op)) {
if (currentColumn) {
- return 'replace_compatible'; // in the future also 'swap_compatible' and 'duplicate_compatible'
+ return { dropType: 'replace_compatible' }; // in the future also 'swap_compatible' and 'duplicate_compatible'
} else {
- return 'move_compatible'; // in the future also 'duplicate_compatible'
+ return { dropType: 'move_compatible' }; // in the future also 'duplicate_compatible'
}
}
// suggest
const field =
hasField(op) && props.state.indexPatterns[layerIndexPatternId].getFieldByName(op.sourceField);
- if (field && hasOperationForField(field)) {
+ const operationsForNewField = field && getOperationTypesForField(field, props.filterOperations);
+
+ if (operationsForNewField && operationsForNewField?.length) {
+ const highestPriorityOperationLabel = operationLabels[operationsForNewField[0]].displayName;
+
if (currentColumn) {
- return 'replace_incompatible'; // in the future also 'swap_incompatible', 'duplicate_incompatible'
+ const persistingOperationLabel =
+ currentColumn &&
+ operationsForNewField.includes(currentColumn.operationType) &&
+ operationLabels[currentColumn.operationType].displayName;
+ return {
+ dropType: 'replace_incompatible',
+ nextLabel: persistingOperationLabel || highestPriorityOperationLabel,
+ }; // in the future also 'swap_incompatible', 'duplicate_incompatible'
} else {
- return 'move_incompatible'; // in the future also 'duplicate_incompatible'
+ return {
+ dropType: 'move_incompatible',
+ nextLabel: highestPriorityOperationLabel,
+ }; // in the future also 'duplicate_incompatible'
}
}
}
@@ -178,6 +204,12 @@ function onMoveDropToNonCompatibleGroup(props: DropHandlerProps {
renderDimensionTrigger: (domElement: Element, props: DatasourceDimensionTriggerProps) => void;
renderDimensionEditor: (domElement: Element, props: DatasourceDimensionEditorProps) => void;
renderLayerPanel: (domElement: Element, props: DatasourceLayerPanelProps) => void;
- getDropTypes: (
+ getDropProps: (
props: DatasourceDimensionDropProps & { groupId: string }
- ) => DropType | undefined;
+ ) => { dropType: DropType; nextLabel?: string } | undefined;
onDrop: (props: DatasourceDimensionDropHandlerProps) => false | true | { deleted: string };
updateStateOnCloseDimension?: (props: {
layerId: string;
From 0d5b2c632ad5b2b036d631036dd30853ec9dc64c Mon Sep 17 00:00:00 2001
From: Joe Reuter
Date: Wed, 17 Feb 2021 16:51:04 +0100
Subject: [PATCH 07/79] Keep volatile information out of stored search source
(#91517)
---
.../public/application/angular/discover.js | 47 ++++++------
.../angular/discover_datagrid.html | 2 +-
.../application/angular/discover_legacy.html | 2 +-
.../application/components/discover.tsx | 1 +
.../components/discover_topnav.test.tsx | 3 +-
.../components/discover_topnav.tsx | 6 +-
.../top_nav/get_top_nav_links.test.ts | 2 +
.../components/top_nav/get_top_nav_links.ts | 6 +-
.../application/helpers/get_sharing_data.ts | 4 +-
.../helpers/persist_saved_search.ts | 3 +-
.../helpers/update_search_source.test.ts | 62 ++++++++++------
.../helpers/update_search_source.ts | 74 +++++++++++--------
12 files changed, 123 insertions(+), 89 deletions(-)
diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js
index 3733e866989588..c2bbbf2e57a9ad 100644
--- a/src/plugins/discover/public/application/angular/discover.js
+++ b/src/plugins/discover/public/application/angular/discover.js
@@ -169,10 +169,10 @@ function discoverController($route, $scope, Promise) {
let inspectorRequest;
let isChangingIndexPattern = false;
const savedSearch = $route.current.locals.savedObjects.savedSearch;
- $scope.searchSource = savedSearch.searchSource;
+ const persistentSearchSource = savedSearch.searchSource;
$scope.indexPattern = resolveIndexPattern(
$route.current.locals.savedObjects.ip,
- $scope.searchSource,
+ persistentSearchSource,
toastNotifications
);
$scope.useNewFieldsApi = !config.get(SEARCH_FIELDS_FROM_SOURCE);
@@ -370,25 +370,19 @@ function discoverController($route, $scope, Promise) {
});
};
- $scope.searchSource
- .setField('index', $scope.indexPattern)
- .setField('highlightAll', true)
- .setField('version', true);
-
- // Even when searching rollups, we want to use the default strategy so that we get back a
- // document-like response.
- $scope.searchSource.setPreferredSearchStrategyId('default');
+ persistentSearchSource.setField('index', $scope.indexPattern);
// searchSource which applies time range
- const timeRangeSearchSource = savedSearch.searchSource.create();
+ const volatileSearchSource = savedSearch.searchSource.create();
if (isDefaultType($scope.indexPattern)) {
- timeRangeSearchSource.setField('filter', () => {
+ volatileSearchSource.setField('filter', () => {
return timefilter.createFilter($scope.indexPattern);
});
}
- $scope.searchSource.setParent(timeRangeSearchSource);
+ volatileSearchSource.setParent(persistentSearchSource);
+ $scope.volatileSearchSource = volatileSearchSource;
const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : '';
chrome.docTitle.change(`Discover${pageTitleSuffix}`);
@@ -403,7 +397,8 @@ function discoverController($route, $scope, Promise) {
}
function getStateDefaults() {
- const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery();
+ const query =
+ persistentSearchSource.getField('query') || data.query.queryString.getDefaultQuery();
const sort = getSortArray(savedSearch.sort, $scope.indexPattern);
const columns = getDefaultColumns();
@@ -415,7 +410,7 @@ function discoverController($route, $scope, Promise) {
columns,
index: $scope.indexPattern.id,
interval: 'auto',
- filters: _.cloneDeep($scope.searchSource.getOwnField('filter')),
+ filters: _.cloneDeep(persistentSearchSource.getOwnField('filter')),
};
if (savedSearch.grid) {
defaultState.grid = savedSearch.grid;
@@ -556,7 +551,7 @@ function discoverController($route, $scope, Promise) {
.then(function () {
$scope.fetchStatus = fetchStatuses.LOADING;
logInspectorRequest({ searchSessionId });
- return $scope.searchSource.fetch({
+ return $scope.volatileSearchSource.fetch({
abortSignal: abortController.signal,
sessionId: searchSessionId,
});
@@ -603,11 +598,13 @@ function discoverController($route, $scope, Promise) {
}
function onResults(resp) {
- inspectorRequest.stats(getResponseInspectorStats(resp, $scope.searchSource)).ok({ json: resp });
+ inspectorRequest
+ .stats(getResponseInspectorStats(resp, $scope.volatileSearchSource))
+ .ok({ json: resp });
if (getTimeField() && !$scope.state.hideChart) {
const tabifiedData = tabifyAggResponse($scope.opts.chartAggConfigs, resp);
- $scope.searchSource.rawResponse = resp;
+ $scope.volatileSearchSource.rawResponse = resp;
$scope.histogramData = discoverResponseHandler(
tabifiedData,
getDimensions($scope.opts.chartAggConfigs.aggs, $scope.timeRange)
@@ -635,8 +632,8 @@ function discoverController($route, $scope, Promise) {
defaultMessage: 'This request queries Elasticsearch to fetch the data for the search.',
});
inspectorRequest = inspectorAdapters.requests.start(title, { description, searchSessionId });
- inspectorRequest.stats(getRequestInspectorStats($scope.searchSource));
- $scope.searchSource.getSearchRequestBody().then((body) => {
+ inspectorRequest.stats(getRequestInspectorStats($scope.volatileSearchSource));
+ $scope.volatileSearchSource.getSearchRequestBody().then((body) => {
inspectorRequest.json(body);
});
}
@@ -693,9 +690,11 @@ function discoverController($route, $scope, Promise) {
};
$scope.updateDataSource = () => {
- const { indexPattern, searchSource, useNewFieldsApi } = $scope;
+ const { indexPattern, useNewFieldsApi } = $scope;
const { columns, sort } = $scope.state;
- updateSearchSource(searchSource, {
+ updateSearchSource({
+ persistentSearchSource,
+ volatileSearchSource: $scope.volatileSearchSource,
indexPattern,
services,
sort,
@@ -731,12 +730,12 @@ function discoverController($route, $scope, Promise) {
visStateAggs
);
- $scope.searchSource.onRequestStart((searchSource, options) => {
+ $scope.volatileSearchSource.onRequestStart((searchSource, options) => {
if (!$scope.opts.chartAggConfigs) return;
return $scope.opts.chartAggConfigs.onSearchRequestStart(searchSource, options);
});
- $scope.searchSource.setField('aggs', function () {
+ $scope.volatileSearchSource.setField('aggs', function () {
if (!$scope.opts.chartAggConfigs) return;
return $scope.opts.chartAggConfigs.toDsl();
});
diff --git a/src/plugins/discover/public/application/angular/discover_datagrid.html b/src/plugins/discover/public/application/angular/discover_datagrid.html
index e59ebbb0fafd03..42218568a838db 100644
--- a/src/plugins/discover/public/application/angular/discover_datagrid.html
+++ b/src/plugins/discover/public/application/angular/discover_datagrid.html
@@ -17,7 +17,7 @@
reset-query="resetQuery"
result-state="resultState"
rows="rows"
- search-source="searchSource"
+ search-source="volatileSearchSource"
set-index-pattern="setIndexPattern"
show-save-query="showSaveQuery"
state="state"
diff --git a/src/plugins/discover/public/application/angular/discover_legacy.html b/src/plugins/discover/public/application/angular/discover_legacy.html
index 501496494106af..a01f285b1a1506 100644
--- a/src/plugins/discover/public/application/angular/discover_legacy.html
+++ b/src/plugins/discover/public/application/angular/discover_legacy.html
@@ -13,7 +13,7 @@
reset-query="resetQuery"
result-state="resultState"
rows="rows"
- search-source="searchSource"
+ search-source="volatileSearchSource"
state="state"
time-range="timeRange"
top-nav-menu="topNavMenu"
diff --git a/src/plugins/discover/public/application/components/discover.tsx b/src/plugins/discover/public/application/components/discover.tsx
index 71650a4a38472e..1d183aa75cf3a5 100644
--- a/src/plugins/discover/public/application/components/discover.tsx
+++ b/src/plugins/discover/public/application/components/discover.tsx
@@ -206,6 +206,7 @@ export function Discover({
query={state.query}
savedQuery={state.savedQuery}
updateQuery={updateQuery}
+ searchSource={searchSource}
/>
diff --git a/src/plugins/discover/public/application/components/discover_topnav.test.tsx b/src/plugins/discover/public/application/components/discover_topnav.test.tsx
index 50c9bcfb5d2557..891dc63c92c7c6 100644
--- a/src/plugins/discover/public/application/components/discover_topnav.test.tsx
+++ b/src/plugins/discover/public/application/components/discover_topnav.test.tsx
@@ -20,7 +20,7 @@ import { SavedObject } from '../../../../../core/types';
import { DiscoverTopNav, DiscoverTopNavProps } from './discover_topnav';
import { RequestAdapter } from '../../../../inspector/common/adapters/request';
import { TopNavMenu } from '../../../../navigation/public';
-import { Query } from '../../../../data/common';
+import { ISearchSource, Query } from '../../../../data/common';
import { DiscoverSearchSessionManager } from '../angular/discover_search_session';
import { Subject } from 'rxjs';
@@ -61,6 +61,7 @@ function getProps(): DiscoverTopNavProps {
savedQuery: '',
updateQuery: jest.fn(),
onOpenInspector: jest.fn(),
+ searchSource: {} as ISearchSource,
};
}
diff --git a/src/plugins/discover/public/application/components/discover_topnav.tsx b/src/plugins/discover/public/application/components/discover_topnav.tsx
index fd2aba22aa41db..ee59ee13583bdd 100644
--- a/src/plugins/discover/public/application/components/discover_topnav.tsx
+++ b/src/plugins/discover/public/application/components/discover_topnav.tsx
@@ -10,7 +10,7 @@ import { DiscoverProps } from './types';
import { getTopNavLinks } from './top_nav/get_top_nav_links';
import { Query, TimeRange } from '../../../../data/common/query';
-export type DiscoverTopNavProps = Pick & {
+export type DiscoverTopNavProps = Pick & {
onOpenInspector: () => void;
query?: Query;
savedQuery?: string;
@@ -24,6 +24,7 @@ export const DiscoverTopNav = ({
query,
savedQuery,
updateQuery,
+ searchSource,
}: DiscoverTopNavProps) => {
const showDatePicker = useMemo(() => indexPattern.isTimeBased(), [indexPattern]);
const { TopNavMenu } = opts.services.navigation.ui;
@@ -38,8 +39,9 @@ export const DiscoverTopNav = ({
services: opts.services,
state: opts.stateContainer,
onOpenInspector,
+ searchSource,
}),
- [indexPattern, opts, onOpenInspector]
+ [indexPattern, opts, onOpenInspector, searchSource]
);
const updateSavedQueryId = (newSavedQueryId: string | undefined) => {
diff --git a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.test.ts
index 89cb60700074d1..30edb102c420aa 100644
--- a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.test.ts
+++ b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.test.ts
@@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
+import { ISearchSource } from 'src/plugins/data/public';
import { getTopNavLinks } from './get_top_nav_links';
import { inspectorPluginMock } from '../../../../../inspector/public/mocks';
import { indexPatternMock } from '../../../__mocks__/index_pattern';
@@ -33,6 +34,7 @@ test('getTopNavLinks result', () => {
savedSearch: savedSearchMock,
services,
state,
+ searchSource: {} as ISearchSource,
});
expect(topNavLinks).toMatchInlineSnapshot(`
Array [
diff --git a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts
index 513508c478aa95..a1215836f9c5fc 100644
--- a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts
+++ b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.ts
@@ -15,7 +15,7 @@ import { Adapters } from '../../../../../inspector/common/adapters';
import { SavedSearch } from '../../../saved_searches';
import { onSaveSearch } from './on_save_search';
import { GetStateReturn } from '../../angular/discover_state';
-import { IndexPattern } from '../../../kibana_services';
+import { IndexPattern, ISearchSource } from '../../../kibana_services';
/**
* Helper function to build the top nav links
@@ -29,6 +29,7 @@ export const getTopNavLinks = ({
services,
state,
onOpenInspector,
+ searchSource,
}: {
getFieldCounts: () => Promise>;
indexPattern: IndexPattern;
@@ -38,6 +39,7 @@ export const getTopNavLinks = ({
services: DiscoverServices;
state: GetStateReturn;
onOpenInspector: () => void;
+ searchSource: ISearchSource;
}) => {
const newSearch = {
id: 'new',
@@ -93,7 +95,7 @@ export const getTopNavLinks = ({
return;
}
const sharingData = await getSharingData(
- savedSearch.searchSource,
+ searchSource,
state.appStateContainer.getState(),
services.uiSettings,
getFieldCounts
diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.ts
index 85acd575138f71..31de1f2f6ed66a 100644
--- a/src/plugins/discover/public/application/helpers/get_sharing_data.ts
+++ b/src/plugins/discover/public/application/helpers/get_sharing_data.ts
@@ -9,7 +9,7 @@
import { Capabilities, IUiSettingsClient } from 'kibana/public';
import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common';
import { getSortForSearchSource } from '../angular/doc_table';
-import { SearchSource } from '../../../../data/common';
+import { ISearchSource } from '../../../../data/common';
import { AppState } from '../angular/discover_state';
import { SortOrder } from '../../saved_searches/types';
@@ -39,7 +39,7 @@ const getSharingDataFields = async (
* Preparing data to share the current state as link or CSV/Report
*/
export async function getSharingData(
- currentSearchSource: SearchSource,
+ currentSearchSource: ISearchSource,
state: AppState,
config: IUiSettingsClient,
getFieldCounts: () => Promise>
diff --git a/src/plugins/discover/public/application/helpers/persist_saved_search.ts b/src/plugins/discover/public/application/helpers/persist_saved_search.ts
index 06e90c93bc77c9..2e4ab90ee58e54 100644
--- a/src/plugins/discover/public/application/helpers/persist_saved_search.ts
+++ b/src/plugins/discover/public/application/helpers/persist_saved_search.ts
@@ -35,7 +35,8 @@ export async function persistSavedSearch(
state: AppState;
}
) {
- updateSearchSource(savedSearch.searchSource, {
+ updateSearchSource({
+ persistentSearchSource: savedSearch.searchSource,
indexPattern,
services,
sort: state.sort as SortOrder[],
diff --git a/src/plugins/discover/public/application/helpers/update_search_source.test.ts b/src/plugins/discover/public/application/helpers/update_search_source.test.ts
index 51586a6bccc234..97e2de3541d354 100644
--- a/src/plugins/discover/public/application/helpers/update_search_source.test.ts
+++ b/src/plugins/discover/public/application/helpers/update_search_source.test.ts
@@ -17,9 +17,12 @@ import { SortOrder } from '../../saved_searches/types';
describe('updateSearchSource', () => {
test('updates a given search source', async () => {
- const searchSourceMock = createSearchSourceMock({});
+ const persistentSearchSourceMock = createSearchSourceMock({});
+ const volatileSearchSourceMock = createSearchSourceMock({});
const sampleSize = 250;
- const result = updateSearchSource(searchSourceMock, {
+ updateSearchSource({
+ persistentSearchSource: persistentSearchSourceMock,
+ volatileSearchSource: volatileSearchSourceMock,
indexPattern: indexPatternMock,
services: ({
data: dataPluginMock.createStartContract(),
@@ -36,15 +39,18 @@ describe('updateSearchSource', () => {
columns: [],
useNewFieldsApi: false,
});
- expect(result.getField('index')).toEqual(indexPatternMock);
- expect(result.getField('size')).toEqual(sampleSize);
- expect(result.getField('fields')).toBe(undefined);
+ expect(persistentSearchSourceMock.getField('index')).toEqual(indexPatternMock);
+ expect(volatileSearchSourceMock.getField('size')).toEqual(sampleSize);
+ expect(volatileSearchSourceMock.getField('fields')).toBe(undefined);
});
test('updates a given search source with the usage of the new fields api', async () => {
- const searchSourceMock = createSearchSourceMock({});
+ const persistentSearchSourceMock = createSearchSourceMock({});
+ const volatileSearchSourceMock = createSearchSourceMock({});
const sampleSize = 250;
- const result = updateSearchSource(searchSourceMock, {
+ updateSearchSource({
+ persistentSearchSource: persistentSearchSourceMock,
+ volatileSearchSource: volatileSearchSourceMock,
indexPattern: indexPatternMock,
services: ({
data: dataPluginMock.createStartContract(),
@@ -61,16 +67,19 @@ describe('updateSearchSource', () => {
columns: [],
useNewFieldsApi: true,
});
- expect(result.getField('index')).toEqual(indexPatternMock);
- expect(result.getField('size')).toEqual(sampleSize);
- expect(result.getField('fields')).toEqual([{ field: '*' }]);
- expect(result.getField('fieldsFromSource')).toBe(undefined);
+ expect(persistentSearchSourceMock.getField('index')).toEqual(indexPatternMock);
+ expect(volatileSearchSourceMock.getField('size')).toEqual(sampleSize);
+ expect(volatileSearchSourceMock.getField('fields')).toEqual([{ field: '*' }]);
+ expect(volatileSearchSourceMock.getField('fieldsFromSource')).toBe(undefined);
});
test('requests unmapped fields when the flag is provided, using the new fields api', async () => {
- const searchSourceMock = createSearchSourceMock({});
+ const persistentSearchSourceMock = createSearchSourceMock({});
+ const volatileSearchSourceMock = createSearchSourceMock({});
const sampleSize = 250;
- const result = updateSearchSource(searchSourceMock, {
+ updateSearchSource({
+ persistentSearchSource: persistentSearchSourceMock,
+ volatileSearchSource: volatileSearchSourceMock,
indexPattern: indexPatternMock,
services: ({
data: dataPluginMock.createStartContract(),
@@ -88,16 +97,21 @@ describe('updateSearchSource', () => {
useNewFieldsApi: true,
showUnmappedFields: true,
});
- expect(result.getField('index')).toEqual(indexPatternMock);
- expect(result.getField('size')).toEqual(sampleSize);
- expect(result.getField('fields')).toEqual([{ field: '*', include_unmapped: 'true' }]);
- expect(result.getField('fieldsFromSource')).toBe(undefined);
+ expect(persistentSearchSourceMock.getField('index')).toEqual(indexPatternMock);
+ expect(volatileSearchSourceMock.getField('size')).toEqual(sampleSize);
+ expect(volatileSearchSourceMock.getField('fields')).toEqual([
+ { field: '*', include_unmapped: 'true' },
+ ]);
+ expect(volatileSearchSourceMock.getField('fieldsFromSource')).toBe(undefined);
});
test('updates a given search source when showUnmappedFields option is set to true', async () => {
- const searchSourceMock = createSearchSourceMock({});
+ const persistentSearchSourceMock = createSearchSourceMock({});
+ const volatileSearchSourceMock = createSearchSourceMock({});
const sampleSize = 250;
- const result = updateSearchSource(searchSourceMock, {
+ updateSearchSource({
+ persistentSearchSource: persistentSearchSourceMock,
+ volatileSearchSource: volatileSearchSourceMock,
indexPattern: indexPatternMock,
services: ({
data: dataPluginMock.createStartContract(),
@@ -115,9 +129,11 @@ describe('updateSearchSource', () => {
useNewFieldsApi: true,
showUnmappedFields: true,
});
- expect(result.getField('index')).toEqual(indexPatternMock);
- expect(result.getField('size')).toEqual(sampleSize);
- expect(result.getField('fields')).toEqual([{ field: '*', include_unmapped: 'true' }]);
- expect(result.getField('fieldsFromSource')).toBe(undefined);
+ expect(persistentSearchSourceMock.getField('index')).toEqual(indexPatternMock);
+ expect(volatileSearchSourceMock.getField('size')).toEqual(sampleSize);
+ expect(volatileSearchSourceMock.getField('fields')).toEqual([
+ { field: '*', include_unmapped: 'true' },
+ ]);
+ expect(volatileSearchSourceMock.getField('fieldsFromSource')).toBe(undefined);
});
});
diff --git a/src/plugins/discover/public/application/helpers/update_search_source.ts b/src/plugins/discover/public/application/helpers/update_search_source.ts
index 55d2b05a29b690..ba5ac0e8227965 100644
--- a/src/plugins/discover/public/application/helpers/update_search_source.ts
+++ b/src/plugins/discover/public/application/helpers/update_search_source.ts
@@ -15,24 +15,25 @@ import { DiscoverServices } from '../../build_services';
/**
* Helper function to update the given searchSource before fetching/sharing/persisting
*/
-export function updateSearchSource(
- searchSource: ISearchSource,
- {
- indexPattern,
- services,
- sort,
- columns,
- useNewFieldsApi,
- showUnmappedFields,
- }: {
- indexPattern: IndexPattern;
- services: DiscoverServices;
- sort: SortOrder[];
- columns: string[];
- useNewFieldsApi: boolean;
- showUnmappedFields?: boolean;
- }
-) {
+export function updateSearchSource({
+ indexPattern,
+ services,
+ sort,
+ columns,
+ useNewFieldsApi,
+ showUnmappedFields,
+ persistentSearchSource,
+ volatileSearchSource,
+}: {
+ indexPattern: IndexPattern;
+ services: DiscoverServices;
+ sort: SortOrder[];
+ columns: string[];
+ useNewFieldsApi: boolean;
+ showUnmappedFields?: boolean;
+ persistentSearchSource: ISearchSource;
+ volatileSearchSource?: ISearchSource;
+}) {
const { uiSettings, data } = services;
const usedSort = getSortForSearchSource(
sort,
@@ -40,23 +41,32 @@ export function updateSearchSource(
uiSettings.get(SORT_DEFAULT_ORDER_SETTING)
);
- searchSource
+ persistentSearchSource
.setField('index', indexPattern)
- .setField('size', uiSettings.get(SAMPLE_SIZE_SETTING))
- .setField('sort', usedSort)
.setField('query', data.query.queryString.getQuery() || null)
.setField('filter', data.query.filterManager.getFilters());
- if (useNewFieldsApi) {
- searchSource.removeField('fieldsFromSource');
- const fields: Record = { field: '*' };
- if (showUnmappedFields) {
- fields.include_unmapped = 'true';
+
+ if (volatileSearchSource) {
+ volatileSearchSource
+ .setField('size', uiSettings.get(SAMPLE_SIZE_SETTING))
+ .setField('sort', usedSort)
+ .setField('highlightAll', true)
+ .setField('version', true)
+ // Even when searching rollups, we want to use the default strategy so that we get back a
+ // document-like response.
+ .setPreferredSearchStrategyId('default');
+
+ if (useNewFieldsApi) {
+ volatileSearchSource.removeField('fieldsFromSource');
+ const fields: Record = { field: '*' };
+ if (showUnmappedFields) {
+ fields.include_unmapped = 'true';
+ }
+ volatileSearchSource.setField('fields', [fields]);
+ } else {
+ volatileSearchSource.removeField('fields');
+ const fieldNames = indexPattern.fields.map((field) => field.name);
+ volatileSearchSource.setField('fieldsFromSource', fieldNames);
}
- searchSource.setField('fields', [fields]);
- } else {
- searchSource.removeField('fields');
- const fieldNames = indexPattern.fields.map((field) => field.name);
- searchSource.setField('fieldsFromSource', fieldNames);
}
- return searchSource;
}
From ce4c0cda83898f90e7708a762fd3a4625966c643 Mon Sep 17 00:00:00 2001
From: Joe Reuter
Date: Wed, 17 Feb 2021 16:59:19 +0100
Subject: [PATCH 08/79] [Lens] Table column text alignment (#89300)
---
.../__snapshots__/table_basic.test.tsx.snap | 15 ++
.../components/cell_value.test.tsx | 81 +++++++++++
.../components/cell_value.tsx | 8 +-
.../components/dimension_editor.test.tsx | 112 +++++++++++++++
.../components/dimension_editor.tsx | 130 +++++++++++++-----
.../components/table_basic.scss | 16 +++
.../components/table_basic.test.tsx | 35 ++++-
.../components/table_basic.tsx | 18 ++-
.../components/types.ts | 1 +
.../datatable_visualization/expression.tsx | 1 +
.../visualization.test.tsx | 2 +
.../datatable_visualization/visualization.tsx | 2 +
12 files changed, 384 insertions(+), 37 deletions(-)
create mode 100644 x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx
create mode 100644 x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx
diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap b/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap
index 158c7fa4aeec37..992301af13ad04 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap
+++ b/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap
@@ -8,6 +8,11 @@ exports[`DatatableComponent it renders actions column when there are row actions
{
+ const table: Datatable = {
+ type: 'datatable',
+ columns: [
+ {
+ id: 'a',
+ name: 'a',
+ meta: {
+ type: 'number',
+ },
+ },
+ ],
+ rows: [{ a: 123 }],
+ };
+ const CellRenderer = createGridCell(
+ {
+ a: { convert: (x) => `formatted ${x}` } as FieldFormat,
+ },
+ DataContext
+ );
+
+ it('renders formatted value', () => {
+ const instance = mountWithIntl(
+
+ {}}
+ isExpandable={false}
+ isDetails={false}
+ isExpanded={false}
+ />
+
+ );
+ expect(instance.text()).toEqual('formatted 123');
+ });
+
+ it('set class with text alignment', () => {
+ const cell = mountWithIntl(
+
+ {}}
+ isExpandable={false}
+ isDetails={false}
+ isExpanded={false}
+ />
+
+ );
+ expect(cell.find('.lnsTableCell').prop('className')).toContain('--right');
+ });
+});
diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx
index a6e1e3386bcf35..2261dd06b532ba 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx
+++ b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx
@@ -14,19 +14,21 @@ export const createGridCell = (
formatters: Record>,
DataContext: React.Context
) => ({ rowIndex, columnId }: EuiDataGridCellValueElementProps) => {
- const { table } = useContext(DataContext);
+ const { table, alignments } = useContext(DataContext);
const rowValue = table?.rows[rowIndex][columnId];
const content = formatters[columnId]?.convert(rowValue, 'html');
+ const currentAlignment = alignments && alignments[columnId];
+ const alignmentClassName = `lnsTableCell--${currentAlignment}`;
return (
-
);
};
diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx
new file mode 100644
index 00000000000000..e0d31a3ed02012
--- /dev/null
+++ b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx
@@ -0,0 +1,112 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiButtonGroup } from '@elastic/eui';
+import { FramePublicAPI, VisualizationDimensionEditorProps } from '../../types';
+import { DatatableVisualizationState } from '../visualization';
+import { createMockDatasource, createMockFramePublicAPI } from '../../editor_frame_service/mocks';
+import { mountWithIntl } from '@kbn/test/jest';
+import { TableDimensionEditor } from './dimension_editor';
+
+describe('data table dimension editor', () => {
+ let frame: FramePublicAPI;
+ let state: DatatableVisualizationState;
+ let setState: (newState: DatatableVisualizationState) => void;
+ let props: VisualizationDimensionEditorProps;
+
+ function testState(): DatatableVisualizationState {
+ return {
+ layerId: 'first',
+ columns: [
+ {
+ columnId: 'foo',
+ },
+ ],
+ };
+ }
+
+ beforeEach(() => {
+ state = testState();
+ frame = createMockFramePublicAPI();
+ frame.datasourceLayers = {
+ first: createMockDatasource('test').publicAPIMock,
+ };
+ frame.activeData = {
+ first: {
+ type: 'datatable',
+ columns: [
+ {
+ id: 'foo',
+ name: 'foo',
+ meta: {
+ type: 'string',
+ },
+ },
+ ],
+ rows: [],
+ },
+ };
+ setState = jest.fn();
+ props = {
+ accessor: 'foo',
+ frame,
+ groupId: 'columns',
+ layerId: 'first',
+ state,
+ setState,
+ };
+ });
+
+ it('should render default alignment', () => {
+ const instance = mountWithIntl( );
+ expect(instance.find(EuiButtonGroup).prop('idSelected')).toEqual(
+ expect.stringContaining('left')
+ );
+ });
+
+ it('should render default alignment for number', () => {
+ frame.activeData!.first.columns[0].meta.type = 'number';
+ const instance = mountWithIntl( );
+ expect(instance.find(EuiButtonGroup).prop('idSelected')).toEqual(
+ expect.stringContaining('right')
+ );
+ });
+
+ it('should render specific alignment', () => {
+ state.columns[0].alignment = 'center';
+ const instance = mountWithIntl( );
+ expect(instance.find(EuiButtonGroup).prop('idSelected')).toEqual(
+ expect.stringContaining('center')
+ );
+ });
+
+ it('should set state for the right column', () => {
+ state.columns = [
+ {
+ columnId: 'foo',
+ },
+ {
+ columnId: 'bar',
+ },
+ ];
+ const instance = mountWithIntl( );
+ instance.find(EuiButtonGroup).prop('onChange')('center');
+ expect(setState).toHaveBeenCalledWith({
+ ...state,
+ columns: [
+ {
+ columnId: 'foo',
+ alignment: 'center',
+ },
+ {
+ columnId: 'bar',
+ },
+ ],
+ });
+ });
+});
diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx
index 008b805bc8fed3..9c60cd47af3e34 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx
+++ b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx
@@ -7,55 +7,121 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
-import { EuiSwitch, EuiFormRow } from '@elastic/eui';
+import { EuiFormRow, EuiSwitch, EuiButtonGroup, htmlIdGenerator } from '@elastic/eui';
import { VisualizationDimensionEditorProps } from '../../types';
import { DatatableVisualizationState } from '../visualization';
+const idPrefix = htmlIdGenerator()();
+
export function TableDimensionEditor(
props: VisualizationDimensionEditorProps
) {
- const { state, setState, accessor } = props;
- const column = state.columns.find((c) => c.columnId === accessor);
+ const { state, setState, frame, accessor } = props;
+ const column = state.columns.find(({ columnId }) => accessor === columnId);
- const visibleColumnsCount = state.columns.filter((c) => !c.hidden).length;
+ if (!column) return null;
- if (!column) {
- return null;
- }
+ // either read config state or use same logic as chart itself
+ const currentAlignment =
+ column?.alignment ||
+ (frame.activeData &&
+ frame.activeData[state.layerId].columns.find((col) => col.id === accessor)?.meta.type ===
+ 'number'
+ ? 'right'
+ : 'left');
+
+ const visibleColumnsCount = state.columns.filter((c) => !c.hidden).length;
return (
-
-
+ {
- const newState = {
- ...state,
- columns: state.columns.map((currentColumn) => {
+ >
+ {
+ const newMode = id.replace(idPrefix, '') as 'left' | 'right' | 'center';
+ const newColumns = state.columns.map((currentColumn) => {
if (currentColumn.columnId === accessor) {
return {
...currentColumn,
- hidden: !column.hidden,
+ alignment: newMode,
};
} else {
return currentColumn;
}
- }),
- };
- setState(newState);
- }}
- />
-
+ });
+ setState({ ...state, columns: newColumns });
+ }}
+ />
+
+
+ {
+ const newState = {
+ ...state,
+ columns: state.columns.map((currentColumn) => {
+ if (currentColumn.columnId === accessor) {
+ return {
+ ...currentColumn,
+ hidden: !column.hidden,
+ };
+ } else {
+ return currentColumn;
+ }
+ }),
+ };
+ setState(newState);
+ }}
+ />
+
+ >
);
}
diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.scss b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.scss
index 5e5db2c6458095..b99ffb6dce8101 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.scss
+++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.scss
@@ -1,3 +1,19 @@
.lnsDataTableContainer {
height: 100%;
}
+
+.lnsTableCell {
+ @include euiTextTruncate;
+}
+
+.lnsTableCell--left {
+ text-align: left;
+}
+
+.lnsTableCell--right {
+ text-align: right;
+}
+
+.lnsTableCell--center {
+ text-align: center;
+}
\ No newline at end of file
diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx
index 588340fbe97fa9..22577e8ef5fd31 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx
+++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx
@@ -12,7 +12,7 @@ import { EuiDataGrid } from '@elastic/eui';
import { IAggType, IFieldFormat } from 'src/plugins/data/public';
import { EmptyPlaceholder } from '../../shared_components';
import { LensIconChartDatatable } from '../../assets/chart_datatable';
-import { DatatableComponent } from './table_basic';
+import { DataContext, DatatableComponent } from './table_basic';
import { LensMultiTable } from '../../types';
import { DatatableProps } from '../expression';
@@ -427,6 +427,39 @@ describe('DatatableComponent', () => {
expect(wrapper.find(EuiDataGrid).prop('columns')!.length).toEqual(2);
});
+ test('it adds alignment data to context', () => {
+ const { data, args } = sampleArgs();
+
+ const wrapper = shallow(
+ ({ convert: (x) => x } as IFieldFormat)}
+ dispatchEvent={onDispatchEvent}
+ getType={jest.fn()}
+ renderMode="display"
+ />
+ );
+
+ expect(wrapper.find(DataContext.Provider).prop('value').alignments).toEqual({
+ // set via args
+ a: 'center',
+ // default for date
+ b: 'left',
+ // default for number
+ c: 'right',
+ });
+ });
+
test('it should refresh the table header when the datatable data changes', () => {
const { data, args } = sampleArgs();
diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx
index f685990f12dd26..e1687ba28f07bb 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx
+++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx
@@ -40,7 +40,7 @@ import {
createGridSortingConfig,
} from './table_actions';
-const DataContext = React.createContext({});
+export const DataContext = React.createContext({});
const gridStyle: EuiDataGridStyle = {
border: 'horizontal',
@@ -192,6 +192,21 @@ export const DatatableComponent = (props: DatatableRenderProps) => {
]
);
+ const alignments: Record = useMemo(() => {
+ const alignmentMap: Record = {};
+ columnConfig.columns.forEach((column) => {
+ if (column.alignment) {
+ alignmentMap[column.columnId] = column.alignment;
+ } else {
+ const isNumeric =
+ firstLocalTable.columns.find((dataColumn) => dataColumn.id === column.columnId)?.meta
+ .type === 'number';
+ alignmentMap[column.columnId] = isNumeric ? 'right' : 'left';
+ }
+ });
+ return alignmentMap;
+ }, [firstLocalTable, columnConfig]);
+
const trailingControlColumns: EuiDataGridControlColumn[] = useMemo(() => {
if (!hasAtLeastOneRowClickAction || !onRowContextMenuClick) {
return [];
@@ -259,6 +274,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => {
value={{
table: firstLocalTable,
rowHasRowClickTriggerActions: props.rowHasRowClickTriggerActions,
+ alignments,
}}
>
;
}
diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx
index 7ead7be67947c9..f6a38541cda271 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx
+++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx
@@ -138,6 +138,7 @@ export const datatableColumn: ExpressionFunctionDefinition<
inputTypes: ['null'],
args: {
columnId: { types: ['string'], help: '' },
+ alignment: { types: ['string'], help: '' },
hidden: { types: ['boolean'], help: '' },
width: { types: ['number'], help: '' },
},
diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx
index ad5e1e552ccd2b..92136c557ad38d 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx
+++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx
@@ -419,11 +419,13 @@ describe('Datatable Visualization', () => {
columnId: ['c'],
hidden: [],
width: [],
+ alignment: [],
});
expect(columnArgs[1].arguments).toEqual({
columnId: ['b'],
hidden: [],
width: [],
+ alignment: [],
});
});
diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx
index 47f8ce09aea68c..fc69c914deb680 100644
--- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx
+++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx
@@ -23,6 +23,7 @@ export interface ColumnState {
columnId: string;
width?: number;
hidden?: boolean;
+ alignment?: 'left' | 'right' | 'center';
}
export interface SortingState {
@@ -264,6 +265,7 @@ export const datatableVisualization: Visualization
columnId: [column.columnId],
hidden: typeof column.hidden === 'undefined' ? [] : [column.hidden],
width: typeof column.width === 'undefined' ? [] : [column.width],
+ alignment: typeof column.alignment === 'undefined' ? [] : [column.alignment],
},
},
],
From 8f1b39594145d554347280086e16d0f2f04a3b73 Mon Sep 17 00:00:00 2001
From: Maja Grubic
Date: Wed, 17 Feb 2021 16:02:05 +0000
Subject: [PATCH 09/79] [Search Source] Request date fields explicitly (#91478)
* [Search Source] Remove includes when retrieving fields from source
* Removing unusded imports
* Use exclusion filters to retrieve a list of fields
* Exclude _source from fields list
* Fix small check in getting the field list
* Fixing faulty import
* Filter logic
* Adding a unit test for maps use case
* Updating maps unit & functional test
* Add unit test
* Move logic for requesting a field list inside search_source
* Remove unnecessary mock
* Code cleanup as per PR comments
* First pass
* Revert functionality for search_after
* [Search Source] Add date fields with format
* Remove unnecessary log from test
* Use local variable
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../search_source/search_source.test.ts | 66 ++++++++++++++++++-
.../search/search_source/search_source.ts | 66 ++++++++++++++-----
2 files changed, 115 insertions(+), 17 deletions(-)
diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts
index 9e889e85734ee6..23ad7af14b093f 100644
--- a/src/plugins/data/common/search/search_source/search_source.test.ts
+++ b/src/plugins/data/common/search/search_source/search_source.test.ts
@@ -345,8 +345,13 @@ describe('SearchSource', () => {
});
test('allows you to override computed fields if you provide a format', async () => {
+ const indexPatternFields = indexPattern.fields;
+ indexPatternFields.getByType = (type) => {
+ return [];
+ };
searchSource.setField('index', ({
...indexPattern,
+ fields: indexPatternFields,
getComputedFields: () => ({
storedFields: [],
scriptFields: {},
@@ -379,6 +384,11 @@ describe('SearchSource', () => {
test('injects a date format for computed docvalue fields while merging other properties', async () => {
searchSource.setField('index', ({
...indexPattern,
+ fields: {
+ getByType: () => {
+ return [];
+ },
+ },
getComputedFields: () => ({
storedFields: [],
scriptFields: {},
@@ -625,7 +635,7 @@ describe('SearchSource', () => {
searchSource.setField('fields', ['hello', '@timestamp', 'foo-a', 'bar']);
const request = await searchSource.getSearchRequestBody();
- expect(request.fields).toEqual(['hello', '@timestamp', 'bar']);
+ expect(request.fields).toEqual(['hello', '@timestamp', 'bar', 'date']);
expect(request.script_fields).toEqual({ hello: {} });
expect(request.stored_fields).toEqual(['@timestamp', 'bar']);
});
@@ -681,6 +691,60 @@ describe('SearchSource', () => {
});
});
+ describe('handling date fields', () => {
+ test('adds date format to any date field', async () => {
+ searchSource.setField('index', ({
+ ...indexPattern,
+ getComputedFields: () => ({
+ storedFields: [],
+ scriptFields: {},
+ docvalueFields: [{ field: '@timestamp' }],
+ }),
+ fields: {
+ getByType: () => [{ name: '@timestamp', esTypes: ['date_nanos'] }],
+ },
+ getSourceFiltering: () => ({ excludes: [] }),
+ } as unknown) as IndexPattern);
+ searchSource.setField('fields', ['*']);
+
+ const request = await searchSource.getSearchRequestBody();
+ expect(request.fields).toEqual([
+ '*',
+ { field: '@timestamp', format: 'strict_date_optional_time_nanos' },
+ ]);
+ });
+
+ test('adds date format to any date field except the one excluded by source filters', async () => {
+ const indexPatternFields = indexPattern.fields;
+ // @ts-ignore
+ indexPatternFields.getByType = (type) => {
+ return [
+ { name: '@timestamp', esTypes: ['date_nanos'] },
+ { name: 'custom_date', esTypes: ['date'] },
+ ];
+ };
+ searchSource.setField('index', ({
+ ...indexPattern,
+ getComputedFields: () => ({
+ storedFields: [],
+ scriptFields: {},
+ docvalueFields: [{ field: '@timestamp' }, { field: 'custom_date' }],
+ }),
+ fields: indexPatternFields,
+ getSourceFiltering: () => ({ excludes: ['custom_date'] }),
+ } as unknown) as IndexPattern);
+ searchSource.setField('fields', ['*']);
+
+ const request = await searchSource.getSearchRequestBody();
+ expect(request.fields).toEqual([
+ { field: 'foo-bar' },
+ { field: 'field1' },
+ { field: 'field2' },
+ { field: '@timestamp', format: 'strict_date_optional_time_nanos' },
+ ]);
+ });
+ });
+
describe(`#setField('index')`, () => {
describe('auto-sourceFiltering', () => {
describe('new index pattern assigned', () => {
diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts
index 2cf0455ae2df8b..8406c4900bef74 100644
--- a/src/plugins/data/common/search/search_source/search_source.ts
+++ b/src/plugins/data/common/search/search_source/search_source.ts
@@ -59,7 +59,7 @@
*/
import { setWith } from '@elastic/safer-lodash-set';
-import { uniqueId, keyBy, pick, difference, omit, isFunction, isEqual } from 'lodash';
+import { uniqueId, keyBy, pick, difference, omit, isFunction, isEqual, uniqWith } from 'lodash';
import { map, switchMap, tap } from 'rxjs/operators';
import { defer, from } from 'rxjs';
import { isObject } from 'rxjs/internal-compatibility';
@@ -548,6 +548,37 @@ export class SearchSource {
}));
}
+ private getFieldFromDocValueFieldsOrIndexPattern(
+ docvaluesIndex: Record,
+ fld: SearchFieldValue,
+ index?: IndexPattern
+ ) {
+ if (typeof fld === 'string') {
+ return fld;
+ }
+ const fieldName = this.getFieldName(fld);
+ const field = {
+ ...docvaluesIndex[fieldName],
+ ...fld,
+ };
+ if (!index) {
+ return field;
+ }
+ const { fields } = index;
+ const dateFields = fields.getByType('date');
+ const dateField = dateFields.find((indexPatternField) => indexPatternField.name === fieldName);
+ if (!dateField) {
+ return field;
+ }
+ const { esTypes } = dateField;
+ if (esTypes?.includes('date_nanos')) {
+ field.format = 'strict_date_optional_time_nanos';
+ } else if (esTypes?.includes('date')) {
+ field.format = 'strict_date_optional_time';
+ }
+ return field;
+ }
+
private flatten() {
const { getConfig } = this.dependencies;
const searchRequest = this.mergeProps();
@@ -657,22 +688,25 @@ export class SearchSource {
// if items that are in the docvalueFields are provided, we should
// inject the format from the computed fields if one isn't given
const docvaluesIndex = keyBy(filteredDocvalueFields, 'field');
- body.fields = this.getFieldsWithoutSourceFilters(index, body.fields).map(
- (fld: SearchFieldValue) => {
- const fieldName = this.getFieldName(fld);
- if (Object.keys(docvaluesIndex).includes(fieldName)) {
- // either provide the field object from computed docvalues,
- // or merge the user-provided field with the one in docvalues
- return typeof fld === 'string'
- ? docvaluesIndex[fld]
- : {
- ...docvaluesIndex[fieldName],
- ...fld,
- };
- }
- return fld;
+ const bodyFields = this.getFieldsWithoutSourceFilters(index, body.fields);
+ body.fields = uniqWith(
+ bodyFields.concat(filteredDocvalueFields),
+ (fld1: SearchFieldValue, fld2: SearchFieldValue) => {
+ const field1Name = this.getFieldName(fld1);
+ const field2Name = this.getFieldName(fld2);
+ return field1Name === field2Name;
}
- );
+ ).map((fld: SearchFieldValue) => {
+ const fieldName = this.getFieldName(fld);
+ if (Object.keys(docvaluesIndex).includes(fieldName)) {
+ // either provide the field object from computed docvalues,
+ // or merge the user-provided field with the one in docvalues
+ return typeof fld === 'string'
+ ? docvaluesIndex[fld]
+ : this.getFieldFromDocValueFieldsOrIndexPattern(docvaluesIndex, fld, index);
+ }
+ return fld;
+ });
}
} else {
body.fields = filteredDocvalueFields;
From 9c0fdcfdb2c5043e5c21e369895c5c28eb50b8c1 Mon Sep 17 00:00:00 2001
From: Shahzad
Date: Wed, 17 Feb 2021 17:09:47 +0100
Subject: [PATCH 10/79] [UX App] Improve page and filters responsiveness
(#91536)
---
.../RumDashboard/ClientMetrics/Metrics.tsx | 139 +++++++++++++++++
.../app/RumDashboard/ClientMetrics/index.tsx | 142 +++---------------
.../app/RumDashboard/LocalUIFilters/index.tsx | 36 +++--
.../app/RumDashboard/Panels/MainFilters.tsx | 8 +-
.../app/RumDashboard/RumDashboard.tsx | 30 +---
.../components/app/RumDashboard/RumHome.tsx | 15 +-
.../URLFilter/URLSearch/SelectableUrlList.tsx | 1 +
.../RumDashboard/UXMetrics/KeyUXMetrics.tsx | 4 +-
.../app/RumDashboard/UXMetrics/index.tsx | 2 +-
9 files changed, 205 insertions(+), 172 deletions(-)
create mode 100644 x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/Metrics.tsx
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/Metrics.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/Metrics.tsx
new file mode 100644
index 00000000000000..3a9100a0712aab
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/Metrics.tsx
@@ -0,0 +1,139 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import * as React from 'react';
+import numeral from '@elastic/numeral';
+import styled from 'styled-components';
+import { useContext, useEffect } from 'react';
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiStat,
+ EuiToolTip,
+ EuiIconTip,
+} from '@elastic/eui';
+import { useFetcher } from '../../../../hooks/use_fetcher';
+import { I18LABELS } from '../translations';
+import { useUxQuery } from '../hooks/useUxQuery';
+import { formatToSec } from '../UXMetrics/KeyUXMetrics';
+import { CsmSharedContext } from '../CsmSharedContext';
+
+const ClFlexGroup = styled(EuiFlexGroup)`
+ flex-direction: row;
+ @media only screen and (max-width: 768px) {
+ flex-direction: row;
+ justify-content: space-between;
+ }
+`;
+
+function formatTitle(unit: string, value?: number) {
+ if (typeof value === 'undefined') return I18LABELS.dataMissing;
+ return formatToSec(value, unit);
+}
+
+function PageViewsTotalTitle({ pageViews }: { pageViews?: number }) {
+ if (typeof pageViews === 'undefined') {
+ return <>{I18LABELS.dataMissing}>;
+ }
+ return pageViews < 10000 ? (
+ <>{numeral(pageViews).format('0,0')}>
+ ) : (
+
+ <>{numeral(pageViews).format('0 a')}>
+
+ );
+}
+
+export function Metrics() {
+ const uxQuery = useUxQuery();
+
+ const { data, status } = useFetcher(
+ (callApmApi) => {
+ if (uxQuery) {
+ return callApmApi({
+ endpoint: 'GET /api/apm/rum/client-metrics',
+ params: {
+ query: {
+ ...uxQuery,
+ },
+ },
+ });
+ }
+ return Promise.resolve(null);
+ },
+ [uxQuery]
+ );
+
+ const { setSharedData } = useContext(CsmSharedContext);
+
+ useEffect(() => {
+ setSharedData({ totalPageViews: data?.pageViews?.value ?? 0 });
+ }, [data, setSharedData]);
+
+ const STAT_STYLE = { minWidth: '150px', maxWidth: '250px' };
+
+ return (
+
+
+
+ {I18LABELS.totalPageLoad}
+
+ >
+ }
+ isLoading={status !== 'success'}
+ />
+
+
+
+ {I18LABELS.backEnd}
+
+ >
+ }
+ isLoading={status !== 'success'}
+ />
+
+
+
+ {I18LABELS.frontEnd}
+
+ >
+ }
+ isLoading={status !== 'success'}
+ />
+
+
+ }
+ description={I18LABELS.pageViews}
+ isLoading={status !== 'success'}
+ />
+
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx
index 1d6ce21d198c4a..add6ac1b08b281 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx
@@ -6,134 +6,36 @@
*/
import * as React from 'react';
-import numeral from '@elastic/numeral';
-import styled from 'styled-components';
-import { useContext, useEffect } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
- EuiStat,
- EuiToolTip,
- EuiIconTip,
+ EuiPanel,
+ EuiTitle,
+ EuiSpacer,
} from '@elastic/eui';
-import { useFetcher } from '../../../../hooks/use_fetcher';
import { I18LABELS } from '../translations';
-import { useUxQuery } from '../hooks/useUxQuery';
-import { formatToSec } from '../UXMetrics/KeyUXMetrics';
-import { CsmSharedContext } from '../CsmSharedContext';
-
-const ClFlexGroup = styled(EuiFlexGroup)`
- flex-direction: row;
- @media only screen and (max-width: 768px) {
- flex-direction: row;
- justify-content: space-between;
- }
-`;
-
-function formatTitle(unit: string, value?: number) {
- if (typeof value === 'undefined') return I18LABELS.dataMissing;
- return formatToSec(value, unit);
-}
-
-function PageViewsTotalTitle({ pageViews }: { pageViews?: number }) {
- if (typeof pageViews === 'undefined') {
- return <>{I18LABELS.dataMissing}>;
- }
- return pageViews < 10000 ? (
- <>{numeral(pageViews).format('0,0')}>
- ) : (
-
- <>{numeral(pageViews).format('0 a')}>
-
- );
-}
+import { getPercentileLabel } from '../UXMetrics/translations';
+import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
+import { Metrics } from './Metrics';
export function ClientMetrics() {
- const uxQuery = useUxQuery();
-
- const { data, status } = useFetcher(
- (callApmApi) => {
- if (uxQuery) {
- return callApmApi({
- endpoint: 'GET /api/apm/rum/client-metrics',
- params: {
- query: {
- ...uxQuery,
- },
- },
- });
- }
- return Promise.resolve(null);
- },
- [uxQuery]
- );
-
- const { setSharedData } = useContext(CsmSharedContext);
-
- useEffect(() => {
- setSharedData({ totalPageViews: data?.pageViews?.value ?? 0 });
- }, [data, setSharedData]);
-
- const STAT_STYLE = { width: '240px' };
+ const {
+ urlParams: { percentile },
+ } = useUrlParams();
return (
-
-
-
- {I18LABELS.totalPageLoad}
-
- >
- }
- isLoading={status !== 'success'}
- />
-
-
-
- {I18LABELS.backEnd}
-
- >
- }
- isLoading={status !== 'success'}
- />
-
-
-
- {I18LABELS.frontEnd}
-
- >
- }
- isLoading={status !== 'success'}
- />
-
-
- }
- description={I18LABELS.pageViews}
- isLoading={status !== 'success'}
- />
-
-
+
+
+
+
+
+ {I18LABELS.pageLoad} ({getPercentileLabel(percentile!)})
+
+
+
+
+
+
+
);
}
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/LocalUIFilters/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/LocalUIFilters/index.tsx
index 4afecb7623f73e..5b0b475e86e038 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/LocalUIFilters/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/LocalUIFilters/index.tsx
@@ -11,12 +11,14 @@ import {
EuiSpacer,
EuiHorizontalRule,
EuiButtonEmpty,
+ EuiAccordion,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
import { Filter } from './Filter';
import { useLocalUIFilters } from '../hooks/useLocalUIFilters';
import { LocalUIFilterName } from '../../../../../common/ui_filter';
+import { useBreakPoints } from '../../../../hooks/use_break_points';
interface Props {
filterNames: LocalUIFilterName[];
@@ -45,16 +47,20 @@ function LocalUIFilters({
const hasValues = filters.some((filter) => filter.value.length > 0);
- return (
+ const { isSmall } = useBreakPoints();
+
+ const title = (
+
+
+ {i18n.translate('xpack.apm.localFiltersTitle', {
+ defaultMessage: 'Filters',
+ })}
+
+
+ );
+
+ const content = (
<>
-
-
- {i18n.translate('xpack.apm.localFiltersTitle', {
- defaultMessage: 'Filters',
- })}
-
-
-
{children}
{filters.map((filter) => {
return (
@@ -90,6 +96,18 @@ function LocalUIFilters({
) : null}
>
);
+
+ return isSmall ? (
+
+ {content}
+
+ ) : (
+ <>
+ {title}
+
+ {content}
+ >
+ );
}
export { LocalUIFilters };
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/MainFilters.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/MainFilters.tsx
index 7d34328d2653dd..e3e2a979c48d35 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/MainFilters.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/MainFilters.tsx
@@ -13,6 +13,7 @@ import { useFetcher } from '../../../../hooks/use_fetcher';
import { RUM_AGENT_NAMES } from '../../../../../common/agent_name';
import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
import { UserPercentile } from '../UserPercentile';
+import { useBreakPoints } from '../../../../hooks/use_break_points';
export function MainFilters() {
const {
@@ -37,6 +38,11 @@ export function MainFilters() {
[start, end]
);
+ const { isSmall } = useBreakPoints();
+
+ // on mobile we want it to take full width
+ const envStyle = isSmall ? {} : { maxWidth: 200 };
+
return (
<>
@@ -45,7 +51,7 @@ export function MainFilters() {
serviceNames={data ?? []}
/>
-
+
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx
index 50d91ca73c249d..a182de8540f584 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx
@@ -5,46 +5,22 @@
* 2.0.
*/
-import {
- EuiFlexGroup,
- EuiFlexItem,
- EuiTitle,
- EuiSpacer,
- EuiPanel,
-} from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
-import { ClientMetrics } from './ClientMetrics';
-import { I18LABELS } from './translations';
import { UXMetrics } from './UXMetrics';
import { ImpactfulMetrics } from './ImpactfulMetrics';
import { PageLoadAndViews } from './Panels/PageLoadAndViews';
import { VisitorBreakdownsPanel } from './Panels/VisitorBreakdowns';
import { useBreakPoints } from '../../../hooks/use_break_points';
-import { getPercentileLabel } from './UXMetrics/translations';
-import { useUrlParams } from '../../../context/url_params_context/use_url_params';
+import { ClientMetrics } from './ClientMetrics';
export function RumDashboard() {
- const {
- urlParams: { percentile },
- } = useUrlParams();
const { isSmall } = useBreakPoints();
return (
-
-
-
-
-
- {I18LABELS.pageLoad} ({getPercentileLabel(percentile!)})
-
-
-
-
-
-
-
+
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx
index 6e3ed54ed2e780..a0b3781a30b209 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx
@@ -20,24 +20,15 @@ export const UX_LABEL = i18n.translate('xpack.apm.ux.title', {
export function RumHome() {
return (
-
+
{UX_LABEL}
+
-
-
-
-
-
-
+
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/SelectableUrlList.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/SelectableUrlList.tsx
index d3a34a1df25f7a..965449b78f3e08 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/SelectableUrlList.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/SelectableUrlList.tsx
@@ -206,6 +206,7 @@ export function SelectableUrlList({
panelRef={setPopoverRef}
button={search}
closePopover={closePopover}
+ style={{ minWidth: 200 }}
>
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx
index f2e761128e5cea..b8766e8b5ce67d 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx
@@ -36,7 +36,7 @@ export function formatToSec(
}
return (valueInMs / 1000).toFixed(2) + ' s';
}
-const STAT_STYLE = { width: '240px' };
+const STAT_STYLE = { width: '200px' };
interface Props {
data?: UXMetrics | null;
@@ -71,7 +71,7 @@ export function KeyUXMetrics({ data, loading }: Props) {
// Note: FCP value is in ms unit
return (
-
+
-
+
From 3c7e7b0f978e07f4838a5a1a656b6f21527918c2 Mon Sep 17 00:00:00 2001
From: Matthias Wilhelm
Date: Wed, 17 Feb 2021 17:16:32 +0100
Subject: [PATCH 11/79] [Discover] Fix issues with sorting and displaying
geopoints using EuiDataGrid (#91137)
---
.../components/discover_grid/constants.ts | 1 -
.../discover_grid/discover_grid_columns.test.tsx | 14 +++++++-------
.../discover_grid/discover_grid_columns.tsx | 2 +-
.../discover_grid/discover_grid_schema.tsx | 16 +---------------
.../discover_grid/get_render_cell_value.tsx | 16 ----------------
5 files changed, 9 insertions(+), 40 deletions(-)
diff --git a/src/plugins/discover/public/application/components/discover_grid/constants.ts b/src/plugins/discover/public/application/components/discover_grid/constants.ts
index 03e5740793396e..015d0b65246f2e 100644
--- a/src/plugins/discover/public/application/components/discover_grid/constants.ts
+++ b/src/plugins/discover/public/application/components/discover_grid/constants.ts
@@ -8,7 +8,6 @@
// data types
export const kibanaJSON = 'kibana-json';
-export const geoPoint = 'geo-point';
export const gridStyle = {
border: 'all',
fontSize: 's',
diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx
index 2317b8841a37ab..1a721a400803e6 100644
--- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx
+++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx
@@ -27,7 +27,7 @@ describe('Discover grid columns ', function () {
"cellActions": undefined,
"display": undefined,
"id": "extension",
- "isSortable": undefined,
+ "isSortable": false,
"schema": "kibana-json",
},
Object {
@@ -42,7 +42,7 @@ describe('Discover grid columns ', function () {
"cellActions": undefined,
"display": undefined,
"id": "message",
- "isSortable": undefined,
+ "isSortable": false,
"schema": "kibana-json",
},
]
@@ -67,7 +67,7 @@ describe('Discover grid columns ', function () {
"cellActions": undefined,
"display": undefined,
"id": "extension",
- "isSortable": undefined,
+ "isSortable": false,
"schema": "kibana-json",
},
Object {
@@ -79,7 +79,7 @@ describe('Discover grid columns ', function () {
"cellActions": undefined,
"display": undefined,
"id": "message",
- "isSortable": undefined,
+ "isSortable": false,
"schema": "kibana-json",
},
]
@@ -105,7 +105,7 @@ describe('Discover grid columns ', function () {
"display": "Time (timestamp)",
"id": "timestamp",
"initialWidth": 180,
- "isSortable": undefined,
+ "isSortable": false,
"schema": "kibana-json",
},
Object {
@@ -120,7 +120,7 @@ describe('Discover grid columns ', function () {
"cellActions": undefined,
"display": undefined,
"id": "extension",
- "isSortable": undefined,
+ "isSortable": false,
"schema": "kibana-json",
},
Object {
@@ -135,7 +135,7 @@ describe('Discover grid columns ', function () {
"cellActions": undefined,
"display": undefined,
"id": "message",
- "isSortable": undefined,
+ "isSortable": false,
"schema": "kibana-json",
},
]
diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx
index 2e9bd33c606593..c245b402137a01 100644
--- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx
+++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx
@@ -47,7 +47,7 @@ export function buildEuiGridColumn(
const column: EuiDataGridColumn = {
id: columnName,
schema: getSchemaByKbnType(indexPatternField?.type),
- isSortable: indexPatternField?.sortable,
+ isSortable: indexPatternField?.sortable === true,
display:
columnName === '_source'
? i18n.translate('discover.grid.documentHeader', {
diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx
index 83ade88386dbc2..ca5b2c9f199189 100644
--- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx
+++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx
@@ -8,7 +8,7 @@
import React from 'react';
import { EuiCodeBlock, EuiDataGridPopoverContents } from '@elastic/eui';
-import { geoPoint, kibanaJSON } from './constants';
+import { kibanaJSON } from './constants';
import { KBN_FIELD_TYPES } from '../../../../../data/common';
export function getSchemaByKbnType(kbnType: string | undefined) {
@@ -24,8 +24,6 @@ export function getSchemaByKbnType(kbnType: string | undefined) {
return 'string';
case KBN_FIELD_TYPES.DATE:
return 'datetime';
- case KBN_FIELD_TYPES.GEO_POINT:
- return geoPoint;
default:
return kibanaJSON;
}
@@ -43,15 +41,6 @@ export function getSchemaDetectors() {
icon: '',
color: '',
},
- {
- type: geoPoint,
- detector() {
- return 0; // this schema is always explicitly defined
- },
- sortTextAsc: '',
- sortTextDesc: '',
- icon: 'tokenGeo',
- },
];
}
@@ -60,9 +49,6 @@ export function getSchemaDetectors() {
*/
export function getPopoverContents(): EuiDataGridPopoverContents {
return {
- [geoPoint]: ({ children }) => {
- return {children} ;
- },
[kibanaJSON]: ({ children }) => {
return (
diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx
index 45f30a9d26f935..cfcdbec475eda4 100644
--- a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx
+++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx
@@ -7,7 +7,6 @@
*/
import React, { Fragment, useContext, useEffect } from 'react';
-import { i18n } from '@kbn/i18n';
import themeLight from '@elastic/eui/dist/eui_theme_light.json';
import themeDark from '@elastic/eui/dist/eui_theme_dark.json';
@@ -83,21 +82,6 @@ export const getRenderCellValueFn = (
return {JSON.stringify(rowFlattened[columnId])} ;
}
- if (field?.type === 'geo_point' && rowFlattened && rowFlattened[columnId]) {
- const valueFormatted = rowFlattened[columnId] as { lat: number; lon: number };
- return (
-
- {i18n.translate('discover.latitudeAndLongitude', {
- defaultMessage: 'Lat: {lat} Lon: {lon}',
- values: {
- lat: valueFormatted?.lat,
- lon: valueFormatted?.lon,
- },
- })}
-
- );
- }
-
const valueFormatted = indexPattern.formatField(row, columnId);
if (typeof valueFormatted === 'undefined') {
return - ;
From 43e6a145e5dc9e81588b088297a7ac054b06a0f6 Mon Sep 17 00:00:00 2001
From: Rashmi Kulkarni
Date: Wed, 17 Feb 2021 08:18:20 -0800
Subject: [PATCH 12/79] A11y kibana overview test - changed the order of the
tests being run. (#90876)
* fixes https://github.com/elastic/kibana/issues/74449
* changed the order of the tests
* removed a test as it was already covered in home.ts and reoved sampledata
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
x-pack/test/accessibility/apps/kibana_overview.ts | 15 +--------------
x-pack/test/accessibility/config.ts | 2 +-
2 files changed, 2 insertions(+), 15 deletions(-)
diff --git a/x-pack/test/accessibility/apps/kibana_overview.ts b/x-pack/test/accessibility/apps/kibana_overview.ts
index 068b600d2adf2f..fe255a3413bd0f 100644
--- a/x-pack/test/accessibility/apps/kibana_overview.ts
+++ b/x-pack/test/accessibility/apps/kibana_overview.ts
@@ -20,23 +20,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
after(async () => {
- await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', {
- useActualUrl: true,
- });
- await PageObjects.home.removeSampleDataSet('flights');
await esArchiver.unload('empty_kibana');
});
- it('Getting started view', async () => {
- await a11y.testAppSnapshot();
- });
-
- it('Overview view', async () => {
- await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', {
- useActualUrl: true,
- });
- await PageObjects.home.addSampleDataSet('flights');
- await PageObjects.common.navigateToApp('kibanaOverview');
+ it('Kibana overview', async () => {
await a11y.testAppSnapshot();
});
});
diff --git a/x-pack/test/accessibility/config.ts b/x-pack/test/accessibility/config.ts
index 24c46c1a1687e6..bfd12c4eee6e7d 100644
--- a/x-pack/test/accessibility/config.ts
+++ b/x-pack/test/accessibility/config.ts
@@ -18,6 +18,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [
require.resolve('./apps/login_page'),
require.resolve('./apps/home'),
+ require.resolve('./apps/kibana_overview'),
require.resolve('./apps/grok_debugger'),
require.resolve('./apps/search_profiler'),
require.resolve('./apps/uptime'),
@@ -26,7 +27,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
require.resolve('./apps/dashboard_edit_panel'),
require.resolve('./apps/users'),
require.resolve('./apps/roles'),
- require.resolve('./apps/kibana_overview'),
require.resolve('./apps/ingest_node_pipelines'),
require.resolve('./apps/index_lifecycle_management'),
require.resolve('./apps/ml'),
From 5774e97c1be473d098820897faf06b9c9a417b15 Mon Sep 17 00:00:00 2001
From: Lisa Cawley
Date: Wed, 17 Feb 2021 08:26:29 -0800
Subject: [PATCH 13/79] Use doc link service in Upgrade Assistant (#91573)
---
.../checkup/deprecations/reindex/flyout/container.tsx | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/container.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/container.tsx
index 4efe0f866a5496..2f776f3937b502 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/container.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/reindex/flyout/container.tsx
@@ -44,12 +44,8 @@ interface ReindexFlyoutState {
currentFlyoutStep: ReindexFlyoutStep;
}
-// eslint-disable-next-line @typescript-eslint/naming-convention
-const getOpenAndCloseIndexDocLink = ({ ELASTIC_WEBSITE_URL, DOC_LINK_VERSION }: DocLinksStart) => (
-
+const getOpenAndCloseIndexDocLink = (docLinks: DocLinksStart) => (
+
{i18n.translate(
'xpack.upgradeAssistant.checkupTab.reindexing.flyout.openAndCloseDocumentation',
{ defaultMessage: 'documentation' }
From 50bead68ef83dbdc6cdfc604f9a77a7164c6d441 Mon Sep 17 00:00:00 2001
From: Michael Olorunnisola
Date: Wed, 17 Feb 2021 11:37:47 -0500
Subject: [PATCH 14/79] [Security Solution][Resolver] Add no process events
found messaging to resolver (#91561)
---
.../view/resolver_loading_state.test.tsx | 6 +-
.../view/resolver_no_process_events.tsx | 55 +++++++++++++++++++
.../view/resolver_without_providers.tsx | 9 ++-
3 files changed, 66 insertions(+), 4 deletions(-)
create mode 100644 x-pack/plugins/security_solution/public/resolver/view/resolver_no_process_events.tsx
diff --git a/x-pack/plugins/security_solution/public/resolver/view/resolver_loading_state.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/resolver_loading_state.test.tsx
index a854884d2e340b..4a2056963ee7f1 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/resolver_loading_state.test.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/resolver_loading_state.test.tsx
@@ -130,18 +130,18 @@ describe('Resolver: data loading and resolution states', () => {
});
});
- it('should display a resolver graph with 0 nodes', async () => {
+ it('should display a message informing the user why no nodes are present', async () => {
await expect(
simulator.map(() => ({
resolverGraphLoading: simulator.testSubject('resolver:graph:loading').length,
resolverGraphError: simulator.testSubject('resolver:graph:error').length,
- resolverTree: simulator.testSubject('resolver:graph').length,
+ resolverEmptyMessage: simulator.testSubject('resolver:no-process-events').length,
resolverGraphNodes: simulator.testSubject('resolver:node').length,
}))
).toYieldEqualTo({
resolverGraphLoading: 0,
resolverGraphError: 0,
- resolverTree: 1,
+ resolverEmptyMessage: 1,
resolverGraphNodes: 0,
});
});
diff --git a/x-pack/plugins/security_solution/public/resolver/view/resolver_no_process_events.tsx b/x-pack/plugins/security_solution/public/resolver/view/resolver_no_process_events.tsx
new file mode 100644
index 00000000000000..7159b0bda468ba
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/resolver/view/resolver_no_process_events.tsx
@@ -0,0 +1,55 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { i18n } from '@kbn/i18n';
+import styled from 'styled-components';
+import { EuiCodeBlock, EuiFlexGroup, EuiTitle, EuiSpacer, EuiText } from '@elastic/eui';
+
+const StyledEuiCodeBlock = styled(EuiCodeBlock)`
+ align-self: flex-start;
+ display: inline-block;
+`;
+
+const StyledEuiFlexGroup = styled(EuiFlexGroup)`
+ max-width: 600px;
+ margin: 60px auto 0;
+`;
+
+export const ResolverNoProcessEvents = () => (
+
+
+
+ {i18n.translate('xpack.securitySolution.resolver.noProcessEvents.title', {
+ defaultMessage: 'No Process Events Found',
+ })}
+
+
+
+
+ {i18n.translate('xpack.securitySolution.resolver.noProcessEvents.timeRange', {
+ defaultMessage: `
+ The Analyze Event tool creates graphs based on process events.
+ If the analyzed event does not have an associated process in the current time range,
+ or stored in Elasticsearch within any time range, a graph will not be created.
+ You can check for associated processes by expanding your time range.
+ `,
+ })}
+
+
+
+ {i18n.translate('xpack.securitySolution.resolver.noProcessEvents.eventCategory', {
+ defaultMessage: `You may also add the below to your timeline query to check for process events.
+ If none are listed, a graph cannot be created from events found in that query.`,
+ })}
+
+
+
+ {"event.category: 'process'"}
+
+
+);
diff --git a/x-pack/plugins/security_solution/public/resolver/view/resolver_without_providers.tsx b/x-pack/plugins/security_solution/public/resolver/view/resolver_without_providers.tsx
index 6fbb81a5fe0da1..7b528bfa4d99d8 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/resolver_without_providers.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/resolver_without_providers.tsx
@@ -7,7 +7,7 @@
/* eslint-disable react/display-name */
-import React, { useContext, useCallback } from 'react';
+import React, { useContext, useCallback, useMemo } from 'react';
import { useSelector } from 'react-redux';
import { EuiLoadingSpinner } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -26,6 +26,7 @@ import { ResolverProps, ResolverState } from '../types';
import { PanelRouter } from './panels';
import { useColors } from './use_colors';
import { useSyncSelectedNode } from './use_sync_selected_node';
+import { ResolverNoProcessEvents } from './resolver_no_process_events';
/**
* The highest level connected Resolver component. Needs a `Provider` in its ancestry to work.
@@ -96,6 +97,10 @@ export const ResolverWithoutProviders = React.memo(
const activeDescendantId = useSelector(selectors.ariaActiveDescendant);
const colorMap = useColors();
+ const noProcessEventsFound = useMemo(() => processNodePositions.size < 1, [
+ processNodePositions,
+ ]);
+
return (
{isLoading ? (
@@ -112,6 +117,8 @@ export const ResolverWithoutProviders = React.memo(
/>
+ ) : noProcessEventsFound ? (
+
) : (
<>
Date: Wed, 17 Feb 2021 11:44:39 -0500
Subject: [PATCH 15/79] [APM] Disabling time comparison (#91499)
---
.../app/service_inventory/index.tsx | 2 +-
.../components/app/service_overview/index.tsx | 6 +--
.../service_overview_throughput_chart.tsx | 45 +------------------
.../app/transaction_details/index.tsx | 2 +-
.../app/transaction_overview/index.tsx | 2 +-
.../transaction_overview.test.tsx | 4 +-
6 files changed, 8 insertions(+), 53 deletions(-)
diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
index 1cb420a8ac1949..32bc907f624fbd 100644
--- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
@@ -118,7 +118,7 @@ export function ServiceInventory() {
return (
<>
-
+
{displayMlCallout ? (
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
index db0b8283a28c8c..a0ea00ae5c3ad8 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
@@ -57,11 +57,7 @@ export function ServiceOverview({
return (
-
+
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx
index 92111c5671c91e..1d0074da6005ff 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx
@@ -15,10 +15,6 @@ import { useTheme } from '../../../hooks/use_theme';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
import { TimeseriesChart } from '../../shared/charts/timeseries_chart';
-import {
- getTimeRangeComparison,
- getComparisonChartTheme,
-} from '../../shared/time_comparison/get_time_range_comparison';
const INITIAL_STATE = {
currentPeriod: [],
@@ -34,18 +30,7 @@ export function ServiceOverviewThroughputChart({
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
const { transactionType } = useApmServiceContext();
- const { start, end, comparisonEnabled, comparisonType } = urlParams;
- const comparisonChartTheme = getComparisonChartTheme(theme);
- const {
- comparisonStart = undefined,
- comparisonEnd = undefined,
- } = comparisonType
- ? getTimeRangeComparison({
- start,
- end,
- comparisonType,
- })
- : {};
+ const { start, end } = urlParams;
const { data = INITIAL_STATE, status } = useFetcher(
(callApmApi) => {
@@ -61,22 +46,12 @@ export function ServiceOverviewThroughputChart({
end,
transactionType,
uiFilters: JSON.stringify(uiFilters),
- comparisonStart,
- comparisonEnd,
},
},
});
}
},
- [
- serviceName,
- start,
- end,
- uiFilters,
- transactionType,
- comparisonStart,
- comparisonEnd,
- ]
+ [serviceName, start, end, uiFilters, transactionType]
);
return (
@@ -93,7 +68,6 @@ export function ServiceOverviewThroughputChart({
height={height}
showAnnotations={false}
fetchStatus={status}
- customTheme={comparisonChartTheme}
timeseries={[
{
data: data.currentPeriod,
@@ -104,21 +78,6 @@ export function ServiceOverviewThroughputChart({
{ defaultMessage: 'Throughput' }
),
},
- ...(comparisonEnabled
- ? [
- {
- data: data.previousPeriod,
- type: 'area',
- color: theme.eui.euiColorLightestShade,
- title: i18n.translate(
- 'xpack.apm.serviceOverview.throughtputChart.previousPeriodLabel',
- {
- defaultMessage: 'Previous period',
- }
- ),
- },
- ]
- : []),
]}
yLabelFormat={asTransactionRate}
/>
diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx
index a6fe2b170bc3b8..0a322cfc9c80b0 100644
--- a/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx
@@ -95,7 +95,7 @@ export function TransactionDetails({
{transactionName}
-
+
diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
index 720a3857ef5209..97be35ec6f5b95 100644
--- a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
@@ -82,7 +82,7 @@ export function TransactionOverview({ serviceName }: TransactionOverviewProps) {
return (
<>
-
+
diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx
index 8fb5166bd8676e..e4fbd075660605 100644
--- a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx
+++ b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx
@@ -131,7 +131,7 @@ describe('TransactionOverview', () => {
});
expect(history.location.search).toEqual(
- '?transactionType=secondType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=day'
+ '?transactionType=secondType&rangeFrom=now-15m&rangeTo=now'
);
expect(getByText(container, 'firstType')).toBeInTheDocument();
expect(getByText(container, 'secondType')).toBeInTheDocument();
@@ -142,7 +142,7 @@ describe('TransactionOverview', () => {
expect(history.push).toHaveBeenCalled();
expect(history.location.search).toEqual(
- '?transactionType=firstType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=day'
+ '?transactionType=firstType&rangeFrom=now-15m&rangeTo=now'
);
});
});
From 0fcf75eef669df4b055c8f39963cc872a58ff508 Mon Sep 17 00:00:00 2001
From: Chris Cowan
Date: Wed, 17 Feb 2021 09:45:30 -0700
Subject: [PATCH 16/79] [Metrics UI] Fix ignoreLookback behavior for Snapshot
API (#91169)
* [Metrics UI] Fix ignore lookback behavoir for Snapshot API
* Removing unused dependancies
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../lib/create_timerange_with_interval.ts | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/x-pack/plugins/infra/server/routes/snapshot/lib/create_timerange_with_interval.ts b/x-pack/plugins/infra/server/routes/snapshot/lib/create_timerange_with_interval.ts
index 35decbacf2a529..7473907b7410b8 100644
--- a/x-pack/plugins/infra/server/routes/snapshot/lib/create_timerange_with_interval.ts
+++ b/x-pack/plugins/infra/server/routes/snapshot/lib/create_timerange_with_interval.ts
@@ -8,7 +8,6 @@
import { uniq } from 'lodash';
import { InfraTimerangeInput } from '../../../../common/http_api';
import { ESSearchClient } from '../../../lib/metrics/types';
-import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
import { calculateMetricInterval } from '../../../utils/calculate_metric_interval';
import { getMetricsAggregations, InfraSnapshotRequestOptions } from './get_metrics_aggregations';
import {
@@ -19,9 +18,6 @@ import { getDatasetForField } from '../../metrics_explorer/lib/get_dataset_for_f
const createInterval = async (client: ESSearchClient, options: InfraSnapshotRequestOptions) => {
const { timerange } = options;
- if (timerange.forceInterval && timerange.interval) {
- return getIntervalInSeconds(timerange.interval);
- }
const aggregations = getMetricsAggregations(options);
const modules = await aggregationsToModules(client, aggregations, options);
return Math.max(
@@ -44,14 +40,21 @@ export const createTimeRangeWithInterval = async (
options: InfraSnapshotRequestOptions
): Promise => {
const { timerange } = options;
- const calculatedInterval = await createInterval(client, options);
+ if (timerange.forceInterval) {
+ return {
+ interval: timerange.interval,
+ from: timerange.from,
+ to: timerange.to,
+ };
+ }
if (timerange.ignoreLookback) {
return {
- interval: `${calculatedInterval}s`,
+ interval: 'modules',
from: timerange.from,
to: timerange.to,
};
}
+ const calculatedInterval = await createInterval(client, options);
const lookbackSize = Math.max(timerange.lookbackSize || 5, 5);
return {
interval: `${calculatedInterval}s`,
From b1f04137a3424512a706c828a1ad3d7a52e5b5ca Mon Sep 17 00:00:00 2001
From: Matthias Wilhelm
Date: Wed, 17 Feb 2021 17:50:50 +0100
Subject: [PATCH 17/79] [Discover][Doc] Implement usage of fields when
discover:searchFieldsFromSource is set to false (#91627)
---
.../application/components/doc/doc.test.tsx | 8 ++++
.../components/doc/use_es_doc_search.test.tsx | 38 ++++++++++++++++++-
.../components/doc/use_es_doc_search.ts | 22 +++++++----
3 files changed, 59 insertions(+), 9 deletions(-)
diff --git a/src/plugins/discover/public/application/components/doc/doc.test.tsx b/src/plugins/discover/public/application/components/doc/doc.test.tsx
index 623d30d5f53ecf..deaaa1853ae9da 100644
--- a/src/plugins/discover/public/application/components/doc/doc.test.tsx
+++ b/src/plugins/discover/public/application/components/doc/doc.test.tsx
@@ -13,6 +13,7 @@ import { mountWithIntl } from '@kbn/test/jest';
import { ReactWrapper } from 'enzyme';
import { findTestSubject } from '@elastic/eui/lib/test';
import { Doc, DocProps } from './doc';
+import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../../common';
const mockSearchApi = jest.fn();
@@ -36,6 +37,13 @@ jest.mock('../../../kibana_services', () => {
},
},
},
+ uiSettings: {
+ get: (key: string) => {
+ if (key === mockSearchFieldsFromSource) {
+ return false;
+ }
+ },
+ },
}),
getDocViewsRegistry: () => ({
addDocView(view: any) {
diff --git a/src/plugins/discover/public/application/components/doc/use_es_doc_search.test.tsx b/src/plugins/discover/public/application/components/doc/use_es_doc_search.test.tsx
index 6cb0bf1288d3a8..ef2619070a6d8e 100644
--- a/src/plugins/discover/public/application/components/doc/use_es_doc_search.test.tsx
+++ b/src/plugins/discover/public/application/components/doc/use_es_doc_search.test.tsx
@@ -10,6 +10,7 @@ import { renderHook, act } from '@testing-library/react-hooks';
import { buildSearchBody, useEsDocSearch, ElasticRequestState } from './use_es_doc_search';
import { DocProps } from './doc';
import { Observable } from 'rxjs';
+import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../../common';
const mockSearchResult = new Observable();
@@ -22,19 +23,52 @@ jest.mock('../../../kibana_services', () => ({
}),
},
},
+ uiSettings: {
+ get: (key: string) => {
+ if (key === mockSearchFieldsFromSource) {
+ return false;
+ }
+ },
+ },
}),
}));
describe('Test of helper / hook', () => {
- test('buildSearchBody', () => {
+ test('buildSearchBody given useNewFieldsApi is false', () => {
const indexPattern = {
getComputedFields: () => ({ storedFields: [], scriptFields: [], docvalueFields: [] }),
} as any;
- const actual = buildSearchBody('1', indexPattern);
+ const actual = buildSearchBody('1', indexPattern, false);
expect(actual).toMatchInlineSnapshot(`
Object {
"_source": true,
"docvalue_fields": Array [],
+ "fields": undefined,
+ "query": Object {
+ "ids": Object {
+ "values": Array [
+ "1",
+ ],
+ },
+ },
+ "script_fields": Array [],
+ "stored_fields": Array [],
+ }
+ `);
+ });
+
+ test('buildSearchBody useNewFieldsApi is true', () => {
+ const indexPattern = {
+ getComputedFields: () => ({ storedFields: [], scriptFields: [], docvalueFields: [] }),
+ } as any;
+ const actual = buildSearchBody('1', indexPattern, true);
+ expect(actual).toMatchInlineSnapshot(`
+ Object {
+ "_source": false,
+ "docvalue_fields": Array [],
+ "fields": Array [
+ "*",
+ ],
"query": Object {
"ids": Object {
"values": Array [
diff --git a/src/plugins/discover/public/application/components/doc/use_es_doc_search.ts b/src/plugins/discover/public/application/components/doc/use_es_doc_search.ts
index 2a63a62650ca90..295b2ab3831194 100644
--- a/src/plugins/discover/public/application/components/doc/use_es_doc_search.ts
+++ b/src/plugins/discover/public/application/components/doc/use_es_doc_search.ts
@@ -6,10 +6,11 @@
* Side Public License, v 1.
*/
-import { useEffect, useState } from 'react';
+import { useEffect, useState, useMemo } from 'react';
import { IndexPattern, getServices } from '../../../kibana_services';
import { DocProps } from './doc';
import { ElasticSearchHit } from '../../doc_views/doc_views_types';
+import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../common';
export enum ElasticRequestState {
Loading,
@@ -23,7 +24,11 @@ export enum ElasticRequestState {
* helper function to build a query body for Elasticsearch
* https://www.elastic.co/guide/en/elasticsearch/reference/current//query-dsl-ids-query.html
*/
-export function buildSearchBody(id: string, indexPattern: IndexPattern): Record {
+export function buildSearchBody(
+ id: string,
+ indexPattern: IndexPattern,
+ useNewFieldsApi: boolean
+): Record {
const computedFields = indexPattern.getComputedFields();
return {
@@ -33,7 +38,8 @@ export function buildSearchBody(id: string, indexPattern: IndexPattern): Record<
},
},
stored_fields: computedFields.storedFields,
- _source: true,
+ _source: !useNewFieldsApi,
+ fields: useNewFieldsApi ? ['*'] : undefined,
script_fields: computedFields.scriptFields,
docvalue_fields: computedFields.docvalueFields,
};
@@ -51,6 +57,8 @@ export function useEsDocSearch({
const [indexPattern, setIndexPattern] = useState(null);
const [status, setStatus] = useState(ElasticRequestState.Loading);
const [hit, setHit] = useState(null);
+ const { data, uiSettings } = useMemo(() => getServices(), []);
+ const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]);
useEffect(() => {
async function requestData() {
@@ -58,11 +66,11 @@ export function useEsDocSearch({
const indexPatternEntity = await indexPatternService.get(indexPatternId);
setIndexPattern(indexPatternEntity);
- const { rawResponse } = await getServices()
- .data.search.search({
+ const { rawResponse } = await data.search
+ .search({
params: {
index,
- body: buildSearchBody(id, indexPatternEntity),
+ body: buildSearchBody(id, indexPatternEntity, useNewFieldsApi),
},
})
.toPromise();
@@ -86,6 +94,6 @@ export function useEsDocSearch({
}
}
requestData();
- }, [id, index, indexPatternId, indexPatternService]);
+ }, [id, index, indexPatternId, indexPatternService, data.search, useNewFieldsApi]);
return [status, hit, indexPattern];
}
From 65ce3342ce550ee594151797596ae11e22ce86ed Mon Sep 17 00:00:00 2001
From: Rob Bavey
Date: Wed, 17 Feb 2021 12:01:31 -0500
Subject: [PATCH 18/79] [Telemetry] Add Logstash telemetry (#90850)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* [Telemetry] Add Logstash telemetry
This commit adds additional logstash telemetry to `stack_stats.logstash`.
This telemetry data is sourced from logstash-monitoring data, using the existing
`logstash_stats` and `logstash_state` documents to retrieve cluster wide information
on Logstash usage.
This includes data on uses of plugins, persistent queues, pipeline workers and
batch sizes within a logstash cluster.
This is intended to be a simple set of data to be integrated with the
existing/legacy telemetry cluster and indices, and is intended to be
followed up with more data to be targeted to a dedicated logstash
telemetry index.
Co-authored-by: Alejandro Fernández Haro
---
.../fixtures/logstash_state_results_1n1p.json | 168 ++++
.../fixtures/logstash_state_results_1nmp.json | 187 ++++
.../fixtures/logstash_state_results_mnmp.json | 926 ++++++++++++++++++
.../fixtures/logstash_stats_results.json | 323 ++++++
.../get_all_stats.test.ts | 16 +-
.../telemetry_collection/get_all_stats.ts | 6 +-
.../get_high_level_stats.ts | 4 +-
.../get_logstash_stats.test.ts | 401 ++++++++
.../get_logstash_stats.ts | 418 ++++++++
.../apis/telemetry/fixtures/multicluster.json | 46 +-
.../monitoring/multicluster/mappings.json | 379 +++++--
11 files changed, 2775 insertions(+), 99 deletions(-)
create mode 100644 x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_1n1p.json
create mode 100644 x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_1nmp.json
create mode 100644 x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_mnmp.json
create mode 100644 x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_stats_results.json
create mode 100644 x-pack/plugins/monitoring/server/telemetry_collection/get_logstash_stats.test.ts
create mode 100644 x-pack/plugins/monitoring/server/telemetry_collection/get_logstash_stats.ts
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_1n1p.json b/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_1n1p.json
new file mode 100644
index 00000000000000..71656e3468c098
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_1n1p.json
@@ -0,0 +1,168 @@
+[
+ {
+ "hits" : {
+ "hits": [
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "ezNjX3cB1VO1nvgv72Wz",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 125,
+ "workers" : 1,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "stdin",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "elasticsearch",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "mutate",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "ruby",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "split",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "elasticsearch",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "ruby",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "ruby",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "mutate",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "aggregate",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "drop",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ }
+ },
+ {
+ "config_name" : "mutate",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "elasticsearch",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "output"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T15:58:40.943-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "994e68cd-d607-40e6-a54c-02a51caa17e0"
+ ]
+ }
+ }
+ ]
+ }
+}
+]
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_1nmp.json b/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_1nmp.json
new file mode 100644
index 00000000000000..f2e3b1e038476c
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_1nmp.json
@@ -0,0 +1,187 @@
+[
+ {
+ "hits" : {
+ "hits" : [
+ {
+ "_index" : ".monitoring-logstash-7-mb-2021.01.28",
+ "_type" : "_doc",
+ "_id" : "CiziSXcB1VO1nvgvRr-E",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 125,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "stdin",
+ "meta" : {
+ "source" : {
+ "protocol" : "x-pack-config-management"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "x-pack-config-management"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 12
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-01-28T11:45:01.937-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "47a70feb-3cb5-4618-8670-2c0bada61acd"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.01.12",
+ "_type" : "_doc",
+ "_id" : "DSziSXcB1VO1nvgvRr-Z",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 125,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "stdin",
+ "meta" : {
+ "source" : {
+ "protocol" : "x-pack-config-management"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "x-pack-config-management"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 1
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-01-28T11:45:01.955-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "5a65d966-0330-4bd7-82f2-ee81040c13cf"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.01.12",
+ "_type" : "_doc",
+ "_id" : "DCziSXcB1VO1nvgvRr-Z",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 125,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "stdin",
+ "meta" : {
+ "source" : {
+ "protocol" : "x-pack-config-management"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "x-pack-config-management"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 44
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-01-28T11:45:01.937-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "8d33fe25-a2c0-4c54-9ecf-d218cb8dbfe4"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.01.12",
+ "_type" : "_doc",
+ "_id" : "CyziSXcB1VO1nvgvRr-E",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 1251,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "stdin",
+ "meta" : {
+ "source" : {
+ "protocol" : "x-pack-config-management"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "x-pack-config-management"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 12
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-01-28T11:45:01.937-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "f4167a94-20a8-43e7-828e-4cf38d906187"
+ ]
+ }
+ }
+ ]
+ }
+ }]
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_mnmp.json b/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_mnmp.json
new file mode 100644
index 00000000000000..65cb704529efd0
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_state_results_mnmp.json
@@ -0,0 +1,926 @@
+[
+ {
+ "hits" : {
+ "hits" : [
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "ZzNmX3cB1VO1nvgv9Wlp",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 125,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "stdin",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "clone",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "pipeline",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 1
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:01:58.852-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "cf37c6fa-2f1a-41e2-9a89-36b420a8b9a5"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "ujNxX3cB1VO1nvgvv3em",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 100,
+ "workers" : 1,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "pipeline",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:13:45.947-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "bc6ef6f2-ecce-4328-96a2-002de41a144d"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "ODNtX3cB1VO1nvgvo3IK",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 1,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "pipeline",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 10
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.534-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "3193df5f-2a34-4fe3-816e-6b05999aa5ce"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "LDNtX3cB1VO1nvgvonIZ",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 6,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 6
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.322-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "602936f5-98a3-4f8c-9471-cf389a519f4b"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "LTNtX3cB1VO1nvgvonIZ",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 12,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 12
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.332-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "41258219-b129-4fad-a629-f244826281f8"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "LjNtX3cB1VO1nvgvonI3",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 7,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 7
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.335-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "18593052-c021-4158-860d-d8122981a0ac"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "LzNtX3cB1VO1nvgvonI3",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 10,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 10
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.337-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "8b300988-62cc-4bc6-9ee0-9194f3f78e27"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "MDNtX3cB1VO1nvgvonJL",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 8,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 8
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.339-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "802a5994-a03c-44b8-a650-47c0f71c2e48"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "MTNtX3cB1VO1nvgvonJL",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 4,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 4
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.468-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "6ab60531-fb6f-478c-9063-82f2b0af2bed"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "MjNtX3cB1VO1nvgvonJi",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 5,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 5
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.470-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "0ec4702d-b5e5-4c60-91e9-6fa6a836f0d1"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "MzNtX3cB1VO1nvgvonJi",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 2,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "pipeline",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 2
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.489-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "ddf882b7-be26-4a93-8144-0aeb35122651"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "NDNtX3cB1VO1nvgvonJ3",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 13,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 13
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.490-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "6070b400-5c10-4c5e-b5c5-a5bd9be6d321"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "NTNtX3cB1VO1nvgvonJ3",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 3,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 3
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.512-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "e73bc63d-561a-4acd-a0c4-d5f70c4603df"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "NjNtX3cB1VO1nvgvonKM",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 11,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 11
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.513-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "4207025c-9b00-4bea-a36c-6fbf2d3c215e"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "NzNtX3cB1VO1nvgvonKM",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 9,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ },
+ "workers" : 9
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:09:16.533-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "72058ad1-68a1-45f6-a8e8-10621ffc7288"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "uDNxX3cB1VO1nvgvv3cb",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 125,
+ "workers" : 16,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "stdout",
+ "meta" : {
+ "source" : {
+ "protocol" : "file"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:13:45.932-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "c6785d63-6e5f-42c2-839d-5edf139b7c19"
+ ]
+ }
+ },
+ {
+ "_index" : ".monitoring-logstash-7-2021.02.01",
+ "_type" : "_doc",
+ "_id" : "uTNxX3cB1VO1nvgvv3cb",
+ "_score" : 0.0,
+ "_source" : {
+ "logstash_state" : {
+ "pipeline" : {
+ "batch_size" : 125,
+ "workers" : 8,
+ "representation" : {
+ "graph" : {
+ "vertices" : [
+ {
+ "config_name" : "generator",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "input"
+ },
+ {
+ "config_name" : "sleep",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "filter"
+ },
+ {
+ "config_name" : "pipeline",
+ "meta" : {
+ "source" : {
+ "protocol" : "string"
+ }
+ },
+ "plugin_type" : "output"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "type" : "logstash_state",
+ "timestamp" : "2021-02-01T16:13:45.945-0500"
+ },
+ "fields" : {
+ "logstash_state.pipeline.ephemeral_id" : [
+ "2fcd4161-e08f-4eea-818b-703ea3ec6389"
+ ]
+ }
+ }
+ ]
+ }
+}
+]
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_stats_results.json b/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_stats_results.json
new file mode 100644
index 00000000000000..f9b54f62100dc7
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/logstash_stats_results.json
@@ -0,0 +1,323 @@
+[
+ {
+ "hits" : {
+ "hits": [
+ {
+ "_index": ".monitoring-logstash-7-2021.02.01",
+ "_type": "_doc",
+ "_id": "6DN5X3cB1VO1nvgvb4Ft",
+ "_score": null,
+ "_source": {
+ "logstash_stats": {
+ "logstash": {
+ "version": "7.10.0",
+ "snapshot": false,
+ "status": "green"
+ },
+ "pipelines": [
+ {
+ "id": "main",
+ "ephemeral_id": "cf37c6fa-2f1a-41e2-9a89-36b420a8b9a5",
+ "queue": {
+ "max_queue_size_in_bytes": 0,
+ "type": "memory"
+ }
+ }
+ ]
+ },
+ "cluster_uuid": "1n1p",
+ "type": "logstash_stats"
+ },
+ "fields": {
+ "logstash_stats.logstash.uuid": [
+ "12a3b26f-9b8c-4d38-b4d0-54649c1dd244"
+ ]
+ },
+ "sort": [
+ 1612214529897
+ ]
+ },
+ {
+ "_index" : ".monitoring-logstash-7-mb-2021.01.28",
+ "_type" : "_doc",
+ "_id" : "rC2jSncB1VO1nvgvsKx6",
+ "_score" : null,
+ "_source" : {
+ "agent" : {
+ "type" : "metricbeat"
+ },
+ "logstash_stats" : {
+ "logstash" : {
+ "version" : "7.8.0",
+ "snapshot" : false,
+ "status" : "green"
+ },
+ "pipelines" : [
+ {
+ "id" : "test2",
+ "ephemeral_id" : "47a70feb-3cb5-4618-8670-2c0bada61acd",
+ "queue" : {
+ "max_queue_size_in_bytes" : 0,
+ "type" : "memory"
+ }
+ },
+ {
+ "id" : "main",
+ "ephemeral_id" : "5a65d966-0330-4bd7-82f2-ee81040c13cf",
+ "queue" : {
+ "max_queue_size_in_bytes" : 0,
+ "type" : "memory"
+ }
+ },
+ {
+ "id" : "main2",
+ "ephemeral_id" : "8d33fe25-a2c0-4c54-9ecf-d218cb8dbfe4",
+ "queue" : {
+ "max_queue_size_in_bytes" : 0,
+ "type" : "memory"
+ }
+ },
+ {
+ "id" : "main3",
+ "ephemeral_id" : "f4167a94-20a8-43e7-828e-4cf38d906187",
+ "queue" : {
+ "max_queue_size_in_bytes" : 1.19185342464E11,
+ "type" : "persisted"
+ }
+ }
+ ]
+ },
+ "cluster_uuid" : "1nmp",
+ "type" : "logstash_stats"
+ },
+ "fields" : {
+ "logstash_stats.logstash.uuid" : [
+ "1232122-f2b6-4b6c-8cea-22661f9c4134"
+ ]
+ },
+ "sort" : [
+ 1611864976527
+ ]
+ },
+ {
+ "_index": ".monitoring-logstash-7-2021.02.01",
+ "_type": "_doc",
+ "_id": "JzNyX3cB1VO1nvgvDXi5",
+ "_score": null,
+ "_source": {
+ "logstash_stats": {
+ "logstash": {
+ "version": "7.9.2",
+ "snapshot": false,
+ "status": "green"
+ },
+ "pipelines": [
+ {
+ "id": "t2",
+ "ephemeral_id": "2fcd4161-e08f-4eea-818b-703ea3ec6389",
+ "queue": {
+ "max_queue_size_in_bytes": 0,
+ "type": "memory"
+ }
+ },
+ {
+ "id": "aggregate-test",
+ "ephemeral_id": "c6785d63-6e5f-42c2-839d-5edf139b7c19",
+ "queue": {
+ "max_queue_size_in_bytes": 0,
+ "type": "memory"
+ }
+ },
+ {
+ "id": "t1",
+ "ephemeral_id": "bc6ef6f2-ecce-4328-96a2-002de41a144d",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ }
+ ]
+ },
+ "cluster_uuid": "mnmp",
+ "type": "logstash_stats"
+ },
+ "fields": {
+ "logstash_stats.logstash.uuid": [
+ "701ce2f6-4e6a-426b-a959-80e7cdd608aa"
+ ]
+ },
+ "sort": [
+ 1612214046021
+ ]
+ },
+ {
+ "_index": ".monitoring-logstash-7-2021.02.01",
+ "_type": "_doc",
+ "_id": "sTNuX3cB1VO1nvgvtXN6",
+ "_score": null,
+ "_source": {
+ "logstash_stats": {
+ "logstash": {
+ "version": "7.9.1",
+ "snapshot": false,
+ "status": "green"
+ },
+ "pipelines": [
+ {
+ "id": "t9",
+ "ephemeral_id": "72058ad1-68a1-45f6-a8e8-10621ffc7288",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t7",
+ "ephemeral_id": "18593052-c021-4158-860d-d8122981a0ac",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t11",
+ "ephemeral_id": "4207025c-9b00-4bea-a36c-6fbf2d3c215e",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t5",
+ "ephemeral_id": "0ec4702d-b5e5-4c60-91e9-6fa6a836f0d1",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t12",
+ "ephemeral_id": "41258219-b129-4fad-a629-f244826281f8",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t3",
+ "ephemeral_id": "e73bc63d-561a-4acd-a0c4-d5f70c4603df",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t2",
+ "ephemeral_id": "ddf882b7-be26-4a93-8144-0aeb35122651",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t6",
+ "ephemeral_id": "602936f5-98a3-4f8c-9471-cf389a519f4b",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t10",
+ "ephemeral_id": "8b300988-62cc-4bc6-9ee0-9194f3f78e27",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t4",
+ "ephemeral_id": "6ab60531-fb6f-478c-9063-82f2b0af2bed",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t8",
+ "ephemeral_id": "802a5994-a03c-44b8-a650-47c0f71c2e48",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t13",
+ "ephemeral_id": "6070b400-5c10-4c5e-b5c5-a5bd9be6d321",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ },
+ {
+ "id": "t1",
+ "ephemeral_id": "3193df5f-2a34-4fe3-816e-6b05999aa5ce",
+ "queue": {
+ "max_queue_size_in_bytes": 1073741824,
+ "type": "persisted"
+ }
+ }
+ ]
+ },
+ "cluster_uuid": "mnmp",
+ "type": "logstash_stats"
+ },
+ "fields": {
+ "logstash_stats.logstash.uuid": [
+ "255eab6a-e15c-4e21-8d81-a9474eadb3eb"
+ ]
+ },
+ "sort": [
+ 1612213826805
+ ]
+ },
+ {
+ "_index": ".monitoring-logstash-7-2021.02.01",
+ "_type": "_doc",
+ "_id": "FDNkX3cB1VO1nvgvZWaA",
+ "_score": null,
+ "_source": {
+ "logstash_stats": {
+ "logstash": {
+ "version": "7.10.0",
+ "snapshot": false,
+ "status": "green"
+ },
+ "pipelines": [
+ {
+ "id": "main",
+ "ephemeral_id": "994e68cd-d607-40e6-a54c-02a51caa17e0",
+ "queue": {
+ "max_queue_size_in_bytes": 0,
+ "type": "memory"
+ }
+ }
+ ]
+ },
+ "cluster_uuid": "mnmp",
+ "type": "logstash_stats"
+ },
+ "fields": {
+ "logstash_stats.logstash.uuid": [
+ "beb45ee3-ad5f-4895-87f0-97625a6e5295"
+ ]
+ },
+ "sort": [
+ 1612213150986
+ ]
+ }
+ ]
+ }
+ }
+]
+
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts b/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
index cee95869602649..33487ecafd8c52 100644
--- a/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
@@ -9,7 +9,7 @@ import sinon from 'sinon';
import { getStackStats, getAllStats, handleAllStats } from './get_all_stats';
import { ESClusterStats } from './get_es_stats';
import { KibanaStats } from './get_kibana_stats';
-import { ClustersHighLevelStats } from './get_high_level_stats';
+import { LogstashStatsByClusterUuid } from './get_logstash_stats';
describe('get_all_stats', () => {
const timestamp = Date.now();
@@ -145,13 +145,13 @@ describe('get_all_stats', () => {
logstash: {
count: 1,
versions: [{ version: '2.3.4-beta2', count: 1 }],
- os: {
- platforms: [],
- platformReleases: [],
- distros: [],
- distroReleases: [],
+ cluster_stats: {
+ collection_types: {
+ internal_collection: 1,
+ },
+ pipelines: {},
+ plugins: [],
},
- cloud: undefined,
},
},
},
@@ -188,7 +188,7 @@ describe('get_all_stats', () => {
it('handles response', () => {
const clusters = handleAllStats(esClusters as ESClusterStats[], {
kibana: (kibanaStats as unknown) as KibanaStats,
- logstash: (logstashStats as unknown) as ClustersHighLevelStats,
+ logstash: (logstashStats as unknown) as LogstashStatsByClusterUuid,
beats: {},
});
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.ts b/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
index 67a21dc04fd558..60b107cb293426 100644
--- a/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
@@ -19,7 +19,7 @@ import {
import { getElasticsearchStats, ESClusterStats } from './get_es_stats';
import { getKibanaStats, KibanaStats } from './get_kibana_stats';
import { getBeatsStats, BeatsStatsByClusterUuid } from './get_beats_stats';
-import { getHighLevelStats, ClustersHighLevelStats } from './get_high_level_stats';
+import { getLogstashStats, LogstashStatsByClusterUuid } from './get_logstash_stats';
/**
* Get statistics for all products joined by Elasticsearch cluster.
@@ -38,7 +38,7 @@ export async function getAllStats(
const [esClusters, kibana, logstash, beats] = await Promise.all([
getElasticsearchStats(callCluster, clusterUuids, maxBucketSize), // cluster_stats, stack_stats.xpack, cluster_name/uuid, license, version
getKibanaStats(callCluster, clusterUuids, start, end, maxBucketSize), // stack_stats.kibana
- getHighLevelStats(callCluster, clusterUuids, start, end, LOGSTASH_SYSTEM_ID, maxBucketSize), // stack_stats.logstash
+ getLogstashStats(callCluster, clusterUuids), // stack_stats.logstash
getBeatsStats(callCluster, clusterUuids, start, end), // stack_stats.beats
]);
@@ -63,7 +63,7 @@ export function handleAllStats(
beats,
}: {
kibana: KibanaStats;
- logstash: ClustersHighLevelStats;
+ logstash: LogstashStatsByClusterUuid;
beats: BeatsStatsByClusterUuid;
}
) {
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts b/x-pack/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts
index f7252c9d893649..63188be142fdd6 100644
--- a/x-pack/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts
@@ -53,7 +53,7 @@ type Counter = Map;
* @param {Map} map Map to update the counter for the {@code key}.
* @param {String} key The key to increment a counter for.
*/
-function incrementByKey(map: Counter, key?: string) {
+export function incrementByKey(map: Counter, key?: string) {
if (!key) {
return;
}
@@ -207,7 +207,7 @@ function groupInstancesByCluster(
* { [keyName]: key2, count: value2 }
* ]
*/
-function mapToList(map: Map, keyName: string): T[] {
+export function mapToList(map: Map, keyName: string): T[] {
const list: T[] = [];
for (const [key, count] of map) {
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/get_logstash_stats.test.ts b/x-pack/plugins/monitoring/server/telemetry_collection/get_logstash_stats.test.ts
new file mode 100644
index 00000000000000..f2f0c37255d924
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/get_logstash_stats.test.ts
@@ -0,0 +1,401 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import {
+ fetchLogstashStats,
+ fetchLogstashState,
+ processStatsResults,
+ processLogstashStateResults,
+} from './get_logstash_stats';
+import sinon from 'sinon';
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const logstashStatsResultSet = require('./__mocks__/fixtures/logstash_stats_results');
+
+const resultsMap = new Map();
+
+// Load data for state results.
+['1n1p', '1nmp', 'mnmp'].forEach((data) => {
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
+ resultsMap.set(data, require(`./__mocks__/fixtures/logstash_state_results_${data}`));
+});
+
+const getBaseOptions = () => ({
+ clusters: {},
+ allEphemeralIds: {},
+ versions: {},
+ plugins: {},
+});
+
+describe('Get Logstash Stats', () => {
+ const clusterUuids = ['aCluster', 'bCluster', 'cCluster'];
+ let callCluster = sinon.stub();
+
+ beforeEach(() => {
+ callCluster = sinon.stub();
+ });
+
+ describe('fetchLogstashState', () => {
+ const clusterUuid = 'a';
+ const ephemeralIds = ['a', 'b', 'c'];
+ it('should create the logstash state query correctly', async () => {
+ const expected = {
+ bool: {
+ filter: [
+ {
+ terms: {
+ 'logstash_state.pipeline.ephemeral_id': ['a', 'b', 'c'],
+ },
+ },
+ {
+ bool: {
+ must: {
+ term: { type: 'logstash_state' },
+ },
+ },
+ },
+ ],
+ },
+ };
+
+ await fetchLogstashState(callCluster, clusterUuid, ephemeralIds, {} as any);
+ const { args } = callCluster.firstCall;
+ const [api, { body }] = args;
+ expect(api).toEqual('search');
+ expect(body.query).toEqual(expected);
+ });
+
+ it('should set `from: 0, to: 10000` in the query', async () => {
+ await fetchLogstashState(callCluster, clusterUuid, ephemeralIds, {} as any);
+ const { args } = callCluster.firstCall;
+ const [api, { body }] = args;
+ expect(api).toEqual('search');
+ expect(body.from).toEqual(0);
+ expect(body.size).toEqual(10000);
+ });
+
+ it('should set `from: 10000, to: 10000` in the query', async () => {
+ await fetchLogstashState(callCluster, clusterUuid, ephemeralIds, {
+ page: 1,
+ } as any);
+ const { args } = callCluster.firstCall;
+ const [api, { body }] = args;
+
+ expect(api).toEqual('search');
+ expect(body.from).toEqual(10000);
+ expect(body.size).toEqual(10000);
+ });
+
+ it('should set `from: 20000, to: 10000` in the query', async () => {
+ await fetchLogstashState(callCluster, clusterUuid, ephemeralIds, {
+ page: 2,
+ } as any);
+ const { args } = callCluster.firstCall;
+ const [api, { body }] = args;
+
+ expect(api).toEqual('search');
+ expect(body.from).toEqual(20000);
+ expect(body.size).toEqual(10000);
+ });
+ });
+
+ describe('fetchLogstashStats', () => {
+ it('should set `from: 0, to: 10000` in the query', async () => {
+ await fetchLogstashStats(callCluster, clusterUuids, {} as any);
+ const { args } = callCluster.firstCall;
+ const [api, { body }] = args;
+
+ expect(api).toEqual('search');
+ expect(body.from).toEqual(0);
+ expect(body.size).toEqual(10000);
+ });
+
+ it('should set `from: 10000, to: 10000` in the query', async () => {
+ await fetchLogstashStats(callCluster, clusterUuids, { page: 1 } as any);
+ const { args } = callCluster.firstCall;
+ const [api, { body }] = args;
+
+ expect(api).toEqual('search');
+ expect(body.from).toEqual(10000);
+ expect(body.size).toEqual(10000);
+ });
+
+ it('should set `from: 20000, to: 10000` in the query', async () => {
+ await fetchLogstashStats(callCluster, clusterUuids, { page: 2 } as any);
+ const { args } = callCluster.firstCall;
+ const [api, { body }] = args;
+
+ expect(api).toEqual('search');
+ expect(body.from).toEqual(20000);
+ expect(body.size).toEqual(10000);
+ });
+ });
+
+ describe('processLogstashStatsResults', () => {
+ it('should summarize empty results', () => {
+ const resultsEmpty = undefined;
+
+ const options = getBaseOptions();
+ processStatsResults(resultsEmpty as any, options);
+
+ expect(options.clusters).toStrictEqual({});
+ });
+
+ it('should summarize single result with some missing fields', () => {
+ const results = {
+ hits: {
+ hits: [
+ {
+ _source: {
+ type: 'logstash_stats',
+ cluster_uuid: 'FlV4ckTxQ0a78hmBkzzc9A',
+ logstash_stats: {
+ logstash: {
+ uuid: '61de393a-f2b6-4b6c-8cea-22661f9c4134',
+ },
+ pipelines: [
+ {
+ id: 'main',
+ ephemeral_id: 'cf37c6fa-2f1a-41e2-9a89-36b420a8b9a5',
+ queue: {
+ type: 'memory',
+ },
+ },
+ ],
+ },
+ },
+ },
+ ],
+ },
+ };
+
+ const options = getBaseOptions();
+ processStatsResults(results as any, options);
+
+ expect(options.clusters).toStrictEqual({
+ FlV4ckTxQ0a78hmBkzzc9A: {
+ count: 1,
+ cluster_stats: {
+ plugins: [],
+ collection_types: {
+ internal_collection: 1,
+ },
+ pipelines: {},
+ queues: {
+ memory: 1,
+ },
+ },
+ versions: [],
+ },
+ });
+ });
+
+ it('should summarize stats from hits across multiple result objects', () => {
+ const options = getBaseOptions();
+
+ // logstashStatsResultSet is an array of many small query results
+ logstashStatsResultSet.forEach((results: any) => {
+ processStatsResults(results, options);
+ });
+
+ resultsMap.forEach((value: string[], clusterUuid: string) => {
+ value.forEach((results: any) => {
+ processLogstashStateResults(results, clusterUuid, options);
+ });
+ });
+
+ expect(options.clusters).toStrictEqual({
+ '1n1p': {
+ count: 1,
+ versions: [
+ {
+ count: 1,
+ version: '7.10.0',
+ },
+ ],
+ cluster_stats: {
+ collection_types: {
+ internal_collection: 1,
+ },
+ pipelines: {
+ batch_size_avg: 125,
+ batch_size_max: 125,
+ batch_size_min: 125,
+ batch_size_total: 125,
+ count: 1,
+ sources: {
+ file: true,
+ },
+ workers_avg: 1,
+ workers_max: 1,
+ workers_min: 1,
+ workers_total: 1,
+ },
+ plugins: [
+ {
+ count: 1,
+ name: 'logstash-input-stdin',
+ },
+ {
+ count: 1,
+ name: 'logstash-input-elasticsearch',
+ },
+ {
+ count: 3,
+ name: 'logstash-filter-mutate',
+ },
+ {
+ count: 3,
+ name: 'logstash-filter-ruby',
+ },
+ {
+ count: 1,
+ name: 'logstash-filter-split',
+ },
+ {
+ count: 1,
+ name: 'logstash-filter-elasticsearch',
+ },
+ {
+ count: 1,
+ name: 'logstash-filter-aggregate',
+ },
+ {
+ count: 1,
+ name: 'logstash-filter-drop',
+ },
+ {
+ count: 1,
+ name: 'logstash-output-elasticsearch',
+ },
+ {
+ count: 1,
+ name: 'logstash-output-stdout',
+ },
+ ],
+ queues: {
+ memory: 1,
+ },
+ },
+ },
+ '1nmp': {
+ count: 1,
+ versions: [
+ {
+ count: 1,
+ version: '7.8.0',
+ },
+ ],
+ cluster_stats: {
+ collection_types: {
+ metricbeat: 1,
+ },
+ pipelines: {
+ batch_size_avg: 406.5,
+ batch_size_max: 1251,
+ batch_size_min: 125,
+ batch_size_total: 1626,
+ count: 4,
+ sources: {
+ xpack: true,
+ },
+ workers_avg: 17.25,
+ workers_max: 44,
+ workers_min: 1,
+ workers_total: 69,
+ },
+ plugins: [
+ {
+ count: 4,
+ name: 'logstash-input-stdin',
+ },
+ {
+ count: 4,
+ name: 'logstash-output-stdout',
+ },
+ ],
+ queues: {
+ memory: 3,
+ persisted: 1,
+ },
+ },
+ },
+ mnmp: {
+ count: 3,
+ versions: [
+ {
+ count: 1,
+ version: '7.9.2',
+ },
+ {
+ count: 1,
+ version: '7.9.1',
+ },
+ {
+ count: 1,
+ version: '7.10.0',
+ },
+ ],
+ cluster_stats: {
+ collection_types: {
+ internal_collection: 3,
+ },
+ pipelines: {
+ batch_size_avg: 33.294117647058826,
+ batch_size_max: 125,
+ batch_size_min: 1,
+ batch_size_total: 566,
+ count: 17,
+ sources: {
+ file: true,
+ string: true,
+ },
+ workers_avg: 7.411764705882353,
+ workers_max: 16,
+ workers_min: 1,
+ workers_total: 126,
+ },
+ plugins: [
+ {
+ count: 1,
+ name: 'logstash-input-stdin',
+ },
+ {
+ count: 1,
+ name: 'logstash-filter-clone',
+ },
+ {
+ count: 3,
+ name: 'logstash-output-pipeline',
+ },
+ {
+ count: 2,
+ name: 'logstash-input-pipeline',
+ },
+ {
+ count: 16,
+ name: 'logstash-filter-sleep',
+ },
+ {
+ count: 14,
+ name: 'logstash-output-stdout',
+ },
+ {
+ count: 14,
+ name: 'logstash-input-generator',
+ },
+ ],
+ queues: {
+ memory: 3,
+ persisted: 14,
+ },
+ },
+ },
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/get_logstash_stats.ts b/x-pack/plugins/monitoring/server/telemetry_collection/get_logstash_stats.ts
new file mode 100644
index 00000000000000..93c69c644c0649
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/telemetry_collection/get_logstash_stats.ts
@@ -0,0 +1,418 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { SearchResponse } from 'elasticsearch';
+import { LegacyAPICaller } from 'kibana/server';
+import { createQuery } from './create_query';
+import { mapToList } from './get_high_level_stats';
+import { incrementByKey } from './get_high_level_stats';
+
+import { INDEX_PATTERN_LOGSTASH, TELEMETRY_QUERY_SOURCE } from '../../common/constants';
+
+type Counter = Map;
+
+const HITS_SIZE = 10000; // maximum hits to receive from ES with each search
+
+export interface LogstashBaseStats {
+ // stats
+ versions: Array<{ version: string; count: number }>;
+ count: number;
+
+ cluster_stats?: {
+ collection_types?: { [collection_type_type: string]: number };
+ queues?: { [queue_type: string]: number };
+ plugins?: Array<{ name: string; count: number }>;
+ pipelines?: {
+ count?: number;
+ batch_size_max?: number;
+ batch_size_avg?: number;
+ batch_size_min?: number;
+ batch_size_total?: number;
+ workers_max?: number;
+ workers_avg?: number;
+ workers_min?: number;
+ workers_total?: number;
+ sources?: { [source_type: string]: boolean };
+ };
+ };
+}
+
+const getLogstashBaseStats = () => ({
+ versions: [],
+ count: 0,
+ cluster_stats: {
+ pipelines: {},
+ plugins: [],
+ },
+});
+
+export interface LogstashStats {
+ cluster_uuid: string;
+ source_node: string;
+ type: string;
+ agent?: {
+ type: string;
+ };
+ logstash_stats?: {
+ pipelines?: [
+ {
+ id?: string;
+ ephemeral_id: string;
+ queue?: {
+ type: string;
+ };
+ }
+ ];
+ logstash?: {
+ version?: string;
+ uuid?: string;
+ snapshot?: string;
+ };
+ };
+}
+
+export interface LogstashState {
+ logstash_state?: {
+ pipeline?: {
+ batch_size?: number;
+ workers?: number;
+ representation?: {
+ graph?: {
+ vertices?: [
+ {
+ config_name?: string;
+ plugin_type?: string;
+ meta?: {
+ source?: {
+ protocol?: string;
+ };
+ };
+ }
+ ];
+ };
+ };
+ };
+ };
+}
+
+export interface LogstashProcessOptions {
+ clusters: { [clusterUuid: string]: LogstashBaseStats };
+ allEphemeralIds: { [clusterUuid: string]: string[] };
+ versions: { [clusterUuid: string]: Counter };
+ plugins: { [clusterUuid: string]: Counter };
+}
+
+/*
+ * Update a clusters object with processed Logstash stats
+ * @param {Array} results - array of LogstashStats docs from ES
+ * @param {Object} clusters - LogstashBaseStats in an object keyed by the cluster UUIDs
+ * @param {Object} allEphemeralIds - EphemeralIds in an object keyed by cluster UUIDs to track the pipelines for the cluster
+ * @param {Object} versions - Versions in an object keyed by cluster UUIDs to track the logstash versions for the cluster
+ * @param {Object} plugins - plugin information keyed by cluster UUIDs to count the unique plugins
+ */
+export function processStatsResults(
+ results: SearchResponse,
+ { clusters, allEphemeralIds, versions, plugins }: LogstashProcessOptions
+) {
+ const currHits = results?.hits?.hits || [];
+ currHits.forEach((hit) => {
+ const clusterUuid = hit._source.cluster_uuid;
+ if (clusters[clusterUuid] === undefined) {
+ clusters[clusterUuid] = getLogstashBaseStats();
+ versions[clusterUuid] = new Map();
+ plugins[clusterUuid] = new Map();
+ }
+ const logstashStats = hit._source.logstash_stats;
+ const clusterStats = clusters[clusterUuid].cluster_stats;
+
+ if (clusterStats !== undefined && logstashStats !== undefined) {
+ clusters[clusterUuid].count = (clusters[clusterUuid].count || 0) + 1;
+
+ const thisVersion = logstashStats.logstash?.version;
+ const a: Counter = versions[clusterUuid];
+ incrementByKey(a, thisVersion);
+ clusters[clusterUuid].versions = mapToList(a, 'version');
+
+ // Internal Collection has no agent field, so default to 'internal_collection'
+ let thisCollectionType = hit._source.agent?.type;
+ if (thisCollectionType === undefined) {
+ thisCollectionType = 'internal_collection';
+ }
+ if (!clusterStats.hasOwnProperty('collection_types')) {
+ clusterStats.collection_types = {};
+ }
+ clusterStats.collection_types![thisCollectionType] =
+ (clusterStats.collection_types![thisCollectionType] || 0) + 1;
+
+ const theseEphemeralIds: string[] = [];
+ const pipelines = logstashStats.pipelines || [];
+
+ pipelines.forEach((pipeline) => {
+ const thisQueueType = pipeline.queue?.type;
+ if (thisQueueType !== undefined) {
+ if (!clusterStats.hasOwnProperty('queues')) {
+ clusterStats.queues = {};
+ }
+ clusterStats.queues![thisQueueType] = (clusterStats.queues![thisQueueType] || 0) + 1;
+ }
+
+ const ephemeralId = pipeline.ephemeral_id;
+ if (ephemeralId !== undefined) {
+ theseEphemeralIds.push(ephemeralId);
+ }
+ });
+ allEphemeralIds[clusterUuid] = theseEphemeralIds;
+ }
+ });
+}
+
+/*
+ * Update a clusters object with logstash state details
+ * @param {Array} results - array of LogstashState docs from ES
+ * @param {Object} clusters - LogstashBaseStats in an object keyed by the cluster UUIDs
+ * @param {Object} plugins - plugin information keyed by cluster UUIDs to count the unique plugins
+ */
+export function processLogstashStateResults(
+ results: SearchResponse,
+ clusterUuid: string,
+ { clusters, plugins }: LogstashProcessOptions
+) {
+ const currHits = results?.hits?.hits || [];
+ const clusterStats = clusters[clusterUuid].cluster_stats;
+ const pipelineStats = clusters[clusterUuid].cluster_stats?.pipelines;
+
+ currHits.forEach((hit) => {
+ const thisLogstashStatePipeline = hit._source.logstash_state?.pipeline;
+
+ if (pipelineStats !== undefined && thisLogstashStatePipeline !== undefined) {
+ pipelineStats.count = (pipelineStats.count || 0) + 1;
+
+ const thisPipelineBatchSize = thisLogstashStatePipeline.batch_size;
+
+ if (thisPipelineBatchSize !== undefined) {
+ pipelineStats.batch_size_total =
+ (pipelineStats.batch_size_total || 0) + thisPipelineBatchSize;
+ pipelineStats.batch_size_max = pipelineStats.batch_size_max || 0;
+ pipelineStats.batch_size_min = pipelineStats.batch_size_min || 0;
+ pipelineStats.batch_size_avg = pipelineStats.batch_size_total / pipelineStats.count;
+
+ if (thisPipelineBatchSize > pipelineStats.batch_size_max) {
+ pipelineStats.batch_size_max = thisPipelineBatchSize;
+ }
+ if (
+ pipelineStats.batch_size_min === 0 ||
+ thisPipelineBatchSize < pipelineStats.batch_size_min
+ ) {
+ pipelineStats.batch_size_min = thisPipelineBatchSize;
+ }
+ }
+
+ const thisPipelineWorkers = thisLogstashStatePipeline.workers;
+ if (thisPipelineWorkers !== undefined) {
+ pipelineStats.workers_total = (pipelineStats.workers_total || 0) + thisPipelineWorkers;
+ pipelineStats.workers_max = pipelineStats.workers_max || 0;
+ pipelineStats.workers_min = pipelineStats.workers_min || 0;
+ pipelineStats.workers_avg = pipelineStats.workers_total / pipelineStats.count;
+
+ if (thisPipelineWorkers > pipelineStats.workers_max) {
+ pipelineStats.workers_max = thisPipelineWorkers;
+ }
+ if (pipelineStats.workers_min === 0 || thisPipelineWorkers < pipelineStats.workers_min) {
+ pipelineStats.workers_min = thisPipelineWorkers;
+ }
+ }
+
+ // Extract the vertices object from the pipeline representation. From this, we can
+ // retrieve the source of the pipeline element on the configuration(from file, string, or
+ // x-pack-config-management), and the input, filter and output plugins from that pipeline.
+ const vertices = thisLogstashStatePipeline.representation?.graph?.vertices;
+
+ if (vertices !== undefined) {
+ vertices.forEach((vertex) => {
+ const configName = vertex.config_name;
+ const pluginType = vertex.plugin_type;
+ let pipelineConfig = vertex.meta?.source?.protocol;
+
+ if (pipelineConfig !== undefined) {
+ if (pipelineConfig === 'string' || pipelineConfig === 'str') {
+ pipelineConfig = 'string';
+ } else if (pipelineConfig === 'x-pack-config-management') {
+ pipelineConfig = 'xpack';
+ } else {
+ pipelineConfig = 'file';
+ }
+ if (!pipelineStats.hasOwnProperty('sources')) {
+ pipelineStats.sources = {};
+ }
+ pipelineStats.sources![pipelineConfig] = true;
+ }
+ if (configName !== undefined && pluginType !== undefined) {
+ incrementByKey(plugins[clusterUuid], `logstash-${pluginType}-${configName}`);
+ }
+ });
+ }
+ }
+ });
+ if (clusterStats !== undefined) {
+ clusterStats.plugins = mapToList(plugins[clusterUuid], 'name');
+ }
+}
+
+export async function fetchLogstashStats(
+ callCluster: LegacyAPICaller,
+ clusterUuids: string[],
+ { page = 0, ...options }: { page?: number } & LogstashProcessOptions
+): Promise {
+ const params = {
+ headers: {
+ 'X-QUERY-SOURCE': TELEMETRY_QUERY_SOURCE,
+ },
+ index: INDEX_PATTERN_LOGSTASH,
+ ignoreUnavailable: true,
+ filterPath: [
+ 'hits.hits._source.cluster_uuid',
+ 'hits.hits._source.type',
+ 'hits.hits._source.source_node',
+ 'hits.hits._source.agent.type',
+ 'hits.hits._source.logstash_stats.pipelines.id',
+ 'hits.hits._source.logstash_stats.pipelines.ephemeral_id',
+ 'hits.hits._source.logstash_stats.pipelines.queue.type',
+ 'hits.hits._source.logstash_stats.logstash.version',
+ 'hits.hits._source.logstash_stats.logstash.uuid',
+ ],
+ body: {
+ query: createQuery({
+ filters: [
+ { terms: { cluster_uuid: clusterUuids } },
+ {
+ bool: {
+ must: { term: { type: 'logstash_stats' } },
+ },
+ },
+ ],
+ }),
+ from: page * HITS_SIZE,
+ collapse: { field: 'logstash_stats.logstash.uuid' },
+ sort: [{ ['logstash_stats.timestamp']: { order: 'desc', unmapped_type: 'long' } }],
+ size: HITS_SIZE,
+ },
+ };
+
+ const results = await callCluster>('search', params);
+ const hitsLength = results?.hits?.hits.length || 0;
+
+ if (hitsLength > 0) {
+ // further augment the clusters object with more stats
+ processStatsResults(results, options);
+
+ if (hitsLength === HITS_SIZE) {
+ // call recursively
+ const nextOptions = {
+ page: page + 1,
+ ...options,
+ };
+
+ // returns a promise and keeps the caller blocked from returning until the entire clusters object is built
+ return fetchLogstashStats(callCluster, clusterUuids, nextOptions);
+ }
+ }
+ return Promise.resolve();
+}
+
+export async function fetchLogstashState(
+ callCluster: LegacyAPICaller,
+ clusterUuid: string,
+ ephemeralIds: string[],
+ { page = 0, ...options }: { page?: number } & LogstashProcessOptions
+): Promise {
+ const params = {
+ headers: {
+ 'X-QUERY-SOURCE': TELEMETRY_QUERY_SOURCE,
+ },
+ index: INDEX_PATTERN_LOGSTASH,
+ ignoreUnavailable: true,
+ filterPath: [
+ 'hits.hits._source.logstash_state.pipeline.batch_size',
+ 'hits.hits._source.logstash_state.pipeline.workers',
+ 'hits.hits._source.logstash_state.pipeline.representation.graph.vertices.config_name',
+ 'hits.hits._source.logstash_state.pipeline.representation.graph.vertices.plugin_type',
+ 'hits.hits._source.logstash_state.pipeline.representation.graph.vertices.meta.source.protocol',
+ 'hits.hits._source.logstash_state.pipeline.representation.graph.vertices',
+ 'hits.hits._source.type',
+ ],
+ body: {
+ query: createQuery({
+ filters: [
+ { terms: { 'logstash_state.pipeline.ephemeral_id': ephemeralIds } },
+ {
+ bool: {
+ must: { term: { type: 'logstash_state' } },
+ },
+ },
+ ],
+ }),
+ from: page * HITS_SIZE,
+ collapse: { field: 'logstash_state.pipeline.ephemeral_id' },
+ sort: [{ ['timestamp']: { order: 'desc', unmapped_type: 'long' } }],
+ size: HITS_SIZE,
+ },
+ };
+
+ const results = await callCluster>('search', params);
+ const hitsLength = results?.hits?.hits.length || 0;
+ if (hitsLength > 0) {
+ // further augment the clusters object with more stats
+ processLogstashStateResults(results, clusterUuid, options);
+
+ if (hitsLength === HITS_SIZE) {
+ // call recursively
+ const nextOptions = {
+ page: page + 1,
+ ...options,
+ };
+
+ // returns a promise and keeps the caller blocked from returning until the entire clusters object is built
+ return fetchLogstashState(callCluster, clusterUuid, ephemeralIds, nextOptions);
+ }
+ }
+ return Promise.resolve();
+}
+
+export interface LogstashStatsByClusterUuid {
+ [clusterUuid: string]: LogstashBaseStats;
+}
+
+/*
+ * Call the function for fetching and summarizing Logstash stats
+ * @return {Object} - Logstash stats in an object keyed by the cluster UUIDs
+ */
+export async function getLogstashStats(
+ callCluster: LegacyAPICaller,
+ clusterUuids: string[]
+): Promise {
+ const options: LogstashProcessOptions = {
+ clusters: {}, // the result object to be built up
+ allEphemeralIds: {},
+ versions: {},
+ plugins: {},
+ };
+
+ await fetchLogstashStats(callCluster, clusterUuids, options);
+ await Promise.all(
+ clusterUuids.map(async (clusterUuid) => {
+ if (options.clusters[clusterUuid] !== undefined) {
+ await fetchLogstashState(
+ callCluster,
+ clusterUuid,
+ options.allEphemeralIds[clusterUuid],
+ options
+ );
+ }
+ })
+ );
+ return options.clusters;
+}
diff --git a/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json b/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json
index 90335d303a36d8..f1cc32f33dd2c9 100644
--- a/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json
+++ b/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json
@@ -977,7 +977,51 @@
},
"indices": 1,
"plugins": {}
- }
+ },
+ "logstash": {
+ "count": 1,
+ "cluster_stats": {
+ "collection_types": {
+ "internal_collection": 1
+ },
+ "pipelines": {
+ "batch_size_min": 125,
+ "batch_size_max": 125,
+ "batch_size_avg": 125,
+ "batch_size_total": 125,
+ "count": 1,
+ "sources": {
+ "string": true
+ },
+ "workers_avg": 4,
+ "workers_total": 4,
+ "workers_min": 4,
+ "workers_max": 4
+ },
+ "plugins": [
+ {
+ "count": 1,
+ "name": "logstash-input-twitter"
+ },
+ {
+ "count": 1,
+ "name": "logstash-output-stdout"
+ },
+ {
+ "count": 1,
+ "name": "logstash-output-elasticsearch"
+ }
+ ],
+ "queues": {
+ "memory": 1
+ }
+ },
+ "versions": [
+ {
+ "count": 1,
+ "version": "7.0.0-alpha1"
+ }
+ ]}
},
"timestamp": "2017-08-15T22:10:52.642Z",
"version": "7.0.0-alpha1",
diff --git a/x-pack/test/functional/es_archives/monitoring/multicluster/mappings.json b/x-pack/test/functional/es_archives/monitoring/multicluster/mappings.json
index 7e7084698b7889..319a2e95e47bb3 100644
--- a/x-pack/test/functional/es_archives/monitoring/multicluster/mappings.json
+++ b/x-pack/test/functional/es_archives/monitoring/multicluster/mappings.json
@@ -602,25 +602,83 @@
"cluster_uuid": {
"type": "keyword"
},
- "logstash_state": {
+ "timestamp": {
+ "type": "date",
+ "format": "date_time"
+ },
+ "interval_ms": {
+ "type": "long"
+ },
+ "type": {
+ "type": "keyword"
+ },
+ "source_node": {
"properties": {
- "pipeline": {
+ "uuid": {
+ "type": "keyword"
+ },
+ "host": {
+ "type": "keyword"
+ },
+ "transport_address": {
+ "type": "keyword"
+ },
+ "ip": {
+ "type": "keyword"
+ },
+ "name": {
+ "type": "keyword"
+ },
+ "timestamp": {
+ "type": "date",
+ "format": "date_time"
+ }
+ }
+ },
+ "logstash_stats": {
+ "type": "object",
+ "properties": {
+ "logstash": {
"properties": {
- "hash": {
+ "uuid": {
"type": "keyword"
},
- "id": {
+ "name": {
+ "type": "keyword"
+ },
+ "ephemeral_id": {
+ "type": "keyword"
+ },
+ "host": {
+ "type": "keyword"
+ },
+ "http_address": {
+ "type": "keyword"
+ },
+ "version": {
+ "type": "keyword"
+ },
+ "snapshot": {
+ "type": "boolean"
+ },
+ "status": {
"type": "keyword"
+ },
+ "pipeline": {
+ "properties": {
+ "workers": {
+ "type": "short"
+ },
+ "batch_size": {
+ "type": "long"
+ }
+ }
}
}
- }
- }
- },
- "logstash_stats": {
- "properties": {
+ },
"events": {
"properties": {
- "duration_in_millis": {
+ "filtered": {
"type": "long"
},
"in": {
@@ -628,11 +686,48 @@
},
"out": {
"type": "long"
+ },
+ "duration_in_millis": {
+ "type": "long"
}
}
},
+ "timestamp": {
+ "type": "date"
+ },
"jvm": {
"properties": {
+ "uptime_in_millis": {
+ "type": "long"
+ },
+ "gc": {
+ "properties": {
+ "collectors": {
+ "properties": {
+ "old": {
+ "properties": {
+ "collection_count": {
+ "type": "long"
+ },
+ "collection_time_in_millis": {
+ "type": "long"
+ }
+ }
+ },
+ "young": {
+ "properties": {
+ "collection_count": {
+ "type": "long"
+ },
+ "collection_time_in_millis": {
+ "type": "long"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"mem": {
"properties": {
"heap_max_in_bytes": {
@@ -640,30 +735,50 @@
},
"heap_used_in_bytes": {
"type": "long"
+ },
+ "heap_used_percent": {
+ "type": "long"
}
}
- },
- "uptime_in_millis": {
- "type": "long"
- }
- }
- },
- "logstash": {
- "properties": {
- "uuid": {
- "type": "keyword"
- },
- "version": {
- "type": "keyword"
}
}
},
"os": {
"properties": {
+ "cpu": {
+ "properties": {
+ "load_average": {
+ "properties": {
+ "1m": {
+ "type": "half_float"
+ },
+ "5m": {
+ "type": "half_float"
+ },
+ "15m": {
+ "type": "half_float"
+ }
+ }
+ }
+ }
+ },
"cgroup": {
"properties": {
+ "cpuacct": {
+ "properties": {
+ "control_group": {
+ "type": "keyword"
+ },
+ "usage_nanos": {
+ "type": "long"
+ }
+ }
+ },
"cpu": {
"properties": {
+ "control_group": {
+ "type": "keyword"
+ },
"stat": {
"properties": {
"number_of_elapsed_periods": {
@@ -678,70 +793,103 @@
}
}
}
- },
- "cpuacct": {
- "properties": {
- "usage_nanos": {
- "type": "long"
- }
- }
}
}
- },
+ }
+ }
+ },
+ "process": {
+ "properties": {
"cpu": {
"properties": {
- "load_average": {
- "properties": {
- "15m": {
- "type": "half_float"
- },
- "1m": {
- "type": "half_float"
- },
- "5m": {
- "type": "half_float"
- }
- }
+ "percent": {
+ "type": "long"
}
}
+ },
+ "max_file_descriptors": {
+ "type": "long"
+ },
+ "open_file_descriptors": {
+ "type": "long"
+ }
+ }
+ },
+ "reloads": {
+ "properties": {
+ "failures": {
+ "type": "long"
+ },
+ "successes": {
+ "type": "long"
+ }
+ }
+ },
+ "queue": {
+ "properties": {
+ "events_count": {
+ "type": "long"
+ },
+ "type": {
+ "type": "keyword"
}
}
},
"pipelines": {
+ "type": "nested",
"properties": {
+ "id": {
+ "type": "keyword"
+ },
+ "hash": {
+ "type": "keyword"
+ },
+ "ephemeral_id": {
+ "type": "keyword"
+ },
"events": {
"properties": {
- "duration_in_millis": {
+ "in": {
+ "type": "long"
+ },
+ "filtered": {
"type": "long"
},
"out": {
"type": "long"
+ },
+ "duration_in_millis": {
+ "type": "long"
+ },
+ "queue_push_duration_in_millis": {
+ "type": "long"
}
}
},
- "hash": {
- "type": "keyword"
- },
- "id": {
- "type": "keyword"
- },
"queue": {
"properties": {
+ "events_count": {
+ "type": "long"
+ },
+ "type": {
+ "type": "keyword"
+ },
"max_queue_size_in_bytes": {
"type": "long"
},
"queue_size_in_bytes": {
"type": "long"
- },
- "type": {
- "type": "keyword"
}
}
},
"vertices": {
+ "type": "nested",
"properties": {
- "duration_in_millis": {
- "type": "long"
+ "id": {
+ "type": "keyword"
+ },
+ "pipeline_ephemeral_id": {
+ "type": "keyword"
},
"events_in": {
"type": "long"
@@ -749,41 +897,109 @@
"events_out": {
"type": "long"
},
- "id": {
- "type": "keyword"
- },
- "pipeline_ephemeral_id": {
- "type": "keyword"
+ "duration_in_millis": {
+ "type": "long"
},
"queue_push_duration_in_millis": {
- "type": "float"
+ "type": "long"
+ },
+ "long_counters": {
+ "type": "nested",
+ "properties": {
+ "name": {
+ "type": "keyword"
+ },
+ "value": {
+ "type": "long"
+ }
+ }
+ },
+ "double_gauges": {
+ "type": "nested",
+ "properties": {
+ "name": {
+ "type": "keyword"
+ },
+ "value": {
+ "type": "double"
+ }
+ }
}
- },
- "type": "nested"
- }
- },
- "type": "nested"
- },
- "process": {
- "properties": {
- "cpu": {
+ }
+ },
+ "reloads": {
"properties": {
- "percent": {
+ "failures": {
+ "type": "long"
+ },
+ "successes": {
"type": "long"
}
}
}
}
},
- "queue": {
+ "workers": {
+ "type": "short"
+ },
+ "batch_size": {
+ "type": "integer"
+ }
+ }
+ },
+ "logstash_state": {
+ "properties": {
+ "uuid": {
+ "type": "keyword"
+ },
+ "name": {
+ "type": "keyword"
+ },
+ "host": {
+ "type": "keyword"
+ },
+ "http_address": {
+ "type": "keyword"
+ },
+ "ephemeral_id": {
+ "type": "keyword"
+ },
+ "version": {
+ "type": "keyword"
+ },
+ "snapshot": {
+ "type": "boolean"
+ },
+ "status": {
+ "type": "keyword"
+ },
+ "pipeline": {
"properties": {
- "events_count": {
- "type": "long"
+ "id": {
+ "type": "keyword"
+ },
+ "hash": {
+ "type": "keyword"
+ },
+ "ephemeral_id": {
+ "type": "keyword"
+ },
+ "workers": {
+ "type": "short"
+ },
+ "batch_size": {
+ "type": "integer"
+ },
+ "format": {
+ "type": "keyword"
+ },
+ "version": {
+ "type": "keyword"
+ },
+ "representation": {
+ "enabled": false
}
}
- },
- "timestamp": {
- "type": "date"
}
}
},
@@ -799,13 +1015,6 @@
"type": "text"
}
}
- },
- "timestamp": {
- "format": "date_time",
- "type": "date"
- },
- "type": {
- "type": "keyword"
}
}
},
@@ -1003,4 +1212,4 @@
}
}
}
-}
\ No newline at end of file
+}
From de7ba4d0159aba8cb868c6f423051bd5befff176 Mon Sep 17 00:00:00 2001
From: Christos Nasikas
Date: Wed, 17 Feb 2021 19:01:45 +0200
Subject: [PATCH 19/79] [Security Solution][Case] Replace create case modal
with a flyout (#91407)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../cases/components/create/flyout.test.tsx | 115 ++++++++++++++++++
.../public/cases/components/create/flyout.tsx | 69 +++++++++++
.../timeline_actions/add_to_case_action.tsx | 28 +++--
.../public/common/hooks/use_control.test.tsx | 42 +++++++
.../public/common/hooks/use_control.tsx | 22 ++++
5 files changed, 265 insertions(+), 11 deletions(-)
create mode 100644 x-pack/plugins/security_solution/public/cases/components/create/flyout.test.tsx
create mode 100644 x-pack/plugins/security_solution/public/cases/components/create/flyout.tsx
create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_control.test.tsx
create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_control.tsx
diff --git a/x-pack/plugins/security_solution/public/cases/components/create/flyout.test.tsx b/x-pack/plugins/security_solution/public/cases/components/create/flyout.test.tsx
new file mode 100644
index 00000000000000..842fe9e00ab390
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/cases/components/create/flyout.test.tsx
@@ -0,0 +1,115 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+/* eslint-disable react/display-name */
+import React, { ReactNode } from 'react';
+import { mount } from 'enzyme';
+
+import '../../../common/mock/match_media';
+import { CreateCaseFlyout } from './flyout';
+import { TestProviders } from '../../../common/mock';
+
+jest.mock('../create/form_context', () => {
+ return {
+ FormContext: ({
+ children,
+ onSuccess,
+ }: {
+ children: ReactNode;
+ onSuccess: ({ id }: { id: string }) => void;
+ }) => {
+ return (
+ <>
+ onSuccess({ id: 'case-id' })}
+ >
+ {'submit'}
+
+ {children}
+ >
+ );
+ },
+ };
+});
+
+jest.mock('../create/form', () => {
+ return {
+ CreateCaseForm: () => {
+ return <>{'form'}>;
+ },
+ };
+});
+
+jest.mock('../create/submit_button', () => {
+ return {
+ SubmitCaseButton: () => {
+ return <>{'Submit'}>;
+ },
+ };
+});
+
+const onCloseFlyout = jest.fn();
+const onCaseCreated = jest.fn();
+const defaultProps = {
+ onCloseFlyout,
+ onCaseCreated,
+};
+
+describe('CreateCaseFlyout', () => {
+ beforeEach(() => {
+ jest.resetAllMocks();
+ });
+
+ it('renders', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(wrapper.find(`[data-test-subj='create-case-flyout']`).exists()).toBeTruthy();
+ });
+
+ it('Closing modal calls onCloseCaseModal', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ wrapper.find('.euiFlyout__closeButton').first().simulate('click');
+ expect(onCloseFlyout).toBeCalled();
+ });
+
+ it('pass the correct props to FormContext component', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ const props = wrapper.find('FormContext').props();
+ expect(props).toEqual(
+ expect.objectContaining({
+ onSuccess: onCaseCreated,
+ })
+ );
+ });
+
+ it('onSuccess called when creating a case', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ wrapper.find(`[data-test-subj='form-context-on-success']`).first().simulate('click');
+ expect(onCaseCreated).toHaveBeenCalledWith({ id: 'case-id' });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/cases/components/create/flyout.tsx b/x-pack/plugins/security_solution/public/cases/components/create/flyout.tsx
new file mode 100644
index 00000000000000..cb3436f6ba3bcb
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/cases/components/create/flyout.tsx
@@ -0,0 +1,69 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { memo } from 'react';
+import styled from 'styled-components';
+import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui';
+
+import { FormContext } from '../create/form_context';
+import { CreateCaseForm } from '../create/form';
+import { SubmitCaseButton } from '../create/submit_button';
+import { Case } from '../../containers/types';
+import * as i18n from '../../translations';
+
+export interface CreateCaseModalProps {
+ onCloseFlyout: () => void;
+ onCaseCreated: (theCase: Case) => void;
+}
+
+const Container = styled.div`
+ ${({ theme }) => `
+ margin-top: ${theme.eui.euiSize};
+ text-align: right;
+ `}
+`;
+
+const StyledFlyout = styled(EuiFlyout)`
+ ${({ theme }) => `
+ z-index: ${theme.eui.euiZModal};
+ `}
+`;
+
+// Adding bottom padding because timeline's
+// bottom bar gonna hide the submit button.
+const FormWrapper = styled.div`
+ padding-bottom: 50px;
+`;
+
+const CreateCaseFlyoutComponent: React.FC = ({
+ onCaseCreated,
+ onCloseFlyout,
+}) => {
+ return (
+
+
+
+ {i18n.CREATE_TITLE}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export const CreateCaseFlyout = memo(CreateCaseFlyoutComponent);
+
+CreateCaseFlyout.displayName = 'CreateCaseFlyout';
diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx
index 6067f3fd7b15aa..1b21db0491565f 100644
--- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx
+++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx
@@ -25,10 +25,11 @@ import { APP_ID } from '../../../../common/constants';
import { useKibana } from '../../../common/lib/kibana';
import { getCaseDetailsUrl } from '../../../common/components/link_to';
import { SecurityPageName } from '../../../app/types';
-import { useCreateCaseModal } from '../use_create_case_modal';
import { useAllCasesModal } from '../use_all_cases_modal';
import { createUpdateSuccessToaster } from './helpers';
import * as i18n from './translations';
+import { useControl } from '../../../common/hooks/use_control';
+import { CreateCaseFlyout } from '../create/flyout';
interface AddToCaseActionProps {
ariaLabel?: string;
@@ -61,8 +62,15 @@ const AddToCaseActionComponent: React.FC = ({
[navigateToApp]
);
+ const {
+ isControlOpen: isCreateCaseFlyoutOpen,
+ openControl: openCaseFlyoutOpen,
+ closeControl: closeCaseFlyoutOpen,
+ } = useControl();
+
const attachAlertToCase = useCallback(
(theCase: Case) => {
+ closeCaseFlyoutOpen();
postComment(
theCase.id,
{
@@ -77,13 +85,9 @@ const AddToCaseActionComponent: React.FC = ({
})
);
},
- [postComment, eventId, eventIndex, dispatchToaster, onViewCaseClick]
+ [closeCaseFlyoutOpen, postComment, eventId, eventIndex, dispatchToaster, onViewCaseClick]
);
- const { modal: createCaseModal, openModal: openCreateCaseModal } = useCreateCaseModal({
- onCaseCreated: attachAlertToCase,
- });
-
const onCaseClicked = useCallback(
(theCase) => {
/**
@@ -92,13 +96,13 @@ const AddToCaseActionComponent: React.FC = ({
* We gonna open the create case modal.
*/
if (theCase == null) {
- openCreateCaseModal();
+ openCaseFlyoutOpen();
return;
}
attachAlertToCase(theCase);
},
- [attachAlertToCase, openCreateCaseModal]
+ [attachAlertToCase, openCaseFlyoutOpen]
);
const { modal: allCasesModal, openModal: openAllCaseModal } = useAllCasesModal({
@@ -107,8 +111,8 @@ const AddToCaseActionComponent: React.FC = ({
const addNewCaseClick = useCallback(() => {
closePopover();
- openCreateCaseModal();
- }, [openCreateCaseModal, closePopover]);
+ openCaseFlyoutOpen();
+ }, [openCaseFlyoutOpen, closePopover]);
const addExistingCaseClick = useCallback(() => {
closePopover();
@@ -173,7 +177,9 @@ const AddToCaseActionComponent: React.FC = ({
- {createCaseModal}
+ {isCreateCaseFlyoutOpen && (
+
+ )}
{allCasesModal}
>
);
diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_control.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_control.test.tsx
new file mode 100644
index 00000000000000..953f39fcf23726
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/hooks/use_control.test.tsx
@@ -0,0 +1,42 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { renderHook, act } from '@testing-library/react-hooks';
+import { useControl, UseControlsReturn } from './use_control';
+
+describe('useControl', () => {
+ it('init', async () => {
+ const { result } = renderHook<{}, UseControlsReturn>(() => useControl());
+ expect(result.current.isControlOpen).toBe(false);
+ });
+
+ it('should open the control', async () => {
+ const { result } = renderHook<{}, UseControlsReturn>(() => useControl());
+
+ act(() => {
+ result.current.openControl();
+ });
+
+ expect(result.current.isControlOpen).toBe(true);
+ });
+
+ it('should close the control', async () => {
+ const { result } = renderHook<{}, UseControlsReturn>(() => useControl());
+
+ act(() => {
+ result.current.openControl();
+ });
+
+ expect(result.current.isControlOpen).toBe(true);
+
+ act(() => {
+ result.current.closeControl();
+ });
+
+ expect(result.current.isControlOpen).toBe(false);
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_control.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_control.tsx
new file mode 100644
index 00000000000000..37d7f4ff79986f
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/hooks/use_control.tsx
@@ -0,0 +1,22 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useState, useCallback } from 'react';
+
+export interface UseControlsReturn {
+ isControlOpen: boolean;
+ openControl: () => void;
+ closeControl: () => void;
+}
+
+export const useControl = (): UseControlsReturn => {
+ const [isControlOpen, setIsControlOpen] = useState(false);
+ const openControl = useCallback(() => setIsControlOpen(true), []);
+ const closeControl = useCallback(() => setIsControlOpen(false), []);
+
+ return { isControlOpen, openControl, closeControl };
+};
From 7bb88fdbee574fefc8e326d88a3e60863da3205b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?=
Date: Wed, 17 Feb 2021 18:05:27 +0100
Subject: [PATCH 20/79] Enable auto-merge for backports (#91626)
---
.backportrc.json | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.backportrc.json b/.backportrc.json
index e44d3ce1142993..2752768194e0f6 100644
--- a/.backportrc.json
+++ b/.backportrc.json
@@ -31,5 +31,7 @@
"^v8.0.0$": "master",
"^v7.12.0$": "7.x",
"^v(\\d+).(\\d+).\\d+$": "$1.$2"
- }
+ },
+ "autoMerge": true,
+ "autoMergeMethod": "squash"
}
From dcde4397b26c98f59046077fc443bee73ceb9a12 Mon Sep 17 00:00:00 2001
From: spalger
Date: Wed, 17 Feb 2021 10:06:37 -0700
Subject: [PATCH 21/79] create upgrade PRs automatically for
@elastic/elasticsearch
---
renovate.json5 | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/renovate.json5 b/renovate.json5
index 415aa71fc3820f..59356bb5375cd8 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -11,19 +11,20 @@
],
baseBranches: [
'master',
+ '7.x',
],
labels: [
'release_note:skip',
'renovate',
'v8.0.0',
- 'v7.11.0',
+ 'v7.12.0',
],
major: {
labels: [
'release_note:skip',
'renovate',
'v8.0.0',
- 'v7.11.0',
+ 'v7.12.0',
'renovate:major',
],
},
@@ -49,12 +50,21 @@
groupName: '@elastic/charts',
packageNames: ['@elastic/charts'],
reviewers: ['markov00'],
+ matchBaseBranches: ['master'],
+ enabled: true,
+ },
+ {
+ groupName: '@elastic/elasticsearch',
+ packageNames: ['@elastic/elasticsearch'],
+ reviewers: ['team:kibana-operations'],
+ matchBaseBranches: ['master', '7.x'],
enabled: true,
},
{
groupName: 'vega related modules',
packageNames: ['vega', 'vega-lite', 'vega-schema-url-parser', 'vega-tooltip'],
reviewers: ['team:kibana-app'],
+ matchBaseBranches: ['master'],
labels: ['Feature:Vega', 'Team:KibanaApp'],
enabled: true,
},
From ae6f203ecd2876fe51071791668fee125bb1300f Mon Sep 17 00:00:00 2001
From: spalger
Date: Wed, 17 Feb 2021 10:11:05 -0700
Subject: [PATCH 22/79] [renovate] define labels on each package config
---
renovate.json5 | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/renovate.json5 b/renovate.json5
index 59356bb5375cd8..897d547496683c 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -13,21 +13,6 @@
'master',
'7.x',
],
- labels: [
- 'release_note:skip',
- 'renovate',
- 'v8.0.0',
- 'v7.12.0',
- ],
- major: {
- labels: [
- 'release_note:skip',
- 'renovate',
- 'v8.0.0',
- 'v7.12.0',
- 'renovate:major',
- ],
- },
separateMajorMinor: false,
masterIssue: true,
rangeStrategy: 'bump',
@@ -51,13 +36,23 @@
packageNames: ['@elastic/charts'],
reviewers: ['markov00'],
matchBaseBranches: ['master'],
+ labels: ['release_note:skip', 'v8.0.0', 'v7.12.0'],
+ enabled: true,
+ },
+ {
+ groupName: '@elastic/elasticsearch',
+ packageNames: ['@elastic/elasticsearch'],
+ reviewers: ['team:kibana-operations'],
+ matchBaseBranches: ['master'],
+ labels: ['release_note:skip', 'v8.0.0', 'Team:Operations'],
enabled: true,
},
{
groupName: '@elastic/elasticsearch',
packageNames: ['@elastic/elasticsearch'],
reviewers: ['team:kibana-operations'],
- matchBaseBranches: ['master', '7.x'],
+ matchBaseBranches: ['7.x'],
+ labels: ['release_note:skip', 'v7.12.0', 'Team:Operations'],
enabled: true,
},
{
From a32f86d16154285c9225ee2487247642498e768d Mon Sep 17 00:00:00 2001
From: spalger
Date: Wed, 17 Feb 2021 10:12:17 -0700
Subject: [PATCH 23/79] [renovate] skip backports on elastic/elasticsearch
upgrades
---
renovate.json5 | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/renovate.json5 b/renovate.json5
index 897d547496683c..1d64954e21d474 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -44,7 +44,7 @@
packageNames: ['@elastic/elasticsearch'],
reviewers: ['team:kibana-operations'],
matchBaseBranches: ['master'],
- labels: ['release_note:skip', 'v8.0.0', 'Team:Operations'],
+ labels: ['release_note:skip', 'v8.0.0', 'Team:Operations', 'backport:skip'],
enabled: true,
},
{
@@ -52,7 +52,7 @@
packageNames: ['@elastic/elasticsearch'],
reviewers: ['team:kibana-operations'],
matchBaseBranches: ['7.x'],
- labels: ['release_note:skip', 'v7.12.0', 'Team:Operations'],
+ labels: ['release_note:skip', 'v7.12.0', 'Team:Operations', 'backport:skip'],
enabled: true,
},
{
From d890d2265844d7a656e23cbf9c93dbc5be5559c7 Mon Sep 17 00:00:00 2001
From: James Gowdy
Date: Wed, 17 Feb 2021 17:25:15 +0000
Subject: [PATCH 24/79] [ML] Filtering runtime mappings in anomaly detection
wizards (#91534)
* [ML] Filtering runtime mappings in anomaly detection wizards
* updating tests
* adding check for null when parsing aggs
* removing async from tests
---
.../new_job/common/job_creator/job_creator.ts | 30 ++-
.../util/filter_runtime_mappings.test.ts | 183 ++++++++++++++++++
.../util/filter_runtime_mappings.ts | 104 ++++++++++
3 files changed, 316 insertions(+), 1 deletion(-)
create mode 100644 x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.test.ts
create mode 100644 x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts
diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts
index ca2c2204fb0c11..ec5cb59964ffd5 100644
--- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts
+++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts
@@ -36,6 +36,7 @@ import {
SHARED_RESULTS_INDEX_NAME,
} from '../../../../../../common/constants/new_job';
import { collectAggs } from './util/general';
+import { filterRuntimeMappings } from './util/filter_runtime_mappings';
import { parseInterval } from '../../../../../../common/util/parse_interval';
import { Calendar } from '../../../../../../common/types/calendars';
import { mlCalendarService } from '../../../../services/calendar_service';
@@ -65,6 +66,7 @@ export class JobCreator {
protected _scriptFields: Field[] = [];
protected _runtimeFields: Field[] = [];
protected _runtimeMappings: RuntimeMappings | null = null;
+ protected _filterRuntimeMappingsOnSave: boolean = true;
protected _aggregationFields: Field[] = [];
protected _sparseData: boolean = false;
private _stopAllRefreshPolls: {
@@ -546,7 +548,8 @@ export class JobCreator {
public async createDatafeed(): Promise {
try {
- return await mlJobService.saveNewDatafeed(this._datafeed_config, this._job_config.job_id);
+ const tempDatafeed = this._getDatafeedWithFilteredRuntimeMappings();
+ return await mlJobService.saveNewDatafeed(tempDatafeed, this._job_config.job_id);
} catch (error) {
throw error;
}
@@ -559,6 +562,23 @@ export class JobCreator {
return jobRunner;
}
+ private _getDatafeedWithFilteredRuntimeMappings(): Datafeed {
+ if (this._filterRuntimeMappingsOnSave === false) {
+ return this._datafeed_config;
+ }
+
+ const { runtime_mappings: filteredRuntimeMappings } = filterRuntimeMappings(
+ this._job_config,
+ this._datafeed_config
+ );
+
+ return {
+ ...this._datafeed_config,
+ runtime_mappings:
+ Object.keys(filteredRuntimeMappings).length > 0 ? filteredRuntimeMappings : undefined,
+ };
+ }
+
public subscribeToProgress(func: ProgressSubscriber) {
this._subscribers.push(func);
}
@@ -645,6 +665,14 @@ export class JobCreator {
return JSON.stringify(this._datafeed_config, null, 2);
}
+ public set filterRuntimeMappingsOnSave(filter: boolean) {
+ this._filterRuntimeMappingsOnSave = filter;
+ }
+
+ public get filterRuntimeMappingsOnSave(): boolean {
+ return this._filterRuntimeMappingsOnSave;
+ }
+
protected _initPerPartitionCategorization() {
if (this._job_config.analysis_config.per_partition_categorization === undefined) {
this._job_config.analysis_config.per_partition_categorization = {};
diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.test.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.test.ts
new file mode 100644
index 00000000000000..43e7d4e45b6e0c
--- /dev/null
+++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.test.ts
@@ -0,0 +1,183 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { Job, Datafeed } from '../../../../../../../common/types/anomaly_detection_jobs';
+import { filterRuntimeMappings } from './filter_runtime_mappings';
+
+function getJob(): Job {
+ return {
+ job_id: 'test',
+ description: '',
+ groups: [],
+ analysis_config: {
+ bucket_span: '15m',
+ detectors: [
+ {
+ function: 'mean',
+ field_name: 'responsetime',
+ },
+ ],
+ influencers: [],
+ },
+ data_description: {
+ time_field: '@timestamp',
+ },
+ analysis_limits: {
+ model_memory_limit: '11MB',
+ },
+ model_plot_config: {
+ enabled: false,
+ annotations_enabled: false,
+ },
+ };
+}
+
+function getDatafeed(): Datafeed {
+ return {
+ datafeed_id: 'datafeed-test',
+ job_id: 'dds',
+ indices: ['farequote-*'],
+ query: {
+ bool: {
+ must: [
+ {
+ match_all: {},
+ },
+ ],
+ },
+ },
+ runtime_mappings: {
+ responsetime_big: {
+ type: 'double',
+ script: {
+ source: "emit(doc['responsetime'].value * 100.0)",
+ },
+ },
+ airline_lower: {
+ type: 'keyword',
+ script: {
+ source: "emit(doc['airline'].value.toLowerCase())",
+ },
+ },
+ },
+ };
+}
+
+function getAggs() {
+ return {
+ buckets: {
+ date_histogram: {
+ field: '@timestamp',
+ fixed_interval: '90000ms',
+ },
+ aggregations: {
+ responsetime: {
+ avg: {
+ field: 'responsetime_big',
+ },
+ },
+ '@timestamp': {
+ max: {
+ field: '@timestamp',
+ },
+ },
+ },
+ },
+ };
+}
+
+describe('filter_runtime_mappings', () => {
+ describe('filterRuntimeMappings()', () => {
+ let job: Job;
+ let datafeed: Datafeed;
+ beforeEach(() => {
+ job = getJob();
+ datafeed = getDatafeed();
+ });
+
+ test('returns no runtime mappings, no mappings in aggs', () => {
+ const resp = filterRuntimeMappings(job, datafeed);
+ expect(Object.keys(resp.runtime_mappings).length).toEqual(0);
+
+ expect(Object.keys(resp.discarded_mappings).length).toEqual(2);
+ expect(resp.discarded_mappings.responsetime_big).not.toEqual(undefined);
+ expect(resp.discarded_mappings.airline_lower).not.toEqual(undefined);
+ });
+
+ test('returns no runtime mappings, no runtime mappings in datafeed', () => {
+ datafeed.runtime_mappings = undefined;
+ const resp = filterRuntimeMappings(job, datafeed);
+ expect(Object.keys(resp.runtime_mappings).length).toEqual(0);
+ expect(resp.runtime_mappings.responsetime_big).toEqual(undefined);
+
+ expect(Object.keys(resp.discarded_mappings).length).toEqual(0);
+ expect(resp.discarded_mappings.airline_lower).toEqual(undefined);
+ });
+
+ test('return one runtime mapping and one unused mapping, mappings in aggs', () => {
+ datafeed.aggregations = getAggs();
+ const resp = filterRuntimeMappings(job, datafeed);
+ expect(Object.keys(resp.runtime_mappings).length).toEqual(1);
+ expect(resp.runtime_mappings.responsetime_big).not.toEqual(undefined);
+
+ expect(Object.keys(resp.discarded_mappings).length).toEqual(1);
+ expect(resp.discarded_mappings.airline_lower).not.toEqual(undefined);
+ });
+
+ test('return no runtime mappings, no mappings in aggs', () => {
+ datafeed.aggregations = getAggs();
+ datafeed.aggregations!.buckets!.aggregations!.responsetime!.avg!.field! = 'responsetime';
+
+ const resp = filterRuntimeMappings(job, datafeed);
+ expect(Object.keys(resp.runtime_mappings).length).toEqual(0);
+
+ expect(Object.keys(resp.discarded_mappings).length).toEqual(2);
+ expect(resp.discarded_mappings.responsetime_big).not.toEqual(undefined);
+ expect(resp.discarded_mappings.airline_lower).not.toEqual(undefined);
+ });
+
+ test('return one runtime mapping and one unused mapping, no mappings in aggs', () => {
+ // set the detector field to be a runtime mapping
+ job.analysis_config.detectors[0].field_name = 'responsetime_big';
+ const resp = filterRuntimeMappings(job, datafeed);
+ expect(Object.keys(resp.runtime_mappings).length).toEqual(1);
+ expect(resp.runtime_mappings.responsetime_big).not.toEqual(undefined);
+
+ expect(Object.keys(resp.discarded_mappings).length).toEqual(1);
+ expect(resp.discarded_mappings.airline_lower).not.toEqual(undefined);
+ });
+
+ test('return two runtime mappings, no mappings in aggs', () => {
+ // set the detector field to be a runtime mapping
+ job.analysis_config.detectors[0].field_name = 'responsetime_big';
+ // set the detector by field to be a runtime mapping
+ job.analysis_config.detectors[0].by_field_name = 'airline_lower';
+ const resp = filterRuntimeMappings(job, datafeed);
+ expect(Object.keys(resp.runtime_mappings).length).toEqual(2);
+ expect(resp.runtime_mappings.responsetime_big).not.toEqual(undefined);
+ expect(resp.runtime_mappings.airline_lower).not.toEqual(undefined);
+
+ expect(Object.keys(resp.discarded_mappings).length).toEqual(0);
+ });
+
+ test('return two runtime mappings, no mappings in aggs, categorization job', () => {
+ job.analysis_config.detectors[0].function = 'count';
+ // set the detector field to be a runtime mapping
+ job.analysis_config.detectors[0].field_name = undefined;
+ // set the detector by field to be a runtime mapping
+ job.analysis_config.detectors[0].by_field_name = 'mlcategory';
+ job.analysis_config.categorization_field_name = 'airline_lower';
+
+ const resp = filterRuntimeMappings(job, datafeed);
+ expect(Object.keys(resp.runtime_mappings).length).toEqual(1);
+ expect(resp.runtime_mappings.airline_lower).not.toEqual(undefined);
+
+ expect(Object.keys(resp.discarded_mappings).length).toEqual(1);
+ expect(resp.discarded_mappings.responsetime_big).not.toEqual(undefined);
+ });
+ });
+});
diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts
new file mode 100644
index 00000000000000..5319cd3c3aabc9
--- /dev/null
+++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts
@@ -0,0 +1,104 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { RuntimeMappings } from '../../../../../../../common/types/fields';
+import type { Datafeed, Job } from '../../../../../../../common/types/anomaly_detection_jobs';
+
+interface Response {
+ runtime_mappings: RuntimeMappings;
+ discarded_mappings: RuntimeMappings;
+}
+
+export function filterRuntimeMappings(job: Job, datafeed: Datafeed): Response {
+ if (datafeed.runtime_mappings === undefined) {
+ return {
+ runtime_mappings: {},
+ discarded_mappings: {},
+ };
+ }
+
+ const usedFields = findFieldsInJob(job, datafeed);
+
+ const { runtimeMappings, discardedMappings } = createMappings(
+ datafeed.runtime_mappings,
+ usedFields
+ );
+
+ return { runtime_mappings: runtimeMappings, discarded_mappings: discardedMappings };
+}
+
+function findFieldsInJob(job: Job, datafeed: Datafeed) {
+ const usedFields = new Set();
+ job.analysis_config.detectors.forEach((d) => {
+ if (d.field_name !== undefined) {
+ usedFields.add(d.field_name);
+ }
+ if (d.by_field_name !== undefined) {
+ usedFields.add(d.by_field_name);
+ }
+ if (d.over_field_name !== undefined) {
+ usedFields.add(d.over_field_name);
+ }
+ if (d.partition_field_name !== undefined) {
+ usedFields.add(d.partition_field_name);
+ }
+ });
+
+ if (job.analysis_config.categorization_field_name !== undefined) {
+ usedFields.add(job.analysis_config.categorization_field_name);
+ }
+
+ if (job.analysis_config.summary_count_field_name !== undefined) {
+ usedFields.add(job.analysis_config.summary_count_field_name);
+ }
+
+ if (job.analysis_config.influencers !== undefined) {
+ job.analysis_config.influencers.forEach((i) => usedFields.add(i));
+ }
+
+ const aggs = datafeed.aggregations ?? datafeed.aggs;
+ if (aggs !== undefined) {
+ findFieldsInAgg(aggs).forEach((f) => usedFields.add(f));
+ }
+
+ return [...usedFields];
+}
+
+function findFieldsInAgg(obj: Record) {
+ const fields: string[] = [];
+ Object.entries(obj).forEach(([key, val]) => {
+ if (typeof val === 'object' && val !== null) {
+ fields.push(...findFieldsInAgg(val));
+ } else if (typeof val === 'string' && key === 'field') {
+ fields.push(val);
+ }
+ });
+ return fields;
+}
+
+function createMappings(rm: RuntimeMappings, usedFieldNames: string[]) {
+ return {
+ runtimeMappings: usedFieldNames.reduce((acc, cur) => {
+ if (rm[cur] !== undefined) {
+ acc[cur] = rm[cur];
+ }
+ return acc;
+ }, {} as RuntimeMappings),
+ discardedMappings: Object.keys(rm).reduce((acc, cur) => {
+ if (usedFieldNames.includes(cur) === false && rm[cur] !== undefined) {
+ acc[cur] = rm[cur];
+ }
+ return acc;
+ }, {} as RuntimeMappings),
+ };
+}
From 9ec1aa3ad3a335177395b529593f20dfe277e375 Mon Sep 17 00:00:00 2001
From: gchaps <33642766+gchaps@users.noreply.github.com>
Date: Wed, 17 Feb 2021 09:53:07 -0800
Subject: [PATCH 25/79] [DOCS] Restructures introduction doc (#91595)
* [DOCS] Restructures introduction doc
* [DOCS] Minor tweaks to introduction
* Update docs/user/introduction.asciidoc
Co-authored-by: Kaarina Tungseth
* Update docs/user/introduction.asciidoc
Co-authored-by: Kaarina Tungseth
* Update docs/user/introduction.asciidoc
Co-authored-by: Kaarina Tungseth
* Update docs/user/introduction.asciidoc
Co-authored-by: Kaarina Tungseth
* Update docs/user/introduction.asciidoc
Co-authored-by: Kaarina Tungseth
* Update docs/user/introduction.asciidoc
Co-authored-by: Kaarina Tungseth
---
docs/user/introduction.asciidoc | 228 ++++++++++++++++----------------
1 file changed, 117 insertions(+), 111 deletions(-)
diff --git a/docs/user/introduction.asciidoc b/docs/user/introduction.asciidoc
index fb91f6a6a1c9a2..65a8c4f6eb1878 100644
--- a/docs/user/introduction.asciidoc
+++ b/docs/user/introduction.asciidoc
@@ -7,20 +7,19 @@
{kib} enables you to give
shape to your data and navigate the Elastic Stack. With {kib}, you can:
-* *Visualize and analyze your data.*
-Search for hidden insights, visualize what you've found in charts, gauges,
-maps and more, and combine them in a dashboard.
-
* *Search, observe, and protect.*
From discovering documents to analyzing logs to finding security vulnerabilities,
{kib} is your portal for accessing these capabilities and more.
+* *Visualize and analyze your data.*
+Search for hidden insights, visualize what you've found in charts, gauges,
+maps and more, and combine them in a dashboard.
+
* *Manage, monitor, and secure the Elastic Stack.*
Manage your indices and ingest pipelines, monitor the health of your
Elastic Stack cluster, and control which users have access to
which features.
-
*{kib} is for administrators, analysts, and business users.*
As an admin, your role is to manage the Elastic Stack, from creating your
deployment to getting {es} data into {kib}, and then
@@ -55,85 +54,66 @@ hamburger icon. To keep the main menu visible at all times, click the *Dock navi
image::images/kibana-main-menu.png[Kibana main menu]
[float]
-[[kibana-navigation-search]]
-=== Search {kib}
-
-Using the Search field in the global header, you can
-search for applications and objects, such as
-dashboards and visualizations.
-
-Search suggestions include deep links into applications,
-allowing you to directly navigate to the views you need most.
-
-[role="screenshot"]
-image::images/app-navigation-search.png[Example of searching for apps]
-
-When searching for objects, you can search by type, name, and tag.
-Tags are keywords or labels that you assign to {kib} objects,
-so you can classify the objects in a way that is meaningful to you.
-You can then quickly search for related objects based on shared tags.
+[[extend-your-use-case]]
+=== Search, observe, and protect
-[role="screenshot"]
-image::images/tags-search.png[Example of searching for tags]
+Being able to search, observe, and protect your data is a requirement for any analyst.
+{kib} provides solutions for each of these use cases.
-To get the most from the search feature, follow these tips:
+* https://www.elastic.co/guide/en/enterprise-search/current/index.html[*Enterprise Search*] enables you to create a search experience for your app, workplace, and website.
-* Use the keyboard shortcut—Ctrl+/ on Windows and Linux, Command+/ on MacOS—to focus on the input at any time.
+* {observability-guide}/observability-introduction.html[*Elastic Observability*] enables you to monitor and apply analytics in real time
+to events happening across all your environments. You can analyze log events, monitor the performance metrics for the host or container
+that it ran in, trace the transaction, and check the overall service availability.
-* Use the provided syntax keywords.
+* Designed for security analysts, {security-guide}/es-overview.html[*Elastic Security*] provides an overview of
+the events and alerts from your environment. Elastic Security helps you defend
+your organization from threats before damage and loss occur.
+
-[cols=2*]
-|===
-|Search by type
-|`type:dashboard`
-
-Available types: `application`, `canvas-workpad`, `dashboard`, `index-pattern`, `lens`, `maps`, `query`, `search`, `visualization`
-
-|Search by tag
-|`tag:mytagname` +
-`tag:"tag name with spaces"`
-
-|Search by type and name
-|`type:dashboard my_dashboard_title`
-
-|Advanced searches
-|`tag:(tagname1 or tagname2) my_dashboard_title` +
-`type:lens tag:(tagname1 or tagname2)` +
-`type:(dashboard or canvas-workpad) logs` +
-|===
+[role="screenshot"]
+image::siem/images/detections-ui.png[Detections view in Elastic Security]
[float]
[[visualize-and-analyze]]
-=== Analyze your data
+=== Visualize and analyze
-Data analysis is the core functionality of {kib}.
+Data analysis is a core functionality of {kib}.
You can quickly search through large amounts of data, explore fields and values,
and then use {kib}’s drag-and-drop interface to rapidly build charts, tables, metrics, and more.
[role="screenshot"]
-image::images/visualization-journey.png[User visualization journey]
+image::images/visualization-journey.png[User data analysis journey]
[[get-data-into-kibana]]
-. *Add data.* The best way to add {es} data to {kib} is to use one of our guided processes,
+[cols=2*]
+|===
+
+| *1*
+| *Add data.* The best way to add {es} data to {kib} is to use one of our guided processes,
available from the <>. You can collect data from an app or service, upload a
file, or add a sample data set.
-. *Explore.* With <>, you can search your data for hidden
+| *2*
+| *Explore.* With <>, you can search your data for hidden
insights and relationships. Ask your questions, and then filter the results to just the data you want.
-You can also limit your results to the most recent documents added to {es}.
+You can limit your results to the most recent documents added to {es}.
-. *Visualize.* {kib} provides many options to create visualizations of your data, from
+| *3*
+| *Visualize.* {kib} provides many options to create visualizations of your data, from
aggregation-based data to time series data.
<> is your starting point to create visualizations,
and then pulling them together to show your data from multiple perspectives.
-. *Present.* With <>, you can display your data on a visually
+| *4*
+| *Present.* With <>, you can display your data on a visually
compelling, pixel-perfect workpad. **Canvas** can give your data
the “wow” factor needed to impress your CEO and captivate coworkers with a big-screen display.
-. *Share.* Ready to <> your findings with a larger audience? {kib} offers many options—embed
+| *5*
+| *Share.* Ready to <> your findings with a larger audience? {kib} offers many options—embed
a dashboard, share a link, export to PDF, and more.
+|===
[float]
==== Plot location data on a map
@@ -147,7 +127,7 @@ You can also visualize and track movement over space and through time.
[float]
==== Model data behavior
-To model the behavior of your data, you'll want to use
+To model the behavior of your data, you'll use
<>.
This app can help you extract insights from your data that you might otherwise miss.
You can forecast unusual behavior in your time series data.
@@ -164,37 +144,17 @@ can help you uncover website vulnerabilities that hackers are targeting,
so you can harden your website. Or, you might provide graph-based
personalized recommendations to your e-commerce customers.
-[float]
-[[extend-your-use-case]]
-=== Search, observe, and protect
-
-Being able to search, observe, and protect your data is a requirement for any analyst.
-{kib} provides solutions for each of these use cases.
-
-* https://www.elastic.co/guide/en/enterprise-search/current/index.html[*Enterprise Search*] enables you to create a search experience for your app, workplace, and website.
-
-* {observability-guide}/observability-introduction.html[*Elastic Observability*] enables you to monitor and apply analytics in real time
-to events happening across all your environments. You can analyze log events, monitor the performance metrics for the host or container
-that it ran in, trace the transaction, and check the overall service availability.
-
-* Designed for security analysts, {security-guide}/es-overview.html[*Elastic Security*] provides an overview of
-the events and alerts from your environment. Elastic Security helps you defend
-your organization from threats before damage and loss occur.
-+
-[role="screenshot"]
-image::siem/images/detections-ui.png[]
-
[float]
[[manage-all-things-stack]]
=== Manage all things Elastic Stack
-{kib}'s <> takes you under the hood,
-so you can twist the levers and turn the knobs. *Stack Management* provides
+{kib}'s <> UIs takes you under the hood,
+so you can twist the levers and turn the knobs. You'll find
guided processes for administering all things Elastic Stack,
including data, indices, clusters, alerts, and security.
[role="screenshot"]
-image::images/intro-management.png[]
+image::images/intro-management.png[Index Management view in Stack Management]
[float]
==== Manage your data, indices, and clusters
@@ -216,8 +176,8 @@ that exists in almost every use case. For example, you might set an alert to not
* System resources, such as memory, CPU and disk space, take a dip.
* An unusually high number of service requests, suspicious processes, and login attempts occurs.
-An alert is triggered when a specified condition is met. For example,
-an alert might trigger when the average or max of one of
+An alert triggers when a specified condition is met. For example,
+you can trigger an alert when the average or max of one of
your metrics exceeds a threshold within a specified time frame.
When the alert triggers, you can send a notification to a system that is part of
@@ -240,7 +200,7 @@ Think of a space as its own mini {kib} installation—it’s isolated from a
so you can tailor it to your specific needs without impacting others.
[role="screenshot"]
-image::images/select-your-space.png[Space selector screen]
+image::images/select-your-space.png[Space selector view]
Most of {kib}’s entities are space-aware, including dashboards, visualizations, index patterns,
Canvas workpads, Timelion visualizations, graphs, tags, and machine learning jobs.
@@ -271,7 +231,7 @@ to specific features on a per-user basis, you must configure
<>.
[role="screenshot"]
-image::images/features-control.png[Features Controls screen]
+image::images/features-control.png[Features Controls view]
[float]
[[intro-kibana-Security]]
@@ -289,7 +249,7 @@ Kibana supports several <>,
allowing you to login using {es}’s built-in realms, or by your own single sign-on provider.
[role="screenshot"]
-image::images/login-screen.png[Login screen]
+image::images/login-screen.png[Login page]
[float]
==== Secure access
@@ -320,6 +280,52 @@ record of who did what, when. The {kib} audit log will record this information f
which can then be correlated with {es} audit logs to gain more insights into your
users’ behavior. For more information, see <>.
+[float]
+[[kibana-navigation-search]]
+=== Quickly find apps and objects
+
+Using the search field in the global header, you can
+search for applications and objects, such as
+dashboards and visualizations. Search suggestions include deep links into applications,
+allowing you to directly navigate to the views you need most.
+
+[role="screenshot"]
+image::images/app-navigation-search.png[Example of searching for apps]
+
+When searching for objects, you can search by type, name, and tag.
+Tags are keywords or labels that you assign to {kib} objects,
+so you can classify the objects in a way that is meaningful to you.
+You can then quickly search for related objects based on shared tags.
+
+[role="screenshot"]
+image::images/tags-search.png[Example of searching for tags]
+
+To get the most from the search feature, follow these tips:
+
+* Use the keyboard shortcut—Ctrl+/ on Windows and Linux, Command+/ on MacOS—to focus on the input at any time.
+
+* Use the provided syntax keywords.
++
+[cols=2*]
+|===
+|Search by type
+|`type:dashboard`
+
+Available types: `application`, `canvas-workpad`, `dashboard`, `index-pattern`, `lens`, `maps`, `query`, `search`, `visualization`
+
+|Search by tag
+|`tag:mytagname` +
+`tag:"tag name with spaces"`
+
+|Search by type and name
+|`type:dashboard my_dashboard_title`
+
+|Advanced searches
+|`tag:(tagname1 or tagname2) my_dashboard_title` +
+`type:lens tag:(tagname1 or tagname2)` +
+`type:(dashboard or canvas-workpad) logs` +
+|===
+
[float]
[[whats-the-right-app]]
=== What’s the right app for you?
@@ -345,32 +351,6 @@ the <>.
|See the full list of {kib} features
|The https://www.elastic.co/kibana/features[{kib} features page on elastic.co]
-2+| *Analyze and visualize your data*
-
-|Know what’s in your data
-|<>
-
-|Create charts and other visualizations
-|<>
-
-|Show your data from different perspectives
-|<>
-
-|Work with location data
-|<>
-
-|Create a presentation of your data
-|<>
-
-|Generate models for your data’s behavior
-|<>
-
-|Explore connections in your data
-|<>
-
-|Share your data
-|<>, <>
-
2+|*Build a search experience*
|Create a search experience for your workplace
@@ -414,6 +394,32 @@ the <>.
|View and manage hosts that are running Endpoint Security
|{security-guide}/admin-page-ov.html[Administration]
+2+| *Analyze and visualize your data*
+
+|Know what’s in your data
+|<>
+
+|Create charts and other visualizations
+|<>
+
+|Show your data from different perspectives
+|<>
+
+|Work with location data
+|<>
+
+|Create a presentation of your data
+|<>
+
+|Generate models for your data’s behavior
+|<>
+
+|Explore connections in your data
+|<>
+
+|Share your data
+|<>, <>
+
2+|*Administer your Kibana instance*
|Manage your Elasticsearch data
@@ -435,7 +441,7 @@ the <>.
[float]
[[try-kibana]]
-=== Getting help
+=== How to get help
Using our in-product guidance can help you get up and running, faster.
Click the help icon image:images/intro-help-icon.png[Help icon in navigation bar]
From 06b8fb44d40d72b9835e8e7d3531bb93110bcdfc Mon Sep 17 00:00:00 2001
From: spalger
Date: Wed, 17 Feb 2021 10:56:45 -0700
Subject: [PATCH 26/79] [renovate] disable pr limits since we only enable for
limited packages
---
renovate.json5 | 2 ++
1 file changed, 2 insertions(+)
diff --git a/renovate.json5 b/renovate.json5
index 1d64954e21d474..52d7a06c88339a 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -13,6 +13,8 @@
'master',
'7.x',
],
+ prConcurrentLimit: 0,
+ prHourlyLimit: 0,
separateMajorMinor: false,
masterIssue: true,
rangeStrategy: 'bump',
From c84047bd36f265992fc5645494aa658ae3036559 Mon Sep 17 00:00:00 2001
From: Dima Arnautov
Date: Wed, 17 Feb 2021 19:26:23 +0100
Subject: [PATCH 27/79] [ML] Prevent duplicate notifications about the same
anomaly result (#91485)
* [ML] check kibana even logs for existing alert instance
* [ML] create alert instance key, add check for alert id
* [ML] use anomaly_utils, check interval gap
* [ML] add detector index
* [ML] fix unit test
* [ML] include detector_index into source
---
x-pack/plugins/ml/common/types/alerts.ts | 13 +-
.../plugins/ml/common/util/anomaly_utils.ts | 4 -
.../lib/alerts/alerting_service.test.ts | 4 +-
.../ml/server/lib/alerts/alerting_service.ts | 143 +++++++++++++++---
.../register_anomaly_detection_alert_type.ts | 10 +-
.../server/lib/alerts/register_ml_alerts.ts | 2 +
x-pack/plugins/ml/server/plugin.ts | 1 +
x-pack/plugins/ml/server/routes/alerting.ts | 4 +-
.../providers/alerting_service.ts | 8 +-
9 files changed, 150 insertions(+), 39 deletions(-)
diff --git a/x-pack/plugins/ml/common/types/alerts.ts b/x-pack/plugins/ml/common/types/alerts.ts
index 51d06b99062300..7e6e9d89c5a651 100644
--- a/x-pack/plugins/ml/common/types/alerts.ts
+++ b/x-pack/plugins/ml/common/types/alerts.ts
@@ -15,7 +15,7 @@ export type TopHitsResultsKeys = 'top_record_hits' | 'top_bucket_hits' | 'top_in
export interface AlertExecutionResult {
count: number;
key: number;
- key_as_string: string;
+ alertInstanceKey: string;
isInterim: boolean;
jobIds: string[];
timestamp: number;
@@ -47,10 +47,13 @@ interface BaseAnomalyAlertDoc {
export interface RecordAnomalyAlertDoc extends BaseAnomalyAlertDoc {
result_type: typeof ANOMALY_RESULT_TYPE.RECORD;
function: string;
- field_name: string;
- by_field_value: string | number;
- over_field_value: string | number;
- partition_field_value: string | number;
+ field_name?: string;
+ by_field_name?: string;
+ by_field_value?: string | number;
+ over_field_name?: string;
+ over_field_value?: string | number;
+ partition_field_name?: string;
+ partition_field_value?: string | number;
}
export interface BucketAnomalyAlertDoc extends BaseAnomalyAlertDoc {
diff --git a/x-pack/plugins/ml/common/util/anomaly_utils.ts b/x-pack/plugins/ml/common/util/anomaly_utils.ts
index 028afee2524c93..68605f29c7be96 100644
--- a/x-pack/plugins/ml/common/util/anomaly_utils.ts
+++ b/x-pack/plugins/ml/common/util/anomaly_utils.ts
@@ -230,8 +230,6 @@ export function getEntityFieldName(record: AnomalyRecordDoc): string | undefined
if (record.partition_field_name !== undefined) {
return record.partition_field_name;
}
-
- return undefined;
}
// Returns the value of the field to use as the entity value from the source record
@@ -249,8 +247,6 @@ export function getEntityFieldValue(record: AnomalyRecordDoc): string | number |
if (record.partition_field_value !== undefined) {
return record.partition_field_value;
}
-
- return undefined;
}
// Returns the list of partitioning entity fields for the source record as a list
diff --git a/x-pack/plugins/ml/server/lib/alerts/alerting_service.test.ts b/x-pack/plugins/ml/server/lib/alerts/alerting_service.test.ts
index 261fac7b620ba9..f029fa24f96078 100644
--- a/x-pack/plugins/ml/server/lib/alerts/alerting_service.test.ts
+++ b/x-pack/plugins/ml/server/lib/alerts/alerting_service.test.ts
@@ -5,10 +5,10 @@
* 2.0.
*/
-import { resolveTimeInterval } from './alerting_service';
+import { resolveBucketSpanInSeconds } from './alerting_service';
describe('Alerting Service', () => {
test('should resolve maximum bucket interval', () => {
- expect(resolveTimeInterval(['15m', '1h', '6h', '90s'])).toBe('43200s');
+ expect(resolveBucketSpanInSeconds(['15m', '1h', '6h', '90s'])).toBe(43200);
});
});
diff --git a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts
index 5ef883cc50fbb3..6e7cd77e450bcc 100644
--- a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts
+++ b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts
@@ -7,6 +7,9 @@
import Boom from '@hapi/boom';
import rison from 'rison-node';
+import { ElasticsearchClient } from 'kibana/server';
+import moment from 'moment';
+import { Duration } from 'moment/moment';
import { MlClient } from '../ml_client';
import {
MlAnomalyDetectionAlertParams,
@@ -25,6 +28,8 @@ import {
import { parseInterval } from '../../../common/util/parse_interval';
import { AnomalyDetectionAlertContext } from './register_anomaly_detection_alert_type';
import { MlJobsResponse } from '../../../common/types/job_service';
+import { ANOMALY_SCORE_MATCH_GROUP_ID } from '../../../common/constants/alerts';
+import { getEntityFieldName, getEntityFieldValue } from '../../../common/util/anomaly_utils';
function isDefined(argument: T | undefined | null): argument is T {
return argument !== undefined && argument !== null;
@@ -34,22 +39,23 @@ function isDefined(argument: T | undefined | null): argument is T {
* Resolves the longest bucket span from the list and multiply it by 2.
* @param bucketSpans Collection of bucket spans
*/
-export function resolveTimeInterval(bucketSpans: string[]): string {
- return `${
+export function resolveBucketSpanInSeconds(bucketSpans: string[]): number {
+ return (
Math.max(
...bucketSpans
.map((b) => parseInterval(b))
.filter(isDefined)
.map((v) => v.asSeconds())
) * 2
- }s`;
+ );
}
/**
* Alerting related server-side methods
* @param mlClient
+ * @param esClient
*/
-export function alertingServiceProvider(mlClient: MlClient) {
+export function alertingServiceProvider(mlClient: MlClient, esClient: ElasticsearchClient) {
const getAggResultsLabel = (resultType: AnomalyResultType) => {
return {
aggGroupLabel: `${resultType}_results` as PreviewResultsKeys,
@@ -177,10 +183,14 @@ export function alertingServiceProvider(mlClient: MlClient) {
'is_interim',
'function',
'field_name',
+ 'by_field_name',
'by_field_value',
+ 'over_field_name',
'over_field_value',
+ 'partition_field_name',
'partition_field_value',
'job_id',
+ 'detector_index',
],
},
size: 3,
@@ -257,14 +267,31 @@ export function alertingServiceProvider(mlClient: MlClient) {
};
};
+ /**
+ * Provides unique key for the anomaly result.
+ */
+ const getAlertInstanceKey = (source: any): string => {
+ let alertInstanceKey = `${source.job_id}_${source.timestamp}`;
+ if (source.result_type === ANOMALY_RESULT_TYPE.INFLUENCER) {
+ alertInstanceKey += `_${source.influencer_field_name}_${source.influencer_field_value}`;
+ } else if (source.result_type === ANOMALY_RESULT_TYPE.RECORD) {
+ const fieldName = getEntityFieldName(source);
+ const fieldValue = getEntityFieldValue(source);
+ alertInstanceKey += `_${source.detector_index}_${source.function}_${fieldName}_${fieldValue}`;
+ }
+ return alertInstanceKey;
+ };
+
/**
* Builds a request body
- * @param params
- * @param previewTimeInterval
+ * @param params - Alert params
+ * @param previewTimeInterval - Relative time interval to test the alert condition
+ * @param checkIntervalGap - Interval between alert executions
*/
const fetchAnomalies = async (
params: MlAnomalyDetectionAlertParams,
- previewTimeInterval?: string
+ previewTimeInterval?: string,
+ checkIntervalGap?: Duration
): Promise => {
const jobAndGroupIds = [
...(params.jobSelection.jobIds ?? []),
@@ -281,9 +308,14 @@ export function alertingServiceProvider(mlClient: MlClient) {
return;
}
- const lookBackTimeInterval = resolveTimeInterval(
- jobsResponse.map((v) => v.analysis_config.bucket_span)
- );
+ /**
+ * The check interval might be bigger than the 2x bucket span.
+ * We need to check the biggest time range to make sure anomalies are not missed.
+ */
+ const lookBackTimeInterval = `${Math.max(
+ resolveBucketSpanInSeconds(jobsResponse.map((v) => v.analysis_config.bucket_span)),
+ checkIntervalGap ? checkIntervalGap.asSeconds() : 0
+ )}s`;
const jobIds = jobsResponse.map((v) => v.job_id);
@@ -370,19 +402,22 @@ export function alertingServiceProvider(mlClient: MlClient) {
const aggTypeResults = v[resultsLabel.aggGroupLabel];
const requestedAnomalies = aggTypeResults[resultsLabel.topHitsLabel].hits.hits;
+ const topAnomaly = requestedAnomalies[0];
+ const alertInstanceKey = getAlertInstanceKey(topAnomaly._source);
+
return {
count: aggTypeResults.doc_count,
key: v.key,
- key_as_string: v.key_as_string,
+ alertInstanceKey,
jobIds: [...new Set(requestedAnomalies.map((h) => h._source.job_id))],
isInterim: requestedAnomalies.some((h) => h._source.is_interim),
- timestamp: requestedAnomalies[0]._source.timestamp,
- timestampIso8601: requestedAnomalies[0].fields.timestamp_iso8601[0],
- timestampEpoch: requestedAnomalies[0].fields.timestamp_epoch[0],
- score: requestedAnomalies[0].fields.score[0],
+ timestamp: topAnomaly._source.timestamp,
+ timestampIso8601: topAnomaly.fields.timestamp_iso8601[0],
+ timestampEpoch: topAnomaly.fields.timestamp_epoch[0],
+ score: topAnomaly.fields.score[0],
bucketRange: {
- start: requestedAnomalies[0].fields.start[0],
- end: requestedAnomalies[0].fields.end[0],
+ start: topAnomaly.fields.start[0],
+ end: topAnomaly.fields.end[0],
},
topRecords: v.record_results.top_record_hits.hits.hits.map((h) => ({
...h._source,
@@ -479,13 +514,24 @@ export function alertingServiceProvider(mlClient: MlClient) {
/**
* Return the result of an alert condition execution.
*
- * @param params
+ * @param params - Alert params
+ * @param publicBaseUrl
+ * @param alertId - Alert ID
+ * @param startedAt
+ * @param previousStartedAt
*/
execute: async (
params: MlAnomalyDetectionAlertParams,
- publicBaseUrl: string | undefined
+ publicBaseUrl: string | undefined,
+ alertId: string,
+ startedAt: Date,
+ previousStartedAt: Date | null
): Promise => {
- const res = await fetchAnomalies(params);
+ const checkIntervalGap = previousStartedAt
+ ? moment.duration(moment(startedAt).diff(previousStartedAt))
+ : undefined;
+
+ const res = await fetchAnomalies(params, undefined, checkIntervalGap);
if (!res) {
throw new Error('No results found');
@@ -496,12 +542,65 @@ export function alertingServiceProvider(mlClient: MlClient) {
const anomalyExplorerUrl = buildExplorerUrl(result, params.resultType as AnomalyResultType);
- return {
+ const executionResult = {
...result,
- name: result.key_as_string,
+ name: result.alertInstanceKey,
anomalyExplorerUrl,
kibanaBaseUrl: publicBaseUrl!,
};
+
+ let kibanaEventLogCount = 0;
+ try {
+ // Check kibana-event-logs for presence of this alert instance
+ const kibanaLogResults = await esClient.count({
+ index: '.kibana-event-log-*',
+ body: {
+ query: {
+ bool: {
+ must: [
+ {
+ term: {
+ 'kibana.alerting.action_group_id': {
+ value: ANOMALY_SCORE_MATCH_GROUP_ID,
+ },
+ },
+ },
+ {
+ term: {
+ 'kibana.alerting.instance_id': {
+ value: executionResult.name,
+ },
+ },
+ },
+ {
+ nested: {
+ path: 'kibana.saved_objects',
+ query: {
+ term: {
+ 'kibana.saved_objects.id': {
+ value: alertId,
+ },
+ },
+ },
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ kibanaEventLogCount = kibanaLogResults.body.count;
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.log('Unable to check kibana event logs', e);
+ }
+
+ if (kibanaEventLogCount > 0) {
+ return;
+ }
+
+ return executionResult;
},
/**
* Checks how often the alert condition will fire an alert instance
diff --git a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts
index 6f8fa59aa231e7..30a92c02cefc3a 100644
--- a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts
+++ b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts
@@ -123,13 +123,19 @@ export function registerAnomalyDetectionAlertType({
},
producer: PLUGIN_ID,
minimumLicenseRequired: MINIMUM_FULL_LICENSE,
- async executor({ services, params }) {
+ async executor({ services, params, alertId, state, previousStartedAt, startedAt }) {
const fakeRequest = {} as KibanaRequest;
const { execute } = mlSharedServices.alertingServiceProvider(
services.savedObjectsClient,
fakeRequest
);
- const executionResult = await execute(params, publicBaseUrl);
+ const executionResult = await execute(
+ params,
+ publicBaseUrl,
+ alertId,
+ startedAt,
+ previousStartedAt
+ );
if (executionResult) {
const alertInstanceName = executionResult.name;
diff --git a/x-pack/plugins/ml/server/lib/alerts/register_ml_alerts.ts b/x-pack/plugins/ml/server/lib/alerts/register_ml_alerts.ts
index 5c9106d78595fc..371c5435f91de9 100644
--- a/x-pack/plugins/ml/server/lib/alerts/register_ml_alerts.ts
+++ b/x-pack/plugins/ml/server/lib/alerts/register_ml_alerts.ts
@@ -5,12 +5,14 @@
* 2.0.
*/
+import { Logger } from 'kibana/server';
import { AlertingPlugin } from '../../../../alerts/server';
import { registerAnomalyDetectionAlertType } from './register_anomaly_detection_alert_type';
import { SharedServices } from '../../shared_services';
export interface RegisterAlertParams {
alerts: AlertingPlugin['setup'];
+ logger: Logger;
mlSharedServices: SharedServices;
publicBaseUrl: string | undefined;
}
diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts
index 10ed70d7f73969..24fac9184cc27c 100644
--- a/x-pack/plugins/ml/server/plugin.ts
+++ b/x-pack/plugins/ml/server/plugin.ts
@@ -211,6 +211,7 @@ export class MlServerPlugin
if (plugins.alerts) {
registerMlAlerts({
alerts: plugins.alerts,
+ logger: this.log,
mlSharedServices: sharedServices,
publicBaseUrl: coreSetup.http.basePath.publicBaseUrl,
});
diff --git a/x-pack/plugins/ml/server/routes/alerting.ts b/x-pack/plugins/ml/server/routes/alerting.ts
index 7b7f3a7db9723a..a268a5200b35e7 100644
--- a/x-pack/plugins/ml/server/routes/alerting.ts
+++ b/x-pack/plugins/ml/server/routes/alerting.ts
@@ -30,9 +30,9 @@ export function alertingRoutes({ router, routeGuard }: RouteInitialization) {
tags: ['access:ml:canGetJobs'],
},
},
- routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => {
+ routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response, client }) => {
try {
- const alertingService = alertingServiceProvider(mlClient);
+ const alertingService = alertingServiceProvider(mlClient, client.asInternalUser);
const result = await alertingService.preview(request.body);
diff --git a/x-pack/plugins/ml/server/shared_services/providers/alerting_service.ts b/x-pack/plugins/ml/server/shared_services/providers/alerting_service.ts
index 318dac200a8772..cbe22478e12d6c 100644
--- a/x-pack/plugins/ml/server/shared_services/providers/alerting_service.ts
+++ b/x-pack/plugins/ml/server/shared_services/providers/alerting_service.ts
@@ -20,7 +20,9 @@ export function getAlertingServiceProvider(getGuards: GetGuards) {
return await getGuards(request, savedObjectsClient)
.isFullLicense()
.hasMlCapabilities(['canGetJobs'])
- .ok(({ mlClient }) => alertingServiceProvider(mlClient).preview(...args));
+ .ok(({ mlClient, scopedClient }) =>
+ alertingServiceProvider(mlClient, scopedClient.asInternalUser).preview(...args)
+ );
},
execute: async (
...args: Parameters
@@ -28,7 +30,9 @@ export function getAlertingServiceProvider(getGuards: GetGuards) {
return await getGuards(request, savedObjectsClient)
.isFullLicense()
.hasMlCapabilities(['canGetJobs'])
- .ok(({ mlClient }) => alertingServiceProvider(mlClient).execute(...args));
+ .ok(({ mlClient, scopedClient }) =>
+ alertingServiceProvider(mlClient, scopedClient.asInternalUser).execute(...args)
+ );
},
};
},
From 88ac3bf541a2437dd207f32374b0c66e52a3d29c Mon Sep 17 00:00:00 2001
From: ymao1
Date: Wed, 17 Feb 2021 13:43:25 -0500
Subject: [PATCH 28/79] [Actions][Docs] Actions and Connectors API Docs
(#90974)
* Stubbing out asciidocs
* wip
* Finishing connector API docs
* Cleanup
* Removing experimental label
* PR fixes
* PR fixes
* PR fixes
---
docs/api/actions-and-connectors.asciidoc | 30 +++++++
.../actions-and-connectors/create.asciidoc | 68 +++++++++++++++
.../actions-and-connectors/delete.asciidoc | 35 ++++++++
.../actions-and-connectors/execute.asciidoc | 83 +++++++++++++++++++
docs/api/actions-and-connectors/get.asciidoc | 50 +++++++++++
.../actions-and-connectors/get_all.asciidoc | 52 ++++++++++++
docs/api/actions-and-connectors/list.asciidoc | 59 +++++++++++++
.../actions-and-connectors/update.asciidoc | 68 +++++++++++++++
.../user/alerting/action-types/email.asciidoc | 1 +
.../user/alerting/action-types/index.asciidoc | 1 +
docs/user/alerting/action-types/jira.asciidoc | 1 +
.../alerting/action-types/pagerduty.asciidoc | 1 +
.../alerting/action-types/resilient.asciidoc | 1 +
.../alerting/action-types/servicenow.asciidoc | 1 +
.../user/alerting/action-types/slack.asciidoc | 1 +
.../user/alerting/action-types/teams.asciidoc | 1 +
.../alerting/action-types/webhook.asciidoc | 1 +
docs/user/api.asciidoc | 1 +
18 files changed, 455 insertions(+)
create mode 100644 docs/api/actions-and-connectors.asciidoc
create mode 100644 docs/api/actions-and-connectors/create.asciidoc
create mode 100644 docs/api/actions-and-connectors/delete.asciidoc
create mode 100644 docs/api/actions-and-connectors/execute.asciidoc
create mode 100644 docs/api/actions-and-connectors/get.asciidoc
create mode 100644 docs/api/actions-and-connectors/get_all.asciidoc
create mode 100644 docs/api/actions-and-connectors/list.asciidoc
create mode 100644 docs/api/actions-and-connectors/update.asciidoc
diff --git a/docs/api/actions-and-connectors.asciidoc b/docs/api/actions-and-connectors.asciidoc
new file mode 100644
index 00000000000000..17e7ea1b7672a3
--- /dev/null
+++ b/docs/api/actions-and-connectors.asciidoc
@@ -0,0 +1,30 @@
+[[actions-and-connectors-api]]
+== Action and connector APIs
+
+Manage Actions and Connectors.
+
+The following action APIs are available:
+
+* <> to retrieve a single action by ID
+
+* <> to retrieve all actions
+
+* <> to retrieve a list of all action types
+
+* <> to create actions
+
+* <> to update the attributes for an existing action
+
+* <> to execute an action by ID
+
+* <> to delete an action by ID
+
+For information about the actions and connectors that {kib} supports, refer to <>.
+
+include::actions-and-connectors/get.asciidoc[]
+include::actions-and-connectors/get_all.asciidoc[]
+include::actions-and-connectors/list.asciidoc[]
+include::actions-and-connectors/create.asciidoc[]
+include::actions-and-connectors/update.asciidoc[]
+include::actions-and-connectors/execute.asciidoc[]
+include::actions-and-connectors/delete.asciidoc[]
diff --git a/docs/api/actions-and-connectors/create.asciidoc b/docs/api/actions-and-connectors/create.asciidoc
new file mode 100644
index 00000000000000..af5ddd050e40ec
--- /dev/null
+++ b/docs/api/actions-and-connectors/create.asciidoc
@@ -0,0 +1,68 @@
+[[actions-and-connectors-api-create]]
+=== Create action API
+++++
+Create action API
+++++
+
+Creates an action.
+
+[[actions-and-connectors-api-create-request]]
+==== Request
+
+`POST :/api/actions/action`
+
+[[actions-and-connectors-api-create-request-body]]
+==== Request body
+
+`name`::
+ (Required, string) The display name for the action.
+
+`actionTypeId`::
+ (Required, string) The action type ID for the action.
+
+`config`::
+ (Required, object) The configuration for the action. Configuration properties vary depending on
+ the action type. For information about the configuration properties, refer to <>.
+
+`secrets`::
+ (Required, object) The secrets configuration for the action. Secrets configuration properties vary
+ depending on the action type. For information about the secrets configuration properties, refer to <>.
+
+[[actions-and-connectors-api-create-request-codes]]
+==== Response code
+
+`200`::
+ Indicates a successful call.
+
+[[actions-and-connectors-api-create-example]]
+==== Example
+
+[source,sh]
+--------------------------------------------------
+$ curl -X POST api/actions/action -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
+{
+ "name": "my-action",
+ "actionTypeId": ".index",
+ "config": {
+ "index": "test-index"
+ }
+}'
+--------------------------------------------------
+// KIBANA
+
+The API returns the following:
+
+[source,sh]
+--------------------------------------------------
+{
+ "id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
+ "actionTypeId": ".index",
+ "name": "my-action",
+ "config": {
+ "index": "test-index",
+ "refresh": false,
+ "executionTimeField": null
+ },
+ "isPreconfigured": false
+}
+--------------------------------------------------
\ No newline at end of file
diff --git a/docs/api/actions-and-connectors/delete.asciidoc b/docs/api/actions-and-connectors/delete.asciidoc
new file mode 100644
index 00000000000000..e90b9ae44c5bd3
--- /dev/null
+++ b/docs/api/actions-and-connectors/delete.asciidoc
@@ -0,0 +1,35 @@
+[[actions-and-connectors-api-delete]]
+=== Delete action API
+++++
+Delete action API
+++++
+
+Deletes an action by ID.
+
+WARNING: When you delete an action, _it cannot be recovered_.
+
+[[actions-and-connectors-api-delete-request]]
+==== Request
+
+`DELETE :/api/actions/action/`
+
+[[actions-and-connectors-api-delete-path-params]]
+==== Path parameters
+
+`id`::
+ (Required, string) The ID of the action.
+
+[[actions-and-connectors-api-delete-response-codes]]
+==== Response code
+
+`200`::
+ Indicates a successful call.
+
+==== Example
+
+[source,sh]
+--------------------------------------------------
+$ curl -X DELETE api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
+--------------------------------------------------
+// KIBANA
+
diff --git a/docs/api/actions-and-connectors/execute.asciidoc b/docs/api/actions-and-connectors/execute.asciidoc
new file mode 100644
index 00000000000000..12f1405eb44560
--- /dev/null
+++ b/docs/api/actions-and-connectors/execute.asciidoc
@@ -0,0 +1,83 @@
+[[actions-and-connectors-api-execute]]
+=== Execute action API
+++++
+Execute action API
+++++
+
+Executes an action by ID.
+
+[[actions-and-connectors-api-execute-request]]
+==== Request
+
+`POST :/api/actions/action//_execute`
+
+[[actions-and-connectors-api-execute-params]]
+==== Path parameters
+
+`id`::
+ (Required, string) The ID of the action.
+
+[[actions-and-connectors-api-execute-request-body]]
+==== Request body
+
+`params`::
+ (Required, object) The parameters of the action. Parameter properties vary depending on
+ the action type. For information about the parameter properties, refer to <>.
+
+[[actions-and-connectors-api-execute-codes]]
+==== Response code
+
+`200`::
+ Indicates a successful call.
+
+[[actions-and-connectors-api-execute-example]]
+==== Example
+
+[source,sh]
+--------------------------------------------------
+$ curl -X POST api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad/_execute -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
+{
+ "params": {
+ "documents": [
+ {
+ "id": "test_doc_id",
+ "name": "test_doc_name",
+ "message": "hello, world"
+ }
+ ]
+ }
+}'
+--------------------------------------------------
+// KIBANA
+
+The API returns the following:
+
+[source,sh]
+--------------------------------------------------
+{
+ "status": "ok",
+ "data": {
+ "took": 197,
+ "errors": false,
+ "items": [
+ {
+ "index": {
+ "_index": "updated-index",
+ "_id": "iKyijHcBKCsmXNFrQe3T",
+ "_version": 1,
+ "result": "created",
+ "_shards": {
+ "total": 2,
+ "successful": 1,
+ "failed": 0
+ },
+ "_seq_no": 0,
+ "_primary_term": 1,
+ "status": 201
+ }
+ }
+ ]
+ },
+ "actionId": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad"
+}
+--------------------------------------------------
\ No newline at end of file
diff --git a/docs/api/actions-and-connectors/get.asciidoc b/docs/api/actions-and-connectors/get.asciidoc
new file mode 100644
index 00000000000000..6be554e65db049
--- /dev/null
+++ b/docs/api/actions-and-connectors/get.asciidoc
@@ -0,0 +1,50 @@
+[[actions-and-connectors-api-get]]
+=== Get action API
+++++
+Get action API
+++++
+
+Retrieves an action by ID.
+
+[[actions-and-connectors-api-get-request]]
+==== Request
+
+`GET :/api/actions/action/`
+
+[[actions-and-connectors-api-get-params]]
+==== Path parameters
+
+`id`::
+ (Required, string) The ID of the action.
+
+[[actions-and-connectors-api-get-codes]]
+==== Response code
+
+`200`::
+ Indicates a successful call.
+
+[[actions-and-connectors-api-get-example]]
+==== Example
+
+[source,sh]
+--------------------------------------------------
+$ curl -X GET api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
+--------------------------------------------------
+// KIBANA
+
+The API returns the following:
+
+[source,sh]
+--------------------------------------------------
+{
+ "id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
+ "actionTypeId": ".index",
+ "name": "my-action",
+ "config": {
+ "index": "test-index",
+ "refresh": false,
+ "executionTimeField": null
+ },
+ "isPreconfigured": false
+}
+--------------------------------------------------
diff --git a/docs/api/actions-and-connectors/get_all.asciidoc b/docs/api/actions-and-connectors/get_all.asciidoc
new file mode 100644
index 00000000000000..9863963c8395e6
--- /dev/null
+++ b/docs/api/actions-and-connectors/get_all.asciidoc
@@ -0,0 +1,52 @@
+[[actions-and-connectors-api-get-all]]
+=== Get all actions API
+++++
+Get all actions API
+++++
+
+Retrieves all actions.
+
+[[actions-and-connectors-api-get-all-request]]
+==== Request
+
+`GET :/api/actions`
+
+[[actions-and-connectors-api-get-all-codes]]
+==== Response code
+
+`200`::
+ Indicates a successful call.
+
+[[actions-and-connectors-api-get-all-example]]
+==== Example
+
+[source,sh]
+--------------------------------------------------
+$ curl -X GET api/actions
+--------------------------------------------------
+// KIBANA
+
+The API returns the following:
+
+[source,sh]
+--------------------------------------------------
+[
+ {
+ "id": "preconfigured-mail-action",
+ "actionTypeId": ".email",
+ "name": "email: preconfigured-mail-action",
+ "isPreconfigured": true
+ },
+ {
+ "id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
+ "actionTypeId": ".index",
+ "name": "my-action",
+ "config": {
+ "index": "test-index",
+ "refresh": false,
+ "executionTimeField": null
+ },
+ "isPreconfigured": false
+ }
+]
+--------------------------------------------------
diff --git a/docs/api/actions-and-connectors/list.asciidoc b/docs/api/actions-and-connectors/list.asciidoc
new file mode 100644
index 00000000000000..b800b7ff3b4f2f
--- /dev/null
+++ b/docs/api/actions-and-connectors/list.asciidoc
@@ -0,0 +1,59 @@
+[[actions-and-connectors-api-list]]
+=== List action types API
+++++
+List all action types API
+++++
+
+Retrieves a list of all action types.
+
+[[actions-and-connectors-api-list-request]]
+==== Request
+
+`GET :/api/actions/list_action_types`
+
+[[actions-and-connectors-api-list-codes]]
+==== Response code
+
+`200`::
+ Indicates a successful call.
+
+[[actions-and-connectors-api-list-example]]
+==== Example
+
+[source,sh]
+--------------------------------------------------
+$ curl -X GET api/actions/list_action_types
+--------------------------------------------------
+// KIBANA
+
+The API returns the following:
+
+[source,sh]
+--------------------------------------------------
+[
+ {
+ "id": ".email", <1>
+ "name": "Email", <2>
+ "minimumLicenseRequired": "gold", <3>
+ "enabled": false, <4>
+ "enabledInConfig": true, <5>
+ "enabledInLicense": false <6>
+ },
+ {
+ "id": ".index",
+ "name": "Index",
+ "minimumLicenseRequired": "basic",
+ "enabled": true,
+ "enabledInConfig": true,
+ "enabledInLicense": true
+ }
+]
+--------------------------------------------------
+
+
+<1> `id` - The unique ID of the action type.
+<2> `name` - The name of the action type.
+<3> `minimumLicenseRequired` - The license required to use the action type.
+<4> `enabled` - Specifies if the action type is enabled or disabled in {kib}.
+<5> `enabledInConfig` - Specifies if the action type is enabled or enabled in the {kib} .yml file.
+<6> `enabledInLicense` - Specifies if the action type is enabled or disabled in the license.
diff --git a/docs/api/actions-and-connectors/update.asciidoc b/docs/api/actions-and-connectors/update.asciidoc
new file mode 100644
index 00000000000000..e08ec2f8da1b67
--- /dev/null
+++ b/docs/api/actions-and-connectors/update.asciidoc
@@ -0,0 +1,68 @@
+[[actions-and-connectors-api-update]]
+=== Update action API
+++++
+Update action API
+++++
+
+Updates the attributes for an existing action.
+
+[[actions-and-connectors-api-update-request]]
+==== Request
+
+`PUT :/api/actions/action/`
+
+[[actions-and-connectors-api-update-params]]
+==== Path parameters
+
+`id`::
+ (Required, string) The ID of the action.
+
+[[actions-and-connectors-api-update-request-body]]
+==== Request body
+
+`name`::
+ (Required, string) The new name of the action.
+
+`config`::
+ (Required, object) The new action configuration. Configuration properties vary depending on the action type. For information about the configuration properties, refer to <>.
+
+`secrets`::
+ (Required, object) The updated secrets configuration for the action. Secrets properties vary depending on the action type. For information about the secrets configuration properties, refer to <>.
+
+[[actions-and-connectors-api-update-codes]]
+==== Response code
+
+`200`::
+ Indicates a successful call.
+
+[[actions-and-connectors-api-update-example]]
+==== Example
+
+[source,sh]
+--------------------------------------------------
+$ curl -X PUT api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
+{
+ "name": "updated-action",
+ "config": {
+ "index": "updated-index"
+ }
+}'
+--------------------------------------------------
+// KIBANA
+
+The API returns the following:
+
+[source,sh]
+--------------------------------------------------
+{
+ "id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
+ "actionTypeId": ".index",
+ "name": "updated-action",
+ "config": {
+ "index": "updated-index",
+ "refresh": false,
+ "executionTimeField": null
+ },
+ "isPreconfigured": false
+}
+--------------------------------------------------
diff --git a/docs/user/alerting/action-types/email.asciidoc b/docs/user/alerting/action-types/email.asciidoc
index d7a9373a6e2a99..3562be1e405f6f 100644
--- a/docs/user/alerting/action-types/email.asciidoc
+++ b/docs/user/alerting/action-types/email.asciidoc
@@ -37,6 +37,7 @@ Password:: password for 'login' type authentication.
password: passwordkeystorevalue
--
+[[email-connector-config-properties]]
`config` defines the action type specific to the configuration and contains the following properties:
[cols="2*<"]
diff --git a/docs/user/alerting/action-types/index.asciidoc b/docs/user/alerting/action-types/index.asciidoc
index 2c6da7c7c3026b..2f459edea28f14 100644
--- a/docs/user/alerting/action-types/index.asciidoc
+++ b/docs/user/alerting/action-types/index.asciidoc
@@ -30,6 +30,7 @@ Execution time field:: This field will be automatically set to the time the ale
executionTimeField: somedate
--
+[[index-connector-config-properties]]
`config` defines the action type specific to the configuration and contains the following properties:
[cols="2*<"]
diff --git a/docs/user/alerting/action-types/jira.asciidoc b/docs/user/alerting/action-types/jira.asciidoc
index 65e5ee4fc4a013..6e47d5618d5982 100644
--- a/docs/user/alerting/action-types/jira.asciidoc
+++ b/docs/user/alerting/action-types/jira.asciidoc
@@ -33,6 +33,7 @@ API token (or password):: Jira API authentication token (or password) for HTTP
apiToken: tokenkeystorevalue
--
+[[jira-connector-config-properties]]
`config` defines the action type specific to the configuration and contains the following properties:
[cols="2*<"]
diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc
index aad192dbddb30f..e1078a55ddd0d5 100644
--- a/docs/user/alerting/action-types/pagerduty.asciidoc
+++ b/docs/user/alerting/action-types/pagerduty.asciidoc
@@ -150,6 +150,7 @@ Integration Key:: A 32 character PagerDuty Integration Key for an integration
routingKey: testroutingkey
--
+[[pagerduty-connector-config-properties]]
`config` defines the action type specific to the configuration.
`config` contains
`apiURL`, a string that corresponds to *API URL*.
diff --git a/docs/user/alerting/action-types/resilient.asciidoc b/docs/user/alerting/action-types/resilient.asciidoc
index b5ddb76d49b0cd..112246ab91162e 100644
--- a/docs/user/alerting/action-types/resilient.asciidoc
+++ b/docs/user/alerting/action-types/resilient.asciidoc
@@ -33,6 +33,7 @@ API key secret:: The authentication key secret for HTTP Basic authentication.
apiKeySecret: tokenkeystorevalue
--
+[[resilient-connector-config-properties]]
`config` defines the action type specific to the configuration and contains the following properties:
[cols="2*<"]
diff --git a/docs/user/alerting/action-types/servicenow.asciidoc b/docs/user/alerting/action-types/servicenow.asciidoc
index 0acb92bcdb5ee5..5d8782c14e5815 100644
--- a/docs/user/alerting/action-types/servicenow.asciidoc
+++ b/docs/user/alerting/action-types/servicenow.asciidoc
@@ -31,6 +31,7 @@ Password:: Password for HTTP Basic authentication.
password: passwordkeystorevalue
--
+[[servicenow-connector-config-properties]]
`config` defines the action type specific to the configuration and contains the following properties:
[cols="2*<"]
diff --git a/docs/user/alerting/action-types/slack.asciidoc b/docs/user/alerting/action-types/slack.asciidoc
index a1fe7a2521b22a..6a38e5c827ab28 100644
--- a/docs/user/alerting/action-types/slack.asciidoc
+++ b/docs/user/alerting/action-types/slack.asciidoc
@@ -26,6 +26,7 @@ Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messa
webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz'
--
+[[slack-connector-config-properties]]
`config` defines the action type specific to the configuration.
`config` contains
`webhookUrl`, a string that corresponds to *Webhook URL*.
diff --git a/docs/user/alerting/action-types/teams.asciidoc b/docs/user/alerting/action-types/teams.asciidoc
index 6706dd2e5643fd..e1ce91fc0c1231 100644
--- a/docs/user/alerting/action-types/teams.asciidoc
+++ b/docs/user/alerting/action-types/teams.asciidoc
@@ -26,6 +26,7 @@ Webhook URL:: The URL of the incoming webhook. See https://docs.microsoft.com/
webhookUrl: 'https://outlook.office.com/webhook/abcd@0123456/IncomingWebhook/abcdefgh/ijklmnopqrstuvwxyz'
--
+[[teams-connector-config-properties]]
`config` defines the action type specific to the configuration.
`config` contains
`webhookUrl`, a string that corresponds to *Webhook URL*.
diff --git a/docs/user/alerting/action-types/webhook.asciidoc b/docs/user/alerting/action-types/webhook.asciidoc
index fff6814325ea45..2d626d53d1c77e 100644
--- a/docs/user/alerting/action-types/webhook.asciidoc
+++ b/docs/user/alerting/action-types/webhook.asciidoc
@@ -36,6 +36,7 @@ Password:: An optional password. If set, HTTP basic authentication is used. Cur
password: passwordkeystorevalue
--
+[[webhook-connector-config-properties]]
`config` defines the action type specific to the configuration and contains the following properties:
[cols="2*<"]
diff --git a/docs/user/api.asciidoc b/docs/user/api.asciidoc
index 20f1fc89367f29..2ae83bee1e06c7 100644
--- a/docs/user/api.asciidoc
+++ b/docs/user/api.asciidoc
@@ -36,6 +36,7 @@ include::{kib-repo-dir}/api/features.asciidoc[]
include::{kib-repo-dir}/api/spaces-management.asciidoc[]
include::{kib-repo-dir}/api/role-management.asciidoc[]
include::{kib-repo-dir}/api/saved-objects.asciidoc[]
+include::{kib-repo-dir}/api/actions-and-connectors.asciidoc[]
include::{kib-repo-dir}/api/dashboard-api.asciidoc[]
include::{kib-repo-dir}/api/logstash-configuration-management.asciidoc[]
include::{kib-repo-dir}/api/url-shortening.asciidoc[]
From 119199ef41c11df2d0fc4cd4dc55d2db6c500f3a Mon Sep 17 00:00:00 2001
From: Dmitry
Date: Wed, 17 Feb 2021 19:45:06 +0100
Subject: [PATCH 29/79] [coverage] change worker, save json files in original
path (#91683)
* [coverage] write data to original path
* [coverage] change worker size
---
.ci/Jenkinsfile_coverage | 2 +-
test/functional/services/remote/remote.ts | 15 +++++++++++----
test/scripts/jenkins_ci_group.sh | 8 --------
test/scripts/jenkins_unit.sh | 2 +-
test/scripts/jenkins_xpack_ci_group.sh | 8 --------
5 files changed, 13 insertions(+), 22 deletions(-)
diff --git a/.ci/Jenkinsfile_coverage b/.ci/Jenkinsfile_coverage
index b804e3071448e7..0aa962a58f53cc 100644
--- a/.ci/Jenkinsfile_coverage
+++ b/.ci/Jenkinsfile_coverage
@@ -10,7 +10,7 @@ kibanaPipeline(timeoutMinutes: 300) {
"TIME_STAMP=${timestamp}",
'CODE_COVERAGE=1', // Enables coverage. Needed for multiple ci scripts, such as remote.ts, test/scripts/*.sh, schema.js, etc.
]) {
- workers.base(name: 'coverage-worker', size: 'l', ramDisk: false, bootstrapped: false) {
+ workers.base(name: 'coverage-worker', size: 'xl', ramDisk: false, bootstrapped: false) {
catchError {
kibanaPipeline.bash("""
diff --git a/test/functional/services/remote/remote.ts b/test/functional/services/remote/remote.ts
index f731ffade6efc7..5bf99b4bf1136b 100644
--- a/test/functional/services/remote/remote.ts
+++ b/test/functional/services/remote/remote.ts
@@ -37,14 +37,21 @@ export async function RemoteProvider({ getService }: FtrProviderContext) {
};
const writeCoverage = (coverageJson: string) => {
- if (!Fs.existsSync(coverageDir)) {
- Fs.mkdirSync(coverageDir, { recursive: true });
+ // on CI we make hard link clone and run tests from kibana${process.env.CI_GROUP} root path
+ const re = new RegExp(`kibana${process.env.CI_GROUP}`, 'g');
+ const dir = process.env.CI ? coverageDir.replace(re, 'kibana') : coverageDir;
+
+ if (!Fs.existsSync(dir)) {
+ Fs.mkdirSync(dir, { recursive: true });
}
+
const id = coverageCounter++;
const timestamp = Date.now();
- const path = resolve(coverageDir, `${id}.${timestamp}.coverage.json`);
+ const path = resolve(dir, `${id}.${timestamp}.coverage.json`);
log.info('writing coverage to', path);
- Fs.writeFileSync(path, JSON.stringify(JSON.parse(coverageJson), null, 2));
+
+ const jsonString = process.env.CI ? coverageJson.replace(re, 'kibana') : coverageJson;
+ Fs.writeFileSync(path, JSON.stringify(JSON.parse(jsonString), null, 2));
};
const browserConfig: BrowserConfig = {
diff --git a/test/scripts/jenkins_ci_group.sh b/test/scripts/jenkins_ci_group.sh
index 4faf645975c778..58dcc9f52c0893 100755
--- a/test/scripts/jenkins_ci_group.sh
+++ b/test/scripts/jenkins_ci_group.sh
@@ -29,14 +29,6 @@ else
echo " -> running tests from the clone folder"
node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --exclude-tag "skipCoverage" || true;
- if [[ -d target/kibana-coverage/functional ]]; then
- echo " -> replacing kibana${CI_GROUP} with kibana in json files"
- sed -i "s|kibana${CI_GROUP}|kibana|g" target/kibana-coverage/functional/*.json
- echo " -> copying coverage to the original folder"
- mkdir -p ../kibana/target/kibana-coverage/functional
- mv target/kibana-coverage/functional/* ../kibana/target/kibana-coverage/functional/
- fi
-
echo " -> moving junit output, silently fail in case of no report"
mkdir -p ../kibana/target/junit
mv target/junit/* ../kibana/target/junit/ || echo "copying junit failed"
diff --git a/test/scripts/jenkins_unit.sh b/test/scripts/jenkins_unit.sh
index a483f8378b8b41..3300ed2d0884f9 100755
--- a/test/scripts/jenkins_unit.sh
+++ b/test/scripts/jenkins_unit.sh
@@ -28,7 +28,7 @@ if [[ -z "$CODE_COVERAGE" ]] ; then
./test/scripts/checks/test_hardening.sh
else
echo " -> Running jest tests with coverage"
- node scripts/jest --ci --verbose --maxWorkers=6 --coverage || true;
+ node scripts/jest --ci --verbose --maxWorkers=8 --coverage || true;
echo " -> Running jest integration tests with coverage"
node scripts/jest_integration --ci --verbose --coverage || true;
diff --git a/test/scripts/jenkins_xpack_ci_group.sh b/test/scripts/jenkins_xpack_ci_group.sh
index 648605135b3595..0198a5d0ac5fac 100755
--- a/test/scripts/jenkins_xpack_ci_group.sh
+++ b/test/scripts/jenkins_xpack_ci_group.sh
@@ -25,14 +25,6 @@ else
echo " -> running tests from the clone folder"
node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --exclude-tag "skipCoverage" || true;
- if [[ -d ../target/kibana-coverage/functional ]]; then
- echo " -> replacing kibana${CI_GROUP} with kibana in json files"
- sed -i "s|kibana${CI_GROUP}|kibana|g" ../target/kibana-coverage/functional/*.json
- echo " -> copying coverage to the original folder"
- mkdir -p ../../kibana/target/kibana-coverage/functional
- mv ../target/kibana-coverage/functional/* ../../kibana/target/kibana-coverage/functional/
- fi
-
echo " -> moving junit output, silently fail in case of no report"
mkdir -p ../../kibana/target/junit
mv ../target/junit/* ../../kibana/target/junit/ || echo "copying junit failed"
From 03c423f11d9d02c84e8499ed07323013d537c494 Mon Sep 17 00:00:00 2001
From: Dario Gieselaar
Date: Wed, 17 Feb 2021 20:11:54 +0100
Subject: [PATCH 30/79] [APM] use top_metrics aggregation where appropriate
(#91479)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Søren Louv-Jansen
---
.../lib/environments/get_all_environments.ts | 4 ++--
.../__snapshots__/queries.test.ts.snap | 18 +++++++-------
.../get_destination_map.ts | 21 ++++++++--------
.../get_service_transaction_stats.ts | 16 +++++++------
.../__snapshots__/queries.test.ts.snap | 24 +++++++++++--------
.../get_transaction_group_stats.ts | 18 +++++++-------
.../distribution/get_buckets/index.ts | 13 ++++++----
x-pack/plugins/apm/server/routes/services.ts | 1 +
.../traces/__snapshots__/top_traces.snap | 2 +-
.../tests/transactions/distribution.ts | 8 +++----
10 files changed, 69 insertions(+), 56 deletions(-)
diff --git a/x-pack/plugins/apm/server/lib/environments/get_all_environments.ts b/x-pack/plugins/apm/server/lib/environments/get_all_environments.ts
index 8fedcf6224e3c9..1bf01c24776fb0 100644
--- a/x-pack/plugins/apm/server/lib/environments/get_all_environments.ts
+++ b/x-pack/plugins/apm/server/lib/environments/get_all_environments.ts
@@ -31,8 +31,8 @@ export async function getAllEnvironments({
includeMissing?: boolean;
}) {
const spanName = serviceName
- ? 'get_all_environments_for_all_services'
- : 'get_all_environments_for_service';
+ ? 'get_all_environments_for_service'
+ : 'get_all_environments_for_all_services';
return withApmSpan(spanName, async () => {
const { apmEventClient, config } = setup;
const maxServiceEnvironments = config['xpack.apm.maxServiceEnvironments'];
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 606ce870351564..3e68831ee7cba7 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
@@ -101,14 +101,6 @@ Array [
"aggs": Object {
"transactionType": Object {
"aggs": Object {
- "agentName": Object {
- "top_hits": Object {
- "docvalue_fields": Array [
- "agent.name",
- ],
- "size": 1,
- },
- },
"avg_duration": Object {
"avg": Object {
"field": "transaction.duration.us",
@@ -129,6 +121,16 @@ Array [
],
},
},
+ "sample": Object {
+ "top_metrics": Object {
+ "metrics": Object {
+ "field": "agent.name",
+ },
+ "sort": Object {
+ "@timestamp": "desc",
+ },
+ },
+ },
"timeseries": Object {
"aggs": Object {
"avg_duration": Object {
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts
index ef2b50cbdbedfe..aa53e8da6cad05 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts
@@ -71,14 +71,13 @@ export const getDestinationMap = ({
],
},
aggs: {
- docs: {
- top_hits: {
- docvalue_fields: [
- SPAN_TYPE,
- SPAN_SUBTYPE,
- SPAN_ID,
+ sample: {
+ top_metrics: {
+ metrics: [
+ { field: SPAN_TYPE },
+ { field: SPAN_SUBTYPE },
+ { field: SPAN_ID },
] as const,
- _source: false,
sort: {
'@timestamp': 'desc',
},
@@ -93,15 +92,15 @@ export const getDestinationMap = ({
const outgoingConnections =
response.aggregations?.connections.buckets.map((bucket) => {
- const doc = bucket.docs.hits.hits[0];
+ const fieldValues = bucket.sample.top[0].metrics;
return {
[SPAN_DESTINATION_SERVICE_RESOURCE]: String(
bucket.key[SPAN_DESTINATION_SERVICE_RESOURCE]
),
- [SPAN_ID]: String(doc.fields[SPAN_ID]?.[0]),
- [SPAN_TYPE]: String(doc.fields[SPAN_TYPE]?.[0] ?? ''),
- [SPAN_SUBTYPE]: String(doc.fields[SPAN_SUBTYPE]?.[0] ?? ''),
+ [SPAN_ID]: (fieldValues[SPAN_ID] ?? '') as string,
+ [SPAN_TYPE]: (fieldValues[SPAN_TYPE] ?? '') as string,
+ [SPAN_SUBTYPE]: (fieldValues[SPAN_SUBTYPE] ?? '') as string,
};
}) ?? [];
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts
index 36540b01a07cc9..acfdc1d8c1710a 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts
@@ -98,10 +98,12 @@ export async function getServiceTransactionStats({
missing: '',
},
},
- agentName: {
- top_hits: {
- docvalue_fields: [AGENT_NAME] as const,
- size: 1,
+ sample: {
+ top_metrics: {
+ metrics: { field: AGENT_NAME } as const,
+ sort: {
+ '@timestamp': 'desc',
+ },
},
},
timeseries: {
@@ -139,9 +141,9 @@ export async function getServiceTransactionStats({
environments: topTransactionTypeBucket.environments.buckets
.map((environmentBucket) => environmentBucket.key as string)
.filter(Boolean),
- agentName: topTransactionTypeBucket.agentName.hits.hits[0].fields[
- 'agent.name'
- ]?.[0] as AgentName,
+ agentName: topTransactionTypeBucket.sample.top[0].metrics[
+ AGENT_NAME
+ ] as AgentName,
avgResponseTime: {
value: topTransactionTypeBucket.avg_duration.value,
timeseries: topTransactionTypeBucket.timeseries.buckets.map(
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
index 89069d74bacf8b..443159611883fc 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
@@ -13,11 +13,13 @@ Array [
"transaction_groups": Object {
"aggs": Object {
"transaction_type": Object {
- "top_hits": Object {
- "_source": Array [
- "transaction.type",
- ],
- "size": 1,
+ "top_metrics": Object {
+ "metrics": Object {
+ "field": "transaction.type",
+ },
+ "sort": Object {
+ "@timestamp": "desc",
+ },
},
},
},
@@ -222,11 +224,13 @@ Array [
"transaction_groups": Object {
"aggs": Object {
"transaction_type": Object {
- "top_hits": Object {
- "_source": Array [
- "transaction.type",
- ],
- "size": 1,
+ "top_metrics": Object {
+ "metrics": Object {
+ "field": "transaction.type",
+ },
+ "sort": Object {
+ "@timestamp": "desc",
+ },
},
},
},
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
index ee19a6a8d15918..5ee46bf1a5918a 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_group_stats.ts
@@ -69,9 +69,13 @@ export function getCounts({ request, setup }: MetricParams) {
return withApmSpan('get_transaction_group_transaction_count', async () => {
const params = mergeRequestWithAggs(request, {
transaction_type: {
- top_hits: {
- size: 1,
- _source: [TRANSACTION_TYPE],
+ top_metrics: {
+ sort: {
+ '@timestamp': 'desc',
+ },
+ metrics: {
+ field: TRANSACTION_TYPE,
+ } as const,
},
},
});
@@ -81,14 +85,12 @@ export function getCounts({ request, setup }: MetricParams) {
return arrayUnionToCallable(
response.aggregations?.transaction_groups.buckets ?? []
).map((bucket) => {
- // type is Transaction | APMBaseDoc because it could be a metric document
- const source = (bucket.transaction_type.hits.hits[0]
- ._source as unknown) as { transaction: { type: string } };
-
return {
key: bucket.key as BucketKey,
count: bucket.doc_count,
- transactionType: source.transaction.type,
+ transactionType: bucket.transaction_type.top[0].metrics[
+ TRANSACTION_TYPE
+ ] as string,
};
});
});
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
index 22a34ded4c20d5..d1d23f538e96b0 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
@@ -109,8 +109,11 @@ export async function getBuckets({
}),
aggs: {
samples: {
- top_hits: {
- _source: [TRANSACTION_ID, TRACE_ID],
+ top_metrics: {
+ metrics: [
+ { field: TRANSACTION_ID },
+ { field: TRACE_ID },
+ ] as const,
size: 10,
sort: {
_score: 'desc',
@@ -128,9 +131,9 @@ export async function getBuckets({
response.aggregations?.distribution.buckets.map((bucket) => {
return {
key: bucket.key,
- samples: bucket.samples.hits.hits.map((hit) => ({
- traceId: hit._source.trace.id,
- transactionId: hit._source.transaction.id,
+ samples: bucket.samples.top.map((sample) => ({
+ traceId: sample.metrics[TRACE_ID] as string,
+ transactionId: sample.metrics[TRANSACTION_ID] as string,
})),
};
}) ?? []
diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts
index ff064e0571d138..84ccb4b06c2e6a 100644
--- a/x-pack/plugins/apm/server/routes/services.ts
+++ b/x-pack/plugins/apm/server/routes/services.ts
@@ -304,6 +304,7 @@ export const serviceErrorGroupsRoute = createRoute({
transactionType,
},
} = context.params;
+
return getServiceErrorGroups({
serviceName,
setup,
diff --git a/x-pack/test/apm_api_integration/tests/traces/__snapshots__/top_traces.snap b/x-pack/test/apm_api_integration/tests/traces/__snapshots__/top_traces.snap
index c8104b9858027c..ad9eca1cc0900c 100644
--- a/x-pack/test/apm_api_integration/tests/traces/__snapshots__/top_traces.snap
+++ b/x-pack/test/apm_api_integration/tests/traces/__snapshots__/top_traces.snap
@@ -995,7 +995,7 @@ Array [
},
"serviceName": "kibana-frontend",
"transactionName": "/app/dev_tools",
- "transactionType": "page-load",
+ "transactionType": "route-change",
"transactionsPerMinute": 0.0666666666666667,
},
Object {
diff --git a/x-pack/test/apm_api_integration/tests/transactions/distribution.ts b/x-pack/test/apm_api_integration/tests/transactions/distribution.ts
index e46ec7a181fc00..56d5e217068a4b 100644
--- a/x-pack/test/apm_api_integration/tests/transactions/distribution.ts
+++ b/x-pack/test/apm_api_integration/tests/transactions/distribution.ts
@@ -83,16 +83,16 @@ export default function ApiTest({ getService }: FtrProviderContext) {
.toMatchInline(`
Array [
Object {
- "traceId": "af0f18dc0841cfc1f567e7e1d55cfda7",
- "transactionId": "925f02e5ac122897",
+ "traceId": "a4eb3781a21dc11d289293076fd1a1b3",
+ "transactionId": "21892bde4ff1364d",
},
Object {
"traceId": "ccd327537120e857bdfa407434dfb9a4",
"transactionId": "c5f923159cc1b8a6",
},
Object {
- "traceId": "a4eb3781a21dc11d289293076fd1a1b3",
- "transactionId": "21892bde4ff1364d",
+ "traceId": "af0f18dc0841cfc1f567e7e1d55cfda7",
+ "transactionId": "925f02e5ac122897",
},
]
`);
From a3dfecb03f2f2a17c29c0f89064b6dad201afda7 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 17 Feb 2021 13:23:51 -0600
Subject: [PATCH 31/79] Update dependency @elastic/charts to v25 (#91557)
---
package.json | 2 +-
.../public/static/utils/transform_click_event.ts | 7 ++-----
.../public/components/detailed_tooltip.tsx | 7 ++-----
.../public/utils/get_legend_actions.tsx | 2 +-
.../public/utils/use_color_picker.tsx | 2 +-
src/plugins/vis_type_xy/public/vis_component.tsx | 16 +++-------------
.../components/common/charts/duration_chart.tsx | 4 ++--
yarn.lock | 8 ++++----
8 files changed, 16 insertions(+), 32 deletions(-)
diff --git a/package.json b/package.json
index cc8ab44c51c96a..a7959f047ab5c0 100644
--- a/package.json
+++ b/package.json
@@ -351,7 +351,7 @@
"@cypress/webpack-preprocessor": "^5.5.0",
"@elastic/apm-rum": "^5.6.1",
"@elastic/apm-rum-react": "^1.2.5",
- "@elastic/charts": "24.6.0",
+ "@elastic/charts": "25.0.1",
"@elastic/eslint-config-kibana": "link:packages/elastic-eslint-config-kibana",
"@elastic/eslint-plugin-eui": "0.0.2",
"@elastic/github-checks-reporter": "0.0.20b3",
diff --git a/src/plugins/charts/public/static/utils/transform_click_event.ts b/src/plugins/charts/public/static/utils/transform_click_event.ts
index e875967616bbdb..0c303b92bf1a1c 100644
--- a/src/plugins/charts/public/static/utils/transform_click_event.ts
+++ b/src/plugins/charts/public/static/utils/transform_click_event.ts
@@ -30,9 +30,6 @@ export interface BrushTriggerEvent {
type AllSeriesAccessors = Array<[accessor: Accessor | AccessorFn, value: string | number]>;
-// TODO: replace when exported from elastic/charts
-const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__';
-
/**
* returns accessor value from string or function accessor
* @param datum
@@ -97,11 +94,11 @@ function getSplitChartValue({
| string
| number
| undefined {
- if (smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) {
+ if (smHorizontalAccessorValue !== undefined) {
return smHorizontalAccessorValue;
}
- if (smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) {
+ if (smVerticalAccessorValue !== undefined) {
return smVerticalAccessorValue;
}
diff --git a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
index 0c1ab262755a73..c9ed82fcf58e55 100644
--- a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
+++ b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
@@ -27,9 +27,6 @@ interface TooltipData {
value: string;
}
-// TODO: replace when exported from elastic/charts
-const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__';
-
export const getTooltipData = (
aspects: Aspects,
header: TooltipValue | null,
@@ -81,7 +78,7 @@ export const getTooltipData = (
if (
aspects.splitColumn &&
valueSeries.smHorizontalAccessorValue !== undefined &&
- valueSeries.smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE
+ valueSeries.smHorizontalAccessorValue !== undefined
) {
data.push({
label: aspects.splitColumn.title,
@@ -92,7 +89,7 @@ export const getTooltipData = (
if (
aspects.splitRow &&
valueSeries.smVerticalAccessorValue !== undefined &&
- valueSeries.smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE
+ valueSeries.smVerticalAccessorValue !== undefined
) {
data.push({
label: aspects.splitRow.title,
diff --git a/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx b/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx
index 9f557806cf1427..5c28ca77da0c4a 100644
--- a/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx
+++ b/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx
@@ -20,7 +20,7 @@ export const getLegendActions = (
onFilter: (data: ClickTriggerEvent, negate?: any) => void,
getSeriesName: (series: XYChartSeriesIdentifier) => SeriesName
): LegendAction => {
- return ({ series: xySeries }) => {
+ return ({ series: [xySeries] }) => {
const [popoverOpen, setPopoverOpen] = useState(false);
const [isfilterable, setIsfilterable] = useState(false);
const series = xySeries as XYChartSeriesIdentifier;
diff --git a/src/plugins/vis_type_xy/public/utils/use_color_picker.tsx b/src/plugins/vis_type_xy/public/utils/use_color_picker.tsx
index 80e7e12adf799d..5028bc379c375d 100644
--- a/src/plugins/vis_type_xy/public/utils/use_color_picker.tsx
+++ b/src/plugins/vis_type_xy/public/utils/use_color_picker.tsx
@@ -36,7 +36,7 @@ export const useColorPicker = (
getSeriesName: (series: XYChartSeriesIdentifier) => SeriesName
): LegendColorPicker =>
useMemo(
- () => ({ anchor, color, onClose, onChange, seriesIdentifier }) => {
+ () => ({ anchor, color, onClose, onChange, seriesIdentifiers: [seriesIdentifier] }) => {
const seriesName = getSeriesName(seriesIdentifier as XYChartSeriesIdentifier);
const handlChange = (newColor: string | null, event: BaseSyntheticEvent) => {
if (!seriesName) {
diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx
index c7ba5021196ff7..ab398101bac9d6 100644
--- a/src/plugins/vis_type_xy/public/vis_component.tsx
+++ b/src/plugins/vis_type_xy/public/vis_component.tsx
@@ -157,17 +157,12 @@ const VisComponent = (props: VisComponentProps) => {
(
visData: Datatable,
xAccessor: Accessor | AccessorFn,
- splitSeriesAccessors: Array,
- splitChartAccessor?: Accessor | AccessorFn
+ splitSeriesAccessors: Array
) => {
const splitSeriesAccessorFnMap = getSplitSeriesAccessorFnMap(splitSeriesAccessors);
return (series: XYChartSeriesIdentifier): ClickTriggerEvent | null => {
if (xAccessor !== null) {
- return getFilterFromSeriesFn(visData)(
- series,
- splitSeriesAccessorFnMap,
- splitChartAccessor
- );
+ return getFilterFromSeriesFn(visData)(series, splitSeriesAccessorFnMap);
}
return null;
@@ -373,12 +368,7 @@ const VisComponent = (props: VisComponentProps) => {
config.aspects.series && (config.aspects.series?.length ?? 0) > 0
? getLegendActions(
canFilter,
- getFilterEventData(
- visData,
- xAccessor,
- splitSeriesAccessors,
- splitChartColumnAccessor ?? splitChartRowAccessor
- ),
+ getFilterEventData(visData, xAccessor, splitSeriesAccessors),
handleFilterAction,
getSeriesName
)
diff --git a/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx b/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx
index b91b50666cf07b..68b9ab0435aaac 100644
--- a/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx
+++ b/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx
@@ -15,8 +15,8 @@ import {
Position,
timeFormatter,
Settings,
- SeriesIdentifier,
BrushEndListener,
+ LegendItemListener,
} from '@elastic/charts';
import { getChartDateLabel } from '../../../lib/helper';
import { LocationDurationLine } from '../../../../common/types';
@@ -75,7 +75,7 @@ export const DurationChartComponent = ({
});
};
- const legendToggleVisibility = (legendItem: SeriesIdentifier | null) => {
+ const legendToggleVisibility: LegendItemListener = ([legendItem]) => {
if (legendItem) {
setHiddenLegends((prevState) => {
if (prevState.includes(legendItem.specId)) {
diff --git a/yarn.lock b/yarn.lock
index 1a939e3dddf579..6cb2a6864eb752 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2138,10 +2138,10 @@
dependencies:
object-hash "^1.3.0"
-"@elastic/charts@24.6.0":
- version "24.6.0"
- resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-24.6.0.tgz#2123c72e69e1e4557be41ae55c085a5a9f75d3b6"
- integrity sha512-fL0301EcHxJEYRzdlD4JIA3VXY4qwRPSkRrk8hvJNryTlQWEdyXZF3HNczk0IrgST5cfCOGAWG8IVtO59HxUJw==
+"@elastic/charts@25.0.1":
+ version "25.0.1"
+ resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-25.0.1.tgz#7c61fc22887b7b1feba3e52fc1b44f21a9c1d0bc"
+ integrity sha512-UYGO9Yg2+cdJOOs9DjlYeB2Oy/3UMXTtF6/yWZt2iXh7mmW9jI5DqfjgG/9BFSCwQZHUKBZ58d5ZCQfe72maGA==
dependencies:
"@popperjs/core" "^2.4.0"
chroma-js "^2.1.0"
From f650c38e12ae09caa1e5bd76dc39f66639a0b9ba Mon Sep 17 00:00:00 2001
From: Nathan L Smith
Date: Wed, 17 Feb 2021 14:28:50 -0600
Subject: [PATCH 32/79] Remove environment from uiFilters (#89647)
---
.../plugins/apm/common/utils/queries.test.ts | 33 +++++
.../utils/queries.ts} | 30 +++-
.../plugins/apm/common/utils/range_filter.ts | 16 --
.../app/ErrorGroupDetails/index.tsx | 5 +-
.../app/correlations/error_correlations.tsx | 10 +-
.../app/correlations/latency_correlations.tsx | 10 +-
.../app/error_group_overview/index.tsx | 5 +-
.../app/service_inventory/index.tsx | 11 +-
.../index.tsx | 7 +-
.../service_overview_errors_table/index.tsx | 4 +-
...ice_overview_instances_chart_and_table.tsx | 5 +-
.../service_overview_throughput_chart.tsx | 5 +-
.../index.tsx | 5 +-
.../components/app/trace_overview/index.tsx | 5 +-
.../use_transaction_list.ts | 5 +-
.../shared/EnvironmentFilter/index.tsx | 5 +-
.../use_transaction_breakdown.ts | 13 +-
.../transaction_error_rate_chart/index.tsx | 13 +-
.../url_params_context/resolve_url_params.ts | 3 +-
.../url_params_context/url_params_context.tsx | 2 -
.../use_error_group_distribution_fetcher.tsx | 5 +-
.../use_service_metric_charts_fetcher.ts | 13 +-
.../use_transaction_distribution_fetcher.ts | 12 +-
.../use_transaction_latency_chart_fetcher.ts | 10 +-
...se_transaction_throughput_chart_fetcher.ts | 13 +-
.../plugins/apm/public/utils/testHelpers.tsx | 4 +-
.../chart_preview/get_transaction_duration.ts | 7 +-
.../get_transaction_error_count.ts | 7 +-
.../get_transaction_error_rate.ts | 7 +-
.../alerts/register_error_count_alert_type.ts | 4 +-
...egister_transaction_duration_alert_type.ts | 4 +-
...ister_transaction_error_rate_alert_type.ts | 4 +-
.../create_anomaly_detection_jobs.ts | 6 +-
.../index.ts | 9 +-
.../index.ts | 9 +-
.../lib/environments/get_environments.ts | 5 +-
.../errors/distribution/get_buckets.test.ts | 11 +-
.../lib/errors/distribution/get_buckets.ts | 7 +-
.../errors/distribution/get_distribution.ts | 3 +
.../lib/errors/get_error_group_sample.ts | 7 +-
.../apm/server/lib/errors/get_error_groups.ts | 8 +-
.../helpers/aggregated_transactions/index.ts | 4 +-
.../get_environment_ui_filter_es.test.ts | 32 ----
.../convert_ui_filters/get_es_filter.ts | 6 +-
.../__snapshots__/queries.test.ts.snap | 140 +++++++++---------
.../server/lib/metrics/by_agent/default.ts | 17 ++-
.../java/gc/fetch_and_transform_gc_metrics.ts | 3 +
.../by_agent/java/gc/get_gc_rate_chart.ts | 3 +
.../by_agent/java/gc/get_gc_time_chart.ts | 3 +
.../by_agent/java/heap_memory/index.ts | 3 +
.../server/lib/metrics/by_agent/java/index.ts | 21 ++-
.../by_agent/java/non_heap_memory/index.ts | 3 +
.../by_agent/java/thread_count/index.ts | 3 +
.../lib/metrics/by_agent/shared/cpu/index.ts | 3 +
.../metrics/by_agent/shared/memory/index.ts | 4 +
.../metrics/fetch_and_transform_metrics.ts | 3 +
.../get_metrics_chart_data_by_agent.ts | 11 +-
.../get_service_count.ts | 4 +-
.../get_transaction_coordinates.ts | 4 +-
.../apm/server/lib/rum_client/has_rum_data.ts | 6 +-
.../fetch_service_paths_from_trace_ids.ts | 4 +-
.../lib/service_map/get_service_anomalies.ts | 16 +-
.../server/lib/service_map/get_service_map.ts | 13 +-
.../get_service_map_from_trace_ids.ts | 9 +-
.../get_service_map_service_node_info.test.ts | 4 +-
.../get_service_map_service_node_info.ts | 17 +--
.../lib/service_map/get_trace_sample_ids.ts | 9 +-
.../__snapshots__/queries.test.ts.snap | 28 ++--
.../get_derived_service_annotations.ts | 7 +-
.../annotations/get_stored_annotations.ts | 13 +-
.../lib/services/get_service_agent_name.ts | 4 +-
.../get_destination_map.ts | 11 +-
.../get_service_dependencies/get_metrics.ts | 9 +-
.../get_service_dependencies/index.ts | 2 +-
.../get_service_error_groups/index.ts | 10 +-
...et_service_instance_system_metric_stats.ts | 6 +-
.../get_service_instance_transaction_stats.ts | 6 +-
.../services/get_service_instances/index.ts | 1 +
.../services/get_service_metadata_details.ts | 4 +-
.../services/get_service_metadata_icons.ts | 4 +-
...transaction_group_comparison_statistics.ts | 7 +-
.../get_service_transaction_groups.ts | 7 +-
.../services/get_service_transaction_types.ts | 4 +-
.../get_services/get_health_statuses.ts | 17 +--
.../get_service_transaction_stats.ts | 9 +-
.../get_services/get_services_items.ts | 6 +-
.../server/lib/services/get_services/index.ts | 3 +
.../apm/server/lib/services/get_throughput.ts | 11 +-
.../apm/server/lib/traces/get_trace_items.ts | 6 +-
.../__snapshots__/queries.test.ts.snap | 64 ++++----
.../server/lib/transaction_groups/fetcher.ts | 2 +
.../lib/transaction_groups/get_error_rate.ts | 11 +-
.../lib/transactions/breakdown/index.ts | 7 +-
.../distribution/get_buckets/index.ts | 10 +-
.../distribution/get_distribution_max.ts | 14 +-
.../lib/transactions/distribution/index.ts | 4 +
.../transactions/get_anomaly_data/fetcher.ts | 11 +-
.../transactions/get_anomaly_data/index.ts | 20 +--
.../transactions/get_latency_charts/index.ts | 14 +-
.../get_throughput_charts/index.ts | 16 +-
.../lib/transactions/get_transaction/index.ts | 4 +-
.../plugins/apm/server/projections/errors.ts | 7 +-
.../plugins/apm/server/projections/metrics.ts | 7 +-
.../projections/rum_page_load_transactions.ts | 6 +-
.../apm/server/projections/services.ts | 4 +-
.../apm/server/projections/transactions.ts | 11 +-
.../plugins/apm/server/routes/correlations.ts | 9 +-
.../apm/server/routes/create_apm_api.ts | 8 +-
.../apm/server/routes/default_api_types.ts | 2 +
x-pack/plugins/apm/server/routes/errors.ts | 17 ++-
x-pack/plugins/apm/server/routes/metrics.ts | 6 +-
.../plugins/apm/server/routes/service_map.ts | 4 +-
x-pack/plugins/apm/server/routes/services.ts | 25 +++-
x-pack/plugins/apm/server/routes/traces.ts | 8 +-
.../plugins/apm/server/routes/transactions.ts | 56 ++++---
.../plugins/apm/server/utils/test_helpers.tsx | 2 +-
.../tests/csm/has_rum_data.ts | 2 +-
.../tests/feature_controls.ts | 8 +-
.../tests/services/top_services.ts | 4 +-
.../transactions/__snapshots__/latency.snap | 4 +-
.../tests/transactions/latency.ts | 47 +++---
.../tests/transactions/throughput.ts | 4 +-
122 files changed, 757 insertions(+), 528 deletions(-)
create mode 100644 x-pack/plugins/apm/common/utils/queries.test.ts
rename x-pack/plugins/apm/{server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts => common/utils/queries.ts} (52%)
delete mode 100644 x-pack/plugins/apm/common/utils/range_filter.ts
delete mode 100644 x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.test.ts
diff --git a/x-pack/plugins/apm/common/utils/queries.test.ts b/x-pack/plugins/apm/common/utils/queries.test.ts
new file mode 100644
index 00000000000000..546c8627def69c
--- /dev/null
+++ b/x-pack/plugins/apm/common/utils/queries.test.ts
@@ -0,0 +1,33 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { SERVICE_ENVIRONMENT } from '../elasticsearch_fieldnames';
+import { ENVIRONMENT_NOT_DEFINED } from '../environment_filter_values';
+import { environmentQuery } from './queries';
+
+describe('environmentQuery', () => {
+ describe('when environment is undefined', () => {
+ it('returns an empty query', () => {
+ expect(environmentQuery()).toEqual([]);
+ });
+ });
+
+ it('creates a query for a service environment', () => {
+ expect(environmentQuery('test')).toEqual([
+ {
+ term: { [SERVICE_ENVIRONMENT]: 'test' },
+ },
+ ]);
+ });
+
+ it('creates a query for missing service environments', () => {
+ expect(environmentQuery(ENVIRONMENT_NOT_DEFINED.value)[0]).toHaveProperty(
+ ['bool', 'must_not', 'exists', 'field'],
+ SERVICE_ENVIRONMENT
+ );
+ });
+});
diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts b/x-pack/plugins/apm/common/utils/queries.ts
similarity index 52%
rename from x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts
rename to x-pack/plugins/apm/common/utils/queries.ts
index 3dea3344085c92..dbbbf324b964a9 100644
--- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts
+++ b/x-pack/plugins/apm/common/utils/queries.ts
@@ -5,19 +5,41 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../../typings/elasticsearch';
+import { ESFilter } from '../../../../typings/elasticsearch';
import {
ENVIRONMENT_NOT_DEFINED,
ENVIRONMENT_ALL,
-} from '../../../../common/environment_filter_values';
-import { SERVICE_ENVIRONMENT } from '../../../../common/elasticsearch_fieldnames';
+} from '../environment_filter_values';
+import { SERVICE_ENVIRONMENT } from '../elasticsearch_fieldnames';
-export function getEnvironmentUiFilterES(environment?: string): ESFilter[] {
+type QueryContainer = ESFilter;
+
+export function environmentQuery(environment?: string): QueryContainer[] {
if (!environment || environment === ENVIRONMENT_ALL.value) {
return [];
}
+
if (environment === ENVIRONMENT_NOT_DEFINED.value) {
return [{ bool: { must_not: { exists: { field: SERVICE_ENVIRONMENT } } } }];
}
+
return [{ term: { [SERVICE_ENVIRONMENT]: environment } }];
}
+
+export function rangeQuery(
+ start: number,
+ end: number,
+ field = '@timestamp'
+): QueryContainer[] {
+ return [
+ {
+ range: {
+ [field]: {
+ gte: start,
+ lte: end,
+ format: 'epoch_millis',
+ },
+ },
+ },
+ ];
+}
diff --git a/x-pack/plugins/apm/common/utils/range_filter.ts b/x-pack/plugins/apm/common/utils/range_filter.ts
deleted file mode 100644
index 8d5b7d5e1beb1e..00000000000000
--- a/x-pack/plugins/apm/common/utils/range_filter.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * 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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-export function rangeFilter(start: number, end: number) {
- return {
- '@timestamp': {
- gte: start,
- lte: end,
- format: 'epoch_millis',
- },
- };
-}
diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
index 3edf21eae7279f..4cd2db43621a8f 100644
--- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
@@ -68,7 +68,7 @@ type ErrorGroupDetailsProps = RouteComponentProps<{
export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) {
const { serviceName, groupId } = match.params;
const { urlParams, uiFilters } = useUrlParams();
- const { start, end } = urlParams;
+ const { environment, start, end } = urlParams;
const { data: errorGroupData } = useFetcher(
(callApmApi) => {
@@ -81,6 +81,7 @@ export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) {
groupId,
},
query: {
+ environment,
start,
end,
uiFilters: JSON.stringify(uiFilters),
@@ -89,7 +90,7 @@ export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) {
});
}
},
- [serviceName, start, end, groupId, uiFilters]
+ [environment, serviceName, start, end, groupId, uiFilters]
);
const { errorDistributionData } = useErrorGroupDistributionFetcher({
diff --git a/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx
index 7386209310c1fb..9b80ee6fc31b85 100644
--- a/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx
+++ b/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx
@@ -51,7 +51,13 @@ export function ErrorCorrelations({ onClose }: Props) {
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
- const { transactionName, transactionType, start, end } = urlParams;
+ const {
+ environment,
+ transactionName,
+ transactionType,
+ start,
+ end,
+ } = urlParams;
const { defaultFieldNames } = useFieldNames();
const [fieldNames, setFieldNames] = useLocalStorage(
`apm.correlations.errors.fields:${serviceName}`,
@@ -65,6 +71,7 @@ export function ErrorCorrelations({ onClose }: Props) {
endpoint: 'GET /api/apm/correlations/failed_transactions',
params: {
query: {
+ environment,
serviceName,
transactionName,
transactionType,
@@ -78,6 +85,7 @@ export function ErrorCorrelations({ onClose }: Props) {
}
},
[
+ environment,
serviceName,
start,
end,
diff --git a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx
index c88aaa85bb856f..459df99a62f5a2 100644
--- a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx
+++ b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx
@@ -49,7 +49,13 @@ export function LatencyCorrelations({ onClose }: Props) {
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
- const { transactionName, transactionType, start, end } = urlParams;
+ const {
+ environment,
+ transactionName,
+ transactionType,
+ start,
+ end,
+ } = urlParams;
const { defaultFieldNames } = useFieldNames();
const [fieldNames, setFieldNames] = useLocalStorage(
`apm.correlations.latency.fields:${serviceName}`,
@@ -70,6 +76,7 @@ export function LatencyCorrelations({ onClose }: Props) {
endpoint: 'GET /api/apm/correlations/slow_transactions',
params: {
query: {
+ environment,
serviceName,
transactionName,
transactionType,
@@ -84,6 +91,7 @@ export function LatencyCorrelations({ onClose }: Props) {
}
},
[
+ environment,
serviceName,
start,
end,
diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx
index 29bdf6467e5447..bde23eddaa44fa 100644
--- a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx
@@ -28,7 +28,7 @@ interface ErrorGroupOverviewProps {
export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) {
const { urlParams, uiFilters } = useUrlParams();
- const { start, end, sortField, sortDirection } = urlParams;
+ const { environment, start, end, sortField, sortDirection } = urlParams;
const { errorDistributionData } = useErrorGroupDistributionFetcher({
serviceName,
groupId: undefined,
@@ -46,6 +46,7 @@ export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) {
serviceName,
},
query: {
+ environment,
start,
end,
sortField,
@@ -56,7 +57,7 @@ export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) {
});
}
},
- [serviceName, start, end, sortField, sortDirection, uiFilters]
+ [environment, serviceName, start, end, sortField, sortDirection, uiFilters]
);
useTrackPageview({
diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
index 32bc907f624fbd..cd17ca0ce023d2 100644
--- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
@@ -39,19 +39,24 @@ function useServicesFetcher() {
const { urlParams, uiFilters } = useUrlParams();
const { core } = useApmPluginContext();
const upgradeAssistantHref = useUpgradeAssistantHref();
- const { start, end } = urlParams;
+ const { environment, start, end } = urlParams;
const { data = initialData, status } = useFetcher(
(callApmApi) => {
if (start && end) {
return callApmApi({
endpoint: 'GET /api/apm/services',
params: {
- query: { start, end, uiFilters: JSON.stringify(uiFilters) },
+ query: {
+ environment,
+ start,
+ end,
+ uiFilters: JSON.stringify(uiFilters),
+ },
},
});
}
},
- [start, end, uiFilters]
+ [environment, start, end, uiFilters]
);
useEffect(() => {
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx
index baae683e2eba95..2f37e8e4238d8b 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx
@@ -14,10 +14,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
-import {
- ENVIRONMENT_ALL,
- getNextEnvironmentUrlParam,
-} from '../../../../../common/environment_filter_values';
+import { getNextEnvironmentUrlParam } from '../../../../../common/environment_filter_values';
import {
asMillisecondDuration,
asPercent,
@@ -182,7 +179,7 @@ export function ServiceOverviewDependenciesTable({ serviceName }: Props) {
query: {
start,
end,
- environment: environment || ENVIRONMENT_ALL.value,
+ environment,
numBuckets: 20,
},
},
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx
index 7f4823c13d593a..f7f5db32e986cc 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx
@@ -52,7 +52,7 @@ const DEFAULT_SORT = {
export function ServiceOverviewErrorsTable({ serviceName }: Props) {
const {
- urlParams: { start, end },
+ urlParams: { environment, start, end },
uiFilters,
} = useUrlParams();
const { transactionType } = useApmServiceContext();
@@ -152,6 +152,7 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) {
params: {
path: { serviceName },
query: {
+ environment,
start,
end,
uiFilters: JSON.stringify(uiFilters),
@@ -178,6 +179,7 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) {
});
},
[
+ environment,
start,
end,
serviceName,
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx
index e7c1d4442e3b7c..819d65a5d9415e 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx
@@ -25,7 +25,7 @@ export function ServiceOverviewInstancesChartAndTable({
const { transactionType } = useApmServiceContext();
const {
- urlParams: { start, end },
+ urlParams: { environment, start, end },
uiFilters,
} = useUrlParams();
@@ -43,6 +43,7 @@ export function ServiceOverviewInstancesChartAndTable({
serviceName,
},
query: {
+ environment,
start,
end,
transactionType,
@@ -52,7 +53,7 @@ export function ServiceOverviewInstancesChartAndTable({
},
});
},
- [start, end, serviceName, transactionType, uiFilters]
+ [environment, start, end, serviceName, transactionType, uiFilters]
);
return (
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx
index 1d0074da6005ff..2d38ce2c23ca7c 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx
@@ -30,7 +30,7 @@ export function ServiceOverviewThroughputChart({
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
const { transactionType } = useApmServiceContext();
- const { start, end } = urlParams;
+ const { environment, start, end } = urlParams;
const { data = INITIAL_STATE, status } = useFetcher(
(callApmApi) => {
@@ -42,6 +42,7 @@ export function ServiceOverviewThroughputChart({
serviceName,
},
query: {
+ environment,
start,
end,
transactionType,
@@ -51,7 +52,7 @@ export function ServiceOverviewThroughputChart({
});
}
},
- [serviceName, start, end, uiFilters, transactionType]
+ [environment, serviceName, start, end, uiFilters, transactionType]
);
return (
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx
index a0facb2ddbedf6..5529f9028b9dda 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx
@@ -58,7 +58,7 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
const { transactionType } = useApmServiceContext();
const {
uiFilters,
- urlParams: { start, end, latencyAggregationType },
+ urlParams: { environment, start, end, latencyAggregationType },
} = useUrlParams();
const { data = INITIAL_STATE, status } = useFetcher(
@@ -72,6 +72,7 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
params: {
path: { serviceName },
query: {
+ environment,
start,
end,
uiFilters: JSON.stringify(uiFilters),
@@ -87,6 +88,7 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
});
},
[
+ environment,
serviceName,
start,
end,
@@ -125,6 +127,7 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) {
params: {
path: { serviceName },
query: {
+ environment,
start,
end,
uiFilters: JSON.stringify(uiFilters),
diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx
index cae0ef2de2ad1b..8fc9ac12824baa 100644
--- a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx
@@ -23,7 +23,7 @@ const DEFAULT_RESPONSE: TracesAPIResponse = {
export function TraceOverview() {
const { urlParams, uiFilters } = useUrlParams();
- const { start, end } = urlParams;
+ const { environment, start, end } = urlParams;
const { status, data = DEFAULT_RESPONSE } = useFetcher(
(callApmApi) => {
if (start && end) {
@@ -31,6 +31,7 @@ export function TraceOverview() {
endpoint: 'GET /api/apm/traces',
params: {
query: {
+ environment,
start,
end,
uiFilters: JSON.stringify(uiFilters),
@@ -39,7 +40,7 @@ export function TraceOverview() {
});
}
},
- [start, end, uiFilters]
+ [environment, start, end, uiFilters]
);
useTrackPageview({ app: 'apm', path: 'traces_overview' });
diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts b/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts
index 406ba98b79e256..a63788457b8b5d 100644
--- a/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts
+++ b/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts
@@ -21,7 +21,7 @@ const DEFAULT_RESPONSE: Partial = {
export function useTransactionListFetcher() {
const { urlParams, uiFilters } = useUrlParams();
const { serviceName } = useParams<{ serviceName?: string }>();
- const { transactionType, start, end } = urlParams;
+ const { environment, transactionType, start, end } = urlParams;
const { data = DEFAULT_RESPONSE, error, status } = useFetcher(
(callApmApi) => {
if (serviceName && start && end && transactionType) {
@@ -30,6 +30,7 @@ export function useTransactionListFetcher() {
params: {
path: { serviceName },
query: {
+ environment,
start,
end,
transactionType,
@@ -39,7 +40,7 @@ export function useTransactionListFetcher() {
});
}
},
- [serviceName, start, end, transactionType, uiFilters]
+ [environment, serviceName, start, end, transactionType, uiFilters]
);
return {
diff --git a/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx
index 714645fd749612..59c99463144cbe 100644
--- a/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx
+++ b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx
@@ -64,10 +64,9 @@ export function EnvironmentFilter() {
const history = useHistory();
const location = useLocation();
const { serviceName } = useParams<{ serviceName?: string }>();
- const { uiFilters, urlParams } = useUrlParams();
+ const { urlParams } = useUrlParams();
- const { environment } = uiFilters;
- const { start, end } = urlParams;
+ const { environment, start, end } = urlParams;
const { environments, status = 'loading' } = useEnvironmentsFetcher({
serviceName,
start,
diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts
index 466f201ab3398e..293a1929ca6069 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts
+++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts
@@ -13,7 +13,7 @@ import { useApmServiceContext } from '../../../../context/apm_service/use_apm_se
export function useTransactionBreakdown() {
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
- const { start, end, transactionName } = urlParams;
+ const { environment, start, end, transactionName } = urlParams;
const { transactionType } = useApmServiceContext();
const { data = { timeseries: undefined }, error, status } = useFetcher(
@@ -25,6 +25,7 @@ export function useTransactionBreakdown() {
params: {
path: { serviceName },
query: {
+ environment,
start,
end,
transactionName,
@@ -35,7 +36,15 @@ export function useTransactionBreakdown() {
});
}
},
- [serviceName, start, end, transactionType, transactionName, uiFilters]
+ [
+ environment,
+ serviceName,
+ start,
+ end,
+ transactionType,
+ transactionName,
+ uiFilters,
+ ]
);
return {
diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx
index df18e7627faedb..a3da8812966f18 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx
+++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx
@@ -33,7 +33,7 @@ export function TransactionErrorRateChart({
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
const { transactionType } = useApmServiceContext();
- const { start, end, transactionName } = urlParams;
+ const { environment, start, end, transactionName } = urlParams;
const { data, status } = useFetcher(
(callApmApi) => {
@@ -46,6 +46,7 @@ export function TransactionErrorRateChart({
serviceName,
},
query: {
+ environment,
start,
end,
transactionType,
@@ -56,7 +57,15 @@ export function TransactionErrorRateChart({
});
}
},
- [serviceName, start, end, uiFilters, transactionType, transactionName]
+ [
+ environment,
+ serviceName,
+ start,
+ end,
+ uiFilters,
+ transactionType,
+ transactionName,
+ ]
);
const errorRates = data?.transactionErrorRate || [];
diff --git a/x-pack/plugins/apm/public/context/url_params_context/resolve_url_params.ts b/x-pack/plugins/apm/public/context/url_params_context/resolve_url_params.ts
index 63d719205c2ade..b6e7330be30cbd 100644
--- a/x-pack/plugins/apm/public/context/url_params_context/resolve_url_params.ts
+++ b/x-pack/plugins/apm/public/context/url_params_context/resolve_url_params.ts
@@ -6,6 +6,7 @@
*/
import { Location } from 'history';
+import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values';
import { LatencyAggregationType } from '../../../common/latency_aggregation_types';
import { pickKeys } from '../../../common/utils/pick_keys';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
@@ -66,6 +67,7 @@ export function resolveUrlParams(location: Location, state: TimeUrlParams) {
refreshInterval: refreshInterval ? toNumber(refreshInterval) : undefined,
// query params
+ environment: toString(environment) || ENVIRONMENT_ALL.value,
sortDirection,
sortField,
page: toNumber(page) || 0,
@@ -87,7 +89,6 @@ export function resolveUrlParams(location: Location, state: TimeUrlParams) {
: undefined,
comparisonType: comparisonType as TimeRangeComparisonType | undefined,
// ui filters
- environment,
...localUIFilters,
});
}
diff --git a/x-pack/plugins/apm/public/context/url_params_context/url_params_context.tsx b/x-pack/plugins/apm/public/context/url_params_context/url_params_context.tsx
index 8312fedc7eb039..90245b9843b013 100644
--- a/x-pack/plugins/apm/public/context/url_params_context/url_params_context.tsx
+++ b/x-pack/plugins/apm/public/context/url_params_context/url_params_context.tsx
@@ -14,7 +14,6 @@ import React, {
useState,
} from 'react';
import { withRouter } from 'react-router-dom';
-import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values';
import { LocalUIFilterName } from '../../../common/ui_filter';
import { pickKeys } from '../../../common/utils/pick_keys';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
@@ -39,7 +38,6 @@ function useUiFilters(params: IUrlParams): UIFilters {
return useDeepObjectIdentity({
kuery,
- environment: environment || ENVIRONMENT_ALL.value,
...localUiFilters,
});
}
diff --git a/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx
index 834b0cc0527897..9ff179e6af6a06 100644
--- a/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx
+++ b/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx
@@ -16,7 +16,7 @@ export function useErrorGroupDistributionFetcher({
groupId: string | undefined;
}) {
const { urlParams, uiFilters } = useUrlParams();
- const { start, end } = urlParams;
+ const { environment, start, end } = urlParams;
const { data } = useFetcher(
(callApmApi) => {
if (start && end) {
@@ -25,6 +25,7 @@ export function useErrorGroupDistributionFetcher({
params: {
path: { serviceName },
query: {
+ environment,
start,
end,
groupId,
@@ -34,7 +35,7 @@ export function useErrorGroupDistributionFetcher({
});
}
},
- [serviceName, start, end, groupId, uiFilters]
+ [environment, serviceName, start, end, groupId, uiFilters]
);
return { errorDistributionData: data };
diff --git a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts
index ecfa5471189d20..87e10f1e8937b0 100644
--- a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts
+++ b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts
@@ -24,7 +24,7 @@ export function useServiceMetricChartsFetcher({
const { urlParams, uiFilters } = useUrlParams();
const { agentName } = useApmServiceContext();
const { serviceName } = useParams<{ serviceName?: string }>();
- const { start, end } = urlParams;
+ const { environment, start, end } = urlParams;
const { data = INITIAL_DATA, error, status } = useFetcher(
(callApmApi) => {
if (serviceName && start && end && agentName) {
@@ -33,6 +33,7 @@ export function useServiceMetricChartsFetcher({
params: {
path: { serviceName },
query: {
+ environment,
serviceNodeName,
start,
end,
@@ -43,7 +44,15 @@ export function useServiceMetricChartsFetcher({
});
}
},
- [serviceName, start, end, agentName, serviceNodeName, uiFilters]
+ [
+ environment,
+ serviceName,
+ start,
+ end,
+ agentName,
+ serviceNodeName,
+ uiFilters,
+ ]
);
return {
diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts
index 66446bf0dfebad..c493a30716aa5e 100644
--- a/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts
+++ b/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts
@@ -25,6 +25,7 @@ export function useTransactionDistributionFetcher() {
const { serviceName } = useParams<{ serviceName?: string }>();
const { urlParams, uiFilters } = useUrlParams();
const {
+ environment,
start,
end,
transactionType,
@@ -45,6 +46,7 @@ export function useTransactionDistributionFetcher() {
serviceName,
},
query: {
+ environment,
start,
end,
transactionType,
@@ -92,7 +94,15 @@ export function useTransactionDistributionFetcher() {
},
// the histogram should not be refetched if the transactionId or traceId changes
// eslint-disable-next-line react-hooks/exhaustive-deps
- [serviceName, start, end, transactionType, transactionName, uiFilters]
+ [
+ environment,
+ serviceName,
+ start,
+ end,
+ transactionType,
+ transactionName,
+ uiFilters,
+ ]
);
return {
diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts
index d5974ee3543a7e..cca2e99d84dfdf 100644
--- a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts
+++ b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts
@@ -18,7 +18,13 @@ export function useTransactionLatencyChartsFetcher() {
const { transactionType } = useApmServiceContext();
const theme = useTheme();
const {
- urlParams: { start, end, transactionName, latencyAggregationType },
+ urlParams: {
+ environment,
+ start,
+ end,
+ transactionName,
+ latencyAggregationType,
+ },
uiFilters,
} = useUrlParams();
@@ -37,6 +43,7 @@ export function useTransactionLatencyChartsFetcher() {
params: {
path: { serviceName },
query: {
+ environment,
start,
end,
transactionType,
@@ -49,6 +56,7 @@ export function useTransactionLatencyChartsFetcher() {
}
},
[
+ environment,
serviceName,
start,
end,
diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts
index af9a5fee24877a..55765cd40c04eb 100644
--- a/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts
+++ b/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts
@@ -18,7 +18,7 @@ export function useTransactionThroughputChartsFetcher() {
const { transactionType } = useApmServiceContext();
const theme = useTheme();
const {
- urlParams: { start, end, transactionName },
+ urlParams: { environment, start, end, transactionName },
uiFilters,
} = useUrlParams();
@@ -31,6 +31,7 @@ export function useTransactionThroughputChartsFetcher() {
params: {
path: { serviceName },
query: {
+ environment,
start,
end,
transactionType,
@@ -41,7 +42,15 @@ export function useTransactionThroughputChartsFetcher() {
});
}
},
- [serviceName, start, end, transactionName, transactionType, uiFilters]
+ [
+ environment,
+ serviceName,
+ start,
+ end,
+ transactionName,
+ transactionType,
+ uiFilters,
+ ]
);
const memoizedData = useMemo(
diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx
index aac6196c4253c5..f7f6f7486091b7 100644
--- a/x-pack/plugins/apm/public/utils/testHelpers.tsx
+++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx
@@ -182,8 +182,8 @@ export async function inspectSearchParams(
},
}
) as APMConfig,
- uiFilters: { environment: 'test' },
- esFilter: [{ term: { 'service.environment': 'test' } }],
+ uiFilters: {},
+ esFilter: [],
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': 'myIndex',
diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts
index e487684909633e..3457207eeee3c4 100644
--- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts
@@ -13,10 +13,9 @@ import {
TRANSACTION_TYPE,
} from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { AlertParams } from '../../../routes/alerts/chart_preview';
import { withApmSpan } from '../../../utils/with_apm_span';
-import { getEnvironmentUiFilterES } from '../../helpers/convert_ui_filters/get_environment_ui_filter_es';
import { getBucketSize } from '../../helpers/get_bucket_size';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
@@ -39,13 +38,13 @@ export function getTransactionDurationChartPreview({
const query = {
bool: {
filter: [
- { range: rangeFilter(start, end) },
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
...(serviceName ? [{ term: { [SERVICE_NAME]: serviceName } }] : []),
...(transactionType
? [{ term: { [TRANSACTION_TYPE]: transactionType } }]
: []),
- ...getEnvironmentUiFilterES(environment),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
],
},
};
diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts
index 05ad743af0997b..aa85c44284d9d4 100644
--- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts
@@ -7,10 +7,9 @@
import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
-import { rangeFilter } from '../../../../common/utils/range_filter';
import { AlertParams } from '../../../routes/alerts/chart_preview';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { withApmSpan } from '../../../utils/with_apm_span';
-import { getEnvironmentUiFilterES } from '../../helpers/convert_ui_filters/get_environment_ui_filter_es';
import { getBucketSize } from '../../helpers/get_bucket_size';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
@@ -28,9 +27,9 @@ export function getTransactionErrorCountChartPreview({
const query = {
bool: {
filter: [
- { range: rangeFilter(start, end) },
...(serviceName ? [{ term: { [SERVICE_NAME]: serviceName } }] : []),
- ...getEnvironmentUiFilterES(environment),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
],
},
};
diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts
index fa01773c780703..88e249a71a81f0 100644
--- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts
@@ -11,9 +11,8 @@ import {
TRANSACTION_TYPE,
} from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
-import { rangeFilter } from '../../../../common/utils/range_filter';
import { AlertParams } from '../../../routes/alerts/chart_preview';
-import { getEnvironmentUiFilterES } from '../../helpers/convert_ui_filters/get_environment_ui_filter_es';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { getBucketSize } from '../../helpers/get_bucket_size';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import {
@@ -34,13 +33,13 @@ export async function getTransactionErrorRateChartPreview({
const query = {
bool: {
filter: [
- { range: rangeFilter(start, end) },
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
...(serviceName ? [{ term: { [SERVICE_NAME]: serviceName } }] : []),
...(transactionType
? [{ term: { [TRANSACTION_TYPE]: transactionType } }]
: []),
- ...getEnvironmentUiFilterES(environment),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
],
},
};
diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts
index e9e2e078ec344e..c7861eaa819aed 100644
--- a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts
@@ -27,7 +27,7 @@ import {
SERVICE_NAME,
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
-import { getEnvironmentUiFilterES } from '../helpers/convert_ui_filters/get_environment_ui_filter_es';
+import { environmentQuery } from '../../../common/utils/queries';
import { getApmIndices } from '../settings/apm_indices/get_apm_indices';
import { apmActionVariables } from './action_variables';
import { alertingEsClient } from './alerting_es_client';
@@ -104,7 +104,7 @@ export function registerErrorCountAlertType({
...(alertParams.serviceName
? [{ term: { [SERVICE_NAME]: alertParams.serviceName } }]
: []),
- ...getEnvironmentUiFilterES(alertParams.environment),
+ ...environmentQuery(alertParams.environment),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts
index 480efd8d4c7ad0..704aee932a604f 100644
--- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts
@@ -20,7 +20,7 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
import { getDurationFormatter } from '../../../common/utils/formatters';
-import { getEnvironmentUiFilterES } from '../helpers/convert_ui_filters/get_environment_ui_filter_es';
+import { environmentQuery } from '../../../common/utils/queries';
import { getApmIndices } from '../settings/apm_indices/get_apm_indices';
import { apmActionVariables } from './action_variables';
import { alertingEsClient } from './alerting_es_client';
@@ -96,7 +96,7 @@ export function registerTransactionDurationAlertType({
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
{ term: { [SERVICE_NAME]: alertParams.serviceName } },
{ term: { [TRANSACTION_TYPE]: alertParams.transactionType } },
- ...getEnvironmentUiFilterES(alertParams.environment),
+ ...environmentQuery(alertParams.environment),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts
index 882bde87927615..6f58b7714d8324 100644
--- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts
@@ -22,7 +22,7 @@ import {
import { EventOutcome } from '../../../common/event_outcome';
import { ProcessorEvent } from '../../../common/processor_event';
import { asDecimalOrInteger } from '../../../common/utils/formatters';
-import { getEnvironmentUiFilterES } from '../helpers/convert_ui_filters/get_environment_ui_filter_es';
+import { environmentQuery } from '../../../common/utils/queries';
import { getApmIndices } from '../settings/apm_indices/get_apm_indices';
import { apmActionVariables } from './action_variables';
import { alertingEsClient } from './alerting_es_client';
@@ -103,7 +103,7 @@ export function registerTransactionErrorRateAlertType({
},
]
: []),
- ...getEnvironmentUiFilterES(alertParams.environment),
+ ...environmentQuery(alertParams.environment),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts
index a560f011b186bb..d70e19bf4a5f52 100644
--- a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts
+++ b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts
@@ -9,15 +9,15 @@ import { Logger } from 'kibana/server';
import uuid from 'uuid/v4';
import { snakeCase } from 'lodash';
import Boom from '@hapi/boom';
-import { ProcessorEvent } from '../../../common/processor_event';
import { ML_ERRORS } from '../../../common/anomaly_detection';
+import { ProcessorEvent } from '../../../common/processor_event';
+import { environmentQuery } from '../../../common/utils/queries';
import { Setup } from '../helpers/setup_request';
import {
TRANSACTION_DURATION,
PROCESSOR_EVENT,
} from '../../../common/elasticsearch_fieldnames';
import { APM_ML_JOB_GROUP, ML_MODULE_ID_APM_TRANSACTION } from './constants';
-import { getEnvironmentUiFilterES } from '../helpers/convert_ui_filters/get_environment_ui_filter_es';
import { withApmSpan } from '../../utils/with_apm_span';
export async function createAnomalyDetectionJobs(
@@ -86,7 +86,7 @@ async function createAnomalyDetectionJob({
filter: [
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
{ exists: { field: TRANSACTION_DURATION } },
- ...getEnvironmentUiFilterES(environment),
+ ...environmentQuery(environment),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts
index 721e35e2ef60d0..ecefdfc2b3d9b2 100644
--- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts
+++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_failed_transactions/index.ts
@@ -13,7 +13,7 @@ import {
} from '../process_significant_term_aggs';
import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch/aggregations';
import { ESFilter } from '../../../../../../typings/elasticsearch';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import {
EVENT_OUTCOME,
SERVICE_NAME,
@@ -31,12 +31,14 @@ import {
import { withApmSpan } from '../../../utils/with_apm_span';
export async function getCorrelationsForFailedTransactions({
+ environment,
serviceName,
transactionType,
transactionName,
fieldNames,
setup,
}: {
+ environment?: string;
serviceName: string | undefined;
transactionType: string | undefined;
transactionName: string | undefined;
@@ -47,9 +49,10 @@ export async function getCorrelationsForFailedTransactions({
const { start, end, esFilter, apmEventClient } = setup;
const backgroundFilters: ESFilter[] = [
- ...esFilter,
- { range: rangeFilter(start, end) },
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
+ ...esFilter,
];
if (serviceName) {
diff --git a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts
index 816061da5cfc17..832b89a18d1028 100644
--- a/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts
+++ b/x-pack/plugins/apm/server/lib/correlations/get_correlations_for_slow_transactions/index.ts
@@ -7,7 +7,7 @@
import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch/aggregations';
import { ESFilter } from '../../../../../../typings/elasticsearch';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import {
SERVICE_NAME,
TRANSACTION_DURATION,
@@ -23,6 +23,7 @@ import { getLatencyDistribution } from './get_latency_distribution';
import { withApmSpan } from '../../../utils/with_apm_span';
export async function getCorrelationsForSlowTransactions({
+ environment,
serviceName,
transactionType,
transactionName,
@@ -30,6 +31,7 @@ export async function getCorrelationsForSlowTransactions({
fieldNames,
setup,
}: {
+ environment?: string;
serviceName: string | undefined;
transactionType: string | undefined;
transactionName: string | undefined;
@@ -41,9 +43,10 @@ export async function getCorrelationsForSlowTransactions({
const { start, end, esFilter, apmEventClient } = setup;
const backgroundFilters: ESFilter[] = [
- ...esFilter,
- { range: rangeFilter(start, end) },
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
+ ...esFilter,
];
if (serviceName) {
diff --git a/x-pack/plugins/apm/server/lib/environments/get_environments.ts b/x-pack/plugins/apm/server/lib/environments/get_environments.ts
index 56f0a03910c1a4..af88493c148ce1 100644
--- a/x-pack/plugins/apm/server/lib/environments/get_environments.ts
+++ b/x-pack/plugins/apm/server/lib/environments/get_environments.ts
@@ -5,14 +5,13 @@
* 2.0.
*/
-import { ESFilter } from '../../../../../typings/elasticsearch';
import {
SERVICE_ENVIRONMENT,
SERVICE_NAME,
} from '../../../common/elasticsearch_fieldnames';
import { ENVIRONMENT_NOT_DEFINED } from '../../../common/environment_filter_values';
import { ProcessorEvent } from '../../../common/processor_event';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { withApmSpan } from '../../utils/with_apm_span';
import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
@@ -37,7 +36,7 @@ export async function getEnvironments({
return withApmSpan(spanName, async () => {
const { start, end, apmEventClient, config } = setup;
- const filter: ESFilter[] = [{ range: rangeFilter(start, end) }];
+ const filter = rangeQuery(start, end);
if (serviceName) {
filter.push({
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts
index db8414864f5771..1712699162b73c 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts
@@ -25,6 +25,7 @@ describe('get buckets', () => {
});
await getBuckets({
+ environment: 'prod',
serviceName: 'myServiceName',
bucketSize: 10,
setup: {
@@ -42,14 +43,8 @@ describe('get buckets', () => {
get: () => 'myIndex',
}
) as APMConfig,
- uiFilters: {
- environment: 'prod',
- },
- esFilter: [
- {
- term: { 'service.environment': 'prod' },
- },
- ],
+ uiFilters: {},
+ esFilter: [],
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': 'apm-*',
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
index 383fcbb2f5ce72..fbe406d8d1a9d2 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
@@ -11,16 +11,18 @@ import {
SERVICE_NAME,
} from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { withApmSpan } from '../../../utils/with_apm_span';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
export async function getBuckets({
+ environment,
serviceName,
groupId,
bucketSize,
setup,
}: {
+ environment?: string;
serviceName: string;
groupId?: string;
bucketSize: number;
@@ -30,7 +32,8 @@ export async function getBuckets({
const { start, end, esFilter, apmEventClient } = setup;
const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
];
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
index be3a29780a5b65..1fb0cbad4a5f09 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
@@ -14,16 +14,19 @@ function getBucketSize({ start, end }: SetupTimeRange) {
}
export async function getErrorDistribution({
+ environment,
serviceName,
groupId,
setup,
}: {
+ environment?: string;
serviceName: string;
groupId?: string;
setup: Setup & SetupTimeRange;
}) {
const bucketSize = getBucketSize({ start: setup.start, end: setup.end });
const { buckets, noHits } = await getBuckets({
+ environment,
serviceName,
groupId,
bucketSize,
diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts b/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts
index 121b9b3d0c46fb..0ab26f3c6e969d 100644
--- a/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts
@@ -11,16 +11,18 @@ import {
TRANSACTION_SAMPLED,
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../common/utils/queries';
import { withApmSpan } from '../../utils/with_apm_span';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getTransaction } from '../transactions/get_transaction';
export function getErrorGroupSample({
+ environment,
serviceName,
groupId,
setup,
}: {
+ environment?: string;
serviceName: string;
groupId: string;
setup: Setup & SetupTimeRange;
@@ -39,7 +41,8 @@ export function getErrorGroupSample({
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [ERROR_GROUP_ID]: groupId } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
should: [{ term: { [TRANSACTION_SAMPLED]: true } }],
diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
index 6e91f8fe7cdd28..28d89eb0574709 100644
--- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
@@ -21,11 +21,13 @@ import { getErrorName } from '../helpers/get_error_name';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
export function getErrorGroups({
+ environment,
serviceName,
sortField,
sortDirection = 'desc',
setup,
}: {
+ environment?: string;
serviceName: string;
sortField?: string;
sortDirection?: 'asc' | 'desc';
@@ -37,7 +39,11 @@ export function getErrorGroups({
// sort buckets by last occurrence of error
const sortByLatestOccurrence = sortField === 'latestOccurrenceAt';
- const projection = getErrorGroupsProjection({ setup, serviceName });
+ const projection = getErrorGroupsProjection({
+ environment,
+ setup,
+ serviceName,
+ });
const order: SortOptions = sortByLatestOccurrence
? {
diff --git a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts
index 424c85d55a36e1..71744c3e590929 100644
--- a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts
@@ -6,7 +6,7 @@
*/
import { SearchAggregatedTransactionSetting } from '../../../../common/aggregated_transactions';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { rangeQuery } from '../../../../common/utils/queries';
import { ProcessorEvent } from '../../../../common/processor_event';
import {
TRANSACTION_DURATION,
@@ -35,7 +35,7 @@ export async function getHasAggregatedTransactions({
bool: {
filter: [
{ exists: { field: TRANSACTION_DURATION_HISTOGRAM } },
- ...(start && end ? [{ range: rangeFilter(start, end) }] : []),
+ ...(start && end ? rangeQuery(start, end) : []),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.test.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.test.ts
deleted file mode 100644
index 57bf511f459428..00000000000000
--- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.test.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { getEnvironmentUiFilterES } from './get_environment_ui_filter_es';
-import { ENVIRONMENT_NOT_DEFINED } from '../../../../common/environment_filter_values';
-import { SERVICE_ENVIRONMENT } from '../../../../common/elasticsearch_fieldnames';
-
-describe('getEnvironmentUiFilterES', () => {
- it('should return empty array, when environment is undefined', () => {
- const uiFilterES = getEnvironmentUiFilterES();
- expect(uiFilterES).toHaveLength(0);
- });
-
- it('should create a filter for a service environment', () => {
- const uiFilterES = getEnvironmentUiFilterES('test');
- expect(uiFilterES).toHaveLength(1);
- expect(uiFilterES[0]).toHaveProperty(['term', SERVICE_ENVIRONMENT], 'test');
- });
-
- it('should create a filter for missing service environments', () => {
- const uiFilterES = getEnvironmentUiFilterES(ENVIRONMENT_NOT_DEFINED.value);
- expect(uiFilterES).toHaveLength(1);
- expect(uiFilterES[0]).toHaveProperty(
- ['bool', 'must_not', 'exists', 'field'],
- SERVICE_ENVIRONMENT
- );
- });
-});
diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
index 63e0edd6097bf4..e91c9b52deecf1 100644
--- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
@@ -7,7 +7,6 @@
import { ESFilter } from '../../../../../../typings/elasticsearch';
import { UIFilters } from '../../../../typings/ui_filters';
-import { getEnvironmentUiFilterES } from './get_environment_ui_filter_es';
import {
localUIFilters,
localUIFilterNames,
@@ -28,10 +27,7 @@ export function getEsFilter(uiFilters: UIFilters) {
};
}) as ESFilter[];
- const esFilters = [
- ...getKueryUiFilterES(uiFilters.kuery),
- ...getEnvironmentUiFilterES(uiFilters.environment),
- ].concat(mappedFilters) as ESFilter[];
+ const esFilters = [...getKueryUiFilterES(uiFilters.kuery), ...mappedFilters];
return esFilters;
}
diff --git a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
index 961a1eee61d1d5..4eed09f3e5c28b 100644
--- a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
@@ -71,6 +71,11 @@ Object {
"service.name": "foo",
},
},
+ Object {
+ "term": Object {
+ "service.node.name": "bar",
+ },
+ },
Object {
"range": Object {
"@timestamp": Object {
@@ -80,11 +85,6 @@ Object {
},
},
},
- Object {
- "term": Object {
- "service.node.name": "bar",
- },
- },
Object {
"term": Object {
"service.environment": "test",
@@ -159,6 +159,11 @@ Object {
"service.name": "foo",
},
},
+ Object {
+ "term": Object {
+ "service.node.name": "bar",
+ },
+ },
Object {
"range": Object {
"@timestamp": Object {
@@ -168,11 +173,6 @@ Object {
},
},
},
- Object {
- "term": Object {
- "service.node.name": "bar",
- },
- },
Object {
"term": Object {
"service.environment": "test",
@@ -322,6 +322,11 @@ Object {
"service.name": "foo",
},
},
+ Object {
+ "term": Object {
+ "service.node.name": "bar",
+ },
+ },
Object {
"range": Object {
"@timestamp": Object {
@@ -331,11 +336,6 @@ Object {
},
},
},
- Object {
- "term": Object {
- "service.node.name": "bar",
- },
- },
Object {
"term": Object {
"service.environment": "test",
@@ -415,6 +415,11 @@ Object {
"service.name": "foo",
},
},
+ Object {
+ "term": Object {
+ "service.node.name": "bar",
+ },
+ },
Object {
"range": Object {
"@timestamp": Object {
@@ -424,11 +429,6 @@ Object {
},
},
},
- Object {
- "term": Object {
- "service.node.name": "bar",
- },
- },
Object {
"term": Object {
"service.environment": "test",
@@ -498,6 +498,11 @@ Object {
"service.name": "foo",
},
},
+ Object {
+ "term": Object {
+ "service.node.name": "bar",
+ },
+ },
Object {
"range": Object {
"@timestamp": Object {
@@ -507,11 +512,6 @@ Object {
},
},
},
- Object {
- "term": Object {
- "service.node.name": "bar",
- },
- },
Object {
"term": Object {
"service.environment": "test",
@@ -601,15 +601,6 @@ Object {
"service.name": "foo",
},
},
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
- },
- },
Object {
"bool": Object {
"must_not": Array [
@@ -621,6 +612,15 @@ Object {
],
},
},
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
Object {
"term": Object {
"service.environment": "test",
@@ -695,15 +695,6 @@ Object {
"service.name": "foo",
},
},
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
- },
- },
Object {
"bool": Object {
"must_not": Array [
@@ -715,6 +706,15 @@ Object {
],
},
},
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
Object {
"term": Object {
"service.environment": "test",
@@ -864,15 +864,6 @@ Object {
"service.name": "foo",
},
},
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
- },
- },
Object {
"bool": Object {
"must_not": Array [
@@ -884,6 +875,15 @@ Object {
],
},
},
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
Object {
"term": Object {
"service.environment": "test",
@@ -963,15 +963,6 @@ Object {
"service.name": "foo",
},
},
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
- },
- },
Object {
"bool": Object {
"must_not": Array [
@@ -983,6 +974,15 @@ Object {
],
},
},
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
Object {
"term": Object {
"service.environment": "test",
@@ -1052,15 +1052,6 @@ Object {
"service.name": "foo",
},
},
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
- },
- },
Object {
"bool": Object {
"must_not": Array [
@@ -1072,6 +1063,15 @@ Object {
],
},
},
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
Object {
"term": Object {
"service.environment": "test",
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
index e03be2391597d4..c5e80600b69d43 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
@@ -9,13 +9,18 @@ import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getCPUChartData } from './shared/cpu';
import { getMemoryChartData } from './shared/memory';
-export async function getDefaultMetricsCharts(
- setup: Setup & SetupTimeRange,
- serviceName: string
-) {
+export async function getDefaultMetricsCharts({
+ environment,
+ serviceName,
+ setup,
+}: {
+ environment?: string;
+ serviceName: string;
+ setup: Setup & SetupTimeRange;
+}) {
const charts = await Promise.all([
- getCPUChartData({ setup, serviceName }),
- getMemoryChartData({ setup, serviceName }),
+ getCPUChartData({ environment, setup, serviceName }),
+ getMemoryChartData({ environment, setup, serviceName }),
]);
return { charts };
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
index e76ad10b535b09..d7c9294c8ec7a1 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
@@ -22,12 +22,14 @@ import { getBucketSize } from '../../../../helpers/get_bucket_size';
import { getVizColorForIndex } from '../../../../../../common/viz_colors';
export async function fetchAndTransformGcMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
chartBase,
fieldName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
@@ -39,6 +41,7 @@ export async function fetchAndTransformGcMetrics({
const { bucketSize } = getBucketSize({ start, end });
const projection = getMetricsProjection({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
index 7989f57046ae78..8c5b9fb3db9221 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
@@ -33,16 +33,19 @@ const chartBase: ChartBase = {
};
function getGcRateChart({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
return withApmSpan('get_gc_rate_charts', () =>
fetchAndTransformGcMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
index 446894f82b75e3..98f31f06c1b644 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
@@ -33,16 +33,19 @@ const chartBase: ChartBase = {
};
function getGcTimeChart({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
return withApmSpan('get_gc_time_charts', () =>
fetchAndTransformGcMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
index 2b7bb9ea8da6ed..d6cbc4a07e8f9b 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
@@ -53,16 +53,19 @@ const chartBase: ChartBase = {
};
export function getHeapMemoryChart({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
return withApmSpan('get_heap_memory_charts', () =>
fetchAndTransformMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
index e137720000262a..970b4d3499b798 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
@@ -16,23 +16,30 @@ import { getGcRateChart } from './gc/get_gc_rate_chart';
import { getGcTimeChart } from './gc/get_gc_time_chart';
export function getJavaMetricsCharts({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
return withApmSpan('get_java_system_metric_charts', async () => {
const charts = await Promise.all([
- getCPUChartData({ setup, serviceName, serviceNodeName }),
- getMemoryChartData({ setup, serviceName, serviceNodeName }),
- getHeapMemoryChart({ setup, serviceName, serviceNodeName }),
- getNonHeapMemoryChart({ setup, serviceName, serviceNodeName }),
- getThreadCountChart({ setup, serviceName, serviceNodeName }),
- getGcRateChart({ setup, serviceName, serviceNodeName }),
- getGcTimeChart({ setup, serviceName, serviceNodeName }),
+ getCPUChartData({ environment, setup, serviceName, serviceNodeName }),
+ getMemoryChartData({ environment, setup, serviceName, serviceNodeName }),
+ getHeapMemoryChart({ environment, setup, serviceName, serviceNodeName }),
+ getNonHeapMemoryChart({
+ environment,
+ setup,
+ serviceName,
+ serviceNodeName,
+ }),
+ getThreadCountChart({ environment, setup, serviceName, serviceNodeName }),
+ getGcRateChart({ environment, setup, serviceName, serviceNodeName }),
+ getGcTimeChart({ environment, setup, serviceName, serviceNodeName }),
]);
return { charts };
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
index a3e253d2c81d67..25abd2c34c83a9 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
@@ -50,16 +50,19 @@ const chartBase: ChartBase = {
};
export async function getNonHeapMemoryChart({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
return withApmSpan('get_non_heap_memory_charts', () =>
fetchAndTransformMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
index e176c156ad05a5..c8a209fee701a1 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
@@ -42,16 +42,19 @@ const chartBase: ChartBase = {
};
export async function getThreadCountChart({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
return withApmSpan('get_thread_count_charts', () =>
fetchAndTransformMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
index e7f576b73c5aef..ebfe504e5269b7 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
@@ -54,16 +54,19 @@ const chartBase: ChartBase = {
};
export function getCPUChartData({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
return withApmSpan('get_cpu_metric_charts', () =>
fetchAndTransformMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
index 0f7954d86d3e2c..55b3328bcd2a9b 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
@@ -71,10 +71,12 @@ export const percentCgroupMemoryUsedScript = {
};
export async function getMemoryChartData({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
@@ -84,6 +86,7 @@ export async function getMemoryChartData({
'get_cgroup_memory_metrics_charts',
() =>
fetchAndTransformMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
@@ -101,6 +104,7 @@ export async function getMemoryChartData({
if (cgroupResponse.noHits) {
return await withApmSpan('get_system_memory_metrics_charts', () =>
fetchAndTransformMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
index 7a52806601e0e8..17e9aef29ba82d 100644
--- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
@@ -48,6 +48,7 @@ interface Filter {
}
export async function fetchAndTransformMetrics({
+ environment,
setup,
serviceName,
serviceNodeName,
@@ -55,6 +56,7 @@ export async function fetchAndTransformMetrics({
aggs,
additionalFilters = [],
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
@@ -65,6 +67,7 @@ export async function fetchAndTransformMetrics({
const { start, end, apmEventClient, config } = setup;
const projection = getMetricsProjection({
+ environment,
setup,
serviceName,
serviceNodeName,
diff --git a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
index 5083982f1cb9cb..eda71ef380ee9c 100644
--- a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
@@ -15,11 +15,13 @@ export interface MetricsChartsByAgentAPIResponse {
}
export async function getMetricsChartDataByAgent({
+ environment,
setup,
serviceName,
serviceNodeName,
agentName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
@@ -27,11 +29,16 @@ export async function getMetricsChartDataByAgent({
}): Promise {
switch (agentName) {
case 'java': {
- return getJavaMetricsCharts({ setup, serviceName, serviceNodeName });
+ return getJavaMetricsCharts({
+ environment,
+ setup,
+ serviceName,
+ serviceNodeName,
+ });
}
default: {
- return getDefaultMetricsCharts(setup, serviceName);
+ return getDefaultMetricsCharts({ environment, setup, serviceName });
}
}
}
diff --git a/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts b/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts
index 4af57a685bf839..c7ac678899b58c 100644
--- a/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts
+++ b/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts
@@ -6,7 +6,7 @@
*/
import { ProcessorEvent } from '../../../common/processor_event';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions';
@@ -36,7 +36,7 @@ export function getServiceCount({
size: 0,
query: {
bool: {
- filter: [{ range: rangeFilter(start, end) }],
+ filter: rangeQuery(start, end),
},
},
aggs: { serviceCount: { cardinality: { field: SERVICE_NAME } } },
diff --git a/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts b/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts
index 87394567afc50b..2da4b0f8de3632 100644
--- a/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts
+++ b/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { Coordinates } from '../../../../observability/typings/common';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions';
@@ -38,7 +38,7 @@ export function getTransactionCoordinates({
size: 0,
query: {
bool: {
- filter: [{ range: rangeFilter(start, end) }],
+ filter: rangeQuery(start, end),
},
},
aggs: {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts b/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts
index 368c5ec5463592..9626019347e5bb 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts
@@ -11,7 +11,7 @@ import {
TRANSACTION_TYPE,
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { TRANSACTION_PAGE_LOAD } from '../../../common/transaction_types';
export async function hasRumData({ setup }: { setup: Setup & SetupTimeRange }) {
@@ -31,9 +31,7 @@ export async function hasRumData({ setup }: { setup: Setup & SetupTimeRange }) {
},
aggs: {
services: {
- filter: {
- range: rangeFilter(start, end),
- },
+ filter: rangeQuery(start, end)[0],
aggs: {
mostTraffic: {
terms: {
diff --git a/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts
index 939ebbb1f79419..259a0e6daea6fc 100644
--- a/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { ProcessorEvent } from '../../../common/processor_event';
import { TRACE_ID } from '../../../common/elasticsearch_fieldnames';
import {
@@ -42,7 +42,7 @@ export async function fetchServicePathsFromTraceIds(
[TRACE_ID]: traceIds,
},
},
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts
index 2c64678eb082ec..ab221e30ea4897 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts
@@ -17,6 +17,7 @@ import {
TRANSACTION_PAGE_LOAD,
TRANSACTION_REQUEST,
} from '../../../common/transaction_types';
+import { rangeQuery } from '../../../common/utils/queries';
import { withApmSpan } from '../../utils/with_apm_span';
import { getMlJobsWithAPMGroup } from '../anomaly_detection/get_ml_jobs_with_apm_group';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
@@ -51,16 +52,11 @@ export async function getServiceAnomalies({
bool: {
filter: [
{ terms: { result_type: ['model_plot', 'record'] } },
- {
- range: {
- timestamp: {
- // fetch data for at least 30 minutes
- gte: Math.min(end - 30 * 60 * 1000, start),
- lte: end,
- format: 'epoch_millis',
- },
- },
- },
+ ...rangeQuery(
+ Math.min(end - 30 * 60 * 1000, start),
+ end,
+ 'timestamp'
+ ),
{
terms: {
// Only retrieving anomalies for transaction types "request" and "page-load"
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
index 951484308db195..1aee1bb5b242af 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
@@ -15,8 +15,8 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { getServicesProjection } from '../../projections/services';
import { mergeProjection } from '../../projections/util/merge_projection';
+import { environmentQuery } from '../../../common/utils/queries';
import { withApmSpan } from '../../utils/with_apm_span';
-import { getEnvironmentUiFilterES } from '../helpers/convert_ui_filters/get_environment_ui_filter_es';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
DEFAULT_ANOMALIES,
@@ -88,14 +88,17 @@ async function getConnectionData({
async function getServicesData(options: IEnvOptions) {
return withApmSpan('get_service_stats_for_service_map', async () => {
- const { setup, searchAggregatedTransactions } = options;
+ const { environment, setup, searchAggregatedTransactions } = options;
const projection = getServicesProjection({
setup: { ...setup, esFilter: [] },
searchAggregatedTransactions,
});
- let { filter } = projection.body.query.bool;
+ let filter = [
+ ...projection.body.query.bool.filter,
+ ...environmentQuery(environment),
+ ];
if (options.serviceName) {
filter = filter.concat({
@@ -105,10 +108,6 @@ async function getServicesData(options: IEnvOptions) {
});
}
- if (options.environment) {
- filter = filter.concat(getEnvironmentUiFilterES(options.environment));
- }
-
const params = mergeProjection(projection, {
body: {
size: 0,
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts
index ef4faf94063461..6e9225041b199c 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts
@@ -6,7 +6,10 @@
*/
import { find, uniqBy } from 'lodash';
-import { ENVIRONMENT_NOT_DEFINED } from '../../../common/environment_filter_values';
+import {
+ ENVIRONMENT_ALL,
+ ENVIRONMENT_NOT_DEFINED,
+} from '../../../common/environment_filter_values';
import {
SERVICE_ENVIRONMENT,
SERVICE_NAME,
@@ -27,8 +30,10 @@ export function getConnections({
if (!paths) {
return [];
}
+ const isEnvironmentSelected =
+ environment && environment !== ENVIRONMENT_ALL.value;
- if (serviceName || environment) {
+ if (serviceName || isEnvironmentSelected) {
paths = paths.filter((path) => {
return (
path
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
index 63f28abab8f3aa..b161345e729d35 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
@@ -19,11 +19,13 @@ describe('getServiceMapServiceNodeInfo', () => {
hits: { total: { value: 0 } },
}),
},
+ esFilter: [],
indices: {},
- uiFilters: { environment: 'test environment' },
+ uiFilters: {},
} as unknown) as Setup & SetupTimeRange;
const serviceName = 'test service name';
const result = await getServiceMapServiceNodeInfo({
+ environment: 'test environment',
setup,
serviceName,
searchAggregatedTransactions: false,
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
index 213702bf06f4ce..e384b15685dad4 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
@@ -19,14 +19,13 @@ import {
TRANSACTION_PAGE_LOAD,
TRANSACTION_REQUEST,
} from '../../../common/transaction_types';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../common/utils/queries';
import { withApmSpan } from '../../utils/with_apm_span';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
getTransactionDurationFieldForAggregatedTransactions,
} from '../helpers/aggregated_transactions';
-import { getEnvironmentUiFilterES } from '../helpers/convert_ui_filters/get_environment_ui_filter_es';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
percentCgroupMemoryUsedScript,
@@ -51,6 +50,7 @@ interface TaskParameters {
}
export function getServiceMapServiceNodeInfo({
+ environment,
serviceName,
setup,
searchAggregatedTransactions,
@@ -59,9 +59,9 @@ export function getServiceMapServiceNodeInfo({
const { start, end, uiFilters } = setup;
const filter: ESFilter[] = [
- { range: rangeFilter(start, end) },
{ term: { [SERVICE_NAME]: serviceName } },
- ...getEnvironmentUiFilterES(uiFilters.environment),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
];
const minutes = Math.abs((end - start) / (1000 * 60));
@@ -106,16 +106,13 @@ async function getErrorStats({
searchAggregatedTransactions: boolean;
}) {
return withApmSpan('get_error_rate_for_service_map_node', async () => {
- const setupWithBlankUiFilters = {
- ...setup,
- uiFilters: { environment },
- esFilter: getEnvironmentUiFilterES(environment),
- };
const { noHits, average } = await getErrorRate({
- setup: setupWithBlankUiFilters,
+ environment,
+ setup,
serviceName,
searchAggregatedTransactions,
});
+
return { avgErrorRate: noHits ? null : average };
});
}
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
index deb9104a839051..e8dcb28baa9a38 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
@@ -16,9 +16,8 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
import { SERVICE_MAP_TIMEOUT_ERROR } from '../../../common/service_map';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../common/utils/queries';
import { withApmSpan } from '../../utils/with_apm_span';
-import { getEnvironmentUiFilterES } from '../helpers/convert_ui_filters/get_environment_ui_filter_es';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
const MAX_TRACES_TO_INSPECT = 1000;
@@ -35,8 +34,6 @@ export function getTraceSampleIds({
return withApmSpan('get_trace_sample_ids', async () => {
const { start, end, apmEventClient, config } = setup;
- const rangeQuery = { range: rangeFilter(start, end) };
-
const query = {
bool: {
filter: [
@@ -45,7 +42,7 @@ export function getTraceSampleIds({
field: SPAN_DESTINATION_SERVICE_RESOURCE,
},
},
- rangeQuery,
+ ...rangeQuery(start, end),
] as ESFilter[],
},
} as { bool: { filter: ESFilter[]; must_not?: ESFilter[] | ESFilter } };
@@ -54,7 +51,7 @@ export function getTraceSampleIds({
query.bool.filter.push({ term: { [SERVICE_NAME]: serviceName } });
}
- query.bool.filter.push(...getEnvironmentUiFilterES(environment));
+ query.bool.filter.push(...environmentQuery(environment));
const fingerprintBucketSize = serviceName
? config['xpack.apm.serviceMapFingerprintBucketSize']
diff --git a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
index d83e558775be40..e6d702cc03c0b1 100644
--- a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
@@ -35,6 +35,11 @@ Object {
"service.name": "foo",
},
},
+ Object {
+ "term": Object {
+ "service.node.name": "bar",
+ },
+ },
Object {
"range": Object {
"@timestamp": Object {
@@ -44,11 +49,6 @@ Object {
},
},
},
- Object {
- "term": Object {
- "service.node.name": "bar",
- },
- },
Object {
"term": Object {
"service.environment": "test",
@@ -97,15 +97,6 @@ Object {
"service.name": "foo",
},
},
- Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
- },
- },
Object {
"bool": Object {
"must_not": Array [
@@ -117,6 +108,15 @@ Object {
],
},
},
+ Object {
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
+ },
+ },
Object {
"term": Object {
"service.environment": "test",
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 7c746aac29af92..67aa9d7fcd8707 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
@@ -12,13 +12,12 @@ import {
SERVICE_NAME,
SERVICE_VERSION,
} from '../../../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { withApmSpan } from '../../../utils/with_apm_span';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
} from '../../helpers/aggregated_transactions';
-import { getEnvironmentUiFilterES } from '../../helpers/convert_ui_filters/get_environment_ui_filter_es';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
export async function getDerivedServiceAnnotations({
@@ -40,7 +39,7 @@ export async function getDerivedServiceAnnotations({
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
- ...getEnvironmentUiFilterES(environment),
+ ...environmentQuery(environment),
];
const versions =
@@ -57,7 +56,7 @@ export async function getDerivedServiceAnnotations({
size: 0,
query: {
bool: {
- filter: [...filter, { range: rangeFilter(start, end) }],
+ filter: [...filter, ...rangeQuery(start, end)],
},
},
aggs: {
diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts
index fc53f763dac0ff..6c7cbc26ea6536 100644
--- a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts
+++ b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts
@@ -5,19 +5,18 @@
* 2.0.
*/
-import { ElasticsearchClient, Logger } from 'kibana/server';
import { ResponseError } from '@elastic/elasticsearch/lib/errors';
+import { ElasticsearchClient, Logger } from 'kibana/server';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import {
unwrapEsResponse,
WrappedElasticsearchClientError,
} from '../../../../../observability/server';
-import { rangeFilter } from '../../../../common/utils/range_filter';
import { ESSearchResponse } from '../../../../../../typings/elasticsearch';
import { Annotation as ESAnnotation } from '../../../../../observability/common/annotations';
import { ScopedAnnotationsClient } from '../../../../../observability/server';
import { Annotation, AnnotationType } from '../../../../common/annotations';
import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames';
-import { getEnvironmentUiFilterES } from '../../helpers/convert_ui_filters/get_environment_ui_filter_es';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { withApmSpan } from '../../../utils/with_apm_span';
@@ -37,18 +36,18 @@ export function getStoredAnnotations({
logger: Logger;
}): Promise {
return withApmSpan('get_stored_annotations', async () => {
+ const { start, end } = setup;
+
const body = {
size: 50,
query: {
bool: {
filter: [
- {
- range: rangeFilter(setup.start, setup.end),
- },
{ term: { 'annotation.type': 'deployment' } },
{ term: { tags: 'apm' } },
{ term: { [SERVICE_NAME]: serviceName } },
- ...getEnvironmentUiFilterES(environment),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts
index 29c77da6e4075f..3683a069342a9b 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts
@@ -10,7 +10,7 @@ import {
AGENT_NAME,
SERVICE_NAME,
} from '../../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions';
import { withApmSpan } from '../../utils/with_apm_span';
@@ -44,7 +44,7 @@ export function getServiceAgentName({
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts
index aa53e8da6cad05..558d6ae22f00f5 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts
@@ -19,9 +19,8 @@ import {
SPAN_SUBTYPE,
SPAN_TYPE,
} from '../../../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../../../common/utils/range_filter';
import { ProcessorEvent } from '../../../../common/processor_event';
-import { getEnvironmentUiFilterES } from '../../helpers/convert_ui_filters/get_environment_ui_filter_es';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { joinByKey } from '../../../../common/utils/join_by_key';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { withApmSpan } from '../../../utils/with_apm_span';
@@ -33,7 +32,7 @@ export const getDestinationMap = ({
}: {
setup: Setup & SetupTimeRange;
serviceName: string;
- environment: string;
+ environment?: string;
}) => {
return withApmSpan('get_service_destination_map', async () => {
const { start, end, apmEventClient } = setup;
@@ -50,8 +49,8 @@ export const getDestinationMap = ({
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ exists: { field: SPAN_DESTINATION_SERVICE_RESOURCE } },
- { range: rangeFilter(start, end) },
- ...getEnvironmentUiFilterES(environment),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
],
},
},
@@ -122,7 +121,7 @@ export const getDestinationMap = ({
),
},
},
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts
index 9a020daa7e095d..dfbdfb3f504e8c 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts
@@ -13,9 +13,8 @@ import {
SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT,
SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM,
} from '../../../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../../../common/utils/range_filter';
import { ProcessorEvent } from '../../../../common/processor_event';
-import { getEnvironmentUiFilterES } from '../../helpers/convert_ui_filters/get_environment_ui_filter_es';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { getBucketSize } from '../../helpers/get_bucket_size';
import { EventOutcome } from '../../../../common/event_outcome';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
@@ -29,7 +28,7 @@ export const getMetrics = ({
}: {
setup: Setup & SetupTimeRange;
serviceName: string;
- environment: string;
+ environment?: string;
numBuckets: number;
}) => {
return withApmSpan('get_service_destination_metrics', async () => {
@@ -49,8 +48,8 @@ export const getMetrics = ({
{
exists: { field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT },
},
- { range: rangeFilter(start, end) },
- ...getEnvironmentUiFilterES(environment),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/index.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/index.ts
index 19f306f5cb8035..724b5278d7edfc 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/index.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/index.ts
@@ -51,7 +51,7 @@ export function getServiceDependencies({
}: {
serviceName: string;
setup: Setup & SetupTimeRange;
- environment: string;
+ environment?: string;
numBuckets: number;
}): Promise {
return withApmSpan('get_service_dependencies', async () => {
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts
index f5aa01e1dfa58a..a17fb6da2007fa 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts
@@ -9,7 +9,7 @@ import { ValuesType } from 'utility-types';
import { orderBy } from 'lodash';
import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n';
import { PromiseReturnType } from '../../../../../observability/typings/common';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { ProcessorEvent } from '../../../../common/processor_event';
import {
ERROR_EXC_MESSAGE,
@@ -28,6 +28,7 @@ export type ServiceErrorGroupItem = ValuesType<
>;
export async function getServiceErrorGroups({
+ environment,
serviceName,
setup,
size,
@@ -37,6 +38,7 @@ export async function getServiceErrorGroups({
sortField,
transactionType,
}: {
+ environment?: string;
serviceName: string;
setup: Setup & SetupTimeRange;
size: number;
@@ -63,7 +65,8 @@ export async function getServiceErrorGroups({
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
},
@@ -145,7 +148,8 @@ export async function getServiceErrorGroups({
{ terms: { [ERROR_GROUP_ID]: sortedErrorGroupIds } },
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts
index 4f8088352d0ae2..ef90e5197229b9 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_system_metric_stats.ts
@@ -6,7 +6,7 @@
*/
import { AggregationOptionsByType } from '../../../../../../typings/elasticsearch';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes';
import {
METRIC_CGROUP_MEMORY_USAGE_BYTES,
@@ -26,6 +26,7 @@ import {
import { withApmSpan } from '../../../utils/with_apm_span';
export async function getServiceInstanceSystemMetricStats({
+ environment,
setup,
serviceName,
size,
@@ -95,8 +96,9 @@ export async function getServiceInstanceSystemMetricStats({
query: {
bool: {
filter: [
- { range: rangeFilter(start, end) },
{ term: { [SERVICE_NAME]: serviceName } },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
should: [cgroupMemoryFilter, systemMemoryFilter, cpuUsageFilter],
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_transaction_stats.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_transaction_stats.ts
index 2cbe5a42206d18..b56625bcebc998 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_transaction_stats.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instance_transaction_stats.ts
@@ -6,7 +6,7 @@
*/
import { EventOutcome } from '../../../../common/event_outcome';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes';
import {
EVENT_OUTCOME,
@@ -24,6 +24,7 @@ import { calculateThroughput } from '../../helpers/calculate_throughput';
import { withApmSpan } from '../../../utils/with_apm_span';
export async function getServiceInstanceTransactionStats({
+ environment,
setup,
transactionType,
serviceName,
@@ -72,9 +73,10 @@ export async function getServiceInstanceTransactionStats({
query: {
bool: {
filter: [
- { range: rangeFilter(start, end) },
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/index.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/index.ts
index 021774f9522c1b..4c16940e6d2538 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_instances/index.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/index.ts
@@ -12,6 +12,7 @@ import { getServiceInstanceSystemMetricStats } from './get_service_instance_syst
import { getServiceInstanceTransactionStats } from './get_service_instance_transaction_stats';
export interface ServiceInstanceParams {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
transactionType: string;
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts b/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts
index f1198a4d858fda..5c43191cf588c2 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts
@@ -21,7 +21,7 @@ import {
SERVICE_VERSION,
} from '../../../common/elasticsearch_fieldnames';
import { ContainerType } from '../../../common/service_metadata';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw';
import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
@@ -74,7 +74,7 @@ export function getServiceMetadataDetails({
const filter = [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
];
const params = {
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts b/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts
index 0ea95a08abaa98..b342ffea02464e 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts
@@ -16,7 +16,7 @@ import {
HOST_OS_PLATFORM,
} from '../../../common/elasticsearch_fieldnames';
import { ContainerType } from '../../../common/service_metadata';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw';
import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
@@ -55,7 +55,7 @@ export function getServiceMetadataIcons({
const filter = [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
];
const params = {
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_comparison_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_comparison_statistics.ts
index 54cf89d6125b6e..ce36db3e82babe 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_comparison_statistics.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_comparison_statistics.ts
@@ -14,7 +14,7 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { EventOutcome } from '../../../common/event_outcome';
import { LatencyAggregationType } from '../../../common/latency_aggregation_types';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../common/utils/queries';
import { Coordinate } from '../../../typings/timeseries';
import { withApmSpan } from '../../utils/with_apm_span';
import {
@@ -31,6 +31,7 @@ import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { calculateTransactionErrorPercentage } from '../helpers/transaction_error_rate';
export async function getServiceTransactionGroupComparisonStatistics({
+ environment,
serviceName,
transactionNames,
setup,
@@ -39,6 +40,7 @@ export async function getServiceTransactionGroupComparisonStatistics({
transactionType,
latencyAggregationType,
}: {
+ environment?: string;
serviceName: string;
transactionNames: string[];
setup: Setup & SetupTimeRange;
@@ -82,10 +84,11 @@ export async function getServiceTransactionGroupComparisonStatistics({
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
- { range: rangeFilter(start, end) },
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts
index 168eed8e38374c..ddbfd617faf65b 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts
@@ -13,7 +13,7 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { EventOutcome } from '../../../common/event_outcome';
import { LatencyAggregationType } from '../../../common/latency_aggregation_types';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../common/utils/queries';
import { withApmSpan } from '../../utils/with_apm_span';
import {
getDocumentTypeFilterForAggregatedTransactions,
@@ -36,12 +36,14 @@ export type ServiceOverviewTransactionGroupSortField =
| 'impact';
export async function getServiceTransactionGroups({
+ environment,
serviceName,
setup,
searchAggregatedTransactions,
transactionType,
latencyAggregationType,
}: {
+ environment?: string;
serviceName: string;
setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
@@ -70,10 +72,11 @@ export async function getServiceTransactionGroups({
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
- { range: rangeFilter(start, end) },
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts
index bc4660e2c01a5e..3d77bf5bd6baf3 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts
@@ -9,7 +9,7 @@ import {
SERVICE_NAME,
TRANSACTION_TYPE,
} from '../../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
getDocumentTypeFilterForAggregatedTransactions,
@@ -46,7 +46,7 @@ export function getServiceTransactionTypes({
searchAggregatedTransactions
),
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts
index e88fafb061912d..6fc868b0f0a4ec 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts
@@ -8,28 +8,25 @@
import { getSeverity } from '../../../../common/anomaly_detection';
import { getServiceHealthStatus } from '../../../../common/service_health_status';
import { getServiceAnomalies } from '../../service_map/get_service_anomalies';
-import {
- ServicesItemsProjection,
- ServicesItemsSetup,
-} from './get_services_items';
+import { ServicesItemsSetup } from './get_services_items';
interface AggregationParams {
+ environment?: string;
setup: ServicesItemsSetup;
- projection: ServicesItemsProjection;
searchAggregatedTransactions: boolean;
}
-export const getHealthStatuses = async (
- { setup }: AggregationParams,
- mlAnomaliesEnvironment?: string
-) => {
+export const getHealthStatuses = async ({
+ environment,
+ setup,
+}: AggregationParams) => {
if (!setup.ml) {
return [];
}
const anomalies = await getServiceAnomalies({
setup,
- environment: mlAnomaliesEnvironment,
+ environment,
});
return anomalies.serviceAnomalies.map((anomalyStats) => {
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts
index acfdc1d8c1710a..e1f8bca83829cc 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts
@@ -15,7 +15,7 @@ import {
TRANSACTION_PAGE_LOAD,
TRANSACTION_REQUEST,
} from '../../../../common/transaction_types';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent';
import {
getDocumentTypeFilterForAggregatedTransactions,
@@ -32,6 +32,7 @@ import { ServicesItemsSetup } from './get_services_items';
import { withApmSpan } from '../../../utils/with_apm_span';
interface AggregationParams {
+ environment?: string;
setup: ServicesItemsSetup;
searchAggregatedTransactions: boolean;
}
@@ -39,6 +40,7 @@ interface AggregationParams {
const MAX_NUMBER_OF_SERVICES = 500;
export async function getServiceTransactionStats({
+ environment,
setup,
searchAggregatedTransactions,
}: AggregationParams) {
@@ -71,11 +73,12 @@ export async function getServiceTransactionStats({
query: {
bool: {
filter: [
- { range: rangeFilter(start, end) },
- ...esFilter,
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
+ ...esFilter,
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
index 1ba9aaf980201f..c2677af038486b 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
@@ -14,19 +14,21 @@ import { getHealthStatuses } from './get_health_statuses';
import { getServiceTransactionStats } from './get_service_transaction_stats';
export type ServicesItemsSetup = Setup & SetupTimeRange;
-export type ServicesItemsProjection = ReturnType;
export async function getServicesItems({
+ environment,
setup,
searchAggregatedTransactions,
logger,
}: {
+ environment?: string;
setup: ServicesItemsSetup;
searchAggregatedTransactions: boolean;
logger: Logger;
}) {
return withApmSpan('get_services_items', async () => {
const params = {
+ environment,
projection: getServicesProjection({
setup,
searchAggregatedTransactions,
@@ -37,7 +39,7 @@ export async function getServicesItems({
const [transactionStats, healthStatuses] = await Promise.all([
getServiceTransactionStats(params),
- getHealthStatuses(params, setup.uiFilters.environment).catch((err) => {
+ getHealthStatuses(params).catch((err) => {
logger.error(err);
return [];
}),
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/index.ts b/x-pack/plugins/apm/server/lib/services/get_services/index.ts
index 45efd80c45a980..1a0ddeda11651b 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/index.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/index.ts
@@ -14,10 +14,12 @@ import { getServicesItems } from './get_services_items';
import { hasHistoricalAgentData } from './has_historical_agent_data';
export async function getServices({
+ environment,
setup,
searchAggregatedTransactions,
logger,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
logger: Logger;
@@ -25,6 +27,7 @@ export async function getServices({
return withApmSpan('get_services', async () => {
const [items, hasLegacyData] = await Promise.all([
getServicesItems({
+ environment,
setup,
searchAggregatedTransactions,
logger,
diff --git a/x-pack/plugins/apm/server/lib/services/get_throughput.ts b/x-pack/plugins/apm/server/lib/services/get_throughput.ts
index 33268e9b3332d7..f7cd23b0e37a7b 100644
--- a/x-pack/plugins/apm/server/lib/services/get_throughput.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_throughput.ts
@@ -10,7 +10,7 @@ import {
SERVICE_NAME,
TRANSACTION_TYPE,
} from '../../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../common/utils/queries';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
@@ -20,6 +20,7 @@ import { Setup } from '../helpers/setup_request';
import { withApmSpan } from '../../utils/with_apm_span';
interface Options {
+ environment?: string;
searchAggregatedTransactions: boolean;
serviceName: string;
setup: Setup;
@@ -29,6 +30,7 @@ interface Options {
}
function fetcher({
+ environment,
searchAggregatedTransactions,
serviceName,
setup,
@@ -36,16 +38,17 @@ function fetcher({
start,
end,
}: Options) {
- const { apmEventClient } = setup;
+ const { esFilter, apmEventClient } = setup;
const { intervalString } = getBucketSize({ start, end });
const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
- { range: rangeFilter(start, end) },
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
- ...setup.esFilter,
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
+ ...esFilter,
];
const params = {
diff --git a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts
index bd3ecf1e0f862d..f631657f872761 100644
--- a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts
+++ b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts
@@ -15,7 +15,7 @@ import {
ERROR_LOG_LEVEL,
} from '../../../common/elasticsearch_fieldnames';
import { APMError } from '../../../typings/es_schemas/ui/apm_error';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { rangeQuery } from '../../../common/utils/queries';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { PromiseValueType } from '../../../typings/common';
import { withApmSpan } from '../../utils/with_apm_span';
@@ -44,7 +44,7 @@ export async function getTraceItems(
bool: {
filter: [
{ term: { [TRACE_ID]: traceId } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
],
must_not: { terms: { [ERROR_LOG_LEVEL]: excludedLogLevels } },
},
@@ -74,7 +74,7 @@ export async function getTraceItems(
bool: {
filter: [
{ term: { [TRACE_ID]: traceId } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
],
should: {
exists: { field: PARENT_ID },
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
index 443159611883fc..7fb2bb2fcbeeb1 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
@@ -244,12 +244,8 @@ Array [
"bool": Object {
"filter": Array [
Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
+ "term": Object {
+ "service.name": "foo",
},
},
Object {
@@ -258,8 +254,12 @@ Array [
},
},
Object {
- "term": Object {
- "service.name": "foo",
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
},
},
Object {
@@ -299,12 +299,8 @@ Array [
"bool": Object {
"filter": Array [
Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
+ "term": Object {
+ "service.name": "foo",
},
},
Object {
@@ -313,8 +309,12 @@ Array [
},
},
Object {
- "term": Object {
- "service.name": "foo",
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
},
},
Object {
@@ -354,12 +354,8 @@ Array [
"bool": Object {
"filter": Array [
Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
+ "term": Object {
+ "service.name": "foo",
},
},
Object {
@@ -368,8 +364,12 @@ Array [
},
},
Object {
- "term": Object {
- "service.name": "foo",
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
},
},
Object {
@@ -415,12 +415,8 @@ Array [
"bool": Object {
"filter": Array [
Object {
- "range": Object {
- "@timestamp": Object {
- "format": "epoch_millis",
- "gte": 1528113600000,
- "lte": 1528977600000,
- },
+ "term": Object {
+ "service.name": "foo",
},
},
Object {
@@ -429,8 +425,12 @@ Array [
},
},
Object {
- "term": Object {
- "service.name": "foo",
+ "range": Object {
+ "@timestamp": Object {
+ "format": "epoch_millis",
+ "gte": 1528113600000,
+ "lte": 1528977600000,
+ },
},
},
Object {
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
index 0fad948edde19b..09e5e358a1b7c1 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
@@ -27,6 +27,7 @@ import {
} from './get_transaction_group_stats';
interface TopTransactionOptions {
+ environment?: string;
type: 'top_transactions';
serviceName: string;
transactionType: string;
@@ -35,6 +36,7 @@ interface TopTransactionOptions {
}
interface TopTraceOptions {
+ environment?: string;
type: 'top_traces';
transactionName?: string;
searchAggregatedTransactions: boolean;
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
index 839efc9009c389..d1a056002db078 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
@@ -14,7 +14,7 @@ import {
TRANSACTION_TYPE,
} from '../../../common/elasticsearch_fieldnames';
import { EventOutcome } from '../../../common/event_outcome';
-import { rangeFilter } from '../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../common/utils/queries';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
@@ -29,12 +29,14 @@ import {
import { withApmSpan } from '../../utils/with_apm_span';
export async function getErrorRate({
+ environment,
serviceName,
transactionType,
transactionName,
setup,
searchAggregatedTransactions,
}: {
+ environment?: string;
serviceName: string;
transactionType?: string;
transactionName?: string;
@@ -57,17 +59,18 @@ export async function getErrorRate({
const filter = [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
{
terms: {
[EVENT_OUTCOME]: [EventOutcome.failure, EventOutcome.success],
},
},
+ ...transactionNamefilter,
+ ...transactionTypefilter,
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
- ...transactionNamefilter,
- ...transactionTypefilter,
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
];
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 8a2579b4a2b875..c3741184c807db 100644
--- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
@@ -18,18 +18,20 @@ import {
TRANSACTION_BREAKDOWN_COUNT,
} from '../../../../common/elasticsearch_fieldnames';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { getMetricsDateHistogramParams } from '../../helpers/metrics';
import { MAX_KPIS } from './constants';
import { getVizColorForIndex } from '../../../../common/viz_colors';
import { withApmSpan } from '../../../utils/with_apm_span';
export function getTransactionBreakdown({
+ environment,
setup,
serviceName,
transactionName,
transactionType,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
transactionName?: string;
@@ -82,7 +84,8 @@ export function getTransactionBreakdown({
const filters = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
];
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
index d1d23f538e96b0..7ed016cd4b4c61 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
@@ -17,7 +17,10 @@ import {
} from '../../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../../common/processor_event';
import { joinByKey } from '../../../../../common/utils/join_by_key';
-import { rangeFilter } from '../../../../../common/utils/range_filter';
+import {
+ environmentQuery,
+ rangeQuery,
+} from '../../../../../common/utils/queries';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
@@ -46,6 +49,7 @@ function getHistogramAggOptions({
}
export async function getBuckets({
+ environment,
serviceName,
transactionName,
transactionType,
@@ -56,6 +60,7 @@ export async function getBuckets({
setup,
searchAggregatedTransactions,
}: {
+ environment?: string;
serviceName: string;
transactionName: string;
transactionType: string;
@@ -75,7 +80,8 @@ export async function getBuckets({
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [TRANSACTION_NAME]: transactionName } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
];
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
index ed54dae32704e3..f8061ea9894698 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
@@ -15,15 +15,18 @@ import {
getProcessorEventForAggregatedTransactions,
getTransactionDurationFieldForAggregatedTransactions,
} from '../../helpers/aggregated_transactions';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import { withApmSpan } from '../../../utils/with_apm_span';
export async function getDistributionMax({
+ environment,
serviceName,
transactionName,
transactionType,
setup,
searchAggregatedTransactions,
}: {
+ environment?: string;
serviceName: string;
transactionName: string;
transactionType: string;
@@ -49,15 +52,8 @@ export async function getDistributionMax({
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [TRANSACTION_NAME]: transactionName } },
- {
- range: {
- '@timestamp': {
- gte: start,
- lte: end,
- format: 'epoch_millis',
- },
- },
- },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
},
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
index 22436cac40183d..92d1d96b4a8e3a 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
@@ -20,6 +20,7 @@ function getBucketSize(max: number) {
}
export async function getTransactionDistribution({
+ environment,
serviceName,
transactionName,
transactionType,
@@ -28,6 +29,7 @@ export async function getTransactionDistribution({
setup,
searchAggregatedTransactions,
}: {
+ environment?: string;
serviceName: string;
transactionName: string;
transactionType: string;
@@ -38,6 +40,7 @@ export async function getTransactionDistribution({
}) {
return withApmSpan('get_transaction_latency_distribution', async () => {
const distributionMax = await getDistributionMax({
+ environment,
serviceName,
transactionName,
transactionType,
@@ -52,6 +55,7 @@ export async function getTransactionDistribution({
const bucketSize = getBucketSize(distributionMax);
const { buckets, noHits } = await getBuckets({
+ environment,
serviceName,
transactionName,
transactionType,
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts
index 002ddd1ec35f04..d566f3a169e78a 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts
@@ -7,6 +7,7 @@
import { ESSearchResponse } from '../../../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../../../observability/typings/common';
+import { rangeQuery } from '../../../../common/utils/queries';
import { withApmSpan } from '../../../utils/with_apm_span';
import { Setup } from '../../helpers/setup_request';
@@ -40,15 +41,7 @@ export function anomalySeriesFetcher({
{ terms: { result_type: ['model_plot', 'record'] } },
{ term: { partition_field_value: serviceName } },
{ term: { by_field_value: transactionType } },
- {
- range: {
- timestamp: {
- gte: start,
- lte: end,
- format: 'epoch_millis',
- },
- },
- },
+ ...rangeQuery(start, end, 'timestamp'),
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/index.ts
index 29dd562330cc15..a03b1ac82e90a6 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/index.ts
@@ -18,20 +18,21 @@ import { ANOMALY_THRESHOLD } from '../../../../../ml/common';
import { withApmSpan } from '../../../utils/with_apm_span';
export async function getAnomalySeries({
+ environment,
serviceName,
transactionType,
transactionName,
setup,
logger,
}: {
+ environment?: string;
serviceName: string;
transactionType: string;
transactionName?: string;
setup: Setup & SetupTimeRange;
logger: Logger;
}) {
- const { uiFilters, start, end, ml } = setup;
- const { environment } = uiFilters;
+ const { start, end, ml } = setup;
// don't fetch anomalies if the ML plugin is not setup
if (!ml) {
@@ -45,18 +46,17 @@ export async function getAnomalySeries({
}
// don't fetch anomalies when no specific environment is selected
- if (environment === ENVIRONMENT_ALL.value) {
+ if (!environment || environment === ENVIRONMENT_ALL.value) {
return undefined;
}
- // don't fetch anomalies if unknown uiFilters are applied
- const knownFilters = ['environment', 'serviceName'];
- const hasUnknownFiltersApplied = Object.entries(setup.uiFilters)
- .filter(([key, value]) => !!value)
- .map(([key]) => key)
- .some((uiFilterName) => !knownFilters.includes(uiFilterName));
+ // Don't fetch anomalies if uiFilters are applied. This filters out anything
+ // with empty values so `kuery: ''` returns false but `kuery: 'x:y'` returns true.
+ const hasUiFiltersApplied =
+ Object.entries(setup.uiFilters).filter(([_key, value]) => !!value).length >
+ 0;
- if (hasUnknownFiltersApplied) {
+ if (hasUiFiltersApplied) {
return undefined;
}
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts
index ee27d00fdc0d4d..e1d3921d298c77 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts
@@ -13,7 +13,7 @@ import {
TRANSACTION_TYPE,
} from '../../../../common/elasticsearch_fieldnames';
import { LatencyAggregationType } from '../../../../common/latency_aggregation_types';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
@@ -31,6 +31,7 @@ export type LatencyChartsSearchResponse = PromiseReturnType<
>;
function searchLatency({
+ environment,
serviceName,
transactionType,
transactionName,
@@ -38,6 +39,7 @@ function searchLatency({
searchAggregatedTransactions,
latencyAggregationType,
}: {
+ environment?: string;
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
@@ -45,16 +47,17 @@ function searchLatency({
searchAggregatedTransactions: boolean;
latencyAggregationType: LatencyAggregationType;
}) {
- const { start, end, apmEventClient } = setup;
+ const { esFilter, start, end, apmEventClient } = setup;
const { intervalString } = getBucketSize({ start, end });
const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
- ...setup.esFilter,
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
+ ...esFilter,
];
if (transactionName) {
@@ -102,6 +105,7 @@ function searchLatency({
}
export function getLatencyTimeseries({
+ environment,
serviceName,
transactionType,
transactionName,
@@ -109,6 +113,7 @@ export function getLatencyTimeseries({
searchAggregatedTransactions,
latencyAggregationType,
}: {
+ environment?: string;
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
@@ -118,6 +123,7 @@ export function getLatencyTimeseries({
}) {
return withApmSpan('get_latency_charts', async () => {
const response = await searchLatency({
+ environment,
serviceName,
transactionType,
transactionName,
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts
index 7c1fa9c3a2368e..ec5dbf0eab3e9e 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts
@@ -13,7 +13,7 @@ import {
TRANSACTION_RESULT,
TRANSACTION_TYPE,
} from '../../../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../../../common/utils/queries';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
@@ -28,6 +28,7 @@ export type ThroughputChartsResponse = PromiseReturnType<
>;
function searchThroughput({
+ environment,
serviceName,
transactionType,
transactionName,
@@ -35,6 +36,7 @@ function searchThroughput({
searchAggregatedTransactions,
intervalString,
}: {
+ environment?: string;
serviceName: string;
transactionType: string;
transactionName: string | undefined;
@@ -42,16 +44,17 @@ function searchThroughput({
searchAggregatedTransactions: boolean;
intervalString: string;
}) {
- const { start, end, apmEventClient } = setup;
+ const { esFilter, start, end, apmEventClient } = setup;
const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
+ { term: { [TRANSACTION_TYPE]: transactionType } },
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
- { term: { [TRANSACTION_TYPE]: transactionType } },
- ...setup.esFilter,
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
+ ...esFilter,
];
if (transactionName) {
@@ -91,12 +94,14 @@ function searchThroughput({
}
export async function getThroughputCharts({
+ environment,
serviceName,
transactionType,
transactionName,
setup,
searchAggregatedTransactions,
}: {
+ environment?: string;
serviceName: string;
transactionType: string;
transactionName: string | undefined;
@@ -107,6 +112,7 @@ export async function getThroughputCharts({
const { bucketSize, intervalString } = getBucketSize(setup);
const response = await searchThroughput({
+ environment,
serviceName,
transactionType,
transactionName,
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
index 2fdb8e25fd9962..38d6b593dc72dc 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
@@ -9,7 +9,7 @@ import {
TRACE_ID,
TRANSACTION_ID,
} from '../../../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../../../common/utils/range_filter';
+import { rangeQuery } from '../../../../common/utils/queries';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { ProcessorEvent } from '../../../../common/processor_event';
import { withApmSpan } from '../../../utils/with_apm_span';
@@ -37,7 +37,7 @@ export function getTransaction({
filter: [
{ term: { [TRANSACTION_ID]: transactionId } },
{ term: { [TRACE_ID]: traceId } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
],
},
},
diff --git a/x-pack/plugins/apm/server/projections/errors.ts b/x-pack/plugins/apm/server/projections/errors.ts
index 082fd53a0ca93d..342d78608efbf6 100644
--- a/x-pack/plugins/apm/server/projections/errors.ts
+++ b/x-pack/plugins/apm/server/projections/errors.ts
@@ -10,13 +10,15 @@ import {
SERVICE_NAME,
ERROR_GROUP_ID,
} from '../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../common/utils/queries';
import { ProcessorEvent } from '../../common/processor_event';
export function getErrorGroupsProjection({
+ environment,
setup,
serviceName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
}) {
@@ -31,7 +33,8 @@ export function getErrorGroupsProjection({
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
],
},
diff --git a/x-pack/plugins/apm/server/projections/metrics.ts b/x-pack/plugins/apm/server/projections/metrics.ts
index f6c3f85ed48075..a32c2ae46c870b 100644
--- a/x-pack/plugins/apm/server/projections/metrics.ts
+++ b/x-pack/plugins/apm/server/projections/metrics.ts
@@ -10,7 +10,7 @@ import {
SERVICE_NAME,
SERVICE_NODE_NAME,
} from '../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../common/utils/queries';
import { SERVICE_NODE_NAME_MISSING } from '../../common/service_nodes';
import { ProcessorEvent } from '../../common/processor_event';
@@ -27,10 +27,12 @@ function getServiceNodeNameFilters(serviceNodeName?: string) {
}
export function getMetricsProjection({
+ environment,
setup,
serviceName,
serviceNodeName,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
@@ -39,8 +41,9 @@ export function getMetricsProjection({
const filter = [
{ term: { [SERVICE_NAME]: serviceName } },
- { range: rangeFilter(start, end) },
...getServiceNodeNameFilters(serviceNodeName),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
...esFilter,
];
diff --git a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
index ff8d868bc4abe5..1d5f7316b69ad6 100644
--- a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
+++ b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
@@ -11,7 +11,7 @@ import {
TRANSACTION_TYPE,
SERVICE_LANGUAGE_NAME,
} from '../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../common/utils/range_filter';
+import { rangeQuery } from '../../common/utils/queries';
import { ProcessorEvent } from '../../common/processor_event';
import { TRANSACTION_PAGE_LOAD } from '../../common/transaction_types';
@@ -28,7 +28,7 @@ export function getRumPageLoadTransactionsProjection({
const bool = {
filter: [
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
{ term: { [TRANSACTION_TYPE]: TRANSACTION_PAGE_LOAD } },
...(checkFetchStartFieldExists
? [
@@ -79,7 +79,7 @@ export function getRumErrorsProjection({
const bool = {
filter: [
- { range: rangeFilter(start, end) },
+ ...rangeQuery(start, end),
{ term: { [AGENT_NAME]: 'rum-js' } },
{
term: {
diff --git a/x-pack/plugins/apm/server/projections/services.ts b/x-pack/plugins/apm/server/projections/services.ts
index 33ffb45a006378..a9f5a7efd0e67e 100644
--- a/x-pack/plugins/apm/server/projections/services.ts
+++ b/x-pack/plugins/apm/server/projections/services.ts
@@ -7,7 +7,7 @@
import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import { SERVICE_NAME } from '../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../common/utils/range_filter';
+import { rangeQuery } from '../../common/utils/queries';
import { ProcessorEvent } from '../../common/processor_event';
import { getProcessorEventForAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
@@ -34,7 +34,7 @@ export function getServicesProjection({
size: 0,
query: {
bool: {
- filter: [{ range: rangeFilter(start, end) }, ...esFilter],
+ filter: [...rangeQuery(start, end), ...esFilter],
},
},
aggs: {
diff --git a/x-pack/plugins/apm/server/projections/transactions.ts b/x-pack/plugins/apm/server/projections/transactions.ts
index 76f2fc164e3fd2..45ed5d2865a67e 100644
--- a/x-pack/plugins/apm/server/projections/transactions.ts
+++ b/x-pack/plugins/apm/server/projections/transactions.ts
@@ -11,19 +11,21 @@ import {
TRANSACTION_TYPE,
TRANSACTION_NAME,
} from '../../common/elasticsearch_fieldnames';
-import { rangeFilter } from '../../common/utils/range_filter';
+import { environmentQuery, rangeQuery } from '../../common/utils/queries';
import {
getProcessorEventForAggregatedTransactions,
getDocumentTypeFilterForAggregatedTransactions,
} from '../lib/helpers/aggregated_transactions';
export function getTransactionsProjection({
+ environment,
setup,
serviceName,
transactionName,
transactionType,
searchAggregatedTransactions,
}: {
+ environment?: string;
setup: Setup & SetupTimeRange;
serviceName?: string;
transactionName?: string;
@@ -44,14 +46,15 @@ export function getTransactionsProjection({
const bool = {
filter: [
- { range: rangeFilter(start, end) },
+ ...serviceNameFilter,
...transactionNameFilter,
...transactionTypeFilter,
- ...serviceNameFilter,
- ...esFilter,
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
+ ...rangeQuery(start, end),
+ ...environmentQuery(environment),
+ ...esFilter,
],
};
diff --git a/x-pack/plugins/apm/server/routes/correlations.ts b/x-pack/plugins/apm/server/routes/correlations.ts
index 3c2ff00153ce79..d4a0db3c0d6c7b 100644
--- a/x-pack/plugins/apm/server/routes/correlations.ts
+++ b/x-pack/plugins/apm/server/routes/correlations.ts
@@ -13,7 +13,7 @@ import { getCorrelationsForFailedTransactions } from '../lib/correlations/get_co
import { getCorrelationsForSlowTransactions } from '../lib/correlations/get_correlations_for_slow_transactions';
import { setupRequest } from '../lib/helpers/setup_request';
import { createRoute } from './create_route';
-import { rangeRt } from './default_api_types';
+import { environmentRt, rangeRt } from './default_api_types';
const INVALID_LICENSE = i18n.translate(
'xpack.apm.significanTerms.license.text',
@@ -37,6 +37,7 @@ export const correlationsForSlowTransactionsRoute = createRoute({
fieldNames: t.string,
}),
t.partial({ uiFilters: t.string }),
+ environmentRt,
rangeRt,
]),
}),
@@ -47,6 +48,7 @@ export const correlationsForSlowTransactionsRoute = createRoute({
}
const setup = await setupRequest(context, request);
const {
+ environment,
serviceName,
transactionType,
transactionName,
@@ -55,6 +57,7 @@ export const correlationsForSlowTransactionsRoute = createRoute({
} = context.params.query;
return getCorrelationsForSlowTransactions({
+ environment,
serviceName,
transactionType,
transactionName,
@@ -78,6 +81,7 @@ export const correlationsForFailedTransactionsRoute = createRoute({
fieldNames: t.string,
}),
t.partial({ uiFilters: t.string }),
+ environmentRt,
rangeRt,
]),
}),
@@ -88,14 +92,15 @@ export const correlationsForFailedTransactionsRoute = createRoute({
}
const setup = await setupRequest(context, request);
const {
+ environment,
serviceName,
transactionType,
transactionName,
-
fieldNames,
} = context.params.query;
return getCorrelationsForFailedTransactions({
+ environment,
serviceName,
transactionType,
transactionName,
diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts
index fc5d6a3dd0bcdb..822a45fca269fd 100644
--- a/x-pack/plugins/apm/server/routes/create_apm_api.ts
+++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts
@@ -63,8 +63,8 @@ import {
transactionChartsErrorRateRoute,
transactionGroupsRoute,
transactionGroupsPrimaryStatisticsRoute,
- transactionLatencyChatsRoute,
- transactionThroughputChatsRoute,
+ transactionLatencyChartsRoute,
+ transactionThroughputChartsRoute,
transactionGroupsComparisonStatisticsRoute,
} from './transactions';
import { serviceMapRoute, serviceMapServiceNodeRoute } from './service_map';
@@ -167,8 +167,8 @@ const createApmApi = () => {
.add(transactionChartsErrorRateRoute)
.add(transactionGroupsRoute)
.add(transactionGroupsPrimaryStatisticsRoute)
- .add(transactionLatencyChatsRoute)
- .add(transactionThroughputChatsRoute)
+ .add(transactionLatencyChartsRoute)
+ .add(transactionThroughputChartsRoute)
.add(transactionGroupsComparisonStatisticsRoute)
// Service map
diff --git a/x-pack/plugins/apm/server/routes/default_api_types.ts b/x-pack/plugins/apm/server/routes/default_api_types.ts
index fdc1e8ebe5a55f..990b462a520d23 100644
--- a/x-pack/plugins/apm/server/routes/default_api_types.ts
+++ b/x-pack/plugins/apm/server/routes/default_api_types.ts
@@ -18,4 +18,6 @@ export const comparisonRangeRt = t.partial({
comparisonEnd: isoToEpochRt,
});
+export const environmentRt = t.partial({ environment: t.string });
+
export const uiFiltersRt = t.type({ uiFilters: t.string });
diff --git a/x-pack/plugins/apm/server/routes/errors.ts b/x-pack/plugins/apm/server/routes/errors.ts
index cc9db2e6a48551..073a91bfe15487 100644
--- a/x-pack/plugins/apm/server/routes/errors.ts
+++ b/x-pack/plugins/apm/server/routes/errors.ts
@@ -11,7 +11,7 @@ import { getErrorDistribution } from '../lib/errors/distribution/get_distributio
import { getErrorGroupSample } from '../lib/errors/get_error_group_sample';
import { getErrorGroups } from '../lib/errors/get_error_groups';
import { setupRequest } from '../lib/helpers/setup_request';
-import { uiFiltersRt, rangeRt } from './default_api_types';
+import { environmentRt, uiFiltersRt, rangeRt } from './default_api_types';
export const errorsRoute = createRoute({
endpoint: 'GET /api/apm/services/{serviceName}/errors',
@@ -24,6 +24,7 @@ export const errorsRoute = createRoute({
sortField: t.string,
sortDirection: t.union([t.literal('asc'), t.literal('desc')]),
}),
+ environmentRt,
uiFiltersRt,
rangeRt,
]),
@@ -33,9 +34,10 @@ export const errorsRoute = createRoute({
const setup = await setupRequest(context, request);
const { params } = context;
const { serviceName } = params.path;
- const { sortField, sortDirection } = params.query;
+ const { environment, sortField, sortDirection } = params.query;
return getErrorGroups({
+ environment,
serviceName,
sortField,
sortDirection,
@@ -51,13 +53,15 @@ export const errorGroupsRoute = createRoute({
serviceName: t.string,
groupId: t.string,
}),
- query: t.intersection([uiFiltersRt, rangeRt]),
+ query: t.intersection([environmentRt, uiFiltersRt, rangeRt]),
}),
options: { tags: ['access:apm'] },
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
const { serviceName, groupId } = context.params.path;
- return getErrorGroupSample({ serviceName, groupId, setup });
+ const { environment } = context.params.query;
+
+ return getErrorGroupSample({ environment, serviceName, groupId, setup });
},
});
@@ -71,6 +75,7 @@ export const errorDistributionRoute = createRoute({
t.partial({
groupId: t.string,
}),
+ environmentRt,
uiFiltersRt,
rangeRt,
]),
@@ -80,7 +85,7 @@ export const errorDistributionRoute = createRoute({
const setup = await setupRequest(context, request);
const { params } = context;
const { serviceName } = params.path;
- const { groupId } = params.query;
- return getErrorDistribution({ serviceName, groupId, setup });
+ const { environment, groupId } = params.query;
+ return getErrorDistribution({ environment, serviceName, groupId, setup });
},
});
diff --git a/x-pack/plugins/apm/server/routes/metrics.ts b/x-pack/plugins/apm/server/routes/metrics.ts
index d07504a1046ee5..08376ed0e37ffb 100644
--- a/x-pack/plugins/apm/server/routes/metrics.ts
+++ b/x-pack/plugins/apm/server/routes/metrics.ts
@@ -9,7 +9,7 @@ import * as t from 'io-ts';
import { setupRequest } from '../lib/helpers/setup_request';
import { getMetricsChartDataByAgent } from '../lib/metrics/get_metrics_chart_data_by_agent';
import { createRoute } from './create_route';
-import { uiFiltersRt, rangeRt } from './default_api_types';
+import { environmentRt, uiFiltersRt, rangeRt } from './default_api_types';
export const metricsChartsRoute = createRoute({
endpoint: `GET /api/apm/services/{serviceName}/metrics/charts`,
@@ -24,6 +24,7 @@ export const metricsChartsRoute = createRoute({
t.partial({
serviceNodeName: t.string,
}),
+ environmentRt,
uiFiltersRt,
rangeRt,
]),
@@ -33,8 +34,9 @@ export const metricsChartsRoute = createRoute({
const setup = await setupRequest(context, request);
const { params } = context;
const { serviceName } = params.path;
- const { agentName, serviceNodeName } = params.query;
+ const { agentName, environment, serviceNodeName } = params.query;
return await getMetricsChartDataByAgent({
+ environment,
setup,
serviceName,
agentName,
diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts
index 7cca6cd0a19430..65c7b245958f32 100644
--- a/x-pack/plugins/apm/server/routes/service_map.ts
+++ b/x-pack/plugins/apm/server/routes/service_map.ts
@@ -12,7 +12,7 @@ import { setupRequest } from '../lib/helpers/setup_request';
import { getServiceMap } from '../lib/service_map/get_service_map';
import { getServiceMapServiceNodeInfo } from '../lib/service_map/get_service_map_service_node_info';
import { createRoute } from './create_route';
-import { rangeRt, uiFiltersRt } from './default_api_types';
+import { environmentRt, rangeRt, uiFiltersRt } from './default_api_types';
import { notifyFeatureUsage } from '../feature';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
import { isActivePlatinumLicense } from '../../common/license_check';
@@ -22,9 +22,9 @@ export const serviceMapRoute = createRoute({
params: t.type({
query: t.intersection([
t.partial({
- environment: t.string,
serviceName: t.string,
}),
+ environmentRt,
rangeRt,
]),
}),
diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts
index 84ccb4b06c2e6a..e59b438305b349 100644
--- a/x-pack/plugins/apm/server/routes/services.ts
+++ b/x-pack/plugins/apm/server/routes/services.ts
@@ -25,23 +25,29 @@ import { getServiceTransactionTypes } from '../lib/services/get_service_transact
import { getThroughput } from '../lib/services/get_throughput';
import { offsetPreviousPeriodCoordinates } from '../utils/offset_previous_period_coordinate';
import { createRoute } from './create_route';
-import { comparisonRangeRt, rangeRt, uiFiltersRt } from './default_api_types';
+import {
+ comparisonRangeRt,
+ environmentRt,
+ rangeRt,
+ uiFiltersRt,
+} from './default_api_types';
import { withApmSpan } from '../utils/with_apm_span';
export const servicesRoute = createRoute({
endpoint: 'GET /api/apm/services',
params: t.type({
- query: t.intersection([uiFiltersRt, rangeRt]),
+ query: t.intersection([environmentRt, uiFiltersRt, rangeRt]),
}),
options: { tags: ['access:apm'] },
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
-
+ const { environment } = context.params.query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
const services = await getServices({
+ environment,
setup,
searchAggregatedTransactions,
logger: context.logger,
@@ -273,6 +279,7 @@ export const serviceErrorGroupsRoute = createRoute({
serviceName: t.string,
}),
query: t.intersection([
+ environmentRt,
rangeRt,
uiFiltersRt,
t.type({
@@ -296,6 +303,7 @@ export const serviceErrorGroupsRoute = createRoute({
const {
path: { serviceName },
query: {
+ environment,
numBuckets,
pageIndex,
size,
@@ -306,6 +314,7 @@ export const serviceErrorGroupsRoute = createRoute({
} = context.params;
return getServiceErrorGroups({
+ environment,
serviceName,
setup,
size,
@@ -326,6 +335,7 @@ export const serviceThroughputRoute = createRoute({
}),
query: t.intersection([
t.type({ transactionType: t.string }),
+ environmentRt,
uiFiltersRt,
rangeRt,
comparisonRangeRt,
@@ -336,6 +346,7 @@ export const serviceThroughputRoute = createRoute({
const setup = await setupRequest(context, request);
const { serviceName } = context.params.path;
const {
+ environment,
transactionType,
comparisonStart,
comparisonEnd,
@@ -356,12 +367,14 @@ export const serviceThroughputRoute = createRoute({
const [currentPeriod, previousPeriod] = await Promise.all([
getThroughput({
...commonProps,
+ environment,
start,
end,
}),
comparisonStart && comparisonEnd
? getThroughput({
...commonProps,
+ environment,
start: comparisonStart,
end: comparisonEnd,
}).then((coordinates) =>
@@ -389,6 +402,7 @@ export const serviceInstancesRoute = createRoute({
}),
query: t.intersection([
t.type({ transactionType: t.string, numBuckets: toNumberRt }),
+ environmentRt,
uiFiltersRt,
rangeRt,
]),
@@ -397,13 +411,14 @@ export const serviceInstancesRoute = createRoute({
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
const { serviceName } = context.params.path;
- const { transactionType, numBuckets } = context.params.query;
+ const { environment, transactionType, numBuckets } = context.params.query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
return getServiceInstances({
+ environment,
serviceName,
setup,
transactionType,
@@ -421,9 +436,9 @@ export const serviceDependenciesRoute = createRoute({
}),
query: t.intersection([
t.type({
- environment: t.string,
numBuckets: toNumberRt,
}),
+ environmentRt,
rangeRt,
]),
}),
diff --git a/x-pack/plugins/apm/server/routes/traces.ts b/x-pack/plugins/apm/server/routes/traces.ts
index 722675906487ce..5d3f99be7af344 100644
--- a/x-pack/plugins/apm/server/routes/traces.ts
+++ b/x-pack/plugins/apm/server/routes/traces.ts
@@ -10,23 +10,25 @@ import { setupRequest } from '../lib/helpers/setup_request';
import { getTrace } from '../lib/traces/get_trace';
import { getTransactionGroupList } from '../lib/transaction_groups';
import { createRoute } from './create_route';
-import { rangeRt, uiFiltersRt } from './default_api_types';
+import { environmentRt, rangeRt, uiFiltersRt } from './default_api_types';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
import { getRootTransactionByTraceId } from '../lib/transactions/get_transaction_by_trace';
export const tracesRoute = createRoute({
endpoint: 'GET /api/apm/traces',
params: t.type({
- query: t.intersection([rangeRt, uiFiltersRt]),
+ query: t.intersection([environmentRt, rangeRt, uiFiltersRt]),
}),
options: { tags: ['access:apm'] },
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
+ const { environment } = context.params.query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
+
return getTransactionGroupList(
- { type: 'top_traces', searchAggregatedTransactions },
+ { environment, type: 'top_traces', searchAggregatedTransactions },
setup
);
},
diff --git a/x-pack/plugins/apm/server/routes/transactions.ts b/x-pack/plugins/apm/server/routes/transactions.ts
index bef96cb7f0767e..5a4be216a817c2 100644
--- a/x-pack/plugins/apm/server/routes/transactions.ts
+++ b/x-pack/plugins/apm/server/routes/transactions.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import Boom from '@hapi/boom';
import * as t from 'io-ts';
import {
LatencyAggregationType,
@@ -25,7 +24,7 @@ import { getThroughputCharts } from '../lib/transactions/get_throughput_charts';
import { getTransactionGroupList } from '../lib/transaction_groups';
import { getErrorRate } from '../lib/transaction_groups/get_error_rate';
import { createRoute } from './create_route';
-import { rangeRt, uiFiltersRt } from './default_api_types';
+import { environmentRt, rangeRt, uiFiltersRt } from './default_api_types';
/**
* Returns a list of transactions grouped by name
@@ -39,6 +38,7 @@ export const transactionGroupsRoute = createRoute({
}),
query: t.intersection([
t.type({ transactionType: t.string }),
+ environmentRt,
uiFiltersRt,
rangeRt,
]),
@@ -47,7 +47,7 @@ export const transactionGroupsRoute = createRoute({
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
const { serviceName } = context.params.path;
- const { transactionType } = context.params.query;
+ const { environment, transactionType } = context.params.query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
@@ -55,6 +55,7 @@ export const transactionGroupsRoute = createRoute({
return getTransactionGroupList(
{
+ environment,
type: 'top_transactions',
serviceName,
transactionType,
@@ -71,6 +72,7 @@ export const transactionGroupsPrimaryStatisticsRoute = createRoute({
params: t.type({
path: t.type({ serviceName: t.string }),
query: t.intersection([
+ environmentRt,
rangeRt,
uiFiltersRt,
t.type({
@@ -91,10 +93,11 @@ export const transactionGroupsPrimaryStatisticsRoute = createRoute({
const {
path: { serviceName },
- query: { latencyAggregationType, transactionType },
+ query: { environment, latencyAggregationType, transactionType },
} = context.params;
return getServiceTransactionGroups({
+ environment,
setup,
serviceName,
searchAggregatedTransactions,
@@ -110,6 +113,7 @@ export const transactionGroupsComparisonStatisticsRoute = createRoute({
params: t.type({
path: t.type({ serviceName: t.string }),
query: t.intersection([
+ environmentRt,
rangeRt,
uiFiltersRt,
t.type({
@@ -133,6 +137,7 @@ export const transactionGroupsComparisonStatisticsRoute = createRoute({
const {
path: { serviceName },
query: {
+ environment,
transactionNames,
latencyAggregationType,
numBuckets,
@@ -141,6 +146,7 @@ export const transactionGroupsComparisonStatisticsRoute = createRoute({
} = context.params;
return getServiceTransactionGroupComparisonStatistics({
+ environment,
setup,
serviceName,
transactionNames,
@@ -152,7 +158,7 @@ export const transactionGroupsComparisonStatisticsRoute = createRoute({
},
});
-export const transactionLatencyChatsRoute = createRoute({
+export const transactionLatencyChartsRoute = createRoute({
endpoint: 'GET /api/apm/services/{serviceName}/transactions/charts/latency',
params: t.type({
path: t.type({
@@ -166,6 +172,7 @@ export const transactionLatencyChatsRoute = createRoute({
transactionType: t.string,
latencyAggregationType: latencyAggregationTypeRt,
}),
+ environmentRt,
uiFiltersRt,
rangeRt,
]),
@@ -176,22 +183,18 @@ export const transactionLatencyChatsRoute = createRoute({
const logger = context.logger;
const { serviceName } = context.params.path;
const {
+ environment,
transactionType,
transactionName,
latencyAggregationType,
} = context.params.query;
- if (!setup.uiFilters.environment) {
- throw Boom.badRequest(
- `environment is a required property of the ?uiFilters JSON for transaction_groups/charts.`
- );
- }
-
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
const options = {
+ environment,
serviceName,
transactionType,
transactionName,
@@ -222,7 +225,7 @@ export const transactionLatencyChatsRoute = createRoute({
},
});
-export const transactionThroughputChatsRoute = createRoute({
+export const transactionThroughputChartsRoute = createRoute({
endpoint:
'GET /api/apm/services/{serviceName}/transactions/charts/throughput',
params: t.type({
@@ -234,25 +237,25 @@ export const transactionThroughputChatsRoute = createRoute({
t.partial({ transactionName: t.string }),
uiFiltersRt,
rangeRt,
+ environmentRt,
]),
}),
options: { tags: ['access:apm'] },
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
const { serviceName } = context.params.path;
- const { transactionType, transactionName } = context.params.query;
-
- if (!setup.uiFilters.environment) {
- throw Boom.badRequest(
- `environment is a required property of the ?uiFilters JSON for transaction_groups/charts.`
- );
- }
+ const {
+ environment,
+ transactionType,
+ transactionName,
+ } = context.params.query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
return await getThroughputCharts({
+ environment,
serviceName,
transactionType,
transactionName,
@@ -278,6 +281,7 @@ export const transactionChartsDistributionRoute = createRoute({
transactionId: t.string,
traceId: t.string,
}),
+ environmentRt,
uiFiltersRt,
rangeRt,
]),
@@ -287,6 +291,7 @@ export const transactionChartsDistributionRoute = createRoute({
const setup = await setupRequest(context, request);
const { serviceName } = context.params.path;
const {
+ environment,
transactionType,
transactionName,
transactionId = '',
@@ -298,6 +303,7 @@ export const transactionChartsDistributionRoute = createRoute({
);
return getTransactionDistribution({
+ environment,
serviceName,
transactionType,
transactionName,
@@ -318,6 +324,7 @@ export const transactionChartsBreakdownRoute = createRoute({
query: t.intersection([
t.type({ transactionType: t.string }),
t.partial({ transactionName: t.string }),
+ environmentRt,
uiFiltersRt,
rangeRt,
]),
@@ -326,9 +333,14 @@ export const transactionChartsBreakdownRoute = createRoute({
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
const { serviceName } = context.params.path;
- const { transactionName, transactionType } = context.params.query;
+ const {
+ environment,
+ transactionName,
+ transactionType,
+ } = context.params.query;
return getTransactionBreakdown({
+ environment,
serviceName,
transactionName,
transactionType,
@@ -345,6 +357,7 @@ export const transactionChartsErrorRateRoute = createRoute({
serviceName: t.string,
}),
query: t.intersection([
+ environmentRt,
uiFiltersRt,
rangeRt,
t.type({ transactionType: t.string }),
@@ -356,13 +369,14 @@ export const transactionChartsErrorRateRoute = createRoute({
const setup = await setupRequest(context, request);
const { params } = context;
const { serviceName } = params.path;
- const { transactionType, transactionName } = params.query;
+ const { environment, transactionType, transactionName } = params.query;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
return getErrorRate({
+ environment,
serviceName,
transactionType,
transactionName,
diff --git a/x-pack/plugins/apm/server/utils/test_helpers.tsx b/x-pack/plugins/apm/server/utils/test_helpers.tsx
index 19ac562121d9dc..4df638cc2c5df2 100644
--- a/x-pack/plugins/apm/server/utils/test_helpers.tsx
+++ b/x-pack/plugins/apm/server/utils/test_helpers.tsx
@@ -88,7 +88,7 @@ export async function inspectSearchParams(
},
}
) as APMConfig,
- uiFilters: { environment: 'test' },
+ uiFilters: {},
esFilter: [{ term: { 'service.environment': 'test' } }],
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
diff --git a/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts b/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts
index 1d00d54a3997bf..4474d0996175bb 100644
--- a/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts
+++ b/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts
@@ -31,7 +31,7 @@ export default function rumHasDataApiTests({ getService }: FtrProviderContext) {
'has RUM data with data',
{ config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] },
() => {
- it('returns that it has data and service name with most traffice', async () => {
+ it('returns that it has data and service name with most traffic', async () => {
const response = await supertest.get(
'/api/apm/observability_overview/has_rum_data?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters='
);
diff --git a/x-pack/test/apm_api_integration/tests/feature_controls.ts b/x-pack/test/apm_api_integration/tests/feature_controls.ts
index db2b42323c4f64..45114dd506716d 100644
--- a/x-pack/test/apm_api_integration/tests/feature_controls.ts
+++ b/x-pack/test/apm_api_integration/tests/feature_controls.ts
@@ -109,28 +109,28 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
},
{
req: {
- url: `/api/apm/services/foo/transactions/charts/latency?start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&uiFilters=%7B%22environment%22%3A%22testing%22%7D`,
+ url: `/api/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&uiFilters=%7B%7D`,
},
expectForbidden: expect403,
expectResponse: expect200,
},
{
req: {
- url: `/api/apm/services/foo/transactions/charts/latency?start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&transactionName=baz&uiFilters=%7B%22environment%22%3A%22testing%22%7D`,
+ url: `/api/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&transactionName=baz&uiFilters=%7B%7D`,
},
expectForbidden: expect403,
expectResponse: expect200,
},
{
req: {
- url: `/api/apm/services/foo/transactions/charts/throughput?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%22environment%22%3A%22testing%22%7D`,
+ url: `/api/apm/services/foo/transactions/charts/throughput?environment=testing&start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`,
},
expectForbidden: expect403,
expectResponse: expect200,
},
{
req: {
- url: `/api/apm/services/foo/transactions/charts/throughput?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%22environment%22%3A%22testing%22%7D`,
+ url: `/api/apm/services/foo/transactions/charts/throughput?environment=testing&start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`,
},
expectForbidden: expect403,
expectResponse: expect200,
diff --git a/x-pack/test/apm_api_integration/tests/services/top_services.ts b/x-pack/test/apm_api_integration/tests/services/top_services.ts
index efd0b410cf8aaa..3afaec653fcb35 100644
--- a/x-pack/test/apm_api_integration/tests/services/top_services.ts
+++ b/x-pack/test/apm_api_integration/tests/services/top_services.ts
@@ -346,8 +346,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
let response: PromiseReturnType;
before(async () => {
response = await supertest.get(
- `/api/apm/services?start=${start}&end=${end}&uiFilters=${encodeURIComponent(
- `{"kuery":"service.name:opbeans-java","environment":"ENVIRONMENT_ALL"}`
+ `/api/apm/services?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&uiFilters=${encodeURIComponent(
+ `{"kuery":"service.name:opbeans-java"}`
)}`
);
});
diff --git a/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/latency.snap b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/latency.snap
index a384ca2c9364e8..11c557fd02e381 100644
--- a/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/latency.snap
+++ b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/latency.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded when not defined environments seleted should return the correct anomaly boundaries 1`] = `
+exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded when not defined environment is selected should return the correct anomaly boundaries 1`] = `
Array [
Object {
"x": 1607436000000,
@@ -30,7 +30,7 @@ Array [
]
`;
-exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded with environment selected in uiFilters should return a non-empty anomaly series 1`] = `
+exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded with environment selected should return a non-empty anomaly series 1`] = `
Array [
Object {
"x": 1607436000000,
diff --git a/x-pack/test/apm_api_integration/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/tests/transactions/latency.ts
index 3003c6f902f39e..523139717b309e 100644
--- a/x-pack/test/apm_api_integration/tests/transactions/latency.ts
+++ b/x-pack/test/apm_api_integration/tests/transactions/latency.ts
@@ -26,10 +26,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
'Latency with a basic license when data is not loaded ',
{ config: 'basic', archives: [] },
() => {
- const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'testing' }));
+ const uiFilters = encodeURIComponent(JSON.stringify({}));
it('returns 400 when latencyAggregationType is not informed', async () => {
const response = await supertest.get(
- `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request`
+ `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request`
);
expect(response.status).to.be(400);
@@ -37,7 +37,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('returns 400 when transactionType is not informed', async () => {
const response = await supertest.get(
- `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg`
+ `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg`
);
expect(response.status).to.be(400);
@@ -45,7 +45,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
it('handles the empty state', async () => {
const response = await supertest.get(
- `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg&transactionType=request`
+ `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg&transactionType=request`
);
expect(response.status).to.be(200);
@@ -62,12 +62,12 @@ export default function ApiTest({ getService }: FtrProviderContext) {
() => {
let response: PromiseReturnType;
- const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'testing' }));
+ const uiFilters = encodeURIComponent(JSON.stringify({}));
describe('average latency type', () => {
before(async () => {
response = await supertest.get(
- `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=avg`
+ `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=avg`
);
});
@@ -81,7 +81,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('95th percentile latency type', () => {
before(async () => {
response = await supertest.get(
- `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p95`
+ `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p95`
);
});
@@ -95,7 +95,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('99th percentile latency type', () => {
before(async () => {
response = await supertest.get(
- `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p99`
+ `/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p99`
);
});
@@ -116,15 +116,16 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const transactionType = 'request';
- describe('without environment', () => {
+ describe('without an environment', () => {
const uiFilters = encodeURIComponent(JSON.stringify({}));
before(async () => {
response = await supertest.get(
`/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
);
});
- it('should return an error response', () => {
- expect(response.status).to.eql(400);
+
+ it('returns an ok response', () => {
+ expect(response.status).to.eql(200);
});
});
@@ -139,11 +140,11 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
});
- describe('with environment selected in uiFilters', () => {
- const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'production' }));
+ describe('with environment selected', () => {
+ const uiFilters = encodeURIComponent(JSON.stringify({}));
before(async () => {
response = await supertest.get(
- `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
+ `/api/apm/services/opbeans-java/transactions/charts/latency?environment=production&start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
);
});
@@ -166,13 +167,11 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
});
- describe('when not defined environments seleted', () => {
- const uiFilters = encodeURIComponent(
- JSON.stringify({ environment: 'ENVIRONMENT_NOT_DEFINED' })
- );
+ describe('when not defined environment is selected', () => {
+ const uiFilters = encodeURIComponent(JSON.stringify({}));
before(async () => {
response = await supertest.get(
- `/api/apm/services/opbeans-python/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
+ `/api/apm/services/opbeans-python/transactions/charts/latency?environment=ENVIRONMENT_NOT_DEFINED&start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
);
});
@@ -195,10 +194,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
describe('with all environments selected', () => {
- const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'ENVIRONMENT_ALL' }));
+ const uiFilters = encodeURIComponent(JSON.stringify({}));
before(async () => {
response = await supertest.get(
- `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
+ `/api/apm/services/opbeans-java/transactions/charts/latency?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
);
});
@@ -212,12 +211,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
describe('with environment selected and empty kuery filter', () => {
- const uiFilters = encodeURIComponent(
- JSON.stringify({ kuery: '', environment: 'production' })
- );
+ const uiFilters = encodeURIComponent(JSON.stringify({ kuery: '' }));
before(async () => {
response = await supertest.get(
- `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
+ `/api/apm/services/opbeans-java/transactions/charts/latency?environment=production&start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg`
);
});
diff --git a/x-pack/test/apm_api_integration/tests/transactions/throughput.ts b/x-pack/test/apm_api_integration/tests/transactions/throughput.ts
index 040e280e3157f2..430392a32bfb82 100644
--- a/x-pack/test/apm_api_integration/tests/transactions/throughput.ts
+++ b/x-pack/test/apm_api_integration/tests/transactions/throughput.ts
@@ -20,7 +20,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
// url parameters
const { start, end } = metadata;
- const uiFilters = JSON.stringify({ environment: 'testing' });
+ const uiFilters = JSON.stringify({});
registry.when('Throughput when data is not loaded', { config: 'basic', archives: [] }, () => {
it('handles the empty state', async () => {
@@ -28,6 +28,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
url.format({
pathname: `/api/apm/services/opbeans-node/transactions/charts/throughput`,
query: {
+ environment: 'testing',
start,
end,
uiFilters,
@@ -53,6 +54,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
url.format({
pathname: `/api/apm/services/opbeans-node/transactions/charts/throughput`,
query: {
+ environment: 'testing',
start,
end,
uiFilters,
From 481c92296e9114ac0b3297016c3f7df902ccd862 Mon Sep 17 00:00:00 2001
From: Yuliia Naumenko
Date: Wed, 17 Feb 2021 12:53:33 -0800
Subject: [PATCH 33/79] [Alerts][Docs] Cleanup alerts README.md to remove
duplication from docs (#91074)
* Cleanup alerts README.md to remove duplication from docs
* fixed due to comments
* fixed due to comments
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
x-pack/plugins/alerts/README.md | 290 +++++---------------------------
1 file changed, 46 insertions(+), 244 deletions(-)
diff --git a/x-pack/plugins/alerts/README.md b/x-pack/plugins/alerts/README.md
index 2191b23eec11ee..aab848d4555d2d 100644
--- a/x-pack/plugins/alerts/README.md
+++ b/x-pack/plugins/alerts/README.md
@@ -20,26 +20,13 @@ Table of Contents
- [Example](#example)
- [Role Based Access-Control](#role-based-access-control)
- [Alert Navigation](#alert-navigation)
- - [RESTful API](#restful-api)
- - [`POST /api/alerts/alert`: Create alert](#post-apialert-create-alert)
- - [`DELETE /api/alerts/alert/{id}`: Delete alert](#delete-apialertid-delete-alert)
- - [`GET /api/alerts/_find`: Find alerts](#get-apialertfind-find-alerts)
- - [`GET /api/alerts/alert/{id}`: Get alert](#get-apialertid-get-alert)
+ - [Experimental RESTful API](#restful-api)
- [`GET /api/alerts/alert/{id}/state`: Get alert state](#get-apialertidstate-get-alert-state)
- [`GET /api/alerts/alert/{id}/_instance_summary`: Get alert instance summary](#get-apialertidstate-get-alert-instance-summary)
- - [`GET /api/alerts/list_alert_types`: List alert types](#get-apialerttypes-list-alert-types)
- - [`PUT /api/alerts/alert/{id}`: Update alert](#put-apialertid-update-alert)
- - [`POST /api/alerts/alert/{id}/_enable`: Enable an alert](#post-apialertidenable-enable-an-alert)
- - [`POST /api/alerts/alert/{id}/_disable`: Disable an alert](#post-apialertiddisable-disable-an-alert)
- - [`POST /api/alerts/alert/{id}/_mute_all`: Mute all alert instances](#post-apialertidmuteall-mute-all-alert-instances)
- - [`POST /api/alerts/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute`: Mute alert instance](#post-apialertalertidalertinstancealertinstanceidmute-mute-alert-instance)
- - [`POST /api/alerts/alert/{id}/_unmute_all`: Unmute all alert instances](#post-apialertidunmuteall-unmute-all-alert-instances)
- - [`POST /api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`: Unmute an alert instance](#post-apialertalertidalertinstancealertinstanceidunmute-unmute-an-alert-instance)
- [`POST /api/alerts/alert/{id}/_update_api_key`: Update alert API key](#post-apialertidupdateapikey-update-alert-api-key)
- - [Schedule Formats](#schedule-formats)
- [Alert instance factory](#alert-instance-factory)
- [Templating actions](#templating-actions)
- - [Examples](#examples)
+ - [Examples](#examples)
## Terminology
@@ -61,7 +48,7 @@ A Kibana alert detects a condition and executes one or more actions when that co
1. Develop and register an alert type (see alert types -> example).
2. Configure feature level privileges using RBAC
-3. Create an alert using the RESTful API (see alerts -> create).
+3. Create an alert using the RESTful API [Documentation](https://www.elastic.co/guide/en/kibana/master/alerts-api-update.html) (see alerts -> create).
## Limitations
@@ -96,6 +83,7 @@ The following table describes the properties of the `options` object.
|validate.params|When developing an alert type, you can choose to accept a series of parameters. You may also have the parameters validated before they are passed to the `executor` function or created as an alert saved object. In order to do this, provide a `@kbn/config-schema` schema that we will use to validate the `params` attribute.|@kbn/config-schema|
|executor|This is where the code of the alert type lives. This is a function to be called when executing an alert on an interval basis. For full details, see executor section below.|Function|
|producer|The id of the application producing this alert type.|string|
+|minimumLicenseRequired|The value of a minimum license. Most of the alerts are licensed as "basic".|string|
### Executor
@@ -142,13 +130,13 @@ This example receives server and threshold as parameters. It will read the CPU u
```typescript
import { schema } from '@kbn/config-schema';
+import { AlertType, AlertExecutorOptions } from '../../../alerts/server';
import {
- Alert,
- AlertTypeParams,
- AlertTypeState,
- AlertInstanceState,
- AlertInstanceContext
-} from 'x-pack/plugins/alerts/common';
+ AlertTypeParams,
+ AlertTypeState,
+ AlertInstanceState,
+ AlertInstanceContext,
+} from '../../../alerts/common';
...
interface MyAlertTypeParams extends AlertTypeParams {
server: string;
@@ -156,7 +144,7 @@ interface MyAlertTypeParams extends AlertTypeParams {
}
interface MyAlertTypeState extends AlertTypeState {
- lastChecked: number;
+ lastChecked: Date;
}
interface MyAlertTypeInstanceState extends AlertInstanceState {
@@ -257,83 +245,6 @@ const myAlertType: AlertType<
server.newPlatform.setup.plugins.alerts.registerType(myAlertType);
```
-This example only receives threshold as a parameter. It will read the CPU usage of all the servers and schedule individual actions if the reading for a server is greater than the threshold. This is a better implementation than above as only one query is performed for all the servers instead of one query per server.
-
-```typescript
-server.newPlatform.setup.plugins.alerts.registerType({
- id: 'my-alert-type',
- name: 'My alert type',
- validate: {
- params: schema.object({
- threshold: schema.number({ min: 0, max: 1 }),
- }),
- },
- actionGroups: [
- {
- id: 'default',
- name: 'Default',
- },
- ],
- defaultActionGroupId: 'default',
- minimumLicenseRequired: 'basic',
- actionVariables: {
- context: [
- { name: 'server', description: 'the server' },
- { name: 'hasCpuUsageIncreased', description: 'boolean indicating if the cpu usage has increased' },
- ],
- state: [
- { name: 'cpuUsage', description: 'CPU usage' },
- ],
- },
- async executor({
- alertId,
- startedAt,
- previousStartedAt,
- services,
- params,
- state,
- }: AlertExecutorOptions) {
- const { threshold } = params; // Let's assume params is { threshold: 0.8 }
-
- // Call a function to get the CPU readings on all the servers. The result will be
- // an array of { server, cpuUsage }.
- const cpuUsageByServer = await getCpuUsageByServer();
-
- for (const { server, cpuUsage: currentCpuUsage } of cpuUsageByServer) {
- // Only execute if CPU usage is greater than threshold
- if (currentCpuUsage > threshold) {
- // The first argument is a unique identifier the alert instance is about. In this scenario
- // the provided server will be used. Also, this id will be used to make `getState()` return
- // previous state, if any, on matching identifiers.
- const alertInstance = services.alertInstanceFactory(server);
-
- // State from last execution. This will exist if an alert instance was created and executed
- // in the previous execution
- const { cpuUsage: previousCpuUsage } = alertInstance.getState();
-
- // Replace state entirely with new values
- alertInstance.replaceState({
- cpuUsage: currentCpuUsage,
- });
-
- // 'default' refers to the id of a group of actions to be scheduled for execution, see 'actions' in create alert section
- alertInstance.scheduleActions('default', {
- server,
- hasCpuUsageIncreased: currentCpuUsage > previousCpuUsage,
- });
- }
- }
-
- // Single object containing state that isn't specific to a server, this will become available
- // within the `state` function parameter at the next execution
- return {
- lastChecked: new Date(),
- };
- },
- producer: 'alerting',
-});
-```
-
## Role Based Access-Control
Once you have registered your AlertType, you need to grant your users privileges to use it.
When registering a feature in Kibana you can specify multiple types of privileges which are granted to users when they're assigned certain roles.
@@ -387,29 +298,37 @@ It's important to note that any role can be granted a mix of `all` and `read` pr
```typescript
features.registerKibanaFeature({
- id: 'my-application-id',
- name: 'My Application',
- app: [],
- privileges: {
- all: {
- alerting: {
- all: [
- 'my-application-id.my-alert-type',
- 'my-application-id.my-restricted-alert-type'
- ],
- },
- },
- read: {
- alerting: {
- all: [
- 'my-application-id.my-alert-type'
- ]
- read: [
- 'my-application-id.my-restricted-alert-type'
- ],
- },
- },
- },
+ id: 'my-application-id',
+ name: 'My Application',
+ app: [],
+ privileges: {
+ all: {
+ app: ['my-application-id', 'kibana'],
+ savedObject: {
+ all: [],
+ read: [],
+ },
+ ui: [],
+ api: [],
+ },
+ read: {
+ app: ['lens', 'kibana'],
+ alerting: {
+ all: [
+ 'my-application-id.my-alert-type'
+ ],
+ read: [
+ 'my-application-id.my-restricted-alert-type'
+ ],
+ },
+ savedObject: {
+ all: [],
+ read: [],
+ },
+ ui: [],
+ api: [],
+ },
+ },
});
```
@@ -494,46 +413,10 @@ The only case in which this handler will not be used to evaluate the navigation
You can use the `registerNavigation` api to specify as many AlertType specific handlers as you like, but you can only use it once per AlertType as we wouldn't know which handler to use if you specified two for the same AlertType. For the same reason, you can only use `registerDefaultNavigation` once per plugin, as it covers all cases for your specific plugin.
-## RESTful API
-
-Using an alert type requires you to create an alert that will contain parameters and actions for a given alert type. See below for CRUD operations using the API.
+## Experimental RESTful API
-### `POST /api/alerts/alert`: Create alert
-
-Payload:
-
-|Property|Description|Type|
-|---|---|---|
-|enabled|Indicate if you want the alert to start executing on an interval basis after it has been created.|boolean|
-|name|A name to reference and search in the future.|string|
-|tags|A list of keywords to reference and search in the future.|string[]|
-|alertTypeId|The id value of the alert type you want to call when the alert is scheduled to execute.|string|
-|schedule|The schedule specifying when this alert should be run, using one of the available schedule formats specified under _Schedule Formats_ below|object|
-|throttle|A Duration specifying how often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a `schedule` of 1 minute stays in a triggered state for 90 minutes, setting a `throttle` of `10m` or `1h` will prevent it from sending 90 notifications over this period.|string|
-|params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object|
-|actions|Array of the following: - `group` (string): We support grouping actions in the scenario of escalations or different types of alert instances. If you don't need this, feel free to use `default` as a value. - `id` (string): The id of the action saved object to execute. - `params` (object): The map to the `params` the action type will receive. In order to help apply context to strings, we handle them as mustache templates and pass in a default set of context. (see templating actions).|array|
-
-### `DELETE /api/alerts/alert/{id}`: Delete alert
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|id|The id of the alert you're trying to delete.|string|
-
-### `GET /api/alerts/_find`: Find alerts
-
-Params:
-
-See the saved objects API documentation for find. All the properties are the same except you cannot pass in `type`.
-
-### `GET /api/alerts/alert/{id}`: Get alert
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|id|The id of the alert you're trying to get.|string|
+Using of the alert type requires you to create an alert that will contain parameters and actions for a given alert type. API description for CRUD operations is a part of the [user documentation](https://www.elastic.co/guide/en/kibana/master/alerts-api-update.html).
+API listed below is experimental and could be changed or removed in the future.
### `GET /api/alerts/alert/{id}/state`: Get alert state
@@ -560,93 +443,12 @@ Query:
|---|---|---|
|dateStart|The date to start looking for alert events in the event log. Either an ISO date string, or a duration string indicating time since now.|string|
-### `GET /api/alerts/list_alert_types`: List alert types
-
-No parameters.
-
-### `PUT /api/alerts/alert/{id}`: Update alert
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|id|The id of the alert you're trying to update.|string|
-
-Payload:
-
-|Property|Description|Type|
-|---|---|---|
-|schedule|The schedule specifying when this alert should be run, using one of the available schedule formats specified under _Schedule Formats_ below|object|
-|throttle|A Duration specifying how often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a `schedule` of 1 minute stays in a triggered state for 90 minutes, setting a `throttle` of `10m` or `1h` will prevent it from sending 90 notifications over this period.|string|
-|name|A name to reference and search in the future.|string|
-|tags|A list of keywords to reference and search in the future.|string[]|
-|params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object|
-|actions|Array of the following: - `group` (string): We support grouping actions in the scenario of escalations or different types of alert instances. If you don't need this, feel free to use `default` as a value. - `id` (string): The id of the action saved object to execute. - `params` (object): There map to the `params` the action type will receive. In order to help apply context to strings, we handle them as mustache templates and pass in a default set of context. (see templating actions).|array|
-
-### `POST /api/alerts/alert/{id}/_enable`: Enable an alert
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|id|The id of the alert you're trying to enable.|string|
-
-### `POST /api/alerts/alert/{id}/_disable`: Disable an alert
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|id|The id of the alert you're trying to disable.|string|
-
-### `POST /api/alerts/alert/{id}/_mute_all`: Mute all alert instances
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|id|The id of the alert you're trying to mute all alert instances for.|string|
-
-### `POST /api/alerts/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute`: Mute alert instance
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|alertId|The id of the alert you're trying to mute an instance for.|string|
-|alertInstanceId|The instance id of the alert instance you're trying to mute.|string|
-
-### `POST /api/alerts/alert/{id}/_unmute_all`: Unmute all alert instances
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|id|The id of the alert you're trying to unmute all alert instances for.|string|
-
-### `POST /api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`: Unmute an alert instance
-
-Params:
-
-|Property|Description|Type|
-|---|---|---|
-|alertId|The id of the alert you're trying to unmute an instance for.|string|
-|alertInstanceId|The instance id of the alert instance you're trying to unmute.|string|
-
### `POST /api/alerts/alert/{id}/_update_api_key`: Update alert API key
|Property|Description|Type|
|---|---|---|
|id|The id of the alert you're trying to update the API key for. System will use user in request context to generate an API key for.|string|
-## Schedule Formats
-A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.
-
-We currently support the _Interval format_ which specifies the interval in seconds, minutes, hours or days at which the alert should execute.
-Example: `{ interval: "10s" }`, `{ interval: "5m" }`, `{ interval: "1h" }`, `{ interval: "1d" }`.
-
-There are plans to support multiple other schedule formats in the near future.
-
## Alert instance factory
**alertInstanceFactory(id)**
@@ -694,7 +496,7 @@ When an alert instance executes, the first argument is the `group` of actions to
The templating engine is [mustache]. General definition for the [mustache variable] is a double-brace {{}}. All variables are HTML-escaped by default and if there is a requirement to render unescaped HTML, it should be applied the triple mustache: `{{{name}}}`. Also, can be used `&` to unescape a variable.
-## Examples
+### Examples
The following code would be within an alert type. As you can see `cpuUsage ` will replace the state of the alert instance and `server` is the context for the alert instance to execute. The difference between the two is `cpuUsage ` will be accessible at the next execution.
From 5eeae3dff4041686129edb3e186059901e3633e7 Mon Sep 17 00:00:00 2001
From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com>
Date: Wed, 17 Feb 2021 15:57:34 -0500
Subject: [PATCH 34/79] Update entity route schema, use more indices on
detections page (#91718)
---
.../common/endpoint/schema/resolver.ts | 2 +-
.../components/graph_overlay/index.tsx | 17 +++++++----------
.../components/timeline/body/helpers.tsx | 2 +-
3 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts b/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts
index b38cabf33a3db0..3bfe2a7410c080 100644
--- a/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts
@@ -76,6 +76,6 @@ export const validateEntities = {
/**
* Indices to search in.
*/
- indices: schema.arrayOf(schema.string()),
+ indices: schema.oneOf([schema.arrayOf(schema.string()), schema.string()]),
}),
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx
index 9c9c56461609d9..a8cfea1de8e74c 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx
@@ -30,8 +30,7 @@ import { TimelineId } from '../../../../common/types/timeline';
import { timelineSelectors } from '../../store/timeline';
import { timelineDefaults } from '../../store/timeline/defaults';
import { isFullScreen } from '../timeline/body/column_headers';
-import { useSourcererScope } from '../../../common/containers/sourcerer';
-import { SourcererScopeName } from '../../../common/store/sourcerer/model';
+import { sourcererSelectors } from '../../../common/store';
import { updateTimelineGraphEventId } from '../../../timelines/store/timeline/actions';
import { Resolver } from '../../../resolver/view';
import {
@@ -169,14 +168,12 @@ const GraphOverlayComponent: React.FC = ({ isEventViewer, timelineId }
globalFullScreen,
]);
- let sourcereScope = SourcererScopeName.default;
- if ([TimelineId.detectionsRulesDetailsPage, TimelineId.detectionsPage].includes(timelineId)) {
- sourcereScope = SourcererScopeName.detections;
- } else if (timelineId === TimelineId.active) {
- sourcereScope = SourcererScopeName.timeline;
- }
+ const existingIndexNamesSelector = useMemo(
+ () => sourcererSelectors.getAllExistingIndexNamesSelector(),
+ []
+ );
+ const existingIndexNames = useDeepEqualSelector(existingIndexNamesSelector);
- const { selectedPatterns } = useSourcererScope(sourcereScope);
return (
= ({ isEventViewer, timelineId }
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx
index 32a01654bf9aff..dd701aa284997e 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx
@@ -151,7 +151,7 @@ const InvestigateInResolverActionComponent: React.FC !isInvestigateInResolverActionEnabled(ecsData), [ecsData]);
const handleClick = useCallback(() => {
dispatch(updateTimelineGraphEventId({ id: timelineId, graphEventId: ecsData._id }));
- if (TimelineId.active) {
+ if (timelineId === TimelineId.active) {
dispatch(setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.graph }));
}
}, [dispatch, ecsData._id, timelineId]);
From 3e5497484d8c3a034e834a56128c93829b764ad9 Mon Sep 17 00:00:00 2001
From: Nathan Reese
Date: Wed, 17 Feb 2021 14:02:20 -0700
Subject: [PATCH 35/79] [Maps] use stored map buffer to generate queries for
search sessions (#91148)
* [Maps] use stored map buffer to generate queries for search sessions
* getDataFilters unit test
* tslint
* update setQuery unit tests
* only set searchSessionMapBuffer when search session isRestore
* tslint
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../maps/public/actions/map_actions.test.js | 3 +
.../maps/public/actions/map_actions.ts | 5 ++
.../maps/public/embeddable/map_embeddable.tsx | 50 ++++++----------
.../plugins/maps/public/embeddable/types.ts | 3 +-
x-pack/plugins/maps/public/reducers/map.d.ts | 1 +
x-pack/plugins/maps/public/reducers/map.js | 3 +-
.../public/selectors/map_selectors.test.ts | 60 ++++++++++++++++++-
.../maps/public/selectors/map_selectors.ts | 9 ++-
8 files changed, 96 insertions(+), 38 deletions(-)
diff --git a/x-pack/plugins/maps/public/actions/map_actions.test.js b/x-pack/plugins/maps/public/actions/map_actions.test.js
index c0ad934c232e22..fafafa6b6a0711 100644
--- a/x-pack/plugins/maps/public/actions/map_actions.test.js
+++ b/x-pack/plugins/maps/public/actions/map_actions.test.js
@@ -277,6 +277,9 @@ describe('map_actions', () => {
require('../selectors/map_selectors').getSearchSessionId = () => {
return searchSessionId;
};
+ require('../selectors/map_selectors').getSearchSessionMapBuffer = () => {
+ return undefined;
+ };
require('../selectors/map_selectors').getMapSettings = () => {
return {
autoFitToDataBounds: false,
diff --git a/x-pack/plugins/maps/public/actions/map_actions.ts b/x-pack/plugins/maps/public/actions/map_actions.ts
index 33c79c793974b4..9682306852ba92 100644
--- a/x-pack/plugins/maps/public/actions/map_actions.ts
+++ b/x-pack/plugins/maps/public/actions/map_actions.ts
@@ -22,6 +22,7 @@ import {
getTimeFilters,
getLayerList,
getSearchSessionId,
+ getSearchSessionMapBuffer,
} from '../selectors/map_selectors';
import {
CLEAR_GOTO,
@@ -229,12 +230,14 @@ export function setQuery({
filters = [],
forceRefresh = false,
searchSessionId,
+ searchSessionMapBuffer,
}: {
filters?: Filter[];
query?: Query;
timeFilters?: TimeRange;
forceRefresh?: boolean;
searchSessionId?: string;
+ searchSessionMapBuffer?: MapExtent;
}) {
return async (
dispatch: ThunkDispatch,
@@ -255,6 +258,7 @@ export function setQuery({
},
filters: filters ? filters : getFilters(getState()),
searchSessionId,
+ searchSessionMapBuffer,
};
const prevQueryContext = {
@@ -262,6 +266,7 @@ export function setQuery({
query: getQuery(getState()),
filters: getFilters(getState()),
searchSessionId: getSearchSessionId(getState()),
+ searchSessionMapBuffer: getSearchSessionMapBuffer(getState()),
};
if (_.isEqual(nextQueryContext, prevQueryContext)) {
diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx
index f42a055b24d0a2..b7e50815fd1f71 100644
--- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx
+++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx
@@ -44,6 +44,7 @@ import {
} from '../reducers/non_serializable_instances';
import {
getMapCenter,
+ getMapBuffer,
getMapZoom,
getHiddenLayerIds,
getQueryableUniqueIndexPatternIds,
@@ -151,11 +152,7 @@ export class MapEmbeddable
store.dispatch(disableScrollZoom());
this._dispatchSetQuery({
- query: this.input.query,
- timeRange: this.input.timeRange,
- filters: this.input.filters,
forceRefresh: false,
- searchSessionId: this.input.searchSessionId,
});
if (this.input.refreshConfig) {
this._dispatchSetRefreshConfig(this.input.refreshConfig);
@@ -230,11 +227,7 @@ export class MapEmbeddable
this.input.searchSessionId !== this._prevSearchSessionId
) {
this._dispatchSetQuery({
- query: this.input.query,
- timeRange: this.input.timeRange,
- filters: this.input.filters,
forceRefresh: false,
- searchSessionId: this.input.searchSessionId,
});
}
@@ -258,30 +251,24 @@ export class MapEmbeddable
}
}
- _dispatchSetQuery({
- query,
- timeRange,
- filters = [],
- forceRefresh,
- searchSessionId,
- }: {
- query?: Query;
- timeRange?: TimeRange;
- filters?: Filter[];
- forceRefresh: boolean;
- searchSessionId?: string;
- }) {
- this._prevTimeRange = timeRange;
- this._prevQuery = query;
- this._prevFilters = filters;
- this._prevSearchSessionId = searchSessionId;
+ _dispatchSetQuery({ forceRefresh }: { forceRefresh: boolean }) {
+ this._prevTimeRange = this.input.timeRange;
+ this._prevQuery = this.input.query;
+ this._prevFilters = this.input.filters;
+ this._prevSearchSessionId = this.input.searchSessionId;
+ const enabledFilters = this.input.filters
+ ? this.input.filters.filter((filter) => !filter.meta.disabled)
+ : [];
this._savedMap.getStore().dispatch(
setQuery({
- filters: filters.filter((filter) => !filter.meta.disabled),
- query,
- timeFilters: timeRange,
+ filters: enabledFilters,
+ query: this.input.query,
+ timeFilters: this.input.timeRange,
forceRefresh,
- searchSessionId,
+ searchSessionId: this.input.searchSessionId,
+ searchSessionMapBuffer: getIsRestore(this.input.searchSessionId)
+ ? this.input.mapBuffer
+ : undefined,
})
);
}
@@ -432,11 +419,7 @@ export class MapEmbeddable
reload() {
this._dispatchSetQuery({
- query: this.input.query,
- timeRange: this.input.timeRange,
- filters: this.input.filters,
forceRefresh: true,
- searchSessionId: this.input.searchSessionId,
});
}
@@ -457,6 +440,7 @@ export class MapEmbeddable
lon: center.lon,
zoom,
},
+ mapBuffer: getMapBuffer(this._savedMap.getStore().getState()),
});
}
diff --git a/x-pack/plugins/maps/public/embeddable/types.ts b/x-pack/plugins/maps/public/embeddable/types.ts
index 67489802bc31d1..7cd4fa8e1253bd 100644
--- a/x-pack/plugins/maps/public/embeddable/types.ts
+++ b/x-pack/plugins/maps/public/embeddable/types.ts
@@ -12,7 +12,7 @@ import {
SavedObjectEmbeddableInput,
} from '../../../../../src/plugins/embeddable/public';
import { RefreshInterval, Query, Filter, TimeRange } from '../../../../../src/plugins/data/common';
-import { MapCenterAndZoom } from '../../common/descriptor_types';
+import { MapCenterAndZoom, MapExtent } from '../../common/descriptor_types';
import { MapSavedObjectAttributes } from '../../common/map_saved_object_type';
import { MapSettings } from '../reducers/map';
@@ -25,6 +25,7 @@ interface MapEmbeddableState {
isLayerTOCOpen?: boolean;
openTOCDetails?: string[];
mapCenter?: MapCenterAndZoom;
+ mapBuffer?: MapExtent;
mapSettings?: Partial;
hiddenLayers?: string[];
hideFilterActions?: boolean;
diff --git a/x-pack/plugins/maps/public/reducers/map.d.ts b/x-pack/plugins/maps/public/reducers/map.d.ts
index 6a51d4feeb9dfe..1cf37561609640 100644
--- a/x-pack/plugins/maps/public/reducers/map.d.ts
+++ b/x-pack/plugins/maps/public/reducers/map.d.ts
@@ -37,6 +37,7 @@ export type MapContext = {
refreshTimerLastTriggeredAt?: string;
drawState?: DrawState;
searchSessionId?: string;
+ searchSessionMapBuffer?: MapExtent;
};
export type MapSettings = {
diff --git a/x-pack/plugins/maps/public/reducers/map.js b/x-pack/plugins/maps/public/reducers/map.js
index fa7e1308bac4f4..9bf0df612bac40 100644
--- a/x-pack/plugins/maps/public/reducers/map.js
+++ b/x-pack/plugins/maps/public/reducers/map.js
@@ -241,7 +241,7 @@ export function map(state = DEFAULT_MAP_STATE, action) {
};
return { ...state, mapState: { ...state.mapState, ...newMapState } };
case SET_QUERY:
- const { query, timeFilters, filters, searchSessionId } = action;
+ const { query, timeFilters, filters, searchSessionId, searchSessionMapBuffer } = action;
return {
...state,
mapState: {
@@ -250,6 +250,7 @@ export function map(state = DEFAULT_MAP_STATE, action) {
timeFilters,
filters,
searchSessionId,
+ searchSessionMapBuffer,
},
};
case SET_REFRESH_CONFIG:
diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts
index 89cd80f4daab50..fff33ae246b319 100644
--- a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts
+++ b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts
@@ -26,9 +26,67 @@ jest.mock('../kibana_services', () => ({
}));
import { DEFAULT_MAP_STORE_STATE } from '../reducers/store';
-import { areLayersLoaded, getTimeFilters } from './map_selectors';
+import { areLayersLoaded, getDataFilters, getTimeFilters } from './map_selectors';
import { LayerDescriptor } from '../../common/descriptor_types';
import { ILayer } from '../classes/layers/layer';
+import { Filter } from '../../../../../src/plugins/data/public';
+
+describe('getDataFilters', () => {
+ const mapExtent = {
+ maxLat: 1,
+ maxLon: 1,
+ minLat: 0,
+ minLon: 0,
+ };
+ const mapBuffer = {
+ maxLat: 1.5,
+ maxLon: 1.5,
+ minLat: -0.5,
+ minLon: -0.5,
+ };
+ const mapZoom = 4;
+ const timeFilters = { to: '2001-01-01', from: '2001-12-31' };
+ const refreshTimerLastTriggeredAt = '2001-01-01T00:00:00';
+ const query = undefined;
+ const filters: Filter[] = [];
+ const searchSessionId = '12345';
+ const searchSessionMapBuffer = {
+ maxLat: 1.25,
+ maxLon: 1.25,
+ minLat: -0.25,
+ minLon: -0.25,
+ };
+
+ test('should set buffer as searchSessionMapBuffer when using searchSessionId', () => {
+ const dataFilters = getDataFilters.resultFunc(
+ mapExtent,
+ mapBuffer,
+ mapZoom,
+ timeFilters,
+ refreshTimerLastTriggeredAt,
+ query,
+ filters,
+ searchSessionId,
+ searchSessionMapBuffer
+ );
+ expect(dataFilters.buffer).toEqual(searchSessionMapBuffer);
+ });
+
+ test('should fall back to screen buffer when using searchSessionId and searchSessionMapBuffer is not provided', () => {
+ const dataFilters = getDataFilters.resultFunc(
+ mapExtent,
+ mapBuffer,
+ mapZoom,
+ timeFilters,
+ refreshTimerLastTriggeredAt,
+ query,
+ filters,
+ searchSessionId,
+ undefined
+ );
+ expect(dataFilters.buffer).toEqual(mapBuffer);
+ });
+});
describe('getTimeFilters', () => {
test('should return timeFilters when contained in state', () => {
diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts
index b16ac704c3715b..ffaff3263cf484 100644
--- a/x-pack/plugins/maps/public/selectors/map_selectors.ts
+++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts
@@ -183,6 +183,9 @@ export const getFilters = ({ map }: MapStoreState): Filter[] => map.mapState.fil
export const getSearchSessionId = ({ map }: MapStoreState): string | undefined =>
map.mapState.searchSessionId;
+export const getSearchSessionMapBuffer = ({ map }: MapStoreState): MapExtent | undefined =>
+ map.mapState.searchSessionMapBuffer;
+
export const isUsingSearch = (state: MapStoreState): boolean => {
const filters = getFilters(state).filter((filter) => !filter.meta.disabled);
const queryString = _.get(getQuery(state), 'query', '');
@@ -235,6 +238,7 @@ export const getDataFilters = createSelector(
getQuery,
getFilters,
getSearchSessionId,
+ getSearchSessionMapBuffer,
(
mapExtent,
mapBuffer,
@@ -243,11 +247,12 @@ export const getDataFilters = createSelector(
refreshTimerLastTriggeredAt,
query,
filters,
- searchSessionId
+ searchSessionId,
+ searchSessionMapBuffer
) => {
return {
extent: mapExtent,
- buffer: mapBuffer,
+ buffer: searchSessionId && searchSessionMapBuffer ? searchSessionMapBuffer : mapBuffer,
zoom: mapZoom,
timeFilters,
refreshTimerLastTriggeredAt,
From 5bbbcb358f77754f6ee0dfc2db3b9c5ccd978364 Mon Sep 17 00:00:00 2001
From: Diana Derevyankina
<54894989+DziyanaDzeraviankina@users.noreply.github.com>
Date: Thu, 18 Feb 2021 00:04:38 +0300
Subject: [PATCH 36/79] Replace EuiCodeBlock with Monaco editor in Discover
(#90781)
* Update version of react-monaco-editor and monaco-editor libraries
* Fix yarn lock file
* Fix CI
* Fix unit tests
* Fix CI
* Fix comment
* move monaco instance in window.MonacoEnvironment
* Replace EuiCodeBlock with Monaco editor in Discover expanded document
* Replace EuiCodeBlock with EuiErrorBoundary
* Revert changes done by mistake
* Remove unused translations
* Fix doc_viewer test and snapshots
* Add comment and rename the function related to editor height calculation
* Remove "value" from JSON tree and fix resizing
* Update json_code_editor.test.tsx.snap
* Fix JsonCodeEditor props
* Fix json_code_editor test
* Delete JsonCodeBlock and remove inline style in JsonCodeEditor
* Rename jsonCodeEditor CSS class name to dscJsonCodeEditor
Co-authored-by: Uladzislau Lasitsa
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../components/doc_viewer/doc_viewer.test.tsx | 5 ++
.../doc_viewer/doc_viewer_render_error.tsx | 20 ++---
.../components/doc_viewer/doc_viewer_tab.tsx | 6 +-
.../json_code_block.test.tsx.snap | 20 -----
.../json_code_block/json_code_block.tsx | 23 -----
.../json_code_editor.test.tsx.snap | 71 ++++++++++++++++
.../json_code_editor/json_code_editor.scss | 3 +
.../json_code_editor.test.tsx} | 18 ++--
.../json_code_editor/json_code_editor.tsx | 84 +++++++++++++++++++
src/plugins/discover/public/plugin.ts | 4 +-
.../translations/translations/ja-JP.json | 1 -
.../translations/translations/zh-CN.json | 1 -
12 files changed, 187 insertions(+), 69 deletions(-)
delete mode 100644 src/plugins/discover/public/application/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap
delete mode 100644 src/plugins/discover/public/application/components/json_code_block/json_code_block.tsx
create mode 100644 src/plugins/discover/public/application/components/json_code_editor/__snapshots__/json_code_editor.test.tsx.snap
create mode 100644 src/plugins/discover/public/application/components/json_code_editor/json_code_editor.scss
rename src/plugins/discover/public/application/components/{json_code_block/json_code_block.test.tsx => json_code_editor/json_code_editor.test.tsx} (53%)
create mode 100644 src/plugins/discover/public/application/components/json_code_editor/json_code_editor.tsx
diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx
index 62e19b83b016e4..6afa7f89371f98 100644
--- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx
+++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx
@@ -16,6 +16,11 @@ import { DocViewRenderProps } from '../../doc_views/doc_views_types';
jest.mock('../../../kibana_services', () => {
let registry: any[] = [];
return {
+ getServices: () => ({
+ uiSettings: {
+ get: jest.fn(),
+ },
+ }),
getDocViewsRegistry: () => ({
addDocView(view: any) {
registry.push(view);
diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_render_error.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_render_error.tsx
index 571af94f293728..b9b068ce4bd073 100644
--- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_render_error.tsx
+++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_render_error.tsx
@@ -7,20 +7,18 @@
*/
import React from 'react';
-import { EuiCallOut, EuiCodeBlock } from '@elastic/eui';
-import { formatMsg, formatStack } from '../../../../../kibana_legacy/public';
+import { EuiErrorBoundary } from '@elastic/eui';
interface Props {
error: Error | string;
}
-export function DocViewerError({ error }: Props) {
- const errMsg = formatMsg(error);
- const errStack = typeof error === 'object' ? formatStack(error) : '';
+const DocViewerErrorWrapper = ({ error }: Props) => {
+ throw error;
+};
- return (
-
- {errStack && {errStack} }
-
- );
-}
+export const DocViewerError = ({ error }: Props) => (
+
+
+
+);
diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx
index 1da75b45239105..25454a3bad38ab 100644
--- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx
+++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx
@@ -11,6 +11,8 @@ import { I18nProvider } from '@kbn/i18n/react';
import { DocViewRenderTab } from './doc_viewer_render_tab';
import { DocViewerError } from './doc_viewer_render_error';
import { DocViewRenderFn, DocViewRenderProps } from '../../doc_views/doc_views_types';
+import { getServices } from '../../../kibana_services';
+import { KibanaContextProvider } from '../../../../../kibana_react/public';
interface Props {
component?: React.ComponentType;
@@ -72,7 +74,9 @@ export class DocViewerTab extends React.Component {
const Component = component as any;
return (
-
+
+
+
);
}
diff --git a/src/plugins/discover/public/application/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap b/src/plugins/discover/public/application/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap
deleted file mode 100644
index d6f48a9b3c7745..00000000000000
--- a/src/plugins/discover/public/application/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap
+++ /dev/null
@@ -1,20 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`returns the \`JsonCodeEditor\` component 1`] = `
-
- {
- "_index": "test",
- "_type": "doc",
- "_id": "foo",
- "_score": 1,
- "_source": {
- "test": 123
- }
-}
-
-`;
diff --git a/src/plugins/discover/public/application/components/json_code_block/json_code_block.tsx b/src/plugins/discover/public/application/components/json_code_block/json_code_block.tsx
deleted file mode 100644
index 7dab2b199b018f..00000000000000
--- a/src/plugins/discover/public/application/components/json_code_block/json_code_block.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 React from 'react';
-import { EuiCodeBlock } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { DocViewRenderProps } from '../../doc_views/doc_views_types';
-
-export function JsonCodeBlock({ hit }: DocViewRenderProps) {
- const label = i18n.translate('discover.docViews.json.codeEditorAriaLabel', {
- defaultMessage: 'Read only JSON view of an elasticsearch document',
- });
- return (
-
- {JSON.stringify(hit, null, 2)}
-
- );
-}
diff --git a/src/plugins/discover/public/application/components/json_code_editor/__snapshots__/json_code_editor.test.tsx.snap b/src/plugins/discover/public/application/components/json_code_editor/__snapshots__/json_code_editor.test.tsx.snap
new file mode 100644
index 00000000000000..4f27158eee04f1
--- /dev/null
+++ b/src/plugins/discover/public/application/components/json_code_editor/__snapshots__/json_code_editor.test.tsx.snap
@@ -0,0 +1,71 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`returns the \`JsonCodeEditor\` component 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.scss b/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.scss
new file mode 100644
index 00000000000000..5521df5b363acd
--- /dev/null
+++ b/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.scss
@@ -0,0 +1,3 @@
+.dscJsonCodeEditor {
+ width: 100%
+}
diff --git a/src/plugins/discover/public/application/components/json_code_block/json_code_block.test.tsx b/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.test.tsx
similarity index 53%
rename from src/plugins/discover/public/application/components/json_code_block/json_code_block.test.tsx
rename to src/plugins/discover/public/application/components/json_code_editor/json_code_editor.test.tsx
index dd56a1077f1ac6..4ccb3010d5a2b9 100644
--- a/src/plugins/discover/public/application/components/json_code_block/json_code_block.test.tsx
+++ b/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.test.tsx
@@ -8,17 +8,15 @@
import React from 'react';
import { shallow } from 'enzyme';
-import { JsonCodeBlock } from './json_code_block';
-import { IndexPattern } from '../../../../../data/public';
+import { JsonCodeEditor } from './json_code_editor';
it('returns the `JsonCodeEditor` component', () => {
- const props = {
- hit: { _index: 'test', _type: 'doc', _id: 'foo', _score: 1, _source: { test: 123 } },
- columns: [],
- indexPattern: {} as IndexPattern,
- filter: jest.fn(),
- onAddColumn: jest.fn(),
- onRemoveColumn: jest.fn(),
+ const value = {
+ _index: 'test',
+ _type: 'doc',
+ _id: 'foo',
+ _score: 1,
+ _source: { test: 123 },
};
- expect(shallow( )).toMatchSnapshot();
+ expect(shallow( )).toMatchSnapshot();
});
diff --git a/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.tsx b/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.tsx
new file mode 100644
index 00000000000000..85d6aad7552502
--- /dev/null
+++ b/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.tsx
@@ -0,0 +1,84 @@
+/*
+ * 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 './json_code_editor.scss';
+
+import React, { useCallback } from 'react';
+import { i18n } from '@kbn/i18n';
+import { monaco, XJsonLang } from '@kbn/monaco';
+import { EuiButtonEmpty, EuiCopy, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
+import { CodeEditor } from '../../../../../kibana_react/public';
+import { DocViewRenderProps } from '../../../application/doc_views/doc_views_types';
+
+const codeEditorAriaLabel = i18n.translate('discover.json.codeEditorAriaLabel', {
+ defaultMessage: 'Read only JSON view of an elasticsearch document',
+});
+const copyToClipboardLabel = i18n.translate('discover.json.copyToClipboardLabel', {
+ defaultMessage: 'Copy to clipboard',
+});
+
+export const JsonCodeEditor = ({ hit }: DocViewRenderProps) => {
+ const jsonValue = JSON.stringify(hit, null, 2);
+
+ // setting editor height based on lines height and count to stretch and fit its content
+ const setEditorCalculatedHeight = useCallback((editor) => {
+ const editorElement = editor.getDomNode();
+
+ if (!editorElement) {
+ return;
+ }
+
+ const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight);
+ const lineCount = editor.getModel()?.getLineCount() || 1;
+ const height = editor.getTopForLineNumber(lineCount + 1) + lineHeight;
+
+ editorElement.style.height = `${height}px`;
+ editor.layout();
+ }, []);
+
+ return (
+
+
+
+
+
+ {(copy) => (
+
+ {copyToClipboardLabel}
+
+ )}
+
+
+
+
+ {}}
+ editorDidMount={setEditorCalculatedHeight}
+ aria-label={codeEditorAriaLabel}
+ options={{
+ automaticLayout: true,
+ fontSize: 12,
+ minimap: {
+ enabled: false,
+ },
+ overviewRulerBorder: false,
+ readOnly: true,
+ scrollbar: {
+ alwaysConsumeMouseWheel: false,
+ },
+ scrollBeyondLastLine: false,
+ wordWrap: 'on',
+ wrappingIndent: 'indent',
+ }}
+ />
+
+
+ );
+};
diff --git a/src/plugins/discover/public/plugin.ts b/src/plugins/discover/public/plugin.ts
index c53dfaff24112f..47161c2b8298e1 100644
--- a/src/plugins/discover/public/plugin.ts
+++ b/src/plugins/discover/public/plugin.ts
@@ -36,7 +36,7 @@ import { UrlGeneratorState } from '../../share/public';
import { DocViewInput, DocViewInputFn } from './application/doc_views/doc_views_types';
import { DocViewsRegistry } from './application/doc_views/doc_views_registry';
import { DocViewTable } from './application/components/table/table';
-import { JsonCodeBlock } from './application/components/json_code_block/json_code_block';
+import { JsonCodeEditor } from './application/components/json_code_editor/json_code_editor';
import {
setDocViewsRegistry,
setUrlTracker,
@@ -187,7 +187,7 @@ export class DiscoverPlugin
defaultMessage: 'JSON',
}),
order: 20,
- component: JsonCodeBlock,
+ component: JsonCodeEditor,
});
const {
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 3196bcc6031c9e..3a07e303eb7d81 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -1479,7 +1479,6 @@
"discover.docTable.tableRow.viewSingleDocumentLinkText": "単一のドキュメントを表示",
"discover.docTable.tableRow.viewSurroundingDocumentsLinkText": "周りのドキュメントを表示",
"discover.documentsAriaLabel": "ドキュメント",
- "discover.docViews.json.codeEditorAriaLabel": "Elasticsearch ドキュメントの JSON ビューのみを読み込む",
"discover.docViews.json.jsonTitle": "JSON",
"discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel": "警告",
"discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip": "{underscoreSign} で始まるフィールド名はサポートされません",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 7292769cdbd0fa..71a2fd83be9680 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -1479,7 +1479,6 @@
"discover.docTable.tableRow.viewSingleDocumentLinkText": "查看单个文档",
"discover.docTable.tableRow.viewSurroundingDocumentsLinkText": "查看周围文档",
"discover.documentsAriaLabel": "文档",
- "discover.docViews.json.codeEditorAriaLabel": "Elasticsearch 文档的只读 JSON 视图",
"discover.docViews.json.jsonTitle": "JSON",
"discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel": "警告",
"discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip": "不支持以 {underscoreSign} 开头的字段名称",
From a2cd3662b20937b04666b2f48a58abaea9e7a3d8 Mon Sep 17 00:00:00 2001
From: Thomas Neirynck
Date: Wed, 17 Feb 2021 16:23:30 -0500
Subject: [PATCH 37/79] [Maps] rename data load method for clarity (#91547)
---
.../maps/public/classes/layers/layer.tsx | 4 ++--
.../layers/vector_layer/vector_layer.tsx | 2 +-
.../public/selectors/map_selectors.test.ts | 20 ++++++++++---------
.../maps/public/selectors/map_selectors.ts | 2 +-
4 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx
index 89c6d70a217c95..e3a21b596afe14 100644
--- a/x-pack/plugins/maps/public/classes/layers/layer.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx
@@ -77,7 +77,7 @@ export interface ILayer {
canShowTooltip(): boolean;
syncLayerWithMB(mbMap: MbMap): void;
getLayerTypeIconName(): string;
- isDataLoaded(): boolean;
+ isInitialDataLoadComplete(): boolean;
getIndexPatternIds(): string[];
getQueryableIndexPatternIds(): string[];
getType(): string | undefined;
@@ -446,7 +446,7 @@ export class AbstractLayer implements ILayer {
throw new Error('should implement Layer#getLayerTypeIconName');
}
- isDataLoaded(): boolean {
+ isInitialDataLoadComplete(): boolean {
const sourceDataRequest = this.getSourceDataRequest();
return sourceDataRequest ? sourceDataRequest.hasData() : false;
}
diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
index 7e87d99fd4f93f..2373ed3ba2062f 100644
--- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
@@ -174,7 +174,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
return this.getValidJoins().length > 0;
}
- isDataLoaded() {
+ isInitialDataLoadComplete() {
const sourceDataRequest = this.getSourceDataRequest();
if (!sourceDataRequest || !sourceDataRequest.hasData()) {
return false;
diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts
index fff33ae246b319..58268b6ea9d82c 100644
--- a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts
+++ b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts
@@ -124,12 +124,12 @@ describe('getTimeFilters', () => {
describe('areLayersLoaded', () => {
function createLayerMock({
hasErrors = false,
- isDataLoaded = false,
+ isInitialDataLoadComplete = false,
isVisible = true,
showAtZoomLevel = true,
}: {
hasErrors?: boolean;
- isDataLoaded?: boolean;
+ isInitialDataLoadComplete?: boolean;
isVisible?: boolean;
showAtZoomLevel?: boolean;
}) {
@@ -137,8 +137,8 @@ describe('areLayersLoaded', () => {
hasErrors: () => {
return hasErrors;
},
- isDataLoaded: () => {
- return isDataLoaded;
+ isInitialDataLoadComplete: () => {
+ return isInitialDataLoadComplete;
},
isVisible: () => {
return isVisible;
@@ -157,35 +157,37 @@ describe('areLayersLoaded', () => {
});
test('layer should not be counted as loaded if it has not loaded', () => {
- const layerList = [createLayerMock({ isDataLoaded: false })];
+ const layerList = [createLayerMock({ isInitialDataLoadComplete: false })];
const waitingForMapReadyLayerList: LayerDescriptor[] = [];
const zoom = 4;
expect(areLayersLoaded.resultFunc(layerList, waitingForMapReadyLayerList, zoom)).toBe(false);
});
test('layer should be counted as loaded if its not visible', () => {
- const layerList = [createLayerMock({ isVisible: false, isDataLoaded: false })];
+ const layerList = [createLayerMock({ isVisible: false, isInitialDataLoadComplete: false })];
const waitingForMapReadyLayerList: LayerDescriptor[] = [];
const zoom = 4;
expect(areLayersLoaded.resultFunc(layerList, waitingForMapReadyLayerList, zoom)).toBe(true);
});
test('layer should be counted as loaded if its not shown at zoom level', () => {
- const layerList = [createLayerMock({ showAtZoomLevel: false, isDataLoaded: false })];
+ const layerList = [
+ createLayerMock({ showAtZoomLevel: false, isInitialDataLoadComplete: false }),
+ ];
const waitingForMapReadyLayerList: LayerDescriptor[] = [];
const zoom = 4;
expect(areLayersLoaded.resultFunc(layerList, waitingForMapReadyLayerList, zoom)).toBe(true);
});
test('layer should be counted as loaded if it has a loading error', () => {
- const layerList = [createLayerMock({ hasErrors: true, isDataLoaded: false })];
+ const layerList = [createLayerMock({ hasErrors: true, isInitialDataLoadComplete: false })];
const waitingForMapReadyLayerList: LayerDescriptor[] = [];
const zoom = 4;
expect(areLayersLoaded.resultFunc(layerList, waitingForMapReadyLayerList, zoom)).toBe(true);
});
test('layer should be counted as loaded if its loaded', () => {
- const layerList = [createLayerMock({ isDataLoaded: true })];
+ const layerList = [createLayerMock({ isInitialDataLoadComplete: true })];
const waitingForMapReadyLayerList: LayerDescriptor[] = [];
const zoom = 4;
expect(areLayersLoaded.resultFunc(layerList, waitingForMapReadyLayerList, zoom)).toBe(true);
diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts
index ffaff3263cf484..35c73a2bd2f1c7 100644
--- a/x-pack/plugins/maps/public/selectors/map_selectors.ts
+++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts
@@ -437,7 +437,7 @@ export const areLayersLoaded = createSelector(
layer.isVisible() &&
layer.showAtZoomLevel(zoom) &&
!layer.hasErrors() &&
- !layer.isDataLoaded()
+ !layer.isInitialDataLoadComplete()
) {
return false;
}
From e095a6abf2ed3c7d49322643382b2056c89090d4 Mon Sep 17 00:00:00 2001
From: Michael Olorunnisola
Date: Wed, 17 Feb 2021 16:26:40 -0500
Subject: [PATCH 38/79] [Security Solution][Timeline] Minor side panel fixes
(#91691)
---
.../host_overview/endpoint_overview/translations.ts | 4 ++--
.../side_panel/host_details/expandable_host.tsx | 11 +++++++++--
.../network_details/expandable_network.tsx | 11 +++++++++--
.../timeline/body/events/stateful_event.tsx | 12 +++++++++---
x-pack/plugins/translations/translations/ja-JP.json | 2 --
x-pack/plugins/translations/translations/zh-CN.json | 2 --
6 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/translations.ts b/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/translations.ts
index 3e18c7a01c808e..1a007cd7f0f56d 100644
--- a/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/translations.ts
+++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/translations.ts
@@ -17,13 +17,13 @@ export const ENDPOINT_POLICY = i18n.translate(
export const POLICY_STATUS = i18n.translate(
'xpack.securitySolution.host.details.endpoint.policyStatus',
{
- defaultMessage: 'Configuration Status',
+ defaultMessage: 'Policy Status',
}
);
export const SENSORVERSION = i18n.translate(
'xpack.securitySolution.host.details.endpoint.sensorversion',
{
- defaultMessage: 'Sensorversion',
+ defaultMessage: 'Sensor Version',
}
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx
index 4e101e29bb4841..8fce9a186bbd46 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx
@@ -6,6 +6,7 @@
*/
import React from 'react';
+import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { EuiTitle } from '@elastic/eui';
import { HostDetailsLink } from '../../../../common/components/links';
@@ -23,14 +24,20 @@ interface ExpandableHostProps {
hostName: string;
}
+const StyledTitle = styled.h4`
+ word-break: break-all;
+ word-wrap: break-word;
+ white-space: pre-wrap;
+`;
+
export const ExpandableHostDetailsTitle = ({ hostName }: ExpandableHostProps) => (
-
+
{i18n.translate('xpack.securitySolution.timeline.sidePanel.hostDetails.title', {
defaultMessage: 'Host details',
})}
{`: ${hostName}`}
-
+
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx
index b12b575681acf2..19f6e2c9652f92 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx
@@ -7,6 +7,7 @@
import { EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
+import styled from 'styled-components';
import React, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { FlowTarget } from '../../../../../common/search_strategy';
@@ -31,14 +32,20 @@ interface ExpandableNetworkProps {
expandedNetwork: { ip: string; flowTarget: FlowTarget };
}
+const StyledTitle = styled.h4`
+ word-break: break-all;
+ word-wrap: break-word;
+ white-space: pre-wrap;
+`;
+
export const ExpandableNetworkDetailsTitle = ({ ip }: { ip: string }) => (
-
+
{i18n.translate('xpack.securitySolution.timeline.sidePanel.networkDetails.title', {
defaultMessage: 'Network details',
})}
{`: ${ip}`}
-
+
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
index 45b10d635195bb..4191badd6b03fb 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
@@ -109,8 +109,14 @@ const StatefulEventComponent: React.FC = ({
}, [event?.data]);
const hostIPAddresses = useMemo(() => {
- const ipList = getMappedNonEcsValue({ data: event?.data, fieldName: 'host.ip' });
- return ipList;
+ const hostIpList = getMappedNonEcsValue({ data: event?.data, fieldName: 'host.ip' }) ?? [];
+ const sourceIpList = getMappedNonEcsValue({ data: event?.data, fieldName: 'source.ip' }) ?? [];
+ const destinationIpList =
+ getMappedNonEcsValue({
+ data: event?.data,
+ fieldName: 'destination.ip',
+ }) ?? [];
+ return new Set([...hostIpList, ...sourceIpList, ...destinationIpList]);
}, [event?.data]);
const activeTab = tabType ?? TimelineTabs.query;
@@ -123,7 +129,7 @@ const StatefulEventComponent: React.FC = ({
activeExpandedDetail?.params?.hostName === hostName) ||
(activeExpandedDetail?.panelView === 'networkDetail' &&
activeExpandedDetail?.params?.ip &&
- hostIPAddresses?.includes(activeExpandedDetail?.params?.ip)) ||
+ hostIPAddresses?.has(activeExpandedDetail?.params?.ip)) ||
false;
const getNotesByIds = useMemo(() => appSelectors.notesByIdsSelector(), []);
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 3a07e303eb7d81..344a07e53e3edb 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -18875,8 +18875,6 @@
"xpack.securitySolution.hooks.useAddToTimeline.addedFieldMessage": "{fieldOrValue}をタイムラインに追加しました",
"xpack.securitySolution.host.details.architectureLabel": "アーキテクチャー",
"xpack.securitySolution.host.details.endpoint.endpointPolicy": "統合",
- "xpack.securitySolution.host.details.endpoint.policyStatus": "構成ステータス",
- "xpack.securitySolution.host.details.endpoint.sensorversion": "センサーバージョン",
"xpack.securitySolution.host.details.firstSeenTitle": "初回の認識",
"xpack.securitySolution.host.details.lastSeenTitle": "前回の認識",
"xpack.securitySolution.host.details.overview.cloudProviderTitle": "クラウドプロバイダー",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 71a2fd83be9680..579a06d44e6595 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -18921,8 +18921,6 @@
"xpack.securitySolution.hooks.useAddToTimeline.addedFieldMessage": "已将 {fieldOrValue} 添加到时间线",
"xpack.securitySolution.host.details.architectureLabel": "架构",
"xpack.securitySolution.host.details.endpoint.endpointPolicy": "集成",
- "xpack.securitySolution.host.details.endpoint.policyStatus": "配置状态",
- "xpack.securitySolution.host.details.endpoint.sensorversion": "感应器版本",
"xpack.securitySolution.host.details.firstSeenTitle": "首次看到时间",
"xpack.securitySolution.host.details.lastSeenTitle": "最后看到时间",
"xpack.securitySolution.host.details.overview.cloudProviderTitle": "云服务提供商",
From e6ecc9fe255b69f6f950ba54153d707bbfdd1e0a Mon Sep 17 00:00:00 2001
From: "Devin W. Hurley"
Date: Wed, 17 Feb 2021 17:08:41 -0500
Subject: [PATCH 39/79] [Security Solution] [Detections] Prevent early ejection
from big loop when index pattern is missing the given timestamp override
field (#91597)
* fix for when search response yields 400 with missing timestamp override field
* prefer includes over strict equality
* adds integration test to check for this case
* adds a unit test and util function to ensure unit test executes properly and waits for rule to complete running
* remove comments from rebase
---
.../signals/single_search_after.ts | 28 ++++++++++++++++
.../lib/detection_engine/signals/utils.ts | 12 ++++---
.../security_and_spaces/tests/create_rules.ts | 32 +++++++++++++++++++
.../detection_engine_api_integration/utils.ts | 13 ++++++++
4 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts
index fbea610428bd0f..9c58bba296ad35 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts
@@ -87,6 +87,34 @@ export const singleSearchAfter = async ({
};
} catch (exc) {
logger.error(buildRuleMessage(`[-] nextSearchAfter threw an error ${exc}`));
+ if (
+ exc.message.includes('No mapping found for [@timestamp] in order to sort on') ||
+ exc.message.includes(`No mapping found for [${timestampOverride}] in order to sort on`)
+ ) {
+ logger.error(buildRuleMessage(`[-] failure reason: ${exc.message}`));
+
+ const searchRes: SignalSearchResponse = {
+ took: 0,
+ timed_out: false,
+ _shards: {
+ total: 1,
+ successful: 1,
+ failed: 0,
+ skipped: 0,
+ },
+ hits: {
+ total: 0,
+ max_score: 0,
+ hits: [],
+ },
+ };
+ return {
+ searchResult: searchRes,
+ searchDuration: '-1.0',
+ searchErrors: exc.message,
+ };
+ }
+
throw exc;
}
};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts
index f6bd5c8a325be1..323986e6ffecbc 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts
@@ -134,9 +134,10 @@ export const hasTimestampFields = async (
? 'timestamp field "@timestamp"'
: `timestamp override field "${timestampField}"`
}: ${JSON.stringify(
- isEmpty(timestampFieldCapsResponse.body.fields)
+ isEmpty(timestampFieldCapsResponse.body.fields) ||
+ isEmpty(timestampFieldCapsResponse.body.fields[timestampField])
? timestampFieldCapsResponse.body.indices
- : timestampFieldCapsResponse.body.fields[timestampField].unmapped.indices
+ : timestampFieldCapsResponse.body.fields[timestampField]?.unmapped?.indices
)}`;
logger.error(buildRuleMessage(errorString));
await ruleStatusService.warning(errorString);
@@ -698,9 +699,12 @@ export const createSearchAfterReturnTypeFromResponse = ({
searchResult._shards.failed === 0 ||
searchResult._shards.failures?.every((failure) => {
return (
- failure.reason?.reason === 'No mapping found for [@timestamp] in order to sort on' ||
- failure.reason?.reason ===
+ failure.reason?.reason?.includes(
+ 'No mapping found for [@timestamp] in order to sort on'
+ ) ||
+ failure.reason?.reason?.includes(
`No mapping found for [${timestampOverride}] in order to sort on`
+ )
);
}),
lastLookBackDate: lastValidDate({ searchResult, timestampOverride }),
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts
index a9120bde274f24..8d494724d9f766 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts
@@ -27,11 +27,13 @@ import {
getSimpleMlRuleOutput,
waitForRuleSuccessOrStatus,
waitForSignalsToBePresent,
+ waitForAlertToComplete,
getRuleForSignalTesting,
getRuleForSignalTestingWithTimestampOverride,
} from '../../utils';
import { ROLES } from '../../../../plugins/security_solution/common/test';
import { createUserAndRole, deleteUserAndRole } from '../roles_users_utils';
+import { RuleStatusResponse } from '../../../../plugins/security_solution/server/lib/detection_engine/rules/types';
// eslint-disable-next-line import/no-default-export
export default ({ getService }: FtrProviderContext) => {
@@ -287,6 +289,36 @@ export default ({ getService }: FtrProviderContext) => {
await deleteAllAlerts(supertest);
await esArchiver.unload('security_solution/timestamp_override');
});
+
+ it('should create a single rule which has a timestamp override for an index pattern that does not exist and write a warning status', async () => {
+ // defaults to event.ingested timestamp override.
+ // event.ingested is one of the timestamp fields set on the es archive data
+ // inside of x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz
+ const simpleRule = getRuleForSignalTestingWithTimestampOverride(['myfakeindex-1']);
+ const { body } = await supertest
+ .post(DETECTION_ENGINE_RULES_URL)
+ .set('kbn-xsrf', 'true')
+ .send(simpleRule)
+ .expect(200);
+ const bodyId = body.id;
+
+ await waitForAlertToComplete(supertest, bodyId);
+ await waitForRuleSuccessOrStatus(supertest, bodyId, 'warning');
+
+ const { body: statusBody } = await supertest
+ .post(DETECTION_ENGINE_RULES_STATUS_URL)
+ .set('kbn-xsrf', 'true')
+ .send({ ids: [bodyId] })
+ .expect(200);
+
+ expect((statusBody as RuleStatusResponse)[bodyId].current_status?.status).to.eql('warning');
+ expect(
+ (statusBody as RuleStatusResponse)[bodyId].current_status?.last_success_message
+ ).to.eql(
+ 'The following indices are missing the timestamp override field "event.ingested": ["myfakeindex-1"]'
+ );
+ });
+
it('should create a single rule which has a timestamp override and generates two signals with a "warning" status', async () => {
// defaults to event.ingested timestamp override.
// event.ingested is one of the timestamp fields set on the es archive data
diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts
index 4875e837556fcc..684cbb6368ad90 100644
--- a/x-pack/test/detection_engine_api_integration/utils.ts
+++ b/x-pack/test/detection_engine_api_integration/utils.ts
@@ -964,6 +964,19 @@ export const getRule = async (
return body;
};
+export const waitForAlertToComplete = async (
+ supertest: SuperTest,
+ id: string
+): Promise => {
+ await waitFor(async () => {
+ const { body: alertBody } = await supertest
+ .get(`/api/alerts/alert/${id}/state`)
+ .set('kbn-xsrf', 'true')
+ .expect(200);
+ return alertBody.previousStartedAt != null;
+ }, 'waitForAlertToComplete');
+};
+
/**
* Waits for the rule in find status to be 'succeeded'
* or the provided status, before continuing
From 4ef4b0511b7bb074bafb8ce930b1b8442d1c2b28 Mon Sep 17 00:00:00 2001
From: "Devin W. Hurley"
Date: Wed, 17 Feb 2021 17:08:53 -0500
Subject: [PATCH 40/79] [Security Solution] [Detections] Update error banner
when refreshing rule status on rule details page (#91051)
* refresh error banner on rule details page when refreshing status
* remove unused test
---
.../rules/rule_status/index.test.tsx | 19 ---
.../components/rules/rule_status/index.tsx | 110 +++---------------
.../detection_engine/rules/details/index.tsx | 76 ++++++++++--
3 files changed, 83 insertions(+), 122 deletions(-)
delete mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.test.tsx
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.test.tsx
deleted file mode 100644
index f3669e128ac6ed..00000000000000
--- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.test.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React from 'react';
-import { shallow } from 'enzyme';
-
-import { RuleStatus } from './index';
-
-describe('RuleStatus', () => {
- it('renders loader correctly', () => {
- const wrapper = shallow( );
-
- expect(wrapper.dive().find('[data-test-subj="rule-status-loader"]')).toHaveLength(1);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx
index 677e6de0ff485d..42a6cb8bed1d7d 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx
@@ -5,115 +5,43 @@
* 2.0.
*/
-import {
- EuiButtonIcon,
- EuiFlexGroup,
- EuiFlexItem,
- EuiHealth,
- EuiLoadingSpinner,
- EuiText,
-} from '@elastic/eui';
-import React, { memo, useCallback, useEffect, useState } from 'react';
-import deepEqual from 'fast-deep-equal';
+import { EuiFlexItem, EuiHealth, EuiText } from '@elastic/eui';
+import React, { memo } from 'react';
-import {
- useRuleStatus,
- RuleInfoStatus,
- RuleStatusType,
-} from '../../../containers/detection_engine/rules';
+import { RuleStatusType } from '../../../containers/detection_engine/rules';
import { FormattedDate } from '../../../../common/components/formatted_date';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { getStatusColor } from './helpers';
import * as i18n from './translations';
interface RuleStatusProps {
- ruleId: string | null;
- ruleEnabled?: boolean | null;
+ children: React.ReactNode | null | undefined;
+ statusDate: string | null | undefined;
+ status: RuleStatusType | null | undefined;
}
-const RuleStatusComponent: React.FC = ({ ruleId, ruleEnabled }) => {
- const [loading, ruleStatus, fetchRuleStatus] = useRuleStatus(ruleId);
- const [myRuleEnabled, setMyRuleEnabled] = useState(ruleEnabled ?? null);
- const [currentStatus, setCurrentStatus] = useState(
- ruleStatus?.current_status ?? null
- );
-
- useEffect(() => {
- if (myRuleEnabled !== ruleEnabled && fetchRuleStatus != null && ruleId != null) {
- fetchRuleStatus(ruleId);
- if (myRuleEnabled !== ruleEnabled) {
- setMyRuleEnabled(ruleEnabled ?? null);
- }
- }
- }, [fetchRuleStatus, myRuleEnabled, ruleId, ruleEnabled, setMyRuleEnabled]);
-
- useEffect(() => {
- if (!deepEqual(currentStatus, ruleStatus?.current_status)) {
- setCurrentStatus(ruleStatus?.current_status ?? null);
- }
- }, [currentStatus, ruleStatus, setCurrentStatus]);
-
- const handleRefresh = useCallback(() => {
- if (fetchRuleStatus != null && ruleId != null) {
- fetchRuleStatus(ruleId);
- }
- }, [fetchRuleStatus, ruleId]);
-
- const getStatus = useCallback((status: RuleStatusType | null | undefined) => {
- if (status == null) {
- return getEmptyTagValue();
- } else if (status != null && status === 'partial failure') {
- // Temporary fix if on upgrade a rule has a status of 'partial failure' we want to display that text as 'warning'
- // On the next subsequent rule run, that 'partial failure' status will be re-written as a 'warning' status
- // and this code will no longer be necessary
- // TODO: remove this code in 8.0.0
- return 'warning';
- }
- return status;
- }, []);
-
+const RuleStatusComponent: React.FC = ({ children, statusDate, status }) => {
return (
-
+ <>
- {i18n.STATUS}
- {':'}
+
+
+ {status ?? getEmptyTagValue()}
+
+
- {loading && (
-
-
-
- )}
- {!loading && (
+ {statusDate != null && status != null && (
<>
-
-
- {getStatus(currentStatus?.status)}
-
-
+ <>{i18n.STATUS_AT}>
- {currentStatus?.status_date != null && currentStatus?.status != null && (
- <>
-
- <>{i18n.STATUS_AT}>
-
-
-
-
- >
- )}
-
-
+
+
>
)}
-
+ {children}
+ >
);
};
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
index c4dc9b62c74cd5..4e225917f076df 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
@@ -9,6 +9,7 @@
// TODO: Disabling complexity is temporary till this component is refactored as part of lists UI integration
import {
+ EuiButtonIcon,
EuiLoadingSpinner,
EuiFlexGroup,
EuiFlexItem,
@@ -24,6 +25,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useParams, useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
import {
useDeepEqualSelector,
@@ -41,7 +43,7 @@ import {
} from '../../../../../common/components/link_to/redirect_to_detection_engine';
import { SiemSearchBar } from '../../../../../common/components/search_bar';
import { WrapperPage } from '../../../../../common/components/wrapper_page';
-import { Rule } from '../../../../containers/detection_engine/rules';
+import { Rule, useRuleStatus, RuleInfoStatus } from '../../../../containers/detection_engine/rules';
import { useListsConfig } from '../../../../containers/detection_engine/lists/use_lists_config';
import { SpyRoute } from '../../../../../common/utils/route/spy_routes';
import { StepAboutRuleToggleDetails } from '../../../../components/rules/step_about_rule_details';
@@ -101,6 +103,7 @@ import {
import * as detectionI18n from '../../translations';
import * as ruleI18n from '../translations';
+import * as statusI18n from '../../../../components/rules/rule_status/translations';
import * as i18n from './translations';
import { isTab } from '../../../../../common/components/accessibility/helpers';
import { NeedAdminForUpdateRulesCallOut } from '../../../../components/callouts/need_admin_for_update_callout';
@@ -179,6 +182,15 @@ const RuleDetailsPageComponent = () => {
const loading = userInfoLoading || listsConfigLoading;
const { detailName: ruleId } = useParams<{ detailName: string }>();
const { rule: maybeRule, refresh: refreshRule, loading: ruleLoading } = useRuleAsync(ruleId);
+ const [loadingStatus, ruleStatus, fetchRuleStatus] = useRuleStatus(ruleId);
+ const [currentStatus, setCurrentStatus] = useState(
+ ruleStatus?.current_status ?? null
+ );
+ useEffect(() => {
+ if (!deepEqual(currentStatus, ruleStatus?.current_status)) {
+ setCurrentStatus(ruleStatus?.current_status ?? null);
+ }
+ }, [currentStatus, ruleStatus, setCurrentStatus]);
const [rule, setRule] = useState(null);
const isLoading = ruleLoading && rule == null;
// This is used to re-trigger api rule status when user de/activate rule
@@ -302,33 +314,65 @@ const RuleDetailsPageComponent = () => {
),
[ruleDetailTabs, ruleDetailTab, setRuleDetailTab]
);
+
+ const handleRefresh = useCallback(() => {
+ if (fetchRuleStatus != null && ruleId != null) {
+ fetchRuleStatus(ruleId);
+ }
+ }, [fetchRuleStatus, ruleId]);
+
+ const ruleStatusInfo = useMemo(() => {
+ return loadingStatus ? (
+
+
+
+ ) : (
+ <>
+
+
+
+ >
+ );
+ }, [currentStatus, loadingStatus, handleRefresh]);
const ruleError = useMemo(() => {
- if (
- rule?.status === 'failed' &&
+ if (loadingStatus) {
+ return (
+
+
+
+ );
+ } else if (
+ currentStatus?.status === 'failed' &&
ruleDetailTab === RuleDetailTabs.alerts &&
- rule?.last_failure_at != null
+ currentStatus?.last_failure_at != null
) {
return (
);
} else if (
- (rule?.status === 'warning' || rule?.status === 'partial failure') &&
+ (currentStatus?.status === 'warning' || currentStatus?.status === 'partial failure') &&
ruleDetailTab === RuleDetailTabs.alerts &&
- rule?.last_success_at != null
+ currentStatus?.last_success_at != null
) {
return (
);
}
return null;
- }, [rule, ruleDetailTab]);
+ }, [ruleDetailTab, currentStatus, loadingStatus]);
const updateDateRangeCallback = useCallback(
({ x }) => {
@@ -500,7 +544,15 @@ const RuleDetailsPageComponent = () => {
>,
]
: []),
- ,
+ <>
+
+
+ {statusI18n.STATUS}
+ {':'}
+
+ {ruleStatusInfo}
+
+ >,
]}
title={title}
>
From 9ca41438f1375e257687c027a406f7747b5e2a25 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?=
Date: Wed, 17 Feb 2021 23:15:16 +0100
Subject: [PATCH 41/79] Update backport to 5.6.6 (#91703)
---
package.json | 2 +-
yarn.lock | 18 +++++++++---------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/package.json b/package.json
index a7959f047ab5c0..96db1977237d1c 100644
--- a/package.json
+++ b/package.json
@@ -592,7 +592,7 @@
"babel-plugin-require-context-hook": "^1.0.0",
"babel-plugin-styled-components": "^1.10.7",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
- "backport": "^5.6.4",
+ "backport": "^5.6.6",
"base64-js": "^1.3.1",
"base64url": "^3.0.1",
"broadcast-channel": "^3.0.3",
diff --git a/yarn.lock b/yarn.lock
index 6cb2a6864eb752..836c067c73324a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9020,10 +9020,10 @@ bach@^1.0.0:
async-settle "^1.0.0"
now-and-later "^2.0.0"
-backport@^5.6.4:
- version "5.6.4"
- resolved "https://registry.yarnpkg.com/backport/-/backport-5.6.4.tgz#8cf4bc750b26d27161306858ee9069218ad7cdfd"
- integrity sha512-ZhuZcGxOBHBXFBCwweVf02b+KhWe0tdgg71jPSl583YYxlru+JBRH+TFM8S0J6/6YUuTWO81M9funjehJ18jqg==
+backport@^5.6.6:
+ version "5.6.6"
+ resolved "https://registry.yarnpkg.com/backport/-/backport-5.6.6.tgz#cb03f948a36386734fa491343b93f4ca280e00f3"
+ integrity sha512-8O7z0q6m5DfQgrhLDUOLcH2y/rXwSgqv5WIWSSoZpPyRdPpmd3xApIfohlJ3oBX9W0TdbO3aKzaV00qg3H9P7w==
dependencies:
"@octokit/rest" "^18.0.12"
"@types/lodash.difference" "^4.5.6"
@@ -9040,7 +9040,7 @@ backport@^5.6.4:
lodash.isstring "^4.0.1"
lodash.uniq "^4.5.0"
make-dir "^3.1.0"
- ora "^5.2.0"
+ ora "^5.3.0"
safe-json-stringify "^1.2.0"
strip-json-comments "^3.1.1"
winston "^3.3.3"
@@ -22186,10 +22186,10 @@ ora@^4.0.3, ora@^4.0.4:
strip-ansi "^6.0.0"
wcwidth "^1.0.1"
-ora@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/ora/-/ora-5.2.0.tgz#de10bfd2d15514384af45f3fa9d9b1aaf344fda1"
- integrity sha512-+wG2v8TUU8EgzPHun1k/n45pXquQ9fHnbXVetl9rRgO6kjZszGGbraF3XPTIdgeA+s1lbRjSEftAnyT0w8ZMvQ==
+ora@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f"
+ integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==
dependencies:
bl "^4.0.3"
chalk "^4.1.0"
From da780f5671b06203c3dc5aeb4026377078a888ec Mon Sep 17 00:00:00 2001
From: Ryland Herrick
Date: Wed, 17 Feb 2021 16:52:48 -0600
Subject: [PATCH 42/79] [Security Solution][Detections] Indicator path followup
(#91747)
* Remove eslint issue by not permuting our input
We instead return a new object from our enrich function.
* Fixes editing of non-indicator rules
If the user edits a rule without visiting the About tab, they will
receive a value of threatIndicatorPath: '' which we'll then try to send
to the backend, but it gets rejected.
By removing this defaulting logic we get the correct behavior: existing
rules default to threatIndicatorPath: undefined, which gets stripped
before being sent to the backend. If the rule is an indicator rule, the
value will be persisted as expected.
* Move default indicator path to common constant
---
.../security_solution/common/constants.ts | 5 +++
.../schemas/request/rule_schemas.mock.ts | 3 +-
.../schemas/response/rules_schema.mocks.ts | 3 +-
.../rules/step_about_rule/default_value.ts | 1 -
.../rules/step_about_rule/index.tsx | 3 +-
.../detection_engine/rules/helpers.test.tsx | 1 -
.../pages/detection_engine/rules/helpers.tsx | 2 +-
.../threat_mapping/build_threat_enrichment.ts | 3 +-
.../enrich_signal_threat_matches.test.ts | 45 ++++++++++---------
.../enrich_signal_threat_matches.ts | 22 ++++-----
10 files changed, 48 insertions(+), 40 deletions(-)
diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts
index 34fcfa5f0befdb..bc71df5d9e0084 100644
--- a/x-pack/plugins/security_solution/common/constants.ts
+++ b/x-pack/plugins/security_solution/common/constants.ts
@@ -45,6 +45,11 @@ export const DEFAULT_RULE_REFRESH_INTERVAL_VALUE = 60000; // ms
export const DEFAULT_RULE_REFRESH_IDLE_VALUE = 2700000; // ms
export const DEFAULT_RULE_NOTIFICATION_QUERY_SIZE = 100;
+// Document path where threat indicator fields are expected. Used as
+// both the source of enrichment fields and the destination for enrichment in
+// the generated detection alert
+export const DEFAULT_INDICATOR_PATH = 'threat.indicator';
+
export enum SecurityPageName {
detections = 'detections',
overview = 'overview',
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.mock.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.mock.ts
index fb29e37a53fdbe..3bae9551f4df79 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.mock.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.mock.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { DEFAULT_INDICATOR_PATH } from '../../../constants';
import {
MachineLearningCreateSchema,
MachineLearningUpdateSchema,
@@ -56,7 +57,7 @@ export const getCreateThreatMatchRulesSchemaMock = (
rule_id: ruleId,
threat_query: '*:*',
threat_index: ['list-index'],
- threat_indicator_path: 'threat.indicator',
+ threat_indicator_path: DEFAULT_INDICATOR_PATH,
threat_mapping: [
{
entries: [
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts
index cf07389e207b34..8dc7427ed09331 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { DEFAULT_INDICATOR_PATH } from '../../../constants';
import { getListArrayMock } from '../types/lists.mock';
import { RulesSchema } from './rules_schema';
@@ -150,7 +151,7 @@ export const getThreatMatchingSchemaPartialMock = (enabled = false): Partial = ({
euiFieldProps: {
fullWidth: true,
disabled: isLoading,
- placeholder: 'threat.indicator',
+ placeholder: DEFAULT_INDICATOR_PATH,
},
}}
/>
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
index 111eb8a5594a8d..f0511602bd67f7 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
@@ -116,7 +116,6 @@ describe('rule helpers', () => {
severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false },
tags: ['tag1', 'tag2'],
threat: getThreatMock(),
- threatIndicatorPath: '',
timestampOverride: 'event.ingested',
};
const scheduleRuleStepData = { from: '0s', interval: '5m' };
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx
index d37c2d9141f5d4..c862d484b282b3 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx
@@ -180,7 +180,7 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu
},
falsePositives,
threat: threat as Threats,
- threatIndicatorPath: threatIndicatorPath ?? '',
+ threatIndicatorPath,
};
};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts
index 4f38f2db9230a9..cdbafe692c6305 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts
@@ -5,13 +5,12 @@
* 2.0.
*/
+import { DEFAULT_INDICATOR_PATH } from '../../../../../common/constants';
import { SignalSearchResponse, SignalsEnrichment } from '../types';
import { enrichSignalThreatMatches } from './enrich_signal_threat_matches';
import { getThreatList } from './get_threat_list';
import { BuildThreatEnrichmentOptions, GetMatchedThreats } from './types';
-const DEFAULT_INDICATOR_PATH = 'threat.indicator';
-
export const buildThreatEnrichment = ({
buildRuleMessage,
exceptionItems,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts
index fada3141168711..f98f0c88a2dfae 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts
@@ -6,6 +6,7 @@
*/
import { get } from 'lodash';
+import { DEFAULT_INDICATOR_PATH } from '../../../../../common/constants';
import { getThreatListItemMock } from './build_threat_mapping_filter.mock';
import {
@@ -93,7 +94,7 @@ describe('buildMatchedIndicator', () => {
const indicators = buildMatchedIndicator({
queries: [],
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(indicators).toEqual([]);
@@ -103,7 +104,7 @@ describe('buildMatchedIndicator', () => {
const [indicator] = buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(get(indicator, 'matched.atomic')).toEqual('domain_1');
@@ -113,7 +114,7 @@ describe('buildMatchedIndicator', () => {
const [indicator] = buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(get(indicator, 'matched.field')).toEqual('event.field');
@@ -123,7 +124,7 @@ describe('buildMatchedIndicator', () => {
const [indicator] = buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(get(indicator, 'matched.type')).toEqual('type_1');
@@ -152,7 +153,7 @@ describe('buildMatchedIndicator', () => {
const indicators = buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(indicators).toHaveLength(queries.length);
@@ -162,7 +163,7 @@ describe('buildMatchedIndicator', () => {
const indicators = buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(indicators).toEqual([
@@ -227,7 +228,7 @@ describe('buildMatchedIndicator', () => {
const indicators = buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(indicators).toEqual([
@@ -252,7 +253,7 @@ describe('buildMatchedIndicator', () => {
const indicators = buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(indicators).toEqual([
@@ -284,7 +285,7 @@ describe('buildMatchedIndicator', () => {
const indicators = buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
});
expect(indicators).toEqual([
@@ -316,7 +317,7 @@ describe('buildMatchedIndicator', () => {
buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
})
).toThrowError('Expected indicator field to be an object, but found: not an object');
});
@@ -337,7 +338,7 @@ describe('buildMatchedIndicator', () => {
buildMatchedIndicator({
queries,
threats,
- indicatorPath: 'threat.indicator',
+ indicatorPath: DEFAULT_INDICATOR_PATH,
})
).toThrowError('Expected indicator field to be an object, but found: not an object');
});
@@ -366,7 +367,7 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- 'threat.indicator'
+ DEFAULT_INDICATOR_PATH
);
expect(enrichedSignals.hits.hits).toEqual([]);
@@ -381,10 +382,10 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- 'threat.indicator'
+ DEFAULT_INDICATOR_PATH
);
const [enrichedHit] = enrichedSignals.hits.hits;
- const indicators = get(enrichedHit._source, 'threat.indicator');
+ const indicators = get(enrichedHit._source, DEFAULT_INDICATOR_PATH);
expect(indicators).toEqual([
{ existing: 'indicator' },
@@ -406,10 +407,10 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- 'threat.indicator'
+ DEFAULT_INDICATOR_PATH
);
const [enrichedHit] = enrichedSignals.hits.hits;
- const indicators = get(enrichedHit._source, 'threat.indicator');
+ const indicators = get(enrichedHit._source, DEFAULT_INDICATOR_PATH);
expect(indicators).toEqual([
{
@@ -427,10 +428,10 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- 'threat.indicator'
+ DEFAULT_INDICATOR_PATH
);
const [enrichedHit] = enrichedSignals.hits.hits;
- const indicators = get(enrichedHit._source, 'threat.indicator');
+ const indicators = get(enrichedHit._source, DEFAULT_INDICATOR_PATH);
expect(indicators).toEqual([
{ existing: 'indicator' },
@@ -450,7 +451,7 @@ describe('enrichSignalThreatMatches', () => {
});
const signals = getSignalsResponseMock([signalHit]);
await expect(() =>
- enrichSignalThreatMatches(signals, getMatchedThreats, 'threat.indicator')
+ enrichSignalThreatMatches(signals, getMatchedThreats, DEFAULT_INDICATOR_PATH)
).rejects.toThrowError('Expected threat field to be an object, but found: whoops');
});
@@ -486,7 +487,7 @@ describe('enrichSignalThreatMatches', () => {
'custom_threat.custom_indicator'
);
const [enrichedHit] = enrichedSignals.hits.hits;
- const indicators = get(enrichedHit._source, 'threat.indicator');
+ const indicators = get(enrichedHit._source, DEFAULT_INDICATOR_PATH);
expect(indicators).toEqual([
{
@@ -529,13 +530,13 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- 'threat.indicator'
+ DEFAULT_INDICATOR_PATH
);
expect(enrichedSignals.hits.total).toEqual(expect.objectContaining({ value: 1 }));
expect(enrichedSignals.hits.hits).toHaveLength(1);
const [enrichedHit] = enrichedSignals.hits.hits;
- const indicators = get(enrichedHit._source, 'threat.indicator');
+ const indicators = get(enrichedHit._source, DEFAULT_INDICATOR_PATH);
expect(indicators).toEqual([
{
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts
index c5b032207f1c5a..761a58224fac58 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts
@@ -6,6 +6,7 @@
*/
import { get, isObject } from 'lodash';
+import { DEFAULT_INDICATOR_PATH } from '../../../../../common/constants';
import type { SignalSearchResponse, SignalSourceHit } from '../types';
import type {
@@ -91,7 +92,7 @@ export const enrichSignalThreatMatches = async (
if (!isObject(threat)) {
throw new Error(`Expected threat field to be an object, but found: ${threat}`);
}
- const existingIndicatorValue = get(signalHit._source, 'threat.indicator') ?? [];
+ const existingIndicatorValue = get(signalHit._source, DEFAULT_INDICATOR_PATH) ?? [];
const existingIndicators = [existingIndicatorValue].flat(); // ensure indicators is an array
return {
@@ -105,14 +106,15 @@ export const enrichSignalThreatMatches = async (
},
};
});
- /* eslint-disable require-atomic-updates */
- signals.hits.hits = enrichedSignals;
- if (isObject(signals.hits.total)) {
- signals.hits.total.value = enrichedSignals.length;
- } else {
- signals.hits.total = enrichedSignals.length;
- }
- /* eslint-enable require-atomic-updates */
- return signals;
+ return {
+ ...signals,
+ hits: {
+ ...signals.hits,
+ hits: enrichedSignals,
+ total: isObject(signals.hits.total)
+ ? { ...signals.hits.total, value: enrichedSignals.length }
+ : enrichedSignals.length,
+ },
+ };
};
From 31810c65566ce58acef283062d3f286444cd3da8 Mon Sep 17 00:00:00 2001
From: igoristic
Date: Wed, 17 Feb 2021 18:09:58 -0500
Subject: [PATCH 43/79] Added flag to change the alerts app context requirment
(#91726)
---
x-pack/plugins/monitoring/common/constants.ts | 5 +++++
.../public/alerts/ccr_read_exceptions_alert/index.tsx | 8 ++++++--
.../public/alerts/cpu_usage_alert/cpu_usage_alert.tsx | 8 ++++++--
.../monitoring/public/alerts/disk_usage_alert/index.tsx | 8 ++++++--
.../public/alerts/large_shard_size_alert/index.tsx | 8 ++++++--
.../public/alerts/legacy_alert/legacy_alert.tsx | 8 ++++++--
.../monitoring/public/alerts/memory_usage_alert/index.tsx | 8 ++++++--
.../missing_monitoring_data_alert.tsx | 8 ++++++--
.../public/alerts/thread_pool_rejections_alert/index.tsx | 3 ++-
9 files changed, 49 insertions(+), 15 deletions(-)
diff --git a/x-pack/plugins/monitoring/common/constants.ts b/x-pack/plugins/monitoring/common/constants.ts
index 8b1e91e2caf67e..18c96106566486 100644
--- a/x-pack/plugins/monitoring/common/constants.ts
+++ b/x-pack/plugins/monitoring/common/constants.ts
@@ -580,6 +580,11 @@ export const ALERT_ACTION_TYPE_EMAIL = '.email';
*/
export const ALERT_ACTION_TYPE_LOG = '.server-log';
+/**
+ * To enable modifing of alerts in under actions
+ */
+export const ALERT_REQUIRES_APP_CONTEXT = false;
+
export const ALERT_EMAIL_SERVICES = ['gmail', 'hotmail', 'icloud', 'outlook365', 'ses', 'yahoo'];
/**
diff --git a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx
index f0b58b41015052..3088e2a75c3492 100644
--- a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx
@@ -9,7 +9,11 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { Expression, Props } from '../components/param_details_form/expression';
import { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public';
-import { ALERT_CCR_READ_EXCEPTIONS, ALERT_DETAILS } from '../../../common/constants';
+import {
+ ALERT_CCR_READ_EXCEPTIONS,
+ ALERT_DETAILS,
+ ALERT_REQUIRES_APP_CONTEXT,
+} from '../../../common/constants';
import { AlertTypeParams } from '../../../../alerts/common';
interface ValidateOptions extends AlertTypeParams {
@@ -45,6 +49,6 @@ export function createCCRReadExceptionsAlertType(): AlertTypeModel {
return {
@@ -26,6 +30,6 @@ export function createDiskUsageAlertType(): AlertTypeModel
),
validate,
defaultActionMessage: '{{context.internalFullMessage}}',
- requiresAppContext: true,
+ requiresAppContext: ALERT_REQUIRES_APP_CONTEXT,
};
}
diff --git a/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx b/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx
index 3f23035f526496..87850f893797a2 100644
--- a/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx
@@ -10,7 +10,11 @@ import { i18n } from '@kbn/i18n';
import { EuiTextColor, EuiSpacer } from '@elastic/eui';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
-import { LEGACY_ALERTS, LEGACY_ALERT_DETAILS } from '../../../common/constants';
+import {
+ LEGACY_ALERTS,
+ LEGACY_ALERT_DETAILS,
+ ALERT_REQUIRES_APP_CONTEXT,
+} from '../../../common/constants';
export function createLegacyAlertTypes(): AlertTypeModel[] {
return LEGACY_ALERTS.map((legacyAlert) => {
@@ -34,7 +38,7 @@ export function createLegacyAlertTypes(): AlertTypeModel[] {
),
defaultActionMessage: '{{context.internalFullMessage}}',
validate: () => ({ errors: {} }),
- requiresAppContext: true,
+ requiresAppContext: ALERT_REQUIRES_APP_CONTEXT,
};
});
}
diff --git a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx
index 8437faf2258b97..6639b4f2f6a489 100644
--- a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx
@@ -11,7 +11,11 @@ import { Expression, Props } from '../components/param_details_form/expression';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
-import { ALERT_MEMORY_USAGE, ALERT_DETAILS } from '../../../common/constants';
+import {
+ ALERT_MEMORY_USAGE,
+ ALERT_DETAILS,
+ ALERT_REQUIRES_APP_CONTEXT,
+} from '../../../common/constants';
export function createMemoryUsageAlertType(): AlertTypeModel {
return {
@@ -26,6 +30,6 @@ export function createMemoryUsageAlertType(): AlertTypeModel
Date: Thu, 18 Feb 2021 01:37:52 +0100
Subject: [PATCH 44/79] [Security Solution][Detections] Pre-refactoring for the
rule management table (#91302)
**Related to:** https://github.com/elastic/kibana/pull/89877
## Summary
This is based on https://github.com/elastic/kibana/pull/89877 and the kind of pre-refactoring that has been done there.
Mainly this:
- consolidates application logic in a single place (moves the reducer and the side effects close to each other, etc)
- removes some of the redundant state, leverages the reducer as the source of truth for state
- makes it easier to dispatch events, removes some of the noise
While this refactoring is a totally unfinished work, and might look not good enough (or at all), still I'd like to merge it because of the logic consolidation. I'm going to finalize the refactoring later when I start implementing new filters and other table UX improvements. So the code is going to become better and maybe even quite different from what it is right now. (Btw because of that, I'm not adding or removing any tests here because this is an intermediate kind of state of the code).
### Checklist
- [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
---
.../components/rules/rule_switch/index.tsx | 5 +-
.../detection_engine/rules/index.ts | 2 +-
.../rules/rules_table/index.ts | 11 ++
.../rules/rules_table/rules_table_facade.ts | 84 +++++++++
.../rules_table/rules_table_reducer.test.ts} | 31 ++--
.../rules/rules_table/rules_table_reducer.ts | 165 ++++++++++++++++++
.../{ => rules_table}/use_rules.test.tsx | 4 +-
.../rules/{ => rules_table}/use_rules.tsx | 8 +-
.../rules/rules_table/use_rules_table.ts | 131 ++++++++++++++
.../detection_engine/rules/all/actions.tsx | 13 +-
.../rules/all/batch_actions.tsx | 4 +-
.../detection_engine/rules/all/columns.tsx | 6 +-
.../detection_engine/rules/all/index.test.tsx | 52 ++++--
.../detection_engine/rules/all/reducer.ts | 156 -----------------
.../rules/all/rules_tables.tsx | 151 ++++++----------
15 files changed, 522 insertions(+), 301 deletions(-)
create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/index.ts
create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_facade.ts
rename x-pack/plugins/security_solution/public/detections/{pages/detection_engine/rules/all/reducer.test.ts => containers/detection_engine/rules/rules_table/rules_table_reducer.test.ts} (94%)
create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.ts
rename x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/{ => rules_table}/use_rules.test.tsx (99%)
rename x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/{ => rules_table}/use_rules.tsx (93%)
create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules_table.ts
delete mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/reducer.ts
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx
index 4f87f9332475b1..268ffe620ad4ea 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx
@@ -17,9 +17,8 @@ import styled from 'styled-components';
import React, { useMemo, useCallback, useState, useEffect } from 'react';
import * as i18n from '../../../pages/detection_engine/rules/translations';
-import { enableRules } from '../../../containers/detection_engine/rules';
+import { enableRules, RulesTableAction } from '../../../containers/detection_engine/rules';
import { enableRulesAction } from '../../../pages/detection_engine/rules/all/actions';
-import { Action } from '../../../pages/detection_engine/rules/all/reducer';
import { useStateToaster, displayErrorToast } from '../../../../common/components/toasters';
import { bucketRulesResponse } from '../../../pages/detection_engine/rules/all/helpers';
@@ -33,7 +32,7 @@ const StaticSwitch = styled(EuiSwitch)`
StaticSwitch.displayName = 'StaticSwitch';
export interface RuleSwitchProps {
- dispatch?: React.Dispatch;
+ dispatch?: React.Dispatch;
id: string;
enabled: boolean;
isDisabled?: boolean;
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts
index 751cde64bb87d6..8128eb045f759d 100644
--- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts
@@ -10,6 +10,6 @@ export * from './use_update_rule';
export * from './use_create_rule';
export * from './types';
export * from './use_rule';
-export * from './use_rules';
+export * from './rules_table';
export * from './use_pre_packaged_rules';
export * from './use_rule_status';
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/index.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/index.ts
new file mode 100644
index 00000000000000..a05349fa4fa3a8
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/index.ts
@@ -0,0 +1,11 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export * from './rules_table_facade';
+export * from './rules_table_reducer';
+export * from './use_rules';
+export * from './use_rules_table';
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_facade.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_facade.ts
new file mode 100644
index 00000000000000..77c327c9f7939b
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_facade.ts
@@ -0,0 +1,84 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { Dispatch } from 'react';
+import { Rule, FilterOptions, PaginationOptions } from '../types';
+import { RulesTableAction, LoadingRuleAction } from './rules_table_reducer';
+
+export interface RulesTableFacade {
+ setRules(newRules: Rule[], newPagination: Partial): void;
+ updateRules(rules: Rule[]): void;
+ updateOptions(filter: Partial, pagination: Partial): void;
+ actionStarted(actionType: LoadingRuleAction, ruleIds: string[]): void;
+ actionStopped(): void;
+ setShowIdleModal(show: boolean): void;
+ setLastRefreshDate(): void;
+ setAutoRefreshOn(on: boolean): void;
+}
+
+export const createRulesTableFacade = (dispatch: Dispatch): RulesTableFacade => {
+ return {
+ setRules: (newRules: Rule[], newPagination: Partial) => {
+ dispatch({
+ type: 'setRules',
+ rules: newRules,
+ pagination: newPagination,
+ });
+ },
+
+ updateRules: (rules: Rule[]) => {
+ dispatch({
+ type: 'updateRules',
+ rules,
+ });
+ },
+
+ updateOptions: (filter: Partial, pagination: Partial) => {
+ dispatch({
+ type: 'updateFilterOptions',
+ filterOptions: filter,
+ pagination,
+ });
+ },
+
+ actionStarted: (actionType: LoadingRuleAction, ruleIds: string[]) => {
+ dispatch({
+ type: 'loadingRuleIds',
+ actionType,
+ ids: ruleIds,
+ });
+ },
+
+ actionStopped: () => {
+ dispatch({
+ type: 'loadingRuleIds',
+ actionType: null,
+ ids: [],
+ });
+ },
+
+ setShowIdleModal: (show: boolean) => {
+ dispatch({
+ type: 'setShowIdleModal',
+ show,
+ });
+ },
+
+ setLastRefreshDate: () => {
+ dispatch({
+ type: 'setLastRefreshDate',
+ });
+ },
+
+ setAutoRefreshOn: (on: boolean) => {
+ dispatch({
+ type: 'setAutoRefreshOn',
+ on,
+ });
+ },
+ };
+};
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/reducer.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.test.ts
similarity index 94%
rename from x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/reducer.test.ts
rename to x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.test.ts
index 20e4e5f7473492..1a45c60dba58a4 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/reducer.test.ts
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.test.ts
@@ -5,13 +5,17 @@
* 2.0.
*/
-import { FilterOptions, PaginationOptions } from '../../../../containers/detection_engine/rules';
+import { mockRule } from '../../../../pages/detection_engine/rules/all/__mocks__/mock';
+import { FilterOptions, PaginationOptions } from '../types';
+import { RulesTableAction, RulesTableState, createRulesTableReducer } from './rules_table_reducer';
-import { Action, State, allRulesReducer } from './reducer';
-import { mockRule } from './__mocks__/mock';
-
-const initialState: State = {
- exportRuleIds: [],
+const initialState: RulesTableState = {
+ rules: [],
+ pagination: {
+ page: 1,
+ perPage: 20,
+ total: 0,
+ },
filterOptions: {
filter: '',
sortField: 'enabled',
@@ -20,29 +24,24 @@ const initialState: State = {
showCustomRules: false,
showElasticRules: false,
},
- loadingRuleIds: [],
loadingRulesAction: null,
- pagination: {
- page: 1,
- perPage: 20,
- total: 0,
- },
- rules: [],
+ loadingRuleIds: [],
selectedRuleIds: [],
+ exportRuleIds: [],
lastUpdated: 0,
- showIdleModal: false,
isRefreshOn: false,
+ showIdleModal: false,
};
describe('allRulesReducer', () => {
- let reducer: (state: State, action: Action) => State;
+ let reducer: (state: RulesTableState, action: RulesTableAction) => RulesTableState;
beforeEach(() => {
jest.useFakeTimers();
jest
.spyOn(global.Date, 'now')
.mockImplementationOnce(() => new Date('2020-10-31T11:01:58.135Z').valueOf());
- reducer = allRulesReducer({ current: undefined });
+ reducer = createRulesTableReducer({ current: undefined });
});
afterEach(() => {
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.ts
new file mode 100644
index 00000000000000..edcf4f6395d895
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.ts
@@ -0,0 +1,165 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type React from 'react';
+import { EuiBasicTable } from '@elastic/eui';
+import { FilterOptions, PaginationOptions, Rule } from '../types';
+
+export type LoadingRuleAction =
+ | 'load'
+ | 'duplicate'
+ | 'enable'
+ | 'disable'
+ | 'export'
+ | 'delete'
+ | null;
+
+export interface RulesTableState {
+ rules: Rule[];
+ pagination: PaginationOptions;
+ filterOptions: FilterOptions;
+ loadingRulesAction: LoadingRuleAction;
+ loadingRuleIds: string[];
+ selectedRuleIds: string[];
+ exportRuleIds: string[];
+ lastUpdated: number;
+ isRefreshOn: boolean;
+ showIdleModal: boolean;
+}
+
+export type RulesTableAction =
+ | { type: 'setRules'; rules: Rule[]; pagination: Partial }
+ | { type: 'updateRules'; rules: Rule[] }
+ | {
+ type: 'updateFilterOptions';
+ filterOptions: Partial;
+ pagination: Partial;
+ }
+ | { type: 'loadingRuleIds'; ids: string[]; actionType: LoadingRuleAction }
+ | { type: 'selectedRuleIds'; ids: string[] }
+ | { type: 'exportRuleIds'; ids: string[] }
+ | { type: 'setLastRefreshDate' }
+ | { type: 'setAutoRefreshOn'; on: boolean }
+ | { type: 'setShowIdleModal'; show: boolean }
+ | { type: 'failure' };
+
+export const createRulesTableReducer = (
+ tableRef: React.MutableRefObject | undefined>
+) => {
+ const rulesTableReducer = (state: RulesTableState, action: RulesTableAction): RulesTableState => {
+ switch (action.type) {
+ case 'setRules': {
+ if (
+ tableRef != null &&
+ tableRef.current != null &&
+ tableRef.current.changeSelection != null
+ ) {
+ // for future devs: eui basic table is not giving us a prop to set the value, so
+ // we are using the ref in setTimeout to reset on the next loop so that we
+ // do not get a warning telling us we are trying to update during a render
+ window.setTimeout(() => tableRef?.current?.changeSelection([]), 0);
+ }
+
+ return {
+ ...state,
+ rules: action.rules,
+ selectedRuleIds: [],
+ loadingRuleIds: [],
+ loadingRulesAction: null,
+ pagination: {
+ ...state.pagination,
+ ...action.pagination,
+ },
+ };
+ }
+ case 'updateRules': {
+ const ruleIds = state.rules.map((r) => r.id);
+ const updatedRules = action.rules.reduce((rules, updatedRule) => {
+ let newRules = rules;
+ if (ruleIds.includes(updatedRule.id)) {
+ newRules = newRules.map((r) => (updatedRule.id === r.id ? updatedRule : r));
+ } else {
+ newRules = [...newRules, updatedRule];
+ }
+ return newRules;
+ }, state.rules);
+ const updatedRuleIds = action.rules.map((r) => r.id);
+ const newLoadingRuleIds = state.loadingRuleIds.filter((id) => !updatedRuleIds.includes(id));
+ return {
+ ...state,
+ rules: updatedRules,
+ loadingRuleIds: newLoadingRuleIds,
+ loadingRulesAction: newLoadingRuleIds.length === 0 ? null : state.loadingRulesAction,
+ };
+ }
+ case 'updateFilterOptions': {
+ return {
+ ...state,
+ filterOptions: {
+ ...state.filterOptions,
+ ...action.filterOptions,
+ },
+ pagination: {
+ ...state.pagination,
+ ...action.pagination,
+ },
+ };
+ }
+ case 'loadingRuleIds': {
+ return {
+ ...state,
+ loadingRuleIds: action.actionType == null ? [] : [...state.loadingRuleIds, ...action.ids],
+ loadingRulesAction: action.actionType,
+ };
+ }
+ case 'selectedRuleIds': {
+ return {
+ ...state,
+ selectedRuleIds: action.ids,
+ };
+ }
+ case 'exportRuleIds': {
+ return {
+ ...state,
+ loadingRuleIds: action.ids,
+ loadingRulesAction: 'export',
+ exportRuleIds: action.ids,
+ };
+ }
+ case 'setLastRefreshDate': {
+ return {
+ ...state,
+ lastUpdated: Date.now(),
+ };
+ }
+ case 'setAutoRefreshOn': {
+ return {
+ ...state,
+ isRefreshOn: action.on,
+ };
+ }
+ case 'setShowIdleModal': {
+ return {
+ ...state,
+ showIdleModal: action.show,
+ isRefreshOn: !action.show,
+ };
+ }
+ case 'failure': {
+ return {
+ ...state,
+ rules: [],
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+ };
+
+ return rulesTableReducer;
+};
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rules.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules.test.tsx
similarity index 99%
rename from x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rules.test.tsx
rename to x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules.test.tsx
index 5d226e8152596e..4532d3427375b1 100644
--- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rules.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules.test.tsx
@@ -7,9 +7,9 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useRules, UseRules, ReturnRules } from './use_rules';
-import * as api from './api';
+import * as api from '../api';
-jest.mock('./api');
+jest.mock('../api');
describe('useRules', () => {
beforeEach(() => {
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rules.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules.tsx
similarity index 93%
rename from x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rules.tsx
rename to x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules.tsx
index 02784333992be2..f3c90ae12ae33c 100644
--- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rules.tsx
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules.tsx
@@ -7,10 +7,10 @@
import { useEffect, useState, useRef } from 'react';
-import { FetchRulesResponse, FilterOptions, PaginationOptions, Rule } from './types';
-import { errorToToaster, useStateToaster } from '../../../../common/components/toasters';
-import { fetchRules } from './api';
-import * as i18n from './translations';
+import { FetchRulesResponse, FilterOptions, PaginationOptions, Rule } from '../types';
+import { errorToToaster, useStateToaster } from '../../../../../common/components/toasters';
+import { fetchRules } from '../api';
+import * as i18n from '../translations';
export type ReturnRules = [boolean, FetchRulesResponse | null, () => Promise];
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules_table.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules_table.ts
new file mode 100644
index 00000000000000..f31b2894301ba6
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules_table.ts
@@ -0,0 +1,131 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { Dispatch, useMemo, useReducer, useEffect, useRef } from 'react';
+import { EuiBasicTable } from '@elastic/eui';
+
+import { errorToToaster, useStateToaster } from '../../../../../common/components/toasters';
+import * as i18n from '../translations';
+
+import { fetchRules } from '../api';
+import { createRulesTableReducer, RulesTableState, RulesTableAction } from './rules_table_reducer';
+import { createRulesTableFacade, RulesTableFacade } from './rules_table_facade';
+
+const INITIAL_SORT_FIELD = 'enabled';
+
+const initialStateDefaults: RulesTableState = {
+ rules: [],
+ pagination: {
+ page: 1,
+ perPage: 20,
+ total: 0,
+ },
+ filterOptions: {
+ filter: '',
+ sortField: INITIAL_SORT_FIELD,
+ sortOrder: 'desc',
+ tags: [],
+ showCustomRules: false,
+ showElasticRules: false,
+ },
+ loadingRulesAction: null,
+ loadingRuleIds: [],
+ selectedRuleIds: [],
+ exportRuleIds: [],
+ lastUpdated: 0,
+ isRefreshOn: true,
+ showIdleModal: false,
+};
+
+export interface UseRulesTableParams {
+ tableRef: React.MutableRefObject | undefined>;
+ initialStateOverride?: Partial;
+}
+
+export interface UseRulesTableReturn extends RulesTableFacade {
+ state: RulesTableState;
+ dispatch: Dispatch;
+ reFetchRules: () => Promise;
+}
+
+export const useRulesTable = (params: UseRulesTableParams): UseRulesTableReturn => {
+ const { tableRef, initialStateOverride } = params;
+
+ const initialState: RulesTableState = {
+ ...initialStateDefaults,
+ lastUpdated: Date.now(),
+ ...initialStateOverride,
+ };
+
+ const reducer = useMemo(() => createRulesTableReducer(tableRef), [tableRef]);
+ const [state, dispatch] = useReducer(reducer, initialState);
+ const facade = useRef(createRulesTableFacade(dispatch));
+
+ const reFetchRules = useRef<() => Promise>(() => Promise.resolve());
+ const [, dispatchToaster] = useStateToaster();
+
+ const { pagination, filterOptions } = state;
+ const filterTags = filterOptions.tags.sort().join();
+
+ useEffect(() => {
+ let isSubscribed = true;
+ const abortCtrl = new AbortController();
+
+ const fetchData = async () => {
+ try {
+ facade.current.actionStarted('load', []);
+
+ const fetchRulesResult = await fetchRules({
+ filterOptions,
+ pagination,
+ signal: abortCtrl.signal,
+ });
+
+ if (isSubscribed) {
+ facade.current.setRules(fetchRulesResult.data, {
+ page: fetchRulesResult.page,
+ perPage: fetchRulesResult.perPage,
+ total: fetchRulesResult.total,
+ });
+ }
+ } catch (error) {
+ if (isSubscribed) {
+ errorToToaster({ title: i18n.RULE_AND_TIMELINE_FETCH_FAILURE, error, dispatchToaster });
+ facade.current.setRules([], {});
+ }
+ }
+ if (isSubscribed) {
+ facade.current.actionStopped();
+ }
+ };
+
+ fetchData();
+ reFetchRules.current = () => fetchData();
+
+ return () => {
+ isSubscribed = false;
+ abortCtrl.abort();
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [
+ pagination.page,
+ pagination.perPage,
+ filterOptions.filter,
+ filterOptions.sortField,
+ filterOptions.sortOrder,
+ filterTags,
+ filterOptions.showCustomRules,
+ filterOptions.showElasticRules,
+ ]);
+
+ return {
+ state,
+ dispatch,
+ ...facade.current,
+ reFetchRules: reFetchRules.current,
+ };
+};
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.tsx
index 44b0476501a5b9..3b1f9e620127d8 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.tsx
@@ -13,6 +13,7 @@ import {
duplicateRules,
enableRules,
Rule,
+ RulesTableAction,
} from '../../../../containers/detection_engine/rules';
import { getEditRuleUrl } from '../../../../../common/components/link_to/redirect_to_detection_engine';
@@ -27,7 +28,6 @@ import { track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../../../common/lib/t
import * as i18n from '../translations';
import { bucketRulesResponse } from './helpers';
-import { Action } from './reducer';
export const editRuleAction = (rule: Rule, history: H.History) => {
history.push(getEditRuleUrl(rule.id));
@@ -36,7 +36,7 @@ export const editRuleAction = (rule: Rule, history: H.History) => {
export const duplicateRulesAction = async (
rules: Rule[],
ruleIds: string[],
- dispatch: React.Dispatch,
+ dispatch: React.Dispatch,
dispatchToaster: Dispatch
) => {
try {
@@ -59,13 +59,16 @@ export const duplicateRulesAction = async (
}
};
-export const exportRulesAction = (exportRuleId: string[], dispatch: React.Dispatch) => {
+export const exportRulesAction = (
+ exportRuleId: string[],
+ dispatch: React.Dispatch
+) => {
dispatch({ type: 'exportRuleIds', ids: exportRuleId });
};
export const deleteRulesAction = async (
ruleIds: string[],
- dispatch: React.Dispatch,
+ dispatch: React.Dispatch,
dispatchToaster: Dispatch,
onRuleDeleted?: () => void
) => {
@@ -96,7 +99,7 @@ export const deleteRulesAction = async (
export const enableRulesAction = async (
ids: string[],
enabled: boolean,
- dispatch: React.Dispatch,
+ dispatch: React.Dispatch,
dispatchToaster: Dispatch