From fdd37e3cfb0116772c48fc45c75e8fe61cd55579 Mon Sep 17 00:00:00 2001
From: Jason Stoltzfus
Date: Fri, 9 Jul 2021 17:14:37 -0400
Subject: [PATCH 01/31] Fixed description on Ent Home (#105122)
---
.../product_selector/product_selector.tsx | 10 ++++++----
.../applications/enterprise_search/index.scss | 14 --------------
2 files changed, 6 insertions(+), 18 deletions(-)
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx
index a7eb2424e797a..0dd2b0988b3f4 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx
@@ -19,6 +19,7 @@ import {
EuiFlexItem,
EuiSpacer,
EuiTitle,
+ EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -59,14 +60,15 @@ export const ProductSelector: React.FC = ({ access }) => {
-
+
{i18n.translate('xpack.enterpriseSearch.overview.heading', {
defaultMessage: 'Welcome to Elastic Enterprise Search',
})}
-
-
+
+
+
{config.host
? i18n.translate('xpack.enterpriseSearch.overview.subheading', {
defaultMessage: 'Select a product to get started.',
@@ -75,7 +77,7 @@ export const ProductSelector: React.FC = ({ access }) => {
defaultMessage: 'Choose a product to set up and get started.',
})}
-
+
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.scss b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.scss
index 45bf37def1121..4be8d7322b4c8 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.scss
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.scss
@@ -21,21 +21,7 @@
&__header {
text-align: center;
margin: auto;
- }
-
- &__heading {
- @include euiBreakpoint('xs', 's') {
- font-size: $euiFontSizeXL;
- line-height: map-get(map-get($euiTitles, 'm'), 'line-height');
- }
- }
-
- &__subheading {
- color: $euiColorMediumShade;
- font-size: $euiFontSize;
-
@include euiBreakpoint('m', 'l', 'xl') {
- font-size: $euiFontSizeL;
margin-bottom: $euiSizeL;
}
}
From b7f50c279bf9e348096f2d7b9e289ec1acb5ae47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ece=20=C3=96zalp?=
Date: Fri, 9 Jul 2021 17:17:48 -0400
Subject: [PATCH 02/31] [CTI] updates overview cti panel copy (#105074)
---
.../overview/components/overview_cti_links/translations.ts | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts
index 663ec3a75c902..91abd48eb2b7e 100644
--- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts
+++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/translations.ts
@@ -51,9 +51,10 @@ export const DANGER_TITLE = i18n.translate(
);
export const DANGER_BODY = i18n.translate(
- 'xpack.securitySolution.overview.ctiDashboardDangerPanelBody',
+ 'xpack.securitySolution.overview.ctiDashboardEnableThreatIntel',
{
- defaultMessage: 'You need to enable module in order to view data from different sources.',
+ defaultMessage:
+ 'You need to enable the filebeat threatintel module in order to view data from different sources.',
}
);
From bcc8ee2532133c39e663f73303187a2345ef24a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ece=20=C3=96zalp?=
Date: Fri, 9 Jul 2021 17:28:15 -0400
Subject: [PATCH 03/31] [CTI] makes dashboard links external (#104979)
* [CTI] makes dashboard links external
---
.../cti_disabled_module.tsx | 2 +-
.../threat_intel_panel_view.tsx | 28 ++++++++++---------
2 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx
index e22fec1861f8b..21a4beca72f3b 100644
--- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx
@@ -25,7 +25,7 @@ export const CtiDisabledModuleComponent = () => {
title={i18n.DANGER_TITLE}
body={i18n.DANGER_BODY}
button={
-
+
{i18n.DANGER_BUTTON}
}
diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx
index 4565c16bc2bf6..b34f6e657d39a 100644
--- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/threat_intel_panel_view.tsx
@@ -22,7 +22,6 @@ import { InspectButtonContainer } from '../../../common/components/inspect';
import { HeaderSection } from '../../../common/components/header_section';
import { ID as CTIEventCountQueryId } from '../../containers/overview_cti_links/use_cti_event_counts';
import { CtiListItem } from '../../containers/overview_cti_links/helpers';
-import { LinkButton } from '../../../common/components/links';
import { useKibana } from '../../../common/lib/kibana';
import { CtiInnerPanel } from './cti_inner_panel';
import * as i18n from './translations';
@@ -36,7 +35,7 @@ const DashboardLinkItems = styled(EuiFlexGroup)`
`;
const Title = styled(EuiFlexItem)`
- min-width: 140px;
+ min-width: 110px;
`;
const List = styled.ul`
@@ -45,12 +44,11 @@ const List = styled.ul`
const DashboardRightSideElement = styled(EuiFlexItem)`
align-items: flex-end;
- max-width: 160px;
`;
const RightSideLink = styled(EuiLink)`
text-align: right;
- min-width: 140px;
+ min-width: 180px;
`;
interface ThreatIntelPanelViewProps {
@@ -96,12 +94,12 @@ export const ThreatIntelPanelView: React.FC = ({
const button = useMemo(
() => (
-
+
-
+
),
[buttonHref]
);
@@ -117,7 +115,11 @@ export const ThreatIntelPanelView: React.FC = ({
color={'primary'}
title={i18n.INFO_TITLE}
body={i18n.INFO_BODY}
- button={{i18n.INFO_BUTTON}}
+ button={
+
+ {i18n.INFO_BUTTON}
+
+ }
/>
) : null,
[isDashboardPluginDisabled, threatIntelDashboardDocLink]
@@ -149,9 +151,7 @@ export const ThreatIntelPanelView: React.FC = ({
gutterSize="l"
justifyContent="spaceBetween"
>
-
- {title}
-
+ {title}
= ({
alignItems="center"
justifyContent="flexEnd"
>
-
+
{count}
-
+
{path ? (
- {linkCopy}
+
+ {linkCopy}
+
) : (
{linkCopy}
From 1739b55f94df04b4b99fbc11233cc16d5cef863b Mon Sep 17 00:00:00 2001
From: Constance
Date: Fri, 9 Jul 2021 14:31:01 -0700
Subject: [PATCH 04/31] [Enterprise Search] Fix Error Connecting view not
displaying for auth issues (#105125)
* Fix ent-search authentication to show the error connecting screen
Missed this in #103555
* [Misc] updoot handleConnectionError order/spacing to match
- why? because i've lost control of my life, probably
---
.../server/lib/enterprise_search_request_handler.test.ts | 2 +-
.../server/lib/enterprise_search_request_handler.ts | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.test.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.test.ts
index d0e74f3234c14..f9756119b336c 100644
--- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.test.ts
+++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.test.ts
@@ -403,7 +403,7 @@ describe('EnterpriseSearchRequestHandler', () => {
expect(responseMock.customError).toHaveBeenCalledWith({
statusCode: 502,
body: 'Cannot authenticate Enterprise Search user',
- headers: mockExpectedResponseHeaders,
+ headers: { ...mockExpectedResponseHeaders, [ERROR_CONNECTING_HEADER]: 'true' },
});
expect(mockLogger.error).toHaveBeenCalled();
});
diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts
index 8031fc724f7b3..57b91c2b30c73 100644
--- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts
+++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts
@@ -283,12 +283,11 @@ export class EnterpriseSearchRequestHandler {
handleConnectionError(response: KibanaResponseFactory, e: Error) {
const errorMessage = `Error connecting to Enterprise Search: ${e?.message || e.toString()}`;
+ const headers = { ...this.headers, [ERROR_CONNECTING_HEADER]: 'true' };
this.log.error(errorMessage);
if (e instanceof Error) this.log.debug(e.stack as string);
- const headers = { ...this.headers, [ERROR_CONNECTING_HEADER]: 'true' };
-
return response.customError({ statusCode: 502, headers, body: errorMessage });
}
@@ -298,9 +297,10 @@ export class EnterpriseSearchRequestHandler {
*/
handleAuthenticationError(response: KibanaResponseFactory) {
const errorMessage = 'Cannot authenticate Enterprise Search user';
+ const headers = { ...this.headers, [ERROR_CONNECTING_HEADER]: 'true' };
this.log.error(errorMessage);
- return response.customError({ statusCode: 502, headers: this.headers, body: errorMessage });
+ return response.customError({ statusCode: 502, headers, body: errorMessage });
}
/**
From a05853ab2ad26e5f8fe8654a138c805ba45422be Mon Sep 17 00:00:00 2001
From: Oliver Gupte
Date: Fri, 9 Jul 2021 17:52:17 -0400
Subject: [PATCH 05/31] [APM] Support for data streams index patterns for
cloud migration (#105015)
* [APM] Support for data streams index patterns for cloud migration (#101095)
* [APM] Update apm package version to 0.3.0 (elastic/apm-server/#5579)
---
x-pack/plugins/apm/server/index.test.ts | 2 +-
x-pack/plugins/apm/server/index.ts | 6 ++-
.../get_apm_package_policy_definition.ts | 2 +-
.../create_static_index_pattern.ts | 5 ++-
x-pack/plugins/apm/server/routes/fleet.ts | 41 ++++++++++++++-----
5 files changed, 41 insertions(+), 15 deletions(-)
diff --git a/x-pack/plugins/apm/server/index.test.ts b/x-pack/plugins/apm/server/index.test.ts
index 6052ec921f9f9..56c9825db5a5c 100644
--- a/x-pack/plugins/apm/server/index.test.ts
+++ b/x-pack/plugins/apm/server/index.test.ts
@@ -30,7 +30,7 @@ describe('mergeConfigs', () => {
expect(mergeConfigs(apmOssConfig, apmConfig)).toEqual({
'apm_oss.errorIndices': 'logs-apm*,apm-*-error-*',
- 'apm_oss.indexPattern': 'apm-*',
+ 'apm_oss.indexPattern': 'traces-apm*,logs-apm*,metrics-apm*,apm-*',
'apm_oss.metricsIndices': 'metrics-apm*,apm-*-metric-*',
'apm_oss.spanIndices': 'traces-apm*,apm-*-span-*',
'apm_oss.transactionIndices': 'traces-apm*,apm-*-transaction-*',
diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts
index f14894a76edb4..9031f454f4a7f 100644
--- a/x-pack/plugins/apm/server/index.ts
+++ b/x-pack/plugins/apm/server/index.ts
@@ -74,7 +74,7 @@ export function mergeConfigs(
'apm_oss.metricsIndices': apmOssConfig.metricsIndices,
'apm_oss.sourcemapIndices': apmOssConfig.sourcemapIndices,
'apm_oss.onboardingIndices': apmOssConfig.onboardingIndices,
- 'apm_oss.indexPattern': apmOssConfig.indexPattern, // TODO: add data stream indices: traces-apm*,logs-apm*,metrics-apm*. Blocked by https://github.com/elastic/kibana/issues/87851
+ 'apm_oss.indexPattern': apmOssConfig.indexPattern,
/* eslint-enable @typescript-eslint/naming-convention */
'xpack.apm.serviceMapEnabled': apmConfig.serviceMapEnabled,
'xpack.apm.serviceMapFingerprintBucketSize':
@@ -119,6 +119,10 @@ export function mergeConfigs(
'apm_oss.metricsIndices'
] = `metrics-apm*,${mergedConfig['apm_oss.metricsIndices']}`;
+ mergedConfig[
+ 'apm_oss.indexPattern'
+ ] = `traces-apm*,logs-apm*,metrics-apm*,${mergedConfig['apm_oss.indexPattern']}`;
+
return mergedConfig;
}
diff --git a/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts b/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts
index 82e85e7da9bb3..291b2fa2af99d 100644
--- a/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts
+++ b/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts
@@ -34,7 +34,7 @@ export function getApmPackagePolicyDefinition(
],
package: {
name: APM_PACKAGE_NAME,
- version: '0.3.0-dev.1',
+ version: '0.3.0',
title: 'Elastic APM',
},
};
diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
index 607a7e6227a9d..a2944d6241d2d 100644
--- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
+++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
@@ -19,7 +19,8 @@ export async function createStaticIndexPattern(
setup: Setup,
config: APMRouteHandlerResources['config'],
savedObjectsClient: InternalSavedObjectsClient,
- spaceId: string | undefined
+ spaceId: string | undefined,
+ overwrite = false
): Promise {
return withApmSpan('create_static_index_pattern', async () => {
// don't autocreate APM index pattern if it's been disabled via the config
@@ -45,7 +46,7 @@ export async function createStaticIndexPattern(
},
{
id: APM_STATIC_INDEX_PATTERN_ID,
- overwrite: false,
+ overwrite,
namespace: spaceId,
}
)
diff --git a/x-pack/plugins/apm/server/routes/fleet.ts b/x-pack/plugins/apm/server/routes/fleet.ts
index 6628d29b256f7..b760014d6af89 100644
--- a/x-pack/plugins/apm/server/routes/fleet.ts
+++ b/x-pack/plugins/apm/server/routes/fleet.ts
@@ -25,6 +25,8 @@ import { createCloudApmPackgePolicy } from '../lib/fleet/create_cloud_apm_packag
import { getUnsupportedApmServerSchema } from '../lib/fleet/get_unsupported_apm_server_schema';
import { isSuperuser } from '../lib/fleet/is_superuser';
import { getInternalSavedObjectsClient } from '../lib/helpers/get_internal_saved_objects_client';
+import { setupRequest } from '../lib/helpers/setup_request';
+import { createStaticIndexPattern } from '../lib/index_pattern/create_static_index_pattern';
const hasFleetDataRoute = createApmServerRoute({
endpoint: 'GET /api/apm/fleet/has_data',
@@ -154,7 +156,7 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({
endpoint: 'POST /api/apm/fleet/cloud_apm_package_policy',
options: { tags: ['access:apm', 'access:apm_write'] },
handler: async (resources) => {
- const { plugins, context, config, request, logger } = resources;
+ const { plugins, context, config, request, logger, core } = resources;
const cloudApmMigrationEnabled =
config['xpack.apm.agent.migrations.enabled'];
if (!plugins.fleet || !plugins.security) {
@@ -171,15 +173,34 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({
if (!hasRequiredRole || !cloudApmMigrationEnabled) {
throw Boom.forbidden(CLOUD_SUPERUSER_REQUIRED_MESSAGE);
}
- return {
- cloud_apm_package_policy: await createCloudApmPackgePolicy({
- cloudPluginSetup,
- fleetPluginStart,
- savedObjectsClient,
- esClient,
- logger,
- }),
- };
+
+ const cloudApmAackagePolicy = await createCloudApmPackgePolicy({
+ cloudPluginSetup,
+ fleetPluginStart,
+ savedObjectsClient,
+ esClient,
+ logger,
+ });
+
+ const [setup, internalSavedObjectsClient] = await Promise.all([
+ setupRequest(resources),
+ core
+ .start()
+ .then(({ savedObjects }) => savedObjects.createInternalRepository()),
+ ]);
+
+ const spaceId = plugins.spaces?.setup.spacesService.getSpaceId(request);
+
+ // force update the index pattern title with data streams
+ await createStaticIndexPattern(
+ setup,
+ config,
+ internalSavedObjectsClient,
+ spaceId,
+ true
+ );
+
+ return { cloud_apm_package_policy: cloudApmAackagePolicy };
},
});
From 937a4f381a592f605b1edc6a898de02e4831aadf Mon Sep 17 00:00:00 2001
From: Ryland Herrick
Date: Fri, 9 Jul 2021 17:20:10 -0500
Subject: [PATCH 06/31] [Security Solution] Enrichment details UI cleanup
(#104995)
* Remove the "view threat intel data" button from the alert summary
This can be accomplished by clicking the tab itself; there's no real
need for this button.
* Remove section title from alert summary view
This made sense when we had both alert and threat sections, but we no
longer do.
Removes the corresponding translation, and the analogously unused title
from the defunct threat summary view.
* Smaller spacer on alert summary tab
This is distractingly large as compared to other tabs.
* Move "no enrichments" panel below our threat details table
* Remove old import
* Move inspect button inline with rest of header
* Add HR separator to top of NoEnrichmentsPanel
This should arguably be added a level above so as to keep this panel
context-agnostic, but it's currently only used in one place and will
always require the HR, so YAGNI for now.
* Adds more space between title and description on "no data" panel
It has been suggested that the NoEnrichmentsPanel should be following
the guidelines of the EuiEmptyPrompt. If we end up needing e.g. centered
text, we're better off rewriting NoEnrichmentsPanelView in terms of an
EuiEmptyPrompt.
* StyledEuiInMemoryTable has no header row height
We have never provided column names to this component. However, there is
default padding on the thead tds such that even without content they
take up vertical height.
This has resulted in some extra top-margin on historical uses of this
table (which are just the Alert Details views). However, the addition of
a sibling table (ThreatSummaryView) made the extra margin noticable,
since it made the two tables appear disjointed even though they're right
up against each other.
This fixes the issue by removing the padding, allowing the thead to take
no height.
And now that that space isn't taken up by the table header, we need to
add a little bit of space between the header and table on the Threat
Details view.
* Move test to appropriate location
The ThreatDetailsView is no longer responsible for displaying the "no
data" components, that's now a level above in EventDetails.
* Prune unused translations
These have been changed in the latest designs.
* Only add HR if panel is preceded by enrichments
We do not want an HR if there's nothing above the panel.
---
.../event_details/alert_summary_view.tsx | 2 +-
.../empty_threat_details_view.test.tsx | 48 ----------
.../cti_details/empty_threat_details_view.tsx | 51 -----------
.../cti_details/no_enrichments_panel.test.tsx | 57 ++++++++++++
.../cti_details/no_enrichments_panel.tsx | 88 +++++++++++++++++++
.../cti_details/threat_details_view.test.tsx | 9 --
.../cti_details/threat_details_view.tsx | 18 ++--
.../event_details/cti_details/translations.ts | 34 ++++++-
.../event_details/event_details.test.tsx | 7 ++
.../event_details/event_details.tsx | 53 +++++------
.../components/event_details/summary_view.tsx | 7 +-
.../components/event_details/translations.ts | 12 ---
.../translations/translations/ja-JP.json | 3 -
.../translations/translations/zh-CN.json | 3 -
14 files changed, 222 insertions(+), 170 deletions(-)
delete mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/cti_details/empty_threat_details_view.test.tsx
delete mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/cti_details/empty_threat_details_view.tsx
create mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/cti_details/no_enrichments_panel.test.tsx
create mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/cti_details/no_enrichments_panel.tsx
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx
index 3a1a29b63eadf..329b8e32f057d 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx
@@ -213,7 +213,7 @@ const AlertSummaryViewComponent: React.FC<{
return (
<>
-
+
{
- const mount = useMountAppended();
- const mockTheme = getMockTheme({
- eui: {
- euiBreakpoints: {
- l: '1200px',
- },
- paddingSizes: {
- m: '8px',
- xl: '32px',
- },
- },
- });
-
- test('renders correct items', () => {
- const wrapper = mount(
-
-
-
- );
- expect(wrapper.find('[data-test-subj="empty-threat-details-view"]').exists()).toEqual(true);
- });
-
- test('renders link to docs', () => {
- const wrapper = mount(
-
-
-
- );
- expect(wrapper.find('a').exists()).toEqual(true);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/empty_threat_details_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/empty_threat_details_view.tsx
deleted file mode 100644
index d7e1c4d7754ec..0000000000000
--- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/empty_threat_details_view.tsx
+++ /dev/null
@@ -1,51 +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 { EuiLink, EuiSpacer, EuiTitle } from '@elastic/eui';
-import React from 'react';
-import styled from 'styled-components';
-
-import { useKibana } from '../../../lib/kibana';
-import * as i18n from './translations';
-
-const EmptyThreatDetailsViewContainer = styled.div`
- display: flex;
- flex-direction: column;
- align-items: center;
-`;
-
-const Span = styled.span`
- color: ${({ theme }) => theme.eui.euiColorDarkShade};
- line-height: 1.8em;
- text-align: center;
- padding: ${({ theme }) => `${theme.eui.paddingSizes.m} ${theme.eui.paddingSizes.xl}`};
-`;
-
-const EmptyThreatDetailsViewComponent: React.FC<{}> = () => {
- const threatIntelDocsUrl = `${
- useKibana().services.docLinks.links.filebeat.base
- }/filebeat-module-threatintel.html`;
-
- return (
-
-
-
- {i18n.NO_ENRICHMENT_FOUND}
-
-
- {i18n.IF_CTI_NOT_ENABLED}
-
- {i18n.CHECK_DOCS}
-
-
-
- );
-};
-
-EmptyThreatDetailsViewComponent.displayName = 'EmptyThreatDetailsView';
-
-export const EmptyThreatDetailsView = React.memo(EmptyThreatDetailsViewComponent);
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/no_enrichments_panel.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/no_enrichments_panel.test.tsx
new file mode 100644
index 0000000000000..819c666bd7267
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/no_enrichments_panel.test.tsx
@@ -0,0 +1,57 @@
+/*
+ * 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 { mount } from 'enzyme';
+
+import { NoEnrichmentsPanel } from './no_enrichments_panel';
+import * as i18n from './translations';
+
+jest.mock('../../../lib/kibana');
+
+describe('NoEnrichmentsPanelView', () => {
+ it('renders a qualified container', () => {
+ const wrapper = mount(
+
+ );
+ expect(wrapper.find('[data-test-subj="no-enrichments-panel"]').exists()).toEqual(true);
+ });
+
+ it('renders nothing when all enrichments are present', () => {
+ const wrapper = mount(
+
+ );
+ expect(wrapper.find('[data-test-subj="no-enrichments-panel"]').exists()).toEqual(false);
+ });
+
+ it('renders expected text when no enrichments are present', () => {
+ const wrapper = mount(
+
+ );
+ expect(wrapper.find('[data-test-subj="no-enrichments-panel"]').hostNodes().text()).toContain(
+ i18n.NO_ENRICHMENTS_FOUND_TITLE
+ );
+ });
+
+ it('renders expected text when existing enrichments are absent', () => {
+ const wrapper = mount(
+
+ );
+ expect(wrapper.find('[data-test-subj="no-enrichments-panel"]').hostNodes().text()).toContain(
+ i18n.NO_INDICATOR_ENRICHMENTS_TITLE
+ );
+ });
+
+ it('renders expected text when investigation enrichments are absent', () => {
+ const wrapper = mount(
+
+ );
+ expect(wrapper.find('[data-test-subj="no-enrichments-panel"]').hostNodes().text()).toContain(
+ i18n.NO_INVESTIGATION_ENRICHMENTS_TITLE
+ );
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/no_enrichments_panel.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/no_enrichments_panel.tsx
new file mode 100644
index 0000000000000..b521c3ba92c4d
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/no_enrichments_panel.tsx
@@ -0,0 +1,88 @@
+/*
+ * 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 { EuiHorizontalRule, EuiLink, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui';
+import React from 'react';
+import styled from 'styled-components';
+import { useKibana } from '../../../lib/kibana';
+
+import * as i18n from './translations';
+
+const Container = styled(EuiPanel)`
+ display: flex;
+ flex-direction: column;
+`;
+
+const NoEnrichmentsPanelView: React.FC<{
+ title: React.ReactNode;
+ description: React.ReactNode;
+}> = ({ title, description }) => {
+ return (
+
+ {title}
+
+
+ {description}
+
+
+ );
+};
+
+NoEnrichmentsPanelView.displayName = 'NoEnrichmentsPanelView';
+
+export const NoEnrichmentsPanel: React.FC<{
+ existingEnrichmentsCount: number;
+ investigationEnrichmentsCount: number;
+}> = ({ existingEnrichmentsCount, investigationEnrichmentsCount }) => {
+ const threatIntelDocsUrl = `${
+ useKibana().services.docLinks.links.filebeat.base
+ }/filebeat-module-threatintel.html`;
+ const noIndicatorEnrichmentsDescription = (
+ <>
+ {i18n.IF_CTI_NOT_ENABLED}
+
+ {i18n.CHECK_DOCS}
+
+ >
+ );
+
+ if (existingEnrichmentsCount === 0 && investigationEnrichmentsCount === 0) {
+ return (
+ {i18n.NO_ENRICHMENTS_FOUND_TITLE}}
+ description={
+ <>
+ {noIndicatorEnrichmentsDescription}
+ {i18n.NO_INVESTIGATION_ENRICHMENTS_DESCRIPTION}
+ >
+ }
+ />
+ );
+ } else if (existingEnrichmentsCount === 0) {
+ return (
+ <>
+
+ {i18n.NO_INDICATOR_ENRICHMENTS_TITLE}}
+ description={noIndicatorEnrichmentsDescription}
+ />
+ >
+ );
+ } else if (investigationEnrichmentsCount === 0) {
+ return (
+ <>
+
+ {i18n.NO_INVESTIGATION_ENRICHMENTS_TITLE}}
+ description={i18n.NO_INVESTIGATION_ENRICHMENTS_DESCRIPTION}
+ />
+ >
+ );
+ } else {
+ return null;
+ }
+};
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_details_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_details_view.test.tsx
index 0113dde96a4b6..c25457a5e5e88 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_details_view.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_details_view.test.tsx
@@ -31,15 +31,6 @@ describe('ThreatDetailsView', () => {
);
});
- it('renders an empty view if there are no enrichments', () => {
- const wrapper = mount(
-
-
-
- );
- expect(wrapper.find('[data-test-subj="empty-threat-details-view"]').exists()).toEqual(true);
- });
-
it('renders anchor links for event.url and event.reference', () => {
const enrichments = [
buildEventEnrichmentMock({
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_details_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_details_view.tsx
index d5e985c5757a6..b6b8a47c1dd8c 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_details_view.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_details_view.tsx
@@ -20,7 +20,6 @@ import React, { Fragment } from 'react';
import { StyledEuiInMemoryTable } from '../summary_view';
import { getSummaryColumns, SummaryRow, ThreatDetailsRow } from '../helpers';
-import { EmptyThreatDetailsView } from './empty_threat_details_view';
import { FIRSTSEEN, EVENT_URL, EVENT_REFERENCE } from '../../../../../common/cti/constants';
import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../common/constants';
import { getFirstElement } from '../../../../../common/utils/data_retrieval';
@@ -70,15 +69,13 @@ const ThreatDetailsHeader: React.FC<{
+ {isInvestigationTimeEnrichment(type) && (
+
+
+
+ )}
- {isInvestigationTimeEnrichment(type) && (
-
-
-
-
-
- )}
>
);
@@ -131,10 +128,6 @@ const buildThreatDetailsItems = (enrichment: CtiEnrichment) =>
const ThreatDetailsViewComponent: React.FC<{
enrichments: CtiEnrichment[];
}> = ({ enrichments }) => {
- if (enrichments.length < 1) {
- return ;
- }
-
const sortedEnrichments = enrichments.sort((a, b) => getFirstSeen(b) - getFirstSeen(a));
return (
@@ -146,6 +139,7 @@ const ThreatDetailsViewComponent: React.FC<{
return (
+
{
).toEqual('Summary');
});
});
+
+ describe('threat intel tab', () => {
+ it('renders a "no enrichments" panel view if there are no enrichments', () => {
+ alertsWrapper.find('[data-test-subj="threatIntelTab"]').first().simulate('click');
+ expect(alertsWrapper.find('[data-test-subj="no-enrichments-panel"]').exists()).toEqual(true);
+ });
+ });
});
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
index 9afaaef61b17a..7074212dcdb4c 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
@@ -9,9 +9,6 @@ import {
EuiTabbedContent,
EuiTabbedContentTab,
EuiSpacer,
- EuiButton,
- EuiFlexGroup,
- EuiFlexItem,
EuiLoadingContent,
EuiLoadingSpinner,
} from '@elastic/eui';
@@ -34,6 +31,7 @@ import {
parseExistingEnrichments,
timelineDataToEnrichment,
} from './cti_details/helpers';
+import { NoEnrichmentsPanel } from './cti_details/no_enrichments_panel';
type EventViewTab = EuiTabbedContentTab;
@@ -100,9 +98,6 @@ const EventDetailsComponent: React.FC = ({
(tab: EuiTabbedContentTab) => setSelectedTabId(tab.id as EventViewId),
[setSelectedTabId]
);
- const viewThreatIntelTab = useCallback(() => setSelectedTabId(EventsViewType.threatIntelView), [
- setSelectedTabId,
- ]);
const eventFields = useMemo(() => getEnrichmentFields(data), [data]);
const existingEnrichments = useMemo(
@@ -118,6 +113,9 @@ const EventDetailsComponent: React.FC = ({
loading: enrichmentsLoading,
result: enrichmentsResponse,
} = useInvestigationTimeEnrichment(eventFields);
+ const investigationEnrichments = useMemo(() => enrichmentsResponse?.enrichments ?? [], [
+ enrichmentsResponse?.enrichments,
+ ]);
const allEnrichments = useMemo(() => {
if (enrichmentsLoading || !enrichmentsResponse?.enrichments) {
return existingEnrichments;
@@ -140,29 +138,20 @@ const EventDetailsComponent: React.FC = ({
eventId: id,
browserFields,
timelineId,
- title: i18n.ALERT_SUMMARY,
}}
/>
+ {enrichmentCount > 0 && (
+
+ )}
{enrichmentsLoading && (
<>
>
)}
- {enrichmentCount > 0 && (
- <>
-
-
-
-
- {i18n.VIEW_CTI_DATA}
-
-
- >
- )}
>
),
}
@@ -176,7 +165,6 @@ const EventDetailsComponent: React.FC = ({
enrichmentsLoading,
enrichmentCount,
allEnrichments,
- viewThreatIntelTab,
]
);
@@ -192,10 +180,25 @@ const EventDetailsComponent: React.FC = ({
{enrichmentsLoading ? : `(${enrichmentCount})`}
),
- content: ,
+ content: (
+ <>
+
+
+ >
+ ),
}
: undefined,
- [allEnrichments, enrichmentCount, enrichmentsLoading, isAlert]
+ [
+ allEnrichments,
+ enrichmentCount,
+ enrichmentsLoading,
+ existingEnrichments.length,
+ investigationEnrichments.length,
+ isAlert,
+ ]
);
const tableTab = useMemo(
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx
index 0e846f3f6f699..961860ed6d8b9 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx
@@ -13,12 +13,13 @@ import { SummaryRow } from './helpers';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const StyledEuiInMemoryTable = styled(EuiInMemoryTable as any)`
- .euiTableHeaderCell {
- border: none;
- }
+ .euiTableHeaderCell,
.euiTableRowCell {
border: none;
}
+ .euiTableHeaderCell .euiTableCellContent {
+ padding: 0;
+ }
`;
const StyledEuiTitle = styled(EuiTitle)`
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts
index a17ca5e434ace..c632f5d6332e0 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts
@@ -11,22 +11,10 @@ export const SUMMARY = i18n.translate('xpack.securitySolution.alertDetails.summa
defaultMessage: 'Summary',
});
-export const ALERT_SUMMARY = i18n.translate('xpack.securitySolution.alertDetails.alertSummary', {
- defaultMessage: 'Alert Summary',
-});
-
export const THREAT_INTEL = i18n.translate('xpack.securitySolution.alertDetails.threatIntel', {
defaultMessage: 'Threat Intel',
});
-export const THREAT_SUMMARY = i18n.translate('xpack.securitySolution.alertDetails.threatSummary', {
- defaultMessage: 'Threat Summary',
-});
-
-export const VIEW_CTI_DATA = i18n.translate('xpack.securitySolution.alertDetails.threatIntelCta', {
- defaultMessage: 'View threat intel data',
-});
-
export const INVESTIGATION_GUIDE = i18n.translate(
'xpack.securitySolution.alertDetails.summary.investigationGuide',
{
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 387547295f047..2b1088d8c11ae 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -18562,16 +18562,13 @@
"xpack.securitySolution.administration.os.linux": "Linux",
"xpack.securitySolution.administration.os.macos": "Mac",
"xpack.securitySolution.administration.os.windows": "Windows",
- "xpack.securitySolution.alertDetails.alertSummary": "アラート概要",
"xpack.securitySolution.alertDetails.checkDocs": "マニュアルをご確認ください。",
"xpack.securitySolution.alertDetails.ifCtiNotEnabled": "脅威インテリジェンスソースを有効にしていない場合で、この機能について関心がある場合は、",
- "xpack.securitySolution.alertDetails.noEnrichmentFound": "Threat Intel Enrichmentが見つかりません",
"xpack.securitySolution.alertDetails.summary": "まとめ",
"xpack.securitySolution.alertDetails.summary.investigationGuide": "調査ガイド",
"xpack.securitySolution.alertDetails.summary.readLess": "表示を減らす",
"xpack.securitySolution.alertDetails.summary.readMore": "続きを読む",
"xpack.securitySolution.alertDetails.threatIntel": "Threat Intel",
- "xpack.securitySolution.alertDetails.threatSummary": "脅威概要",
"xpack.securitySolution.alerts.riskScoreMapping.defaultDescriptionLabel": "このルールで生成されたすべてのアラートのリスクスコアを選択します。",
"xpack.securitySolution.alerts.riskScoreMapping.defaultRiskScoreTitle": "デフォルトリスクスコア",
"xpack.securitySolution.alerts.riskScoreMapping.mappingDescriptionLabel": "ソースイベント値を使用して、デフォルトリスクスコアを上書きします。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index d142969022c81..04394a1ac1704 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -18826,16 +18826,13 @@
"xpack.securitySolution.administration.os.linux": "Linux",
"xpack.securitySolution.administration.os.macos": "Mac",
"xpack.securitySolution.administration.os.windows": "Windows",
- "xpack.securitySolution.alertDetails.alertSummary": "告警摘要",
"xpack.securitySolution.alertDetails.checkDocs": "请查看我们的文档。",
"xpack.securitySolution.alertDetails.ifCtiNotEnabled": "如果尚未启用任何威胁情报来源,并希望更多了解此功能,",
- "xpack.securitySolution.alertDetails.noEnrichmentFound": "未找到威胁情报扩充",
"xpack.securitySolution.alertDetails.summary": "摘要",
"xpack.securitySolution.alertDetails.summary.investigationGuide": "调查指南",
"xpack.securitySolution.alertDetails.summary.readLess": "阅读更少内容",
"xpack.securitySolution.alertDetails.summary.readMore": "阅读更多内容",
"xpack.securitySolution.alertDetails.threatIntel": "威胁情报",
- "xpack.securitySolution.alertDetails.threatSummary": "威胁摘要",
"xpack.securitySolution.alerts.riskScoreMapping.defaultDescriptionLabel": "选择此规则生成的所有告警的风险分数。",
"xpack.securitySolution.alerts.riskScoreMapping.defaultRiskScoreTitle": "默认风险分数",
"xpack.securitySolution.alerts.riskScoreMapping.mappingDescriptionLabel": "使用源事件值覆盖默认风险分数。",
From c07f51e5be9b16c58a4988491d8ae2ce021b4aba Mon Sep 17 00:00:00 2001
From: Frank Hassanabad
Date: Fri, 9 Jul 2021 16:23:00 -0600
Subject: [PATCH 07/31] [Security Detections] Fixes ip on threshold preview
button when selecting an ip data type such as source.ip (#105126)
## Summary
See https://github.com/elastic/kibana/issues/100433 for details and test instructions.
This is considered critical and a small fix for 7.14.0 has been requested.
* Wrote Cypress test that exercises the bug
* Fixed mutation in one part of the Cypress Test
* Decided to remove the "missing" that we were telling users was "others" since missing is not the same as others. It no longer errors, but some users might be asking why we don't show "others" anymore. The reality is that we only showed "missing" which isn't adding value to the preview of what detections will end up looking like.
* Later if we want a true "others" we should implement it as a larger feature request and not a bug fix IMHO
Before you would get errors in your network panel:
![errors_threshold](https://user-images.githubusercontent.com/1151048/125126681-b0380e00-e0b8-11eb-9f2c-a75e2909754c.png)
After you now get the `source.ip` without errors:
### Checklist
- [x] [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
---
.../detection_rules/threshold_rule.spec.ts | 27 ++++++++++++++++---
.../cypress/tasks/create_new_rule.ts | 2 +-
.../components/rules/query_preview/index.tsx | 1 +
3 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts
index ad71d54eb2a7a..ce00c9b40aead 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts
@@ -6,7 +6,7 @@
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
-import { indexPatterns, newRule, newThresholdRule } from '../../objects/rule';
+import { indexPatterns, newRule, newThresholdRule, ThresholdRule } from '../../objects/rule';
import {
ALERT_RULE_METHOD,
@@ -180,9 +180,9 @@ describe('Detection rules, threshold', () => {
cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', rule.riskScore);
});
- it('Preview results', () => {
- const previewRule = { ...newThresholdRule };
- previewRule.index!.push('.siem-signals*');
+ it('Preview results of keyword using "host.name"', () => {
+ const previewRule: ThresholdRule = { ...newThresholdRule };
+ previewRule.index = [...previewRule.index, '.siem-signals*'];
createCustomRuleActivated(newRule);
goToManageAlertsDetectionRules();
@@ -194,4 +194,23 @@ describe('Detection rules, threshold', () => {
cy.get(PREVIEW_HEADER_SUBTITLE).should('have.text', '3 unique hits');
});
+
+ it('Preview results of "ip" using "source.ip"', () => {
+ const previewRule: ThresholdRule = {
+ ...newThresholdRule,
+ thresholdField: 'source.ip',
+ threshold: '1',
+ };
+ previewRule.index = [...previewRule.index, '.siem-signals*'];
+
+ createCustomRuleActivated(newRule);
+ goToManageAlertsDetectionRules();
+ waitForRulesTableToBeLoaded();
+ goToCreateNewRule();
+ selectThresholdRuleType();
+ fillDefineThresholdRule(previewRule);
+ previewResults();
+
+ cy.get(PREVIEW_HEADER_SUBTITLE).should('have.text', '10 unique hits');
+ });
});
diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
index 9b74110f0ef77..1b420cd6d1520 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
@@ -275,7 +275,7 @@ export const fillDefineThresholdRule = (rule: ThresholdRule) => {
cy.get(TIMELINE(rule.timeline.id!)).click();
cy.get(COMBO_BOX_CLEAR_BTN).click();
- rule.index!.forEach((index) => {
+ rule.index.forEach((index) => {
cy.get(COMBO_BOX_INPUT).first().type(`${index}{enter}`);
});
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx
index 6342d468f5962..45b66058a04fb 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx
@@ -118,6 +118,7 @@ export const PreviewQuery = ({
startDate: toTime,
filterQuery: queryFilter,
indexNames: index,
+ includeMissingData: false,
histogramType: MatrixHistogramType.events,
stackByField: 'event.category',
threshold: ruleType === 'threshold' ? threshold : undefined,
From b40fc09dfca3c2e7072ba33f65c9c4ee7474764c Mon Sep 17 00:00:00 2001
From: spalger
Date: Fri, 9 Jul 2021 16:40:13 -0700
Subject: [PATCH 08/31] skip another suite blocking es promotion (#104466)
---
test/functional/apps/discover/_large_string.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/test/functional/apps/discover/_large_string.ts b/test/functional/apps/discover/_large_string.ts
index de3f0f2c40ae1..ea219881c7a95 100644
--- a/test/functional/apps/discover/_large_string.ts
+++ b/test/functional/apps/discover/_large_string.ts
@@ -19,7 +19,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const security = getService('security');
const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover']);
- describe('test large strings', function () {
+ // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104466
+ describe.skip('test large strings', function () {
before(async function () {
await security.testUser.setRoles(['kibana_admin', 'kibana_large_strings']);
From d776c0940ee47098bef21a129a03445e5fbf3eda Mon Sep 17 00:00:00 2001
From: spalger
Date: Fri, 9 Jul 2021 18:01:11 -0700
Subject: [PATCH 09/31] skip all discover functional tests to unblock es
promotion (#104466)
---
test/functional/apps/discover/_data_grid_field_data.ts | 3 +--
test/functional/apps/discover/_field_data.ts | 3 +--
test/functional/apps/discover/_field_data_with_fields_api.ts | 3 +--
test/functional/apps/discover/_large_string.ts | 3 +--
test/functional/apps/discover/index.ts | 3 ++-
5 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/test/functional/apps/discover/_data_grid_field_data.ts b/test/functional/apps/discover/_data_grid_field_data.ts
index cf8a6fc1bd60f..94e8e942f86ba 100644
--- a/test/functional/apps/discover/_data_grid_field_data.ts
+++ b/test/functional/apps/discover/_data_grid_field_data.ts
@@ -19,8 +19,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false };
const dataGrid = getService('dataGrid');
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104466
- describe.skip('discover data grid field data tests', function describeIndexTests() {
+ describe('discover data grid field data tests', function describeIndexTests() {
this.tags('includeFirefox');
before(async function () {
await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] });
diff --git a/test/functional/apps/discover/_field_data.ts b/test/functional/apps/discover/_field_data.ts
index 6471b751945a8..ec9f9cf65e0fa 100644
--- a/test/functional/apps/discover/_field_data.ts
+++ b/test/functional/apps/discover/_field_data.ts
@@ -20,8 +20,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']);
const find = getService('find');
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104466
- describe.skip('discover tab', function describeIndexTests() {
+ describe('discover tab', function describeIndexTests() {
this.tags('includeFirefox');
before(async function () {
await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] });
diff --git a/test/functional/apps/discover/_field_data_with_fields_api.ts b/test/functional/apps/discover/_field_data_with_fields_api.ts
index 7c6867e935063..110e255d18c75 100644
--- a/test/functional/apps/discover/_field_data_with_fields_api.ts
+++ b/test/functional/apps/discover/_field_data_with_fields_api.ts
@@ -20,8 +20,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']);
const find = getService('find');
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104466
- describe.skip('discover tab with new fields API', function describeIndexTests() {
+ describe('discover tab with new fields API', function describeIndexTests() {
this.tags('includeFirefox');
before(async function () {
await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] });
diff --git a/test/functional/apps/discover/_large_string.ts b/test/functional/apps/discover/_large_string.ts
index ea219881c7a95..de3f0f2c40ae1 100644
--- a/test/functional/apps/discover/_large_string.ts
+++ b/test/functional/apps/discover/_large_string.ts
@@ -19,8 +19,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const security = getService('security');
const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover']);
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104466
- describe.skip('test large strings', function () {
+ describe('test large strings', function () {
before(async function () {
await security.testUser.setRoles(['kibana_admin', 'kibana_large_strings']);
diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts
index b396f172f6961..a17bf53e7f478 100644
--- a/test/functional/apps/discover/index.ts
+++ b/test/functional/apps/discover/index.ts
@@ -12,7 +12,8 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const browser = getService('browser');
- describe('discover app', function () {
+ // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104466
+ describe.skip('discover app', function () {
this.tags('ciGroup6');
before(function () {
From c0fec48104a7d026802673723ffb8796211bc2e0 Mon Sep 17 00:00:00 2001
From: Frank Hassanabad
Date: Fri, 9 Jul 2021 20:55:20 -0600
Subject: [PATCH 10/31] [Security Solutions][Detection Engine] Fixes button
group alignments in machine learning and tags (#105166)
## Summary
See:
https://github.com/elastic/kibana/issues/104055
For more issue details. This is deemed embarrassing enough to be critical for a fix for 7.14.0 before shipping.
EUI looks to have updated its self and added a new attribute that it wants us to use called `numFIlters` which when set will show the total number of filter items before they are selected. Once selected the number and look and feel change.
```ts
numFilters
```
Before:
After before selections:
After once you have selections:
Before:
After before selections:
After once you have selections:
### Checklist
- [x] [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
---
.../filters/__snapshots__/groups_filter_popover.test.tsx.snap | 1 +
.../ml_popover/jobs_table/filters/groups_filter_popover.tsx | 1 +
.../rules/all/rules_table_filters/tags_filter_popover.tsx | 2 ++
3 files changed, 4 insertions(+)
diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/__snapshots__/groups_filter_popover.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/__snapshots__/groups_filter_popover.test.tsx.snap
index 22805d34d2ee1..410fb7f3ae793 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/__snapshots__/groups_filter_popover.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/__snapshots__/groups_filter_popover.test.tsx.snap
@@ -10,6 +10,7 @@ exports[`GroupsFilterPopover renders correctly against snapshot 1`] = `
iconType="arrowDown"
isSelected={false}
numActiveFilters={0}
+ numFilters={3}
onClick={[Function]}
>
Groups
diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx
index b7425a62f6773..249dc0dfccdbb 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/groups_filter_popover.tsx
@@ -59,6 +59,7 @@ export const GroupsFilterPopoverComponent = ({
iconType="arrowDown"
onClick={() => setIsGroupPopoverOpen(!isGroupPopoverOpen)}
isSelected={isGroupPopoverOpen}
+ numFilters={uniqueGroups.length}
hasActiveFilters={selectedGroups.length > 0}
numActiveFilters={selectedGroups.length}
>
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx
index 45ce5bc18361c..c5262caf6c776 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx
@@ -102,9 +102,11 @@ const TagsFilterPopoverComponent = ({
ownFocus
button={
setIsTagPopoverOpen(!isTagPopoverOpen)}
+ numFilters={tags.length}
isSelected={isTagPopoverOpen}
hasActiveFilters={selectedTags.length > 0}
numActiveFilters={selectedTags.length}
From 857dc9f2e1e1edd86c9cf7cd5f157e3e8b82a14a Mon Sep 17 00:00:00 2001
From: Dario Gieselaar
Date: Sat, 10 Jul 2021 09:27:07 +0200
Subject: [PATCH 11/31] [APM] Don't log error if request was aborted (#105024)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../routes/register_routes/index.test.ts | 23 ++++--
.../server/routes/register_routes/index.ts | 72 ++++++++++++-------
2 files changed, 64 insertions(+), 31 deletions(-)
diff --git a/x-pack/plugins/apm/server/routes/register_routes/index.test.ts b/x-pack/plugins/apm/server/routes/register_routes/index.test.ts
index 158d7ee7e76a3..b9dece866fae5 100644
--- a/x-pack/plugins/apm/server/routes/register_routes/index.test.ts
+++ b/x-pack/plugins/apm/server/routes/register_routes/index.test.ts
@@ -109,6 +109,11 @@ const initApi = (
params: {},
query: {},
body: null,
+ events: {
+ aborted$: {
+ toPromise: () => new Promise(() => {}),
+ },
+ },
...request,
},
responseMock
@@ -202,7 +207,7 @@ describe('createApi', () => {
describe('when validating', () => {
describe('_inspect', () => {
it('allows _inspect=true', async () => {
- const handlerMock = jest.fn();
+ const handlerMock = jest.fn().mockResolvedValue({});
const {
simulateRequest,
mocks: { response },
@@ -234,7 +239,7 @@ describe('createApi', () => {
});
it('rejects _inspect=1', async () => {
- const handlerMock = jest.fn();
+ const handlerMock = jest.fn().mockResolvedValue({});
const {
simulateRequest,
@@ -267,7 +272,7 @@ describe('createApi', () => {
});
it('allows omitting _inspect', async () => {
- const handlerMock = jest.fn();
+ const handlerMock = jest.fn().mockResolvedValue({});
const {
simulateRequest,
@@ -297,7 +302,11 @@ describe('createApi', () => {
simulateRequest,
mocks: { response },
} = initApi([
- { endpoint: 'GET /foo', options: { tags: [] }, handler: jest.fn() },
+ {
+ endpoint: 'GET /foo',
+ options: { tags: [] },
+ handler: jest.fn().mockResolvedValue({}),
+ },
]);
await simulateRequest({
@@ -328,7 +337,7 @@ describe('createApi', () => {
});
it('validates path parameters', async () => {
- const handlerMock = jest.fn();
+ const handlerMock = jest.fn().mockResolvedValue({});
const {
simulateRequest,
mocks: { response },
@@ -402,7 +411,7 @@ describe('createApi', () => {
});
it('validates body parameters', async () => {
- const handlerMock = jest.fn();
+ const handlerMock = jest.fn().mockResolvedValue({});
const {
simulateRequest,
mocks: { response },
@@ -448,7 +457,7 @@ describe('createApi', () => {
});
it('validates query parameters', async () => {
- const handlerMock = jest.fn();
+ const handlerMock = jest.fn().mockResolvedValue({});
const {
simulateRequest,
mocks: { response },
diff --git a/x-pack/plugins/apm/server/routes/register_routes/index.ts b/x-pack/plugins/apm/server/routes/register_routes/index.ts
index 136f3c73d8046..8e6070de722be 100644
--- a/x-pack/plugins/apm/server/routes/register_routes/index.ts
+++ b/x-pack/plugins/apm/server/routes/register_routes/index.ts
@@ -29,6 +29,13 @@ const inspectRt = t.exact(
})
);
+const CLIENT_CLOSED_REQUEST = {
+ statusCode: 499,
+ body: {
+ message: 'Client closed request',
+ },
+};
+
export const inspectableEsQueriesMap = new WeakMap<
KibanaRequest,
InspectResponse
@@ -89,23 +96,40 @@ export function registerRoutes({
runtimeType
);
- const data: Record | undefined | null = (await handler({
- request,
- context,
- config,
- logger,
- core,
- plugins,
- params: merge(
- {
- query: {
- _inspect: false,
+ const { aborted, data } = await Promise.race([
+ handler({
+ request,
+ context,
+ config,
+ logger,
+ core,
+ plugins,
+ params: merge(
+ {
+ query: {
+ _inspect: false,
+ },
},
- },
- validatedParams
- ),
- ruleDataClient,
- })) as any;
+ validatedParams
+ ),
+ ruleDataClient,
+ }).then((value) => {
+ return {
+ aborted: false,
+ data: value as Record | undefined | null,
+ };
+ }),
+ request.events.aborted$.toPromise().then(() => {
+ return {
+ aborted: true,
+ data: undefined,
+ };
+ }),
+ ]);
+
+ if (aborted) {
+ return response.custom(CLIENT_CLOSED_REQUEST);
+ }
if (Array.isArray(data)) {
throw new Error('Return type cannot be an array');
@@ -118,9 +142,6 @@ export function registerRoutes({
}
: { ...data };
- // cleanup
- inspectableEsQueriesMap.delete(request);
-
if (!options.disableTelemetry && telemetryUsageCounter) {
telemetryUsageCounter.incrementCounter({
counterName: `${method.toUpperCase()} ${pathname}`,
@@ -131,6 +152,7 @@ export function registerRoutes({
return response.ok({ body });
} catch (error) {
logger.error(error);
+
if (!options.disableTelemetry && telemetryUsageCounter) {
telemetryUsageCounter.incrementCounter({
counterName: `${method.toUpperCase()} ${pathname}`,
@@ -147,16 +169,18 @@ export function registerRoutes({
},
};
- if (Boom.isBoom(error)) {
- opts.statusCode = error.output.statusCode;
+ if (error instanceof RequestAbortedError) {
+ return response.custom(merge(opts, CLIENT_CLOSED_REQUEST));
}
- if (error instanceof RequestAbortedError) {
- opts.statusCode = 499;
- opts.body.message = 'Client closed request';
+ if (Boom.isBoom(error)) {
+ opts.statusCode = error.output.statusCode;
}
return response.custom(opts);
+ } finally {
+ // cleanup
+ inspectableEsQueriesMap.delete(request);
}
}
);
From e4ba52928c289547d97706b083bed1202be1176d Mon Sep 17 00:00:00 2001
From: "Joey F. Poon"
Date: Sat, 10 Jul 2021 18:34:03 -0500
Subject: [PATCH 12/31] [Security Solution] remove query strategy v1 (#104196)
---
.../common/endpoint/types/index.ts | 14 -
.../management/pages/endpoint_hosts/mocks.ts | 4 -
.../pages/endpoint_hosts/store/builders.ts | 1 -
.../pages/endpoint_hosts/store/middleware.ts | 3 +-
.../store/mock_endpoint_result_list.ts | 16 +-
.../pages/endpoint_hosts/store/reducer.ts | 2 -
.../pages/endpoint_hosts/store/selectors.ts | 7 -
.../management/pages/endpoint_hosts/types.ts | 3 -
.../pages/endpoint_hosts/view/index.test.tsx | 34 +-
.../pages/endpoint_hosts/view/index.tsx | 9 +-
.../endpoint/endpoint_app_context_services.ts | 54 ---
.../endpoint/routes/actions/isolation.ts | 2 +-
.../routes/metadata/enrichment.test.ts | 86 +---
.../endpoint/routes/metadata/handlers.ts | 85 +---
.../server/endpoint/routes/metadata/index.ts | 27 +-
.../endpoint/routes/metadata/metadata.test.ts | 107 +---
.../routes/metadata/metadata_v1.test.ts | 456 ------------------
.../routes/metadata/query_builders.test.ts | 73 ++-
.../routes/metadata/query_builders.ts | 24 +-
.../routes/metadata/query_builders_v1.test.ts | 188 --------
.../metadata/support/query_strategies.ts | 123 ++---
.../routes/metadata/support/test_support.ts | 56 ---
.../server/endpoint/services/metadata.ts | 13 +-
.../server/endpoint/types.ts | 15 +-
.../factory/hosts/details/helpers.ts | 14 +-
.../apis/index.ts | 1 -
.../apis/metadata.ts | 9 -
.../apis/metadata_v1.ts | 290 -----------
28 files changed, 124 insertions(+), 1592 deletions(-)
delete mode 100644 x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata_v1.test.ts
delete mode 100644 x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders_v1.test.ts
delete mode 100644 x-pack/test/security_solution_endpoint_api_int/apis/metadata_v1.ts
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts
index 1e0d798cf7f07..cf8c33d38f8fa 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts
@@ -178,8 +178,6 @@ export interface HostResultList {
request_page_size: number;
/* the page index requested */
request_page_index: number;
- /* the version of the query strategy */
- query_strategy_version: MetadataQueryStrategyVersions;
/* policy IDs and versions */
policy_info?: HostInfo['policy_info'];
}
@@ -404,21 +402,11 @@ export enum HostStatus {
INACTIVE = 'inactive',
}
-export enum MetadataQueryStrategyVersions {
- VERSION_1 = 'v1',
- VERSION_2 = 'v2',
-}
-
export type PolicyInfo = Immutable<{
revision: number;
id: string;
}>;
-export interface HostMetadataInfo {
- metadata: HostMetadata;
- query_strategy_version: MetadataQueryStrategyVersions;
-}
-
export type HostInfo = Immutable<{
metadata: HostMetadata;
host_status: HostStatus;
@@ -438,8 +426,6 @@ export type HostInfo = Immutable<{
*/
endpoint: PolicyInfo;
};
- /* the version of the query strategy */
- query_strategy_version: MetadataQueryStrategyVersions;
}>;
// HostMetadataDetails is now just HostMetadata
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts
index d6b24fa3cbdfc..76de52222bbd3 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts
@@ -15,7 +15,6 @@ import {
HostPolicyResponse,
HostResultList,
HostStatus,
- MetadataQueryStrategyVersions,
} from '../../../../common/endpoint/types';
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
import {
@@ -54,7 +53,6 @@ export const endpointMetadataHttpMocks = httpHandlerMockFactory => {
agentsWithEndpointsTotalError: undefined,
endpointsTotal: 0,
endpointsTotalError: undefined,
- queryStrategyVersion: undefined,
policyVersionInfo: undefined,
hostStatus: undefined,
isolationRequestState: createUninitialisedResourceState(),
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts
index f233fbdec5415..922f10cee2f8b 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts
@@ -28,7 +28,6 @@ import {
nonExistingPolicies,
patterns,
searchBarQuery,
- isTransformEnabled,
getIsIsolationRequestPending,
getCurrentIsolationRequestState,
getActivityLogData,
@@ -180,7 +179,7 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory HostResultList = (options = {}) => {
const {
total = 1,
request_page_size: requestPageSize = 10,
request_page_index: requestPageIndex = 0,
- query_strategy_version: queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2,
} = options;
// Skip any that are before the page we're on
@@ -58,7 +55,6 @@ export const mockEndpointResultList: (options?: {
hosts.push({
metadata: generator.generateHostMetadata(),
host_status: HostStatus.UNHEALTHY,
- query_strategy_version: queryStrategyVersion,
});
}
const mock: HostResultList = {
@@ -66,7 +62,6 @@ export const mockEndpointResultList: (options?: {
total,
request_page_size: requestPageSize,
request_page_index: requestPageIndex,
- query_strategy_version: queryStrategyVersion,
};
return mock;
};
@@ -78,7 +73,6 @@ export const mockEndpointDetailsApiResult = (): HostInfo => {
return {
metadata: generator.generateHostMetadata(),
host_status: HostStatus.UNHEALTHY,
- query_strategy_version: MetadataQueryStrategyVersions.VERSION_2,
};
};
@@ -92,7 +86,6 @@ const endpointListApiPathHandlerMocks = ({
endpointPackagePolicies = [],
policyResponse = generator.generatePolicyResponse(),
agentPolicy = generator.generateAgentPolicy(),
- queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2,
totalAgentsUsingEndpoint = 0,
}: {
/** route handlers will be setup for each individual host in this array */
@@ -101,7 +94,6 @@ const endpointListApiPathHandlerMocks = ({
endpointPackagePolicies?: GetPolicyListResponse['items'];
policyResponse?: HostPolicyResponse;
agentPolicy?: GetAgentPoliciesResponseItem;
- queryStrategyVersion?: MetadataQueryStrategyVersions;
totalAgentsUsingEndpoint?: number;
} = {}) => {
const apiHandlers = {
@@ -119,7 +111,6 @@ const endpointListApiPathHandlerMocks = ({
request_page_size: 10,
request_page_index: 0,
total: endpointsResults?.length || 0,
- query_strategy_version: queryStrategyVersion,
};
},
@@ -192,16 +183,11 @@ export const setEndpointListApiMockImplementation: (
apiResponses?: Parameters[0]
) => void = (
mockedHttpService,
- {
- endpointsResults = mockEndpointResultList({ total: 3 }).hosts,
- queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2,
- ...pathHandlersOptions
- } = {}
+ { endpointsResults = mockEndpointResultList({ total: 3 }).hosts, ...pathHandlersOptions } = {}
) => {
const apiHandlers = endpointListApiPathHandlerMocks({
...pathHandlersOptions,
endpointsResults,
- queryStrategyVersion,
});
mockedHttpService.post
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts
index 1498ce08db8ab..c6bf13a3b5715 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts
@@ -89,7 +89,6 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
total,
request_page_size: pageSize,
request_page_index: pageIndex,
- query_strategy_version: queryStrategyVersion,
policy_info: policyVersionInfo,
} = action.payload;
return {
@@ -98,7 +97,6 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
total,
pageSize,
pageIndex,
- queryStrategyVersion,
policyVersionInfo,
loading: false,
error: undefined,
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts
index 5771fbac957d8..4287cf9a109ea 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts
@@ -15,7 +15,6 @@ import {
HostPolicyResponseAppliedAction,
HostPolicyResponseConfiguration,
HostPolicyResponseActionStatus,
- MetadataQueryStrategyVersions,
HostStatus,
ActivityLog,
HostMetadata,
@@ -90,17 +89,11 @@ export const agentsWithEndpointsTotalError = (state: Immutable) =
state.agentsWithEndpointsTotalError;
export const endpointsTotalError = (state: Immutable) => state.endpointsTotalError;
-const queryStrategyVersion = (state: Immutable) => state.queryStrategyVersion;
export const endpointPackageVersion = createSelector(endpointPackageInfo, (info) =>
isLoadedResourceState(info) ? info.data.version : undefined
);
-export const isTransformEnabled = createSelector(
- queryStrategyVersion,
- (version) => version !== MetadataQueryStrategyVersions.VERSION_1
-);
-
/**
* Returns the index patterns for the SearchBar to use for autosuggest
*/
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts
index 144cc7a64d6cb..875841cb55b73 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts
@@ -13,7 +13,6 @@ import {
HostPolicyResponse,
AppLocation,
PolicyData,
- MetadataQueryStrategyVersions,
HostStatus,
HostIsolationResponse,
EndpointPendingActions,
@@ -96,8 +95,6 @@ export interface EndpointState {
endpointsTotal: number;
/** api error for total, actual Endpoints */
endpointsTotalError?: ServerApiError;
- /** The query strategy version that informs whether the transform for KQL is enabled or not */
- queryStrategyVersion?: MetadataQueryStrategyVersions;
/** The policy IDs and revision number of the corresponding agent, and endpoint. May be more recent than what's running */
policyVersionInfo?: HostInfo['policy_info'];
/** The status of the host, which is mapped to the Elastic Agent status in Fleet */
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx
index aafac38accd89..26d0d53e39982 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx
@@ -23,7 +23,6 @@ import {
HostPolicyResponseActionStatus,
HostPolicyResponseAppliedAction,
HostStatus,
- MetadataQueryStrategyVersions,
} from '../../../../../common/endpoint/types';
import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data';
import { POLICY_STATUS_TO_TEXT } from './host_constants';
@@ -167,31 +166,6 @@ describe('when on the endpoint list page', () => {
});
});
- describe('when loading data with the query_strategy_version is `v1`', () => {
- beforeEach(() => {
- reactTestingLibrary.act(() => {
- const mockedEndpointListData = mockEndpointResultList({
- total: 4,
- query_strategy_version: MetadataQueryStrategyVersions.VERSION_1,
- });
- setEndpointListApiMockImplementation(coreStart.http, {
- endpointsResults: mockedEndpointListData.hosts,
- queryStrategyVersion: mockedEndpointListData.query_strategy_version,
- });
- });
- });
- afterEach(() => {
- jest.clearAllMocks();
- });
- it('should not display the KQL bar', async () => {
- const renderResult = render();
- await reactTestingLibrary.act(async () => {
- await middlewareSpy.waitForAction('serverReturnedEndpointList');
- });
- expect(renderResult.queryByTestId('adminSearchBar')).toBeNull();
- });
- });
-
describe('when determining when to show the enrolling message', () => {
afterEach(() => {
jest.clearAllMocks();
@@ -268,7 +242,6 @@ describe('when on the endpoint list page', () => {
reactTestingLibrary.act(() => {
const mockedEndpointData = mockEndpointResultList({ total: 5 });
const hostListData = mockedEndpointData.hosts;
- const queryStrategyVersion = mockedEndpointData.query_strategy_version;
firstPolicyID = hostListData[0].metadata.Endpoint.policy.applied.id;
firstPolicyRev = hostListData[0].metadata.Endpoint.policy.applied.endpoint_policy_version;
@@ -329,7 +302,6 @@ describe('when on the endpoint list page', () => {
hostListData[index].metadata.Endpoint.policy.applied,
setup.policy
),
- query_strategy_version: queryStrategyVersion,
};
});
hostListData.forEach((item, index) => {
@@ -535,8 +507,6 @@ describe('when on the endpoint list page', () => {
// eslint-disable-next-line @typescript-eslint/naming-convention
host_status,
metadata: { agent, Endpoint, ...details },
- // eslint-disable-next-line @typescript-eslint/naming-convention
- query_strategy_version,
} = mockEndpointDetailsApiResult();
hostDetails = {
@@ -555,7 +525,6 @@ describe('when on the endpoint list page', () => {
id: '1',
},
},
- query_strategy_version,
};
const policy = docGenerator.generatePolicyPackagePolicy();
@@ -1198,7 +1167,7 @@ describe('when on the endpoint list page', () => {
let renderResult: ReturnType;
const mockEndpointListApi = () => {
- const { hosts, query_strategy_version: queryStrategyVersion } = mockEndpointResultList();
+ const { hosts } = mockEndpointResultList();
hostInfo = {
host_status: hosts[0].host_status,
metadata: {
@@ -1222,7 +1191,6 @@ describe('when on the endpoint list page', () => {
version: '7.14.0',
},
},
- query_strategy_version: queryStrategyVersion,
};
const packagePolicy = docGenerator.generatePolicyPackagePolicy();
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx
index 0ee345431055b..c78d4ca6af634 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx
@@ -120,7 +120,6 @@ export const EndpointList = () => {
areEndpointsEnrolling,
agentsWithEndpointsTotalError,
endpointsTotalError,
- isTransformEnabled,
} = useEndpointSelector(selector);
const { search } = useFormatUrl(SecurityPageName.administration);
const { getAppUrl } = useAppUrl();
@@ -476,8 +475,8 @@ export const EndpointList = () => {
const hasListData = listData && listData.length > 0;
const refreshStyle = useMemo(() => {
- return { display: endpointsExist && isTransformEnabled ? 'flex' : 'none', maxWidth: 200 };
- }, [endpointsExist, isTransformEnabled]);
+ return { display: endpointsExist ? 'flex' : 'none', maxWidth: 200 };
+ }, [endpointsExist]);
const refreshIsPaused = useMemo(() => {
return !endpointsExist ? false : hasSelectedEndpoint ? true : !isAutoRefreshEnabled;
@@ -492,8 +491,8 @@ export const EndpointList = () => {
}, [endpointsTotalError, agentsWithEndpointsTotalError]);
const shouldShowKQLBar = useMemo(() => {
- return endpointsExist && !patternsError && isTransformEnabled;
- }, [endpointsExist, patternsError, isTransformEnabled]);
+ return endpointsExist && !patternsError;
+ }, [endpointsExist, patternsError]);
return (
;
-}
-
-export const createMetadataService = (packageService: PackageService): MetadataService => {
- return {
- async queryStrategy(
- savedObjectsClient: SavedObjectsClientContract,
- version?: MetadataQueryStrategyVersions
- ): Promise {
- if (version === MetadataQueryStrategyVersions.VERSION_1) {
- return metadataQueryStrategyV1();
- }
- if (!packageService) {
- throw new Error('package service is uninitialized');
- }
-
- if (version === MetadataQueryStrategyVersions.VERSION_2 || !version) {
- const assets =
- (await packageService.getInstallation({ savedObjectsClient, pkgName: 'endpoint' }))
- ?.installed_es ?? [];
- const expectedTransformAssets = assets.filter(
- (ref) =>
- ref.type === ElasticsearchAssetType.transform &&
- ref.id.startsWith(metadataTransformPrefix)
- );
- if (expectedTransformAssets && expectedTransformAssets.length === 1) {
- return metadataQueryStrategyV2();
- }
- return metadataQueryStrategyV1();
- }
- return metadataQueryStrategyV1();
- },
- };
-};
-
export type EndpointAppContextServiceStartContract = Partial<
Pick<
FleetStartContract,
@@ -114,7 +66,6 @@ export class EndpointAppContextService {
private packagePolicyService: PackagePolicyServiceInterface | undefined;
private agentPolicyService: AgentPolicyServiceInterface | undefined;
private savedObjectsStart: SavedObjectsServiceStart | undefined;
- private metadataService: MetadataService | undefined;
private config: ConfigType | undefined;
private license: LicenseService | undefined;
public security: SecurityPluginStart | undefined;
@@ -128,7 +79,6 @@ export class EndpointAppContextService {
this.agentPolicyService = dependencies.agentPolicyService;
this.manifestManager = dependencies.manifestManager;
this.savedObjectsStart = dependencies.savedObjectsStart;
- this.metadataService = createMetadataService(dependencies.packageService!);
this.config = dependencies.config;
this.license = dependencies.licenseService;
this.security = dependencies.security;
@@ -176,10 +126,6 @@ export class EndpointAppContextService {
return this.agentPolicyService;
}
- public getMetadataService(): MetadataService | undefined {
- return this.metadataService;
- }
-
public getManifestManager(): ManifestManager | undefined {
return this.manifestManager;
}
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts
index 45063ca92e2b0..fceb45b17c258 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts
@@ -83,7 +83,7 @@ export const isolationRequestHandler = function (
// fetch the Agent IDs to send the commands to
const endpointIDs = [...new Set(req.body.endpoint_ids)]; // dedupe
- const endpointData = await getMetadataForEndpoints(endpointIDs, context, endpointContext);
+ const endpointData = await getMetadataForEndpoints(endpointIDs, context);
const casesClient = await endpointContext.service.getCasesClient(req);
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts
index 960f3abda8195..39aa0bf2d8cf7 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { HostStatus, MetadataQueryStrategyVersions } from '../../../../common/endpoint/types';
+import { HostStatus } from '../../../../common/endpoint/types';
import { createMockMetadataRequestContext } from '../../mocks';
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
import { enrichHostMetadata, MetadataRequestContext } from './handlers';
@@ -18,30 +18,6 @@ describe('test document enrichment', () => {
metaReqCtx = createMockMetadataRequestContext();
});
- // verify query version passed through
- describe('metadata query strategy enrichment', () => {
- it('should match v1 strategy when directed', async () => {
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_1
- );
- expect(enrichedHostList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_1
- );
- });
- it('should match v2 strategy when directed', async () => {
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
- expect(enrichedHostList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_2
- );
- });
- });
-
describe('host status enrichment', () => {
let statusFn: jest.Mock;
@@ -57,77 +33,49 @@ describe('test document enrichment', () => {
it('should return host healthy for online agent', async () => {
statusFn.mockImplementation(() => 'online');
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.host_status).toEqual(HostStatus.HEALTHY);
});
it('should return host offline for offline agent', async () => {
statusFn.mockImplementation(() => 'offline');
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.host_status).toEqual(HostStatus.OFFLINE);
});
it('should return host updating for unenrolling agent', async () => {
statusFn.mockImplementation(() => 'unenrolling');
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.host_status).toEqual(HostStatus.UPDATING);
});
it('should return host unhealthy for degraded agent', async () => {
statusFn.mockImplementation(() => 'degraded');
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY);
});
it('should return host unhealthy for erroring agent', async () => {
statusFn.mockImplementation(() => 'error');
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY);
});
it('should return host unhealthy for warning agent', async () => {
statusFn.mockImplementation(() => 'warning');
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY);
});
it('should return host unhealthy for invalid agent', async () => {
statusFn.mockImplementation(() => 'asliduasofb');
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY);
});
});
@@ -164,11 +112,7 @@ describe('test document enrichment', () => {
};
});
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.policy_info).toBeDefined();
expect(enrichedHostList.policy_info!.agent.applied.id).toEqual(policyID);
expect(enrichedHostList.policy_info!.agent.applied.revision).toEqual(policyRev);
@@ -184,11 +128,7 @@ describe('test document enrichment', () => {
};
});
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.policy_info).toBeDefined();
expect(enrichedHostList.policy_info!.agent.configured.id).toEqual(policyID);
expect(enrichedHostList.policy_info!.agent.configured.revision).toEqual(policyRev);
@@ -209,11 +149,7 @@ describe('test document enrichment', () => {
};
});
- const enrichedHostList = await enrichHostMetadata(
- docGen.generateHostMetadata(),
- metaReqCtx,
- MetadataQueryStrategyVersions.VERSION_2
- );
+ const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
expect(enrichedHostList.policy_info).toBeDefined();
expect(enrichedHostList.policy_info!.endpoint.id).toEqual(policyID);
expect(enrichedHostList.policy_info!.endpoint.revision).toEqual(policyRev);
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts
index 815f30e6e7426..2ceca170881e3 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts
@@ -17,10 +17,8 @@ import {
import {
HostInfo,
HostMetadata,
- HostMetadataInfo,
HostResultList,
HostStatus,
- MetadataQueryStrategyVersions,
} from '../../../../common/endpoint/types';
import type { SecuritySolutionRequestHandlerContext } from '../../../types';
@@ -33,6 +31,10 @@ import { findAllUnenrolledAgentIds } from './support/unenroll';
import { findAgentIDsByStatus } from './support/agent_status';
import { EndpointAppContextService } from '../../endpoint_app_context_services';
import { fleetAgentStatusToEndpointHostStatus } from '../../utils';
+import {
+ queryResponseToHostListResult,
+ queryResponseToHostResult,
+} from './support/query_strategies';
export interface MetadataRequestContext {
esClient?: IScopedClusterClient;
@@ -58,8 +60,7 @@ export const getLogger = (endpointAppContext: EndpointAppContext): Logger => {
export const getMetadataListRequestHandler = function (
endpointAppContext: EndpointAppContext,
- logger: Logger,
- queryStrategyVersion?: MetadataQueryStrategyVersions
+ logger: Logger
): RequestHandler<
unknown,
unknown,
@@ -96,24 +97,15 @@ export const getMetadataListRequestHandler = function (
)
: undefined;
- const queryStrategy = await endpointAppContext.service
- ?.getMetadataService()
- ?.queryStrategy(context.core.savedObjects.client, queryStrategyVersion);
-
- const queryParams = await kibanaRequestToMetadataListESQuery(
- request,
- endpointAppContext,
- queryStrategy!,
- {
- unenrolledAgentIds: unenrolledAgentIds.concat(IGNORED_ELASTIC_AGENT_IDS),
- statusAgentIDs: statusIDs,
- }
- );
+ const queryParams = await kibanaRequestToMetadataListESQuery(request, endpointAppContext, {
+ unenrolledAgentIds: unenrolledAgentIds.concat(IGNORED_ELASTIC_AGENT_IDS),
+ statusAgentIDs: statusIDs,
+ });
const result = await context.core.elasticsearch.client.asCurrentUser.search(
queryParams
);
- const hostListQueryResult = queryStrategy!.queryResponseToHostListResult(result.body);
+ const hostListQueryResult = queryResponseToHostListResult(result.body);
return response.ok({
body: await mapToHostResultList(queryParams, hostListQueryResult, metadataRequestContext),
});
@@ -122,8 +114,7 @@ export const getMetadataListRequestHandler = function (
export const getMetadataRequestHandler = function (
endpointAppContext: EndpointAppContext,
- logger: Logger,
- queryStrategyVersion?: MetadataQueryStrategyVersions
+ logger: Logger
): RequestHandler<
TypeOf,
unknown,
@@ -145,11 +136,7 @@ export const getMetadataRequestHandler = function (
};
try {
- const doc = await getHostData(
- metadataRequestContext,
- request?.params?.id,
- queryStrategyVersion
- );
+ const doc = await getHostData(metadataRequestContext, request?.params?.id);
if (doc) {
return response.ok({ body: doc });
}
@@ -169,9 +156,8 @@ export const getMetadataRequestHandler = function (
export async function getHostMetaData(
metadataRequestContext: MetadataRequestContext,
- id: string,
- queryStrategyVersion?: MetadataQueryStrategyVersions
-): Promise {
+ id: string
+): Promise {
if (
!metadataRequestContext.esClient &&
!metadataRequestContext.requestHandlerContext?.core.elasticsearch.client
@@ -190,32 +176,23 @@ export async function getHostMetaData(
metadataRequestContext.requestHandlerContext?.core.elasticsearch
.client) as IScopedClusterClient;
- const esSavedObjectClient =
- metadataRequestContext?.savedObjectsClient ??
- (metadataRequestContext.requestHandlerContext?.core.savedObjects
- .client as SavedObjectsClientContract);
-
- const queryStrategy = await metadataRequestContext.endpointAppContextService
- ?.getMetadataService()
- ?.queryStrategy(esSavedObjectClient, queryStrategyVersion);
- const query = getESQueryHostMetadataByID(id, queryStrategy!);
+ const query = getESQueryHostMetadataByID(id);
const response = await esClient.asCurrentUser.search(query);
- const hostResult = queryStrategy!.queryResponseToHostResult(response.body);
+ const hostResult = queryResponseToHostResult(response.body);
const hostMetadata = hostResult.result;
if (!hostMetadata) {
return undefined;
}
- return { metadata: hostMetadata, query_strategy_version: hostResult.queryStrategyVersion };
+ return hostMetadata;
}
export async function getHostData(
metadataRequestContext: MetadataRequestContext,
- id: string,
- queryStrategyVersion?: MetadataQueryStrategyVersions
+ id: string
): Promise {
if (!metadataRequestContext.savedObjectsClient) {
throw Boom.badRequest('savedObjectsClient not found');
@@ -228,25 +205,21 @@ export async function getHostData(
throw Boom.badRequest('esClient not found');
}
- const hostResult = await getHostMetaData(metadataRequestContext, id, queryStrategyVersion);
+ const hostMetadata = await getHostMetaData(metadataRequestContext, id);
- if (!hostResult) {
+ if (!hostMetadata) {
return undefined;
}
- const agent = await findAgent(metadataRequestContext, hostResult.metadata);
+ const agent = await findAgent(metadataRequestContext, hostMetadata);
if (agent && !agent.active) {
throw Boom.badRequest('the requested endpoint is unenrolled');
}
- const metadata = await enrichHostMetadata(
- hostResult.metadata,
- metadataRequestContext,
- hostResult.query_strategy_version
- );
+ const metadata = await enrichHostMetadata(hostMetadata, metadataRequestContext);
- return { ...metadata, query_strategy_version: hostResult.query_strategy_version };
+ return metadata;
}
async function findAgent(
@@ -293,15 +266,10 @@ export async function mapToHostResultList(
request_page_index: queryParams.from,
hosts: await Promise.all(
hostListQueryResult.resultList.map(async (entry) =>
- enrichHostMetadata(
- entry,
- metadataRequestContext,
- hostListQueryResult.queryStrategyVersion
- )
+ enrichHostMetadata(entry, metadataRequestContext)
)
),
total: totalNumberOfHosts,
- query_strategy_version: hostListQueryResult.queryStrategyVersion,
};
} else {
return {
@@ -309,15 +277,13 @@ export async function mapToHostResultList(
request_page_index: queryParams.from,
total: totalNumberOfHosts,
hosts: [],
- query_strategy_version: hostListQueryResult.queryStrategyVersion,
};
}
}
export async function enrichHostMetadata(
hostMetadata: HostMetadata,
- metadataRequestContext: MetadataRequestContext,
- metadataQueryStrategyVersion: MetadataQueryStrategyVersions
+ metadataRequestContext: MetadataRequestContext
): Promise {
let hostStatus = HostStatus.UNHEALTHY;
let elasticAgentId = hostMetadata?.elastic?.agent?.id;
@@ -413,6 +379,5 @@ export async function enrichHostMetadata(
metadata: hostMetadata,
host_status: hostStatus,
policy_info: policyInfo,
- query_strategy_version: metadataQueryStrategyVersion,
};
}
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts
index b4784c1ff5ed4..d9c3e6c195307 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts
@@ -7,19 +7,15 @@
import { schema } from '@kbn/config-schema';
-import { HostStatus, MetadataQueryStrategyVersions } from '../../../../common/endpoint/types';
+import { HostStatus } from '../../../../common/endpoint/types';
import { EndpointAppContext } from '../../types';
import { getLogger, getMetadataListRequestHandler, getMetadataRequestHandler } from './handlers';
import type { SecuritySolutionPluginRouter } from '../../../types';
import {
- BASE_ENDPOINT_ROUTE,
HOST_METADATA_GET_ROUTE,
HOST_METADATA_LIST_ROUTE,
} from '../../../../common/endpoint/constants';
-export const METADATA_REQUEST_V1_ROUTE = `${BASE_ENDPOINT_ROUTE}/v1/metadata`;
-export const GET_METADATA_REQUEST_V1_ROUTE = `${METADATA_REQUEST_V1_ROUTE}/{id}`;
-
/* Filters that can be applied to the endpoint fetch route */
export const endpointFilters = schema.object({
kql: schema.nullable(schema.string()),
@@ -69,18 +65,6 @@ export function registerEndpointRoutes(
endpointAppContext: EndpointAppContext
) {
const logger = getLogger(endpointAppContext);
- router.post(
- {
- path: `${METADATA_REQUEST_V1_ROUTE}`,
- validate: GetMetadataListRequestSchema,
- options: { authRequired: true, tags: ['access:securitySolution'] },
- },
- getMetadataListRequestHandler(
- endpointAppContext,
- logger,
- MetadataQueryStrategyVersions.VERSION_1
- )
- );
router.post(
{
@@ -91,15 +75,6 @@ export function registerEndpointRoutes(
getMetadataListRequestHandler(endpointAppContext, logger)
);
- router.get(
- {
- path: `${GET_METADATA_REQUEST_V1_ROUTE}`,
- validate: GetMetadataRequestSchema,
- options: { authRequired: true, tags: ['access:securitySolution'] },
- },
- getMetadataRequestHandler(endpointAppContext, logger, MetadataQueryStrategyVersions.VERSION_1)
- );
-
router.get(
{
path: `${HOST_METADATA_GET_ROUTE}`,
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts
index 5250f7c49d6ad..1e56f79aa0b32 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts
@@ -19,12 +19,7 @@ import {
loggingSystemMock,
savedObjectsClientMock,
} from '../../../../../../../src/core/server/mocks';
-import {
- HostInfo,
- HostResultList,
- HostStatus,
- MetadataQueryStrategyVersions,
-} from '../../../../common/endpoint/types';
+import { HostInfo, HostResultList, HostStatus } from '../../../../common/endpoint/types';
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
import { registerEndpointRoutes } from './index';
import {
@@ -39,7 +34,7 @@ import {
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
import { Agent, ElasticsearchAssetType } from '../../../../../fleet/common/types/models';
-import { createV1SearchResponse, createV2SearchResponse } from './support/test_support';
+import { createV2SearchResponse } from './support/test_support';
import { PackageService } from '../../../../../fleet/server/services';
import {
HOST_METADATA_LIST_ROUTE,
@@ -98,94 +93,6 @@ describe('test endpoint route', () => {
);
});
- describe('with no transform package', () => {
- beforeEach(() => {
- endpointAppContextService = new EndpointAppContextService();
- mockPackageService = createMockPackageService();
- mockPackageService.getInstallation.mockReturnValue(Promise.resolve(undefined));
- endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
- mockAgentService = startContract.agentService!;
-
- registerEndpointRoutes(routerMock, {
- logFactory: loggingSystemMock.create(),
- service: endpointAppContextService,
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
- });
- });
-
- afterEach(() => endpointAppContextService.stop());
-
- it('test find the latest of all endpoints', async () => {
- const mockRequest = httpServerMock.createKibanaRequest({});
- const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({ body: response })
- );
- [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
- path.startsWith(`${HOST_METADATA_LIST_ROUTE}`)
- )!;
- mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
- mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(routeConfig.options).toEqual({
- authRequired: true,
- tags: ['access:securitySolution'],
- });
- expect(mockResponse.ok).toBeCalled();
- const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
- expect(endpointResultList.hosts.length).toEqual(1);
- expect(endpointResultList.total).toEqual(1);
- expect(endpointResultList.request_page_index).toEqual(0);
- expect(endpointResultList.request_page_size).toEqual(10);
- expect(endpointResultList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_1
- );
- });
-
- it('should return a single endpoint with status healthy', async () => {
- const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
- const mockRequest = httpServerMock.createKibanaRequest({
- params: { id: response.hits.hits[0]._id },
- });
-
- mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('online');
- mockAgentService.getAgent = jest.fn().mockReturnValue(({
- active: true,
- } as unknown) as Agent);
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({ body: response })
- );
-
- [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
- path.startsWith(`${HOST_METADATA_LIST_ROUTE}`)
- )!;
-
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(routeConfig.options).toEqual({
- authRequired: true,
- tags: ['access:securitySolution'],
- });
- expect(mockResponse.ok).toBeCalled();
- const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
- expect(result).toHaveProperty('metadata.Endpoint');
- expect(result.host_status).toEqual(HostStatus.HEALTHY);
- expect(result.query_strategy_version).toEqual(MetadataQueryStrategyVersions.VERSION_1);
- });
- });
-
describe('with new transform package', () => {
beforeEach(() => {
endpointAppContextService = new EndpointAppContextService();
@@ -254,9 +161,6 @@ describe('test endpoint route', () => {
expect(endpointResultList.total).toEqual(1);
expect(endpointResultList.request_page_index).toEqual(0);
expect(endpointResultList.request_page_size).toEqual(10);
- expect(endpointResultList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_2
- );
});
it('test find the latest of all endpoints with paging properties', async () => {
@@ -311,9 +215,6 @@ describe('test endpoint route', () => {
expect(endpointResultList.total).toEqual(1);
expect(endpointResultList.request_page_index).toEqual(10);
expect(endpointResultList.request_page_size).toEqual(10);
- expect(endpointResultList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_2
- );
});
it('test find the latest of all endpoints with paging and filters properties', async () => {
@@ -405,9 +306,6 @@ describe('test endpoint route', () => {
expect(endpointResultList.total).toEqual(1);
expect(endpointResultList.request_page_index).toEqual(10);
expect(endpointResultList.request_page_size).toEqual(10);
- expect(endpointResultList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_2
- );
});
describe('Endpoint Details route', () => {
@@ -475,7 +373,6 @@ describe('test endpoint route', () => {
const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
expect(result).toHaveProperty('metadata.Endpoint');
expect(result.host_status).toEqual(HostStatus.HEALTHY);
- expect(result.query_strategy_version).toEqual(MetadataQueryStrategyVersions.VERSION_2);
});
it('should return a single endpoint with status unhealthy when AgentService throw 404', async () => {
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata_v1.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata_v1.test.ts
deleted file mode 100644
index 29b2c231cc4a5..0000000000000
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata_v1.test.ts
+++ /dev/null
@@ -1,456 +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 {
- KibanaResponseFactory,
- RequestHandler,
- RouteConfig,
- SavedObjectsClientContract,
- SavedObjectsErrorHelpers,
-} from '../../../../../../../src/core/server';
-import {
- ClusterClientMock,
- ScopedClusterClientMock,
- // eslint-disable-next-line @kbn/eslint/no-restricted-paths
-} from '../../../../../../../src/core/server/elasticsearch/client/mocks';
-import {
- elasticsearchServiceMock,
- httpServerMock,
- httpServiceMock,
- loggingSystemMock,
- savedObjectsClientMock,
-} from '../../../../../../../src/core/server/mocks';
-import {
- HostInfo,
- HostResultList,
- HostStatus,
- MetadataQueryStrategyVersions,
-} from '../../../../common/endpoint/types';
-import { registerEndpointRoutes, METADATA_REQUEST_V1_ROUTE } from './index';
-import {
- createMockEndpointAppContextServiceStartContract,
- createMockPackageService,
- createRouteHandlerContext,
-} from '../../mocks';
-import {
- EndpointAppContextService,
- EndpointAppContextServiceStartContract,
-} from '../../endpoint_app_context_services';
-import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
-import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
-import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
-import { Agent } from '../../../../../fleet/common/types/models';
-import { createV1SearchResponse } from './support/test_support';
-import { PackageService } from '../../../../../fleet/server/services';
-import type { SecuritySolutionPluginRouter } from '../../../types';
-import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
-
-describe('test endpoint route v1', () => {
- let routerMock: jest.Mocked;
- let mockResponse: jest.Mocked;
- let mockClusterClient: ClusterClientMock;
- let mockScopedClient: ScopedClusterClientMock;
- let mockSavedObjectClient: jest.Mocked;
- let mockPackageService: jest.Mocked;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let routeHandler: RequestHandler;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let routeConfig: RouteConfig;
- // tests assume that fleet is enabled, and thus agentService is available
- let mockAgentService: Required<
- ReturnType
- >['agentService'];
- let endpointAppContextService: EndpointAppContextService;
- let startContract: EndpointAppContextServiceStartContract;
- const noUnenrolledAgent = {
- agents: [],
- total: 0,
- page: 1,
- perPage: 1,
- };
-
- beforeEach(() => {
- mockClusterClient = elasticsearchServiceMock.createClusterClient();
- mockScopedClient = elasticsearchServiceMock.createScopedClusterClient();
- mockSavedObjectClient = savedObjectsClientMock.create();
- mockClusterClient.asScoped.mockReturnValue(mockScopedClient);
- routerMock = httpServiceMock.createRouter();
- mockResponse = httpServerMock.createResponseFactory();
- endpointAppContextService = new EndpointAppContextService();
- mockPackageService = createMockPackageService();
- mockPackageService.getInstallation.mockReturnValue(Promise.resolve(undefined));
- startContract = createMockEndpointAppContextServiceStartContract();
- endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
- mockAgentService = startContract.agentService!;
-
- (startContract.packagePolicyService as jest.Mocked).list.mockImplementation(
- () => {
- return Promise.resolve({
- items: [],
- total: 0,
- page: 1,
- perPage: 1000,
- });
- }
- );
-
- registerEndpointRoutes(routerMock, {
- logFactory: loggingSystemMock.create(),
- service: endpointAppContextService,
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
- });
- });
-
- afterEach(() => endpointAppContextService.stop());
-
- it('test find the latest of all endpoints', async () => {
- const mockRequest = httpServerMock.createKibanaRequest({});
- const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({ body: response })
- );
- [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
- path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
- )!;
- mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
- mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(routeConfig.options).toEqual({ authRequired: true, tags: ['access:securitySolution'] });
- expect(mockResponse.ok).toBeCalled();
- const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
- expect(endpointResultList.hosts.length).toEqual(1);
- expect(endpointResultList.total).toEqual(1);
- expect(endpointResultList.request_page_index).toEqual(0);
- expect(endpointResultList.request_page_size).toEqual(10);
- expect(endpointResultList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_1
- );
- });
-
- it('test find the latest of all endpoints with paging properties', async () => {
- const mockRequest = httpServerMock.createKibanaRequest({
- body: {
- paging_properties: [
- {
- page_size: 10,
- },
- {
- page_index: 1,
- },
- ],
- },
- });
-
- mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
- mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({
- body: createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata()),
- })
- );
- [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
- path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
- )!;
-
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(
- (mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[0][0]?.body?.query.bool
- .must_not
- ).toContainEqual({
- terms: {
- 'elastic.agent.id': [
- '00000000-0000-0000-0000-000000000000',
- '11111111-1111-1111-1111-111111111111',
- ],
- },
- });
- expect(routeConfig.options).toEqual({ authRequired: true, tags: ['access:securitySolution'] });
- expect(mockResponse.ok).toBeCalled();
- const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
- expect(endpointResultList.hosts.length).toEqual(1);
- expect(endpointResultList.total).toEqual(1);
- expect(endpointResultList.request_page_index).toEqual(10);
- expect(endpointResultList.request_page_size).toEqual(10);
- expect(endpointResultList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_1
- );
- });
-
- it('test find the latest of all endpoints with paging and filters properties', async () => {
- const mockRequest = httpServerMock.createKibanaRequest({
- body: {
- paging_properties: [
- {
- page_size: 10,
- },
- {
- page_index: 1,
- },
- ],
-
- filters: { kql: 'not host.ip:10.140.73.246' },
- },
- });
-
- mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
- mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({
- body: createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata()),
- })
- );
- [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
- path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
- )!;
-
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toBeCalled();
- // needs to have the KQL filter passed through
- expect(
- (mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[0][0]?.body?.query.bool.must
- ).toContainEqual({
- bool: {
- must_not: {
- bool: {
- should: [
- {
- match: {
- 'host.ip': '10.140.73.246',
- },
- },
- ],
- minimum_should_match: 1,
- },
- },
- },
- });
- // and unenrolled should be filtered out.
- expect(
- (mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[0][0]?.body?.query.bool.must
- ).toContainEqual({
- bool: {
- must_not: [
- {
- terms: {
- 'elastic.agent.id': [
- '00000000-0000-0000-0000-000000000000',
- '11111111-1111-1111-1111-111111111111',
- ],
- },
- },
- {
- terms: {
- // we actually don't care about HostDetails in v1 queries, but
- // harder to set up the expectation to ignore its inclusion succinctly
- 'HostDetails.elastic.agent.id': [
- '00000000-0000-0000-0000-000000000000',
- '11111111-1111-1111-1111-111111111111',
- ],
- },
- },
- ],
- },
- });
- expect(routeConfig.options).toEqual({ authRequired: true, tags: ['access:securitySolution'] });
- expect(mockResponse.ok).toBeCalled();
- const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
- expect(endpointResultList.hosts.length).toEqual(1);
- expect(endpointResultList.total).toEqual(1);
- expect(endpointResultList.request_page_index).toEqual(10);
- expect(endpointResultList.request_page_size).toEqual(10);
- expect(endpointResultList.query_strategy_version).toEqual(
- MetadataQueryStrategyVersions.VERSION_1
- );
- });
-
- describe('Endpoint Details route', () => {
- it('should return 404 on no results', async () => {
- const mockRequest = httpServerMock.createKibanaRequest({ params: { id: 'BADID' } });
-
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({ body: createV1SearchResponse() })
- );
-
- mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
- mockAgentService.getAgent = jest.fn().mockReturnValue(({
- active: true,
- } as unknown) as Agent);
-
- [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
- path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
- )!;
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(routeConfig.options).toEqual({
- authRequired: true,
- tags: ['access:securitySolution'],
- });
- expect(mockResponse.notFound).toBeCalled();
- const message = mockResponse.notFound.mock.calls[0][0]?.body;
- expect(message).toEqual('Endpoint Not Found');
- });
-
- it('should return a single endpoint with status healthy', async () => {
- const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
- const mockRequest = httpServerMock.createKibanaRequest({
- params: { id: response.hits.hits[0]._id },
- });
-
- mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('online');
- mockAgentService.getAgent = jest.fn().mockReturnValue(({
- active: true,
- } as unknown) as Agent);
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({ body: response })
- );
-
- [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
- path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
- )!;
-
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(routeConfig.options).toEqual({
- authRequired: true,
- tags: ['access:securitySolution'],
- });
- expect(mockResponse.ok).toBeCalled();
- const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
- expect(result).toHaveProperty('metadata.Endpoint');
- expect(result.host_status).toEqual(HostStatus.HEALTHY);
- });
-
- it('should return a single endpoint with status unhealthy when AgentService throw 404', async () => {
- const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
-
- const mockRequest = httpServerMock.createKibanaRequest({
- params: { id: response.hits.hits[0]._id },
- });
-
- mockAgentService.getAgentStatusById = jest.fn().mockImplementation(() => {
- SavedObjectsErrorHelpers.createGenericNotFoundError();
- });
-
- mockAgentService.getAgent = jest.fn().mockImplementation(() => {
- SavedObjectsErrorHelpers.createGenericNotFoundError();
- });
-
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({ body: response })
- );
-
- [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
- path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
- )!;
-
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(routeConfig.options).toEqual({
- authRequired: true,
- tags: ['access:securitySolution'],
- });
- expect(mockResponse.ok).toBeCalled();
- const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
- expect(result.host_status).toEqual(HostStatus.UNHEALTHY);
- });
-
- it('should return a single endpoint with status unhealthy when status is not offline, online or enrolling', async () => {
- const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
-
- const mockRequest = httpServerMock.createKibanaRequest({
- params: { id: response.hits.hits[0]._id },
- });
-
- mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('warning');
- mockAgentService.getAgent = jest.fn().mockReturnValue(({
- active: true,
- } as unknown) as Agent);
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({ body: response })
- );
-
- [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
- path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
- )!;
-
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(routeConfig.options).toEqual({
- authRequired: true,
- tags: ['access:securitySolution'],
- });
- expect(mockResponse.ok).toBeCalled();
- const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
- expect(result.host_status).toEqual(HostStatus.UNHEALTHY);
- });
-
- it('should throw error when endpoint agent is not active', async () => {
- const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
-
- const mockRequest = httpServerMock.createKibanaRequest({
- params: { id: response.hits.hits[0]._id },
- });
- (mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
- Promise.resolve({ body: response })
- );
- mockAgentService.getAgent = jest.fn().mockReturnValue(({
- active: false,
- } as unknown) as Agent);
-
- [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
- path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
- )!;
-
- await routeHandler(
- createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
- mockRequest,
- mockResponse
- );
-
- expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
- expect(mockResponse.customError).toBeCalled();
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.test.ts
index e790c1de1a5b8..87de5a540ea99 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.test.ts
@@ -11,38 +11,29 @@ import { EndpointAppContextService } from '../../endpoint_app_context_services';
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
import { metadataCurrentIndexPattern } from '../../../../common/endpoint/constants';
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
-import { metadataQueryStrategyV2 } from './support/query_strategies';
import { get } from 'lodash';
describe('query builder', () => {
describe('MetadataListESQuery', () => {
it('queries the correct index', async () => {
const mockRequest = httpServerMock.createKibanaRequest({ body: {} });
- const query = await kibanaRequestToMetadataListESQuery(
- mockRequest,
- {
- logFactory: loggingSystemMock.create(),
- service: new EndpointAppContextService(),
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
- },
- metadataQueryStrategyV2()
- );
+ const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
+ logFactory: loggingSystemMock.create(),
+ service: new EndpointAppContextService(),
+ config: () => Promise.resolve(createMockConfig()),
+ experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
+ });
expect(query.index).toEqual(metadataCurrentIndexPattern);
});
it('sorts using *event.created', async () => {
const mockRequest = httpServerMock.createKibanaRequest({ body: {} });
- const query = await kibanaRequestToMetadataListESQuery(
- mockRequest,
- {
- logFactory: loggingSystemMock.create(),
- service: new EndpointAppContextService(),
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
- },
- metadataQueryStrategyV2()
- );
+ const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
+ logFactory: loggingSystemMock.create(),
+ service: new EndpointAppContextService(),
+ config: () => Promise.resolve(createMockConfig()),
+ experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
+ });
expect(query.body.sort).toContainEqual({
'event.created': {
order: 'desc',
@@ -61,16 +52,12 @@ describe('query builder', () => {
const mockRequest = httpServerMock.createKibanaRequest({
body: {},
});
- const query = await kibanaRequestToMetadataListESQuery(
- mockRequest,
- {
- logFactory: loggingSystemMock.create(),
- service: new EndpointAppContextService(),
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
- },
- metadataQueryStrategyV2()
- );
+ const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
+ logFactory: loggingSystemMock.create(),
+ service: new EndpointAppContextService(),
+ config: () => Promise.resolve(createMockConfig()),
+ experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
+ });
expect(query.body.query).toHaveProperty('match_all');
});
@@ -87,7 +74,6 @@ describe('query builder', () => {
config: () => Promise.resolve(createMockConfig()),
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
},
- metadataQueryStrategyV2(),
{
unenrolledAgentIds: [unenrolledElasticAgentId],
}
@@ -111,16 +97,12 @@ describe('query builder', () => {
filters: { kql: 'not host.ip:10.140.73.246' },
},
});
- const query = await kibanaRequestToMetadataListESQuery(
- mockRequest,
- {
- logFactory: loggingSystemMock.create(),
- service: new EndpointAppContextService(),
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
- },
- metadataQueryStrategyV2()
- );
+ const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
+ logFactory: loggingSystemMock.create(),
+ service: new EndpointAppContextService(),
+ config: () => Promise.resolve(createMockConfig()),
+ experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
+ });
expect(query.body.query.bool.must).toContainEqual({
bool: {
@@ -160,7 +142,6 @@ describe('query builder', () => {
createMockConfig().enableExperimental
),
},
- metadataQueryStrategyV2(),
{
unenrolledAgentIds: [unenrolledElasticAgentId],
}
@@ -197,13 +178,13 @@ describe('query builder', () => {
describe('MetadataGetQuery', () => {
it('searches the correct index', () => {
- const query = getESQueryHostMetadataByID('nonsense-id', metadataQueryStrategyV2());
+ const query = getESQueryHostMetadataByID('nonsense-id');
expect(query.index).toEqual(metadataCurrentIndexPattern);
});
it('searches for the correct ID', () => {
const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899';
- const query = getESQueryHostMetadataByID(mockID, metadataQueryStrategyV2());
+ const query = getESQueryHostMetadataByID(mockID);
expect(get(query, 'body.query.bool.filter.0.bool.should')).toContainEqual({
term: { 'agent.id': mockID },
@@ -212,7 +193,7 @@ describe('query builder', () => {
it('supports HostDetails in schema for backwards compat', () => {
const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899';
- const query = getESQueryHostMetadataByID(mockID, metadataQueryStrategyV2());
+ const query = getESQueryHostMetadataByID(mockID);
expect(get(query, 'body.query.bool.filter.0.bool.should')).toContainEqual({
term: { 'HostDetails.agent.id': mockID },
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts
index f0950e5fb79ba..99ec1d1022747 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts
@@ -6,9 +6,10 @@
*/
import type { estypes } from '@elastic/elasticsearch';
+import { metadataCurrentIndexPattern } from '../../../../common/endpoint/constants';
import { KibanaRequest } from '../../../../../../../src/core/server';
import { esKuery } from '../../../../../../../src/plugins/data/server';
-import { EndpointAppContext, MetadataQueryStrategy } from '../../types';
+import { EndpointAppContext } from '../../types';
export interface QueryBuilderOptions {
unenrolledAgentIds?: string[];
@@ -39,7 +40,6 @@ export async function kibanaRequestToMetadataListESQuery(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
request: KibanaRequest,
endpointAppContext: EndpointAppContext,
- metadataQueryStrategy: MetadataQueryStrategy,
queryBuilderOptions?: QueryBuilderOptions
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise> {
@@ -49,16 +49,15 @@ export async function kibanaRequestToMetadataListESQuery(
body: {
query: buildQueryBody(
request,
- metadataQueryStrategy,
queryBuilderOptions?.unenrolledAgentIds!,
queryBuilderOptions?.statusAgentIDs!
),
- ...metadataQueryStrategy.extraBodyProperties,
+ track_total_hits: true,
sort: MetadataSortMethod,
},
from: pagingProperties.pageIndex * pagingProperties.pageSize,
size: pagingProperties.pageSize,
- index: metadataQueryStrategy.index,
+ index: metadataCurrentIndexPattern,
};
}
@@ -86,7 +85,6 @@ async function getPagingProperties(
function buildQueryBody(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
request: KibanaRequest,
- metadataQueryStrategy: MetadataQueryStrategy,
unerolledAgentIds: string[] | undefined,
statusAgentIDs: string[] | undefined
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -144,10 +142,7 @@ function buildQueryBody(
};
}
-export function getESQueryHostMetadataByID(
- agentID: string,
- metadataQueryStrategy: MetadataQueryStrategy
-): estypes.SearchRequest {
+export function getESQueryHostMetadataByID(agentID: string): estypes.SearchRequest {
return {
body: {
query: {
@@ -167,14 +162,11 @@ export function getESQueryHostMetadataByID(
sort: MetadataSortMethod,
size: 1,
},
- index: metadataQueryStrategy.index,
+ index: metadataCurrentIndexPattern,
};
}
-export function getESQueryHostMetadataByIDs(
- agentIDs: string[],
- metadataQueryStrategy: MetadataQueryStrategy
-) {
+export function getESQueryHostMetadataByIDs(agentIDs: string[]) {
return {
body: {
query: {
@@ -193,6 +185,6 @@ export function getESQueryHostMetadataByIDs(
},
sort: MetadataSortMethod,
},
- index: metadataQueryStrategy.index,
+ index: metadataCurrentIndexPattern,
};
}
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders_v1.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders_v1.test.ts
deleted file mode 100644
index c18c585cd3d34..0000000000000
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders_v1.test.ts
+++ /dev/null
@@ -1,188 +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 { httpServerMock, loggingSystemMock } from '../../../../../../../src/core/server/mocks';
-import { kibanaRequestToMetadataListESQuery, getESQueryHostMetadataByID } from './query_builders';
-import { EndpointAppContextService } from '../../endpoint_app_context_services';
-import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
-import { metadataIndexPattern } from '../../../../common/endpoint/constants';
-import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
-import { metadataQueryStrategyV1 } from './support/query_strategies';
-import { get } from 'lodash';
-
-describe('query builder v1', () => {
- describe('MetadataListESQuery', () => {
- it('test default query params for all endpoints metadata when no params or body is provided', async () => {
- const mockRequest = httpServerMock.createKibanaRequest({
- body: {},
- });
- const query = await kibanaRequestToMetadataListESQuery(
- mockRequest,
- {
- logFactory: loggingSystemMock.create(),
- service: new EndpointAppContextService(),
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
- },
- metadataQueryStrategyV1()
- );
-
- expect(query.body.query).toHaveProperty('match_all'); // no filtering
- expect(query.body.collapse).toEqual({
- field: 'agent.id',
- inner_hits: {
- name: 'most_recent',
- size: 1,
- sort: [{ 'event.created': 'desc' }],
- },
- });
- expect(query.body.aggs).toEqual({
- total: {
- cardinality: {
- field: 'agent.id',
- },
- },
- });
- expect(query.index).toEqual(metadataIndexPattern);
- });
-
- it(
- 'test default query params for all endpoints metadata when no params or body is provided ' +
- 'with unenrolled host ids excluded',
- async () => {
- const unenrolledElasticAgentId = '1fdca33f-799f-49f4-939c-ea4383c77672';
- const mockRequest = httpServerMock.createKibanaRequest({
- body: {},
- });
- const query = await kibanaRequestToMetadataListESQuery(
- mockRequest,
- {
- logFactory: loggingSystemMock.create(),
- service: new EndpointAppContextService(),
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(
- createMockConfig().enableExperimental
- ),
- },
- metadataQueryStrategyV1(),
- {
- unenrolledAgentIds: [unenrolledElasticAgentId],
- }
- );
- expect(Object.keys(query.body.query.bool)).toEqual(['must_not']); // only filtering out unenrolled
- expect(query.body.query.bool.must_not).toContainEqual({
- terms: { 'elastic.agent.id': [unenrolledElasticAgentId] },
- });
- }
- );
- });
-
- describe('test query builder with kql filter', () => {
- it('test default query params for all endpoints metadata when body filter is provided', async () => {
- const mockRequest = httpServerMock.createKibanaRequest({
- body: {
- filters: { kql: 'not host.ip:10.140.73.246' },
- },
- });
- const query = await kibanaRequestToMetadataListESQuery(
- mockRequest,
- {
- logFactory: loggingSystemMock.create(),
- service: new EndpointAppContextService(),
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
- },
- metadataQueryStrategyV1()
- );
- expect(query.body.query.bool.must).toHaveLength(1); // should not be any other filtering happening
- expect(query.body.query.bool.must).toContainEqual({
- bool: {
- must_not: {
- bool: {
- should: [
- {
- match: {
- 'host.ip': '10.140.73.246',
- },
- },
- ],
- minimum_should_match: 1,
- },
- },
- },
- });
- });
-
- it(
- 'test default query params for all endpoints endpoint metadata excluding unerolled endpoint ' +
- 'and when body filter is provided',
- async () => {
- const unenrolledElasticAgentId = '1fdca33f-799f-49f4-939c-ea4383c77672';
- const mockRequest = httpServerMock.createKibanaRequest({
- body: {
- filters: { kql: 'not host.ip:10.140.73.246' },
- },
- });
- const query = await kibanaRequestToMetadataListESQuery(
- mockRequest,
- {
- logFactory: loggingSystemMock.create(),
- service: new EndpointAppContextService(),
- config: () => Promise.resolve(createMockConfig()),
- experimentalFeatures: parseExperimentalConfigValue(
- createMockConfig().enableExperimental
- ),
- },
- metadataQueryStrategyV1(),
- {
- unenrolledAgentIds: [unenrolledElasticAgentId],
- }
- );
-
- expect(query.body.query.bool.must.length).toBeGreaterThan(1);
- // unenrollment filter should be there
- expect(query.body.query.bool.must).toContainEqual({
- bool: {
- must_not: [
- { terms: { 'elastic.agent.id': [unenrolledElasticAgentId] } },
- // below is not actually necessary behavior for v1, but hard to structure the test to ignore it
- { terms: { 'HostDetails.elastic.agent.id': [unenrolledElasticAgentId] } },
- ],
- },
- });
- // and KQL should also be there
- expect(query.body.query.bool.must).toContainEqual({
- bool: {
- must_not: {
- bool: {
- should: [
- {
- match: {
- 'host.ip': '10.140.73.246',
- },
- },
- ],
- minimum_should_match: 1,
- },
- },
- },
- });
- }
- );
- });
-
- describe('MetadataGetQuery', () => {
- it('searches for the correct ID', () => {
- const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899';
- const query = getESQueryHostMetadataByID(mockID, metadataQueryStrategyV1());
-
- expect(get(query, 'body.query.bool.filter.0.bool.should')).toContainEqual({
- term: { 'agent.id': mockID },
- });
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/query_strategies.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/query_strategies.ts
index 506c02fc2f1ec..2d7bff4a53f3f 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/query_strategies.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/query_strategies.ts
@@ -6,102 +6,39 @@
*/
import { SearchResponse } from '@elastic/elasticsearch/api/types';
-import {
- metadataCurrentIndexPattern,
- metadataIndexPattern,
-} from '../../../../../common/endpoint/constants';
-import { HostMetadata, MetadataQueryStrategyVersions } from '../../../../../common/endpoint/types';
-import { HostListQueryResult, HostQueryResult, MetadataQueryStrategy } from '../../../types';
+import { HostMetadata } from '../../../../../common/endpoint/types';
+import { HostListQueryResult, HostQueryResult } from '../../../types';
-export function metadataQueryStrategyV1(): MetadataQueryStrategy {
- return {
- index: metadataIndexPattern,
- extraBodyProperties: {
- collapse: {
- field: 'agent.id',
- inner_hits: {
- name: 'most_recent',
- size: 1,
- sort: [{ 'event.created': 'desc' }],
- },
- },
- aggs: {
- total: {
- cardinality: {
- field: 'agent.id',
- },
- },
- },
- },
- queryResponseToHostListResult: (
- searchResponse: SearchResponse
- ): HostListQueryResult => {
- const response = searchResponse as SearchResponse;
- return {
- resultLength:
- ((response?.aggregations?.total as unknown) as { value?: number; relation: string })
- ?.value || 0,
- resultList: response.hits.hits
- .map((hit) => hit.inner_hits?.most_recent.hits.hits)
- .flatMap((data) => data)
- .map((entry) => (entry?._source ?? {}) as HostMetadata),
- queryStrategyVersion: MetadataQueryStrategyVersions.VERSION_1,
- };
- },
- queryResponseToHostResult: (searchResponse: SearchResponse): HostQueryResult => {
- const response = searchResponse as SearchResponse;
- return {
- resultLength: response.hits.hits.length,
- result: response.hits.hits.length > 0 ? response.hits.hits[0]._source : undefined,
- queryStrategyVersion: MetadataQueryStrategyVersions.VERSION_1,
- };
- },
- };
+// remove the top-level 'HostDetails' property if found, from previous schemas
+function stripHostDetails(host: HostMetadata | { HostDetails: HostMetadata }): HostMetadata {
+ return 'HostDetails' in host ? host.HostDetails : host;
}
-export function metadataQueryStrategyV2(): MetadataQueryStrategy {
+export const queryResponseToHostResult = (
+ searchResponse: SearchResponse
+): HostQueryResult => {
+ const response = searchResponse as SearchResponse;
return {
- index: metadataCurrentIndexPattern,
- extraBodyProperties: {
- track_total_hits: true,
- },
- queryResponseToHostListResult: (
- searchResponse: SearchResponse
- ): HostListQueryResult => {
- const response = searchResponse as SearchResponse<
- HostMetadata | { HostDetails: HostMetadata }
- >;
- const list =
- response.hits.hits.length > 0
- ? response.hits.hits.map((entry) => stripHostDetails(entry?._source as HostMetadata))
- : [];
-
- return {
- resultLength:
- ((response.hits?.total as unknown) as { value: number; relation: string }).value || 0,
- resultList: list,
- queryStrategyVersion: MetadataQueryStrategyVersions.VERSION_2,
- };
- },
- queryResponseToHostResult: (
- searchResponse: SearchResponse
- ): HostQueryResult => {
- const response = searchResponse as SearchResponse<
- HostMetadata | { HostDetails: HostMetadata }
- >;
- return {
- resultLength: response.hits.hits.length,
- result:
- response.hits.hits.length > 0
- ? stripHostDetails(response.hits.hits[0]._source as HostMetadata)
- : undefined,
- queryStrategyVersion: MetadataQueryStrategyVersions.VERSION_2,
- };
- },
+ resultLength: response.hits.hits.length,
+ result:
+ response.hits.hits.length > 0
+ ? stripHostDetails(response.hits.hits[0]._source as HostMetadata)
+ : undefined,
};
-}
+};
-// remove the top-level 'HostDetails' property if found, from previous schemas
-function stripHostDetails(host: HostMetadata | { HostDetails: HostMetadata }): HostMetadata {
- return 'HostDetails' in host ? host.HostDetails : host;
-}
+export const queryResponseToHostListResult = (
+ searchResponse: SearchResponse
+): HostListQueryResult => {
+ const response = searchResponse as SearchResponse;
+ const list =
+ response.hits.hits.length > 0
+ ? response.hits.hits.map((entry) => stripHostDetails(entry?._source as HostMetadata))
+ : [];
+
+ return {
+ resultLength:
+ ((response.hits?.total as unknown) as { value: number; relation: string }).value || 0,
+ resultList: list,
+ };
+};
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/test_support.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/test_support.ts
index bc23c253c4347..a0530590f5f9f 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/test_support.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/test_support.ts
@@ -8,62 +8,6 @@
import { SearchResponse } from 'elasticsearch';
import { HostMetadata } from '../../../../../common/endpoint/types';
-export function createV1SearchResponse(hostMetadata?: HostMetadata): SearchResponse {
- return ({
- took: 15,
- timed_out: false,
- _shards: {
- total: 1,
- successful: 1,
- skipped: 0,
- failed: 0,
- },
- hits: {
- total: {
- value: 5,
- relation: 'eq',
- },
- max_score: null,
- hits: hostMetadata
- ? [
- {
- _index: 'metrics-endpoint.metadata-default',
- _id: '8FhM0HEBYyRTvb6lOQnw',
- _score: null,
- _source: hostMetadata,
- sort: [1588337587997],
- inner_hits: {
- most_recent: {
- hits: {
- total: {
- value: 2,
- relation: 'eq',
- },
- max_score: null,
- hits: [
- {
- _index: 'metrics-endpoint.metadata-default',
- _id: 'W6Vo1G8BYQH1gtPUgYkC',
- _score: null,
- _source: hostMetadata,
- sort: [1579816615336],
- },
- ],
- },
- },
- },
- },
- ]
- : [],
- },
- aggregations: {
- total: {
- value: 1,
- },
- },
- } as unknown) as SearchResponse;
-}
-
export function createV2SearchResponse(hostMetadata?: HostMetadata): SearchResponse {
return ({
took: 15,
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata.ts
index 0ca1983aa68d5..1a5515d8122f1 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/metadata.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata.ts
@@ -10,20 +10,15 @@ import { SearchResponse } from 'elasticsearch';
import { HostMetadata } from '../../../common/endpoint/types';
import { SecuritySolutionRequestHandlerContext } from '../../types';
import { getESQueryHostMetadataByIDs } from '../routes/metadata/query_builders';
-import { EndpointAppContext } from '../types';
+import { queryResponseToHostListResult } from '../routes/metadata/support/query_strategies';
export async function getMetadataForEndpoints(
endpointIDs: string[],
- requestHandlerContext: SecuritySolutionRequestHandlerContext,
- endpointAppContext: EndpointAppContext
+ requestHandlerContext: SecuritySolutionRequestHandlerContext
): Promise {
- const queryStrategy = await endpointAppContext.service
- ?.getMetadataService()
- ?.queryStrategy(requestHandlerContext.core.savedObjects.client);
-
- const query = getESQueryHostMetadataByIDs(endpointIDs, queryStrategy!);
+ const query = getESQueryHostMetadataByIDs(endpointIDs);
const esClient = requestHandlerContext.core.elasticsearch.client.asCurrentUser;
const { body } = await esClient.search(query as SearchRequest);
- const hosts = queryStrategy!.queryResponseToHostListResult(body as SearchResponse);
+ const hosts = queryResponseToHostListResult(body as SearchResponse);
return hosts.resultList;
}
diff --git a/x-pack/plugins/security_solution/server/endpoint/types.ts b/x-pack/plugins/security_solution/server/endpoint/types.ts
index 6076aa9af635b..bc52b759b9f0a 100644
--- a/x-pack/plugins/security_solution/server/endpoint/types.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/types.ts
@@ -7,11 +7,9 @@
import { LoggerFactory } from 'kibana/server';
-import { SearchResponse } from '@elastic/elasticsearch/api/types';
-import { JsonObject } from '@kbn/common-utils';
import { ConfigType } from '../config';
import { EndpointAppContextService } from './endpoint_app_context_services';
-import { HostMetadata, MetadataQueryStrategyVersions } from '../../common/endpoint/types';
+import { HostMetadata } from '../../common/endpoint/types';
import { ExperimentalFeatures } from '../../common/experimental_features';
/**
@@ -31,20 +29,9 @@ export interface EndpointAppContext {
export interface HostListQueryResult {
resultLength: number;
resultList: HostMetadata[];
- queryStrategyVersion: MetadataQueryStrategyVersions;
}
export interface HostQueryResult {
resultLength: number;
result: HostMetadata | undefined;
- queryStrategyVersion: MetadataQueryStrategyVersions;
-}
-
-export interface MetadataQueryStrategy {
- index: string;
- extraBodyProperties?: JsonObject;
- queryResponseToHostListResult: (
- searchResponse: SearchResponse
- ) => HostListQueryResult;
- queryResponseToHostResult: (searchResponse: SearchResponse) => HostQueryResult;
}
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts
index f4d942f733c1d..9b9f49a167397 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts
@@ -199,10 +199,10 @@ export const getHostEndpoint = async (
};
const endpointData =
id != null && metadataRequestContext.endpointAppContextService.getAgentService() != null
- ? await getHostMetaData(metadataRequestContext, id, undefined)
+ ? await getHostMetaData(metadataRequestContext, id)
: null;
- const fleetAgentId = endpointData?.metadata.elastic.agent.id;
+ const fleetAgentId = endpointData?.elastic.agent.id;
const [fleetAgentStatus, pendingActions] = !fleetAgentId
? [undefined, {}]
: await Promise.all([
@@ -214,13 +214,13 @@ export const getHostEndpoint = async (
}),
]);
- return endpointData != null && endpointData.metadata
+ return endpointData != null && endpointData
? {
- endpointPolicy: endpointData.metadata.Endpoint.policy.applied.name,
- policyStatus: endpointData.metadata.Endpoint.policy.applied.status,
- sensorVersion: endpointData.metadata.agent.version,
+ endpointPolicy: endpointData.Endpoint.policy.applied.name,
+ policyStatus: endpointData.Endpoint.policy.applied.status,
+ sensorVersion: endpointData.agent.version,
elasticAgentStatus: fleetAgentStatusToEndpointHostStatus(fleetAgentStatus!),
- isolation: endpointData.metadata.Endpoint.state?.isolation ?? false,
+ isolation: endpointData.Endpoint.state?.isolation ?? false,
pendingActions,
}
: null;
diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts
index 1a52bd18f80af..e1763b6ad4404 100644
--- a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts
+++ b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts
@@ -29,7 +29,6 @@ export default function endpointAPIIntegrationTests(providerContext: FtrProvider
});
loadTestFile(require.resolve('./resolver/index'));
loadTestFile(require.resolve('./metadata'));
- loadTestFile(require.resolve('./metadata_v1'));
loadTestFile(require.resolve('./policy'));
loadTestFile(require.resolve('./package'));
});
diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts
index b5d98c115d194..1f57cd1b6db34 100644
--- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts
+++ b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts
@@ -12,7 +12,6 @@ import {
deleteAllDocsFromMetadataIndex,
deleteMetadataStream,
} from './data_stream_helper';
-import { MetadataQueryStrategyVersions } from '../../../plugins/security_solution/common/endpoint/types';
import { HOST_METADATA_LIST_ROUTE } from '../../../plugins/security_solution/common/endpoint/constants';
/**
@@ -88,7 +87,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.hosts.length).to.eql(1);
expect(body.request_page_size).to.eql(1);
expect(body.request_page_index).to.eql(1);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
});
/* test that when paging properties produces no result, the total should reflect the actual number of metadata
@@ -113,7 +111,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.hosts.length).to.eql(0);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(30);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
});
it('metadata api should return 400 when pagingProperties is below boundaries.', async () => {
@@ -148,7 +145,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.hosts.length).to.eql(2);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
});
it('metadata api should return page based on filters and paging passed.', async () => {
@@ -186,7 +182,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.hosts.length).to.eql(2);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
});
it('metadata api should return page based on host.os.Ext.variant filter.', async () => {
@@ -208,7 +203,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.hosts.length).to.eql(2);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
});
it('metadata api should return the latest event for all the events for an endpoint', async () => {
@@ -231,7 +225,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.hosts.length).to.eql(1);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
});
it('metadata api should return the latest event for all the events where policy status is not success', async () => {
@@ -275,7 +268,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.hosts.length).to.eql(1);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
});
it('metadata api should return all hosts when filter is empty string', async () => {
@@ -292,7 +284,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.hosts.length).to.eql(numberOfHostsInFixture);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
});
});
});
diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata_v1.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata_v1.ts
deleted file mode 100644
index d8cf1a11fac0a..0000000000000
--- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata_v1.ts
+++ /dev/null
@@ -1,290 +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 expect from '@kbn/expect';
-import { FtrProviderContext } from '../ftr_provider_context';
-import { deleteMetadataStream } from './data_stream_helper';
-import { METADATA_REQUEST_V1_ROUTE } from '../../../plugins/security_solution/server/endpoint/routes/metadata';
-import { MetadataQueryStrategyVersions } from '../../../plugins/security_solution/common/endpoint/types';
-
-/**
- * The number of host documents in the es archive.
- */
-const numberOfHostsInFixture = 3;
-
-export default function ({ getService }: FtrProviderContext) {
- const esArchiver = getService('esArchiver');
- const supertest = getService('supertest');
- describe('test metadata api v1', () => {
- describe(`POST ${METADATA_REQUEST_V1_ROUTE} when index is empty`, () => {
- it('metadata api should return empty result when index is empty', async () => {
- // the endpoint uses data streams and es archiver does not support deleting them at the moment so we need
- // to do it manually
- await deleteMetadataStream(getService);
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send()
- .expect(200);
- expect(body.total).to.eql(0);
- expect(body.hosts.length).to.eql(0);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
- });
-
- describe(`POST ${METADATA_REQUEST_V1_ROUTE} when index is not empty`, () => {
- before(
- async () =>
- await esArchiver.load(
- 'x-pack/test/functional/es_archives/endpoint/metadata/api_feature',
- { useCreate: true }
- )
- );
- // the endpoint uses data streams and es archiver does not support deleting them at the moment so we need
- // to do it manually
- after(async () => await deleteMetadataStream(getService));
- it('metadata api should return one entry for each host with default paging', async () => {
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send()
- .expect(200);
- expect(body.total).to.eql(numberOfHostsInFixture);
- expect(body.hosts.length).to.eql(numberOfHostsInFixture);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
-
- it('metadata api should return page based on paging properties passed.', async () => {
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- paging_properties: [
- {
- page_size: 1,
- },
- {
- page_index: 1,
- },
- ],
- })
- .expect(200);
- expect(body.total).to.eql(numberOfHostsInFixture);
- expect(body.hosts.length).to.eql(1);
- expect(body.request_page_size).to.eql(1);
- expect(body.request_page_index).to.eql(1);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
-
- /* test that when paging properties produces no result, the total should reflect the actual number of metadata
- in the index.
- */
- it('metadata api should return accurate total metadata if page index produces no result', async () => {
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- paging_properties: [
- {
- page_size: 10,
- },
- {
- page_index: 3,
- },
- ],
- })
- .expect(200);
- expect(body.total).to.eql(numberOfHostsInFixture);
- expect(body.hosts.length).to.eql(0);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(30);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
-
- it('metadata api should return 400 when pagingProperties is below boundaries.', async () => {
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- paging_properties: [
- {
- page_size: 0,
- },
- {
- page_index: 1,
- },
- ],
- })
- .expect(400);
- expect(body.message).to.contain('Value must be equal to or greater than [1]');
- });
-
- it('metadata api should return page based on filters passed.', async () => {
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- filters: {
- kql: 'not host.ip:10.46.229.234',
- },
- })
- .expect(200);
- expect(body.total).to.eql(2);
- expect(body.hosts.length).to.eql(2);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
-
- it('metadata api should return page based on filters and paging passed.', async () => {
- const notIncludedIp = '10.46.229.234';
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- paging_properties: [
- {
- page_size: 10,
- },
- {
- page_index: 0,
- },
- ],
- filters: {
- kql: `not host.ip:${notIncludedIp}`,
- },
- })
- .expect(200);
- expect(body.total).to.eql(2);
- const resultIps: string[] = [].concat(
- ...body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.ip)
- );
- expect(resultIps).to.eql([
- '10.192.213.130',
- '10.70.28.129',
- '10.101.149.26',
- '2606:a000:ffc0:39:11ef:37b9:3371:578c',
- ]);
- expect(resultIps).not.include.eql(notIncludedIp);
- expect(body.hosts.length).to.eql(2);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
-
- it('metadata api should return page based on host.os.Ext.variant filter.', async () => {
- const variantValue = 'Windows Pro';
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- filters: {
- kql: `host.os.Ext.variant:${variantValue}`,
- },
- })
- .expect(200);
- expect(body.total).to.eql(2);
- const resultOsVariantValue: Set = new Set(
- body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.os.Ext.variant)
- );
- expect(Array.from(resultOsVariantValue)).to.eql([variantValue]);
- expect(body.hosts.length).to.eql(2);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
-
- it('metadata api should return the latest event for all the events for an endpoint', async () => {
- const targetEndpointIp = '10.46.229.234';
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- filters: {
- kql: `host.ip:${targetEndpointIp}`,
- },
- })
- .expect(200);
- expect(body.total).to.eql(1);
- const resultIp: string = body.hosts[0].metadata.host.ip.filter(
- (ip: string) => ip === targetEndpointIp
- );
- expect(resultIp).to.eql([targetEndpointIp]);
- expect(body.hosts[0].metadata.event.created).to.eql(1618841405309);
- expect(body.hosts.length).to.eql(1);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
-
- it('metadata api should return the latest event for all the events where policy status is not success', async () => {
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- filters: {
- kql: `not Endpoint.policy.applied.status:success`,
- },
- })
- .expect(200);
- const statuses: Set = new Set(
- body.hosts.map(
- (hostInfo: Record) => hostInfo.metadata.Endpoint.policy.applied.status
- )
- );
- expect(statuses.size).to.eql(1);
- expect(Array.from(statuses)).to.eql(['failure']);
- });
-
- it('metadata api should return the endpoint based on the elastic agent id, and status should be unhealthy', async () => {
- const targetEndpointId = 'fc0ff548-feba-41b6-8367-65e8790d0eaf';
- const targetElasticAgentId = '023fa40c-411d-4188-a941-4147bfadd095';
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- filters: {
- kql: `elastic.agent.id:${targetElasticAgentId}`,
- },
- })
- .expect(200);
- expect(body.total).to.eql(1);
- const resultHostId: string = body.hosts[0].metadata.host.id;
- const resultElasticAgentId: string = body.hosts[0].metadata.elastic.agent.id;
- expect(resultHostId).to.eql(targetEndpointId);
- expect(resultElasticAgentId).to.eql(targetElasticAgentId);
- expect(body.hosts[0].metadata.event.created).to.eql(1618841405309);
- expect(body.hosts[0].host_status).to.eql('unhealthy');
- expect(body.hosts.length).to.eql(1);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
-
- it('metadata api should return all hosts when filter is empty string', async () => {
- const { body } = await supertest
- .post(`${METADATA_REQUEST_V1_ROUTE}`)
- .set('kbn-xsrf', 'xxx')
- .send({
- filters: {
- kql: '',
- },
- })
- .expect(200);
- expect(body.total).to.eql(numberOfHostsInFixture);
- expect(body.hosts.length).to.eql(numberOfHostsInFixture);
- expect(body.request_page_size).to.eql(10);
- expect(body.request_page_index).to.eql(0);
- expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
- });
- });
- });
-}
From b2b57a240423e9503d1307075d34c73eb8b2a3c9 Mon Sep 17 00:00:00 2001
From: Tyler Smalley
Date: Mon, 12 Jul 2021 00:02:20 -0700
Subject: [PATCH 13/31] [dev-docs] Add debugging tutorial (#104468)
Signed-off-by: Tyler Smalley
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
dev_docs/tutorials/debugging.mdx | 61 ++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
create mode 100644 dev_docs/tutorials/debugging.mdx
diff --git a/dev_docs/tutorials/debugging.mdx b/dev_docs/tutorials/debugging.mdx
new file mode 100644
index 0000000000000..c0efd249be066
--- /dev/null
+++ b/dev_docs/tutorials/debugging.mdx
@@ -0,0 +1,61 @@
+---
+id: kibDevTutorialDebugging
+slug: /kibana-dev-docs/tutorial/debugging
+title: Debugging in development
+summary: Learn how to debug Kibana while running from source
+date: 2021-04-26
+tags: ['kibana', 'onboarding', 'dev', 'tutorials', 'debugging']
+---
+
+There are multiple ways to go about debugging Kibana when running from source.
+
+## Debugging using Chrome DevTools
+
+You will need to run Node using `--inspect` or `--inspect-brk` in order to enable the inspector. Additional information can be found in the [Node.js docs](https://nodejs.org/en/docs/guides/debugging-getting-started/).
+
+Once Node is running with the inspector enabled, you can open `chrome://inspect` in your Chrome browser. You should see a remote target for the inspector running. Click "inspect". You can now begin using the debugger.
+
+Next we will go over how to exactly enable the inspector for different aspects of the codebase.
+
+### Jest Unit Tests
+
+You will need to run Jest directly from the Node script:
+
+`node --inspect-brk scripts/jest [TestPathPattern]`
+
+### Functional Test Runner
+
+`node --inspect-brk scripts/functional_test_runner`
+
+### Development Server
+
+`node --inspect-brk scripts/kibana`
+
+## Debugging using logging
+
+When running Kibana, it's sometimes helpful to enable verbose logging.
+
+`yarn start --verbose`
+
+Using verbose logging usually results in much more information than you're interested in. The [logging documentation](https://www.elastic.co/guide/en/kibana/current/logging-settings.html) covers ways to change the log level of certain types.
+
+In the following example of a configuration stored in `config/kibana.dev.yml` we are logging all Elasticsearch queries and any logs created by the Management plugin.
+
+```
+logging:
+ appenders:
+ console:
+ type: console
+ layout:
+ type: pattern
+ highlight: true
+ root:
+ appenders: [default, console]
+ level: info
+
+ loggers:
+ - name: plugins.management
+ level: debug
+ - name: elasticsearch.query
+ level: debug
+```
\ No newline at end of file
From 5256b6d23ef4d05ff3dce1d7d91711c1748c8d38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?=
Date: Mon, 12 Jul 2021 11:46:38 +0200
Subject: [PATCH 14/31] Accomodate height of the (#104882)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../sections/agent_policy/edit_package_policy_page/index.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
index b07d76dc6bd8e..ee529b6865e56 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
@@ -430,7 +430,9 @@ export const EditPackagePolicyForm = memo<{
/>
)}
{configurePackage}
-
+ {/* Extra space to accomodate the EuiBottomBar height */}
+
+
From b8dfcafe38e636ec88b2ca03253cf15cec844d27 Mon Sep 17 00:00:00 2001
From: Pete Harverson
Date: Mon, 12 Jul 2021 11:31:54 +0100
Subject: [PATCH 15/31] [ML] Fixes unnecessary too many buckets warning on
anomaly chart embeddable (#105043)
* [ML] Fixes unnecessary too many buckets warning on anomaly chart embeddable
* [ML] Update jest tests for number of axis ticks.
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../explorer_chart_distribution.js | 24 ++++++++++---------
.../explorer_chart_distribution.test.js | 2 +-
.../explorer_chart_single_metric.js | 24 ++++++++++---------
.../explorer_chart_single_metric.test.js | 2 +-
.../anomaly_explorer_charts_service.ts | 11 +++++++--
5 files changed, 37 insertions(+), 26 deletions(-)
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js
index 7efd36bbe57c6..27a934fa841fe 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js
@@ -270,12 +270,6 @@ export class ExplorerChartDistribution extends React.Component {
const tickValuesStart = Math.max(config.selectedEarliest, config.plotEarliest);
// +1 ms to account for the ms that was subtracted for query aggregations.
const interval = config.selectedLatest - config.selectedEarliest + 1;
- const tickValues = getTickValues(
- tickValuesStart,
- interval,
- config.plotEarliest,
- config.plotLatest
- );
const xAxis = d3.svg
.axis()
@@ -286,10 +280,18 @@ export class ExplorerChartDistribution extends React.Component {
.tickPadding(10)
.tickFormat((d) => moment(d).format(xAxisTickFormat));
- // With tooManyBuckets the chart would end up with no x-axis labels
- // because the ticks are based on the span of the emphasis section,
- // and the highlighted area spans the whole chart.
- if (tooManyBuckets === false) {
+ // With tooManyBuckets, or when the chart is used as an embeddable,
+ // the chart would end up with no x-axis labels because the ticks are based on the span of the
+ // emphasis section, and the selected area spans the whole chart.
+ const useAutoTicks =
+ tooManyBuckets === true || interval >= config.plotLatest - config.plotEarliest;
+ if (useAutoTicks === false) {
+ const tickValues = getTickValues(
+ tickValuesStart,
+ interval,
+ config.plotEarliest,
+ config.plotLatest
+ );
xAxis.tickValues(tickValues);
} else {
xAxis.ticks(numTicksForDateFormat(vizWidth, xAxisTickFormat));
@@ -327,7 +329,7 @@ export class ExplorerChartDistribution extends React.Component {
});
}
- if (tooManyBuckets === false) {
+ if (useAutoTicks === false) {
removeLabelOverlap(gAxis, tickValuesStart, interval, vizWidth);
}
}
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js
index 11a15b192fc52..8d2f66a870c75 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js
@@ -139,7 +139,7 @@ describe('ExplorerChart', () => {
expect(+selectedInterval.getAttribute('height')).toBe(166);
const xAxisTicks = wrapper.getDOMNode().querySelector('.x').querySelectorAll('.tick');
- expect([...xAxisTicks]).toHaveLength(0);
+ expect([...xAxisTicks]).toHaveLength(1);
const yAxisTicks = wrapper.getDOMNode().querySelector('.y').querySelectorAll('.tick');
expect([...yAxisTicks]).toHaveLength(5);
const emphasizedAxisLabel = wrapper
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js
index dd07a7d6cbdee..19390017244a8 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js
@@ -196,12 +196,6 @@ export class ExplorerChartSingleMetric extends React.Component {
const tickValuesStart = Math.max(config.selectedEarliest, config.plotEarliest);
// +1 ms to account for the ms that was subtracted for query aggregations.
const interval = config.selectedLatest - config.selectedEarliest + 1;
- const tickValues = getTickValues(
- tickValuesStart,
- interval,
- config.plotEarliest,
- config.plotLatest
- );
const xAxis = d3.svg
.axis()
@@ -212,10 +206,18 @@ export class ExplorerChartSingleMetric extends React.Component {
.tickPadding(10)
.tickFormat((d) => moment(d).format(xAxisTickFormat));
- // With tooManyBuckets the chart would end up with no x-axis labels
- // because the ticks are based on the span of the emphasis section,
- // and the highlighted area spans the whole chart.
- if (tooManyBuckets === false) {
+ // With tooManyBuckets, or when the chart is used as an embeddable,
+ // the chart would end up with no x-axis labels because the ticks are based on the span of the
+ // emphasis section, and the selected area spans the whole chart.
+ const useAutoTicks =
+ tooManyBuckets === true || interval >= config.plotLatest - config.plotEarliest;
+ if (useAutoTicks === false) {
+ const tickValues = getTickValues(
+ tickValuesStart,
+ interval,
+ config.plotEarliest,
+ config.plotLatest
+ );
xAxis.tickValues(tickValues);
} else {
xAxis.ticks(numTicksForDateFormat(vizWidth, xAxisTickFormat));
@@ -243,7 +245,7 @@ export class ExplorerChartSingleMetric extends React.Component {
axes.append('g').attr('class', 'y axis').call(yAxis);
- if (tooManyBuckets === false) {
+ if (useAutoTicks === false) {
removeLabelOverlap(gAxis, tickValuesStart, interval, vizWidth);
}
}
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js
index 981f7515d3d70..00172965bc216 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js
@@ -144,7 +144,7 @@ describe('ExplorerChart', () => {
expect(+selectedInterval.getAttribute('height')).toBe(166);
const xAxisTicks = wrapper.getDOMNode().querySelector('.x').querySelectorAll('.tick');
- expect([...xAxisTicks]).toHaveLength(0);
+ expect([...xAxisTicks]).toHaveLength(1);
const yAxisTicks = wrapper.getDOMNode().querySelector('.y').querySelectorAll('.tick');
expect([...yAxisTicks]).toHaveLength(10);
diff --git a/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts b/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts
index afad043fcc4d1..97ddefac860f2 100644
--- a/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts
+++ b/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts
@@ -225,13 +225,20 @@ export class AnomalyExplorerChartsService {
chartRange.min = chartRange.min + maxBucketSpanMs;
}
+ // When used as an embeddable, selectedEarliestMs is the start date on the time picker,
+ // which may be earlier than the time of the first point plotted in the chart (as we plot
+ // the first full bucket with a start date no earlier than the start).
+ const selectedEarliestBucketCeil = boundsMin
+ ? Math.ceil(Math.max(selectedEarliestMs, boundsMin) / maxBucketSpanMs) * maxBucketSpanMs
+ : Math.ceil(selectedEarliestMs / maxBucketSpanMs) * maxBucketSpanMs;
+
const selectedLatestBucketStart = boundsMax
? Math.floor(Math.min(selectedLatestMs, boundsMax) / maxBucketSpanMs) * maxBucketSpanMs
: Math.floor(selectedLatestMs / maxBucketSpanMs) * maxBucketSpanMs;
if (
- (chartRange.min > selectedEarliestMs || chartRange.max < selectedLatestBucketStart) &&
- chartRange.max - chartRange.min < selectedLatestBucketStart - selectedEarliestMs
+ (chartRange.min > selectedEarliestBucketCeil || chartRange.max < selectedLatestBucketStart) &&
+ chartRange.max - chartRange.min < selectedLatestBucketStart - selectedEarliestBucketCeil
) {
tooManyBuckets = true;
}
From 216bb5e1b8c8406cd23503619972e5a3ea543790 Mon Sep 17 00:00:00 2001
From: Dzmitry Lemechko
Date: Mon, 12 Jul 2021 13:23:08 +0200
Subject: [PATCH 16/31] [load] run puppeteer script before gatling scenarios
(#104836)
* [load] puppeteer script before load testing
* install dependencies after metricbeat configuration
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
test/scripts/jenkins_build_load_testing.sh | 3 +++
x-pack/test/load/runner.ts | 10 ++++++++++
2 files changed, 13 insertions(+)
diff --git a/test/scripts/jenkins_build_load_testing.sh b/test/scripts/jenkins_build_load_testing.sh
index d7c7bda83c9ef..667540515fc83 100755
--- a/test/scripts/jenkins_build_load_testing.sh
+++ b/test/scripts/jenkins_build_load_testing.sh
@@ -53,6 +53,9 @@ echo "cloud.auth: ${USER_FROM_VAULT}:${PASS_FROM_VAULT}" >> cfg/metricbeat/metri
cp cfg/metricbeat/metricbeat.yml $KIBANA_DIR/metricbeat-install/metricbeat.yml
# Disable system monitoring: enabled for now to have more data
#mv $KIBANA_DIR/metricbeat-install/modules.d/system.yml $KIBANA_DIR/metricbeat-install/modules.d/system.yml.disabled
+echo " -> Building puppeteer project"
+cd puppeteer
+yarn install && yarn build
popd
# doesn't persist, also set in kibanaPipeline.groovy
diff --git a/x-pack/test/load/runner.ts b/x-pack/test/load/runner.ts
index 2d379391b2089..0bea5992f5539 100644
--- a/x-pack/test/load/runner.ts
+++ b/x-pack/test/load/runner.ts
@@ -18,6 +18,7 @@ const simulationPackage = 'org.kibanaLoadTest.simulation';
const simulationFIleExtension = '.scala';
const gatlingProjectRootPath: string =
process.env.GATLING_PROJECT_PATH || resolve(REPO_ROOT, '../kibana-load-testing');
+const puppeteerProjectRootPath: string = resolve(gatlingProjectRootPath, 'puppeteer');
const simulationEntry: string = process.env.GATLING_SIMULATIONS || 'branch.DemoJourney';
if (!Fs.existsSync(gatlingProjectRootPath)) {
@@ -52,6 +53,15 @@ export async function GatlingTestRunner({ getService }: FtrProviderContext) {
const log = getService('log');
await withProcRunner(log, async (procs) => {
+ await procs.run('node build/index.js', {
+ cmd: 'node',
+ args: ['build/index.js'],
+ cwd: puppeteerProjectRootPath,
+ env: {
+ ...process.env,
+ },
+ wait: true,
+ });
for (let i = 0; i < simulationClasses.length; i++) {
await procs.run('gatling: test', {
cmd: 'mvn',
From 832d349930be1f8d4a7a47733f413b0ab75303e8 Mon Sep 17 00:00:00 2001
From: Joe Reuter
Date: Mon, 12 Jul 2021 14:13:42 +0200
Subject: [PATCH 17/31] [KibanaLegacy] Remove unused stuff and make things
async if it is easy (#104638)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* remove unused stuff and make things async if it is easy
* fix problems
* load bootstrap in monitoring
* load angular bootstrap for saved searches and in unit tests
* fix vis_type_table tests
* Update x-pack/plugins/monitoring/public/plugin.ts
Co-authored-by: Ester Martí Vilaseca
* Update x-pack/plugins/monitoring/public/plugin.ts
Co-authored-by: Ester Martí Vilaseca
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Stratoula Kalafateli
Co-authored-by: Ester Martí Vilaseca
---
.../doc_table/components/row_headers.test.js | 4 +
.../angular/doc_table/doc_table.test.js | 4 +
.../application/angular/get_inner_angular.ts | 4 +-
.../application/angular/helpers/index.ts | 1 +
.../angular/helpers}/promises.d.ts | 0
.../application/angular/helpers}/promises.js | 0
src/plugins/discover/public/plugin.tsx | 2 +
.../public/angular/angular_config.tsx | 7 +-
.../kibana_legacy/public/angular/index.ts | 2 -
src/plugins/kibana_legacy/public/index.ts | 1 -
src/plugins/kibana_legacy/public/mocks.ts | 1 +
.../kibana_legacy/public/notify/index.ts | 1 -
.../notify/toasts/TOAST_NOTIFICATIONS.md | 100 ------------------
.../public/notify/toasts/index.ts | 9 --
.../notify/toasts/toast_notifications.test.ts | 76 -------------
.../notify/toasts/toast_notifications.ts | 37 -------
src/plugins/kibana_legacy/public/plugin.ts | 8 ++
.../kibana_legacy/public/utils/index.ts | 1 -
.../kibana_legacy/public/utils/system_api.ts | 40 -------
src/plugins/timelion/public/plugin.ts | 5 +-
.../public/legacy/agg_table/agg_table.test.js | 6 +-
.../legacy/agg_table/agg_table_group.test.js | 6 +-
.../public/legacy/get_inner_angular.ts | 3 -
.../paginated_table/paginated_table.test.ts | 5 +
.../legacy/table_vis_controller.test.ts | 4 +
.../public/legacy/vis_controller.ts | 1 +
x-pack/plugins/graph/public/plugin.ts | 7 +-
x-pack/plugins/monitoring/public/plugin.ts | 5 +-
28 files changed, 54 insertions(+), 286 deletions(-)
rename src/plugins/{kibana_legacy/public/angular => discover/public/application/angular/helpers}/promises.d.ts (100%)
rename src/plugins/{kibana_legacy/public/angular => discover/public/application/angular/helpers}/promises.js (100%)
delete mode 100644 src/plugins/kibana_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md
delete mode 100644 src/plugins/kibana_legacy/public/notify/toasts/index.ts
delete mode 100644 src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.test.ts
delete mode 100644 src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.ts
delete mode 100644 src/plugins/kibana_legacy/public/utils/system_api.ts
diff --git a/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js b/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js
index a087ac8697183..1a3b34c45d05e 100644
--- a/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js
+++ b/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js
@@ -19,6 +19,7 @@ import { setScopedHistory, setServices, setDocViewsRegistry } from '../../../../
import { coreMock } from '../../../../../../../core/public/mocks';
import { dataPluginMock } from '../../../../../../data/public/mocks';
import { navigationPluginMock } from '../../../../../../navigation/public/mocks';
+import { initAngularBootstrap } from '../../../../../../kibana_legacy/public/angular_bootstrap';
import { getInnerAngularModule } from '../../get_inner_angular';
import { createBrowserHistory } from 'history';
@@ -41,6 +42,9 @@ describe('Doc Table', () => {
// Stub out a minimal mapping of 4 fields
let mapping;
+ beforeAll(async () => {
+ await initAngularBootstrap();
+ });
beforeAll(() => setScopedHistory(createBrowserHistory()));
beforeEach(() => {
angular.element.prototype.slice = jest.fn(function (index) {
diff --git a/src/plugins/discover/public/application/angular/doc_table/doc_table.test.js b/src/plugins/discover/public/application/angular/doc_table/doc_table.test.js
index 1db35ddf18089..097f32965b141 100644
--- a/src/plugins/discover/public/application/angular/doc_table/doc_table.test.js
+++ b/src/plugins/discover/public/application/angular/doc_table/doc_table.test.js
@@ -17,6 +17,7 @@ import hits from '../../../__fixtures__/real_hits';
import { coreMock } from '../../../../../../core/public/mocks';
import { dataPluginMock } from '../../../../../data/public/mocks';
import { navigationPluginMock } from '../../../../../navigation/public/mocks';
+import { initAngularBootstrap } from '../../../../../kibana_legacy/public/angular_bootstrap';
import { setScopedHistory, setServices } from '../../../kibana_services';
import { getInnerAngularModule } from '../get_inner_angular';
@@ -54,6 +55,9 @@ describe('docTable', () => {
const core = coreMock.createStart();
let $elem;
+ beforeAll(async () => {
+ await initAngularBootstrap();
+ });
beforeAll(() => setScopedHistory(createBrowserHistory()));
beforeEach(() => {
angular.element.prototype.slice = jest.fn(() => {
diff --git a/src/plugins/discover/public/application/angular/get_inner_angular.ts b/src/plugins/discover/public/application/angular/get_inner_angular.ts
index 26d64d5adc8a3..992d82795302b 100644
--- a/src/plugins/discover/public/application/angular/get_inner_angular.ts
+++ b/src/plugins/discover/public/application/angular/get_inner_angular.ts
@@ -33,13 +33,12 @@ import { createDocViewerDirective } from './doc_viewer';
import { createDiscoverGridDirective } from './create_discover_grid_directive';
import { createRenderCompleteDirective } from './directives/render_complete';
import {
- initAngularBootstrap,
configureAppAngularModule,
PrivateProvider,
- PromiseServiceCreator,
registerListenEventListener,
watchMultiDecorator,
} from '../../../../kibana_legacy/public';
+import { PromiseServiceCreator } from './helpers';
import { DiscoverStartPlugins } from '../../plugin';
import { getScopedHistory } from '../../kibana_services';
import { createDiscoverDirective } from './create_discover_directive';
@@ -54,7 +53,6 @@ export function getInnerAngularModule(
deps: DiscoverStartPlugins,
context: PluginInitializerContext
) {
- initAngularBootstrap();
const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data);
configureAppAngularModule(module, { core, env: context.env }, true, getScopedHistory);
return module;
diff --git a/src/plugins/discover/public/application/angular/helpers/index.ts b/src/plugins/discover/public/application/angular/helpers/index.ts
index 3d2c0b1c63b33..6a7f75b7e81a2 100644
--- a/src/plugins/discover/public/application/angular/helpers/index.ts
+++ b/src/plugins/discover/public/application/angular/helpers/index.ts
@@ -8,3 +8,4 @@
export { formatRow, formatTopLevelObject } from './row_formatter';
export { handleSourceColumnState } from './state_helpers';
+export { PromiseServiceCreator } from './promises';
diff --git a/src/plugins/kibana_legacy/public/angular/promises.d.ts b/src/plugins/discover/public/application/angular/helpers/promises.d.ts
similarity index 100%
rename from src/plugins/kibana_legacy/public/angular/promises.d.ts
rename to src/plugins/discover/public/application/angular/helpers/promises.d.ts
diff --git a/src/plugins/kibana_legacy/public/angular/promises.js b/src/plugins/discover/public/application/angular/helpers/promises.js
similarity index 100%
rename from src/plugins/kibana_legacy/public/angular/promises.js
rename to src/plugins/discover/public/application/angular/helpers/promises.js
diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx
index 3e31fe1d46d45..1e8a5cdac95ef 100644
--- a/src/plugins/discover/public/plugin.tsx
+++ b/src/plugins/discover/public/plugin.tsx
@@ -403,6 +403,7 @@ export class DiscoverPlugin
}
// this is used by application mount and tests
const { getInnerAngularModule } = await import('./application/angular/get_inner_angular');
+ await plugins.kibanaLegacy.loadAngularBootstrap();
const module = getInnerAngularModule(
innerAngularName,
core,
@@ -473,6 +474,7 @@ export class DiscoverPlugin
throw Error('Discover plugin getEmbeddableInjector: initializeServices is undefined');
}
const { core, plugins } = await this.initializeServices();
+ await getServices().kibanaLegacy.loadAngularBootstrap();
getServices().kibanaLegacy.loadFontAwesome();
const { getInnerAngularModuleEmbeddable } = await import(
'./application/angular/get_inner_angular'
diff --git a/src/plugins/kibana_legacy/public/angular/angular_config.tsx b/src/plugins/kibana_legacy/public/angular/angular_config.tsx
index daecfbc57ea99..48ee6d2db269e 100644
--- a/src/plugins/kibana_legacy/public/angular/angular_config.tsx
+++ b/src/plugins/kibana_legacy/public/angular/angular_config.tsx
@@ -13,6 +13,7 @@ import {
ILocationProvider,
IModule,
IRootScopeService,
+ IRequestConfig,
} from 'angular';
import $ from 'jquery';
import { set } from '@elastic/safer-lodash-set';
@@ -22,7 +23,6 @@ import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'kibana/public';
import { History } from 'history';
import { CoreStart } from 'kibana/public';
-import { isSystemApiRequest } from '../utils';
import { formatAngularHttpError, isAngularHttpError } from '../notify/lib';
export interface RouteConfiguration {
@@ -38,6 +38,11 @@ export interface RouteConfiguration {
requireUICapability?: string;
}
+function isSystemApiRequest(request: IRequestConfig) {
+ const { headers } = request;
+ return headers && !!headers['kbn-system-request'];
+}
+
/**
* Detects whether a given angular route is a dummy route that doesn't
* require any action. There are two ways this can happen:
diff --git a/src/plugins/kibana_legacy/public/angular/index.ts b/src/plugins/kibana_legacy/public/angular/index.ts
index d9d8c0c19eb7b..369495698591d 100644
--- a/src/plugins/kibana_legacy/public/angular/index.ts
+++ b/src/plugins/kibana_legacy/public/angular/index.ts
@@ -6,8 +6,6 @@
* Side Public License, v 1.
*/
-// @ts-ignore
-export { PromiseServiceCreator } from './promises';
// @ts-ignore
export { watchMultiDecorator } from './watch_multi';
export * from './angular_config';
diff --git a/src/plugins/kibana_legacy/public/index.ts b/src/plugins/kibana_legacy/public/index.ts
index 03adb768cde20..ea5172f78a68f 100644
--- a/src/plugins/kibana_legacy/public/index.ts
+++ b/src/plugins/kibana_legacy/public/index.ts
@@ -14,7 +14,6 @@ export const plugin = (initializerContext: PluginInitializerContext) =>
export * from './plugin';
-export { initAngularBootstrap } from './angular_bootstrap';
export { PaginateDirectiveProvider, PaginateControlsDirectiveProvider } from './paginate/paginate';
export * from './angular';
export * from './notify';
diff --git a/src/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts
index 40834635cc570..6116c0682cb3b 100644
--- a/src/plugins/kibana_legacy/public/mocks.ts
+++ b/src/plugins/kibana_legacy/public/mocks.ts
@@ -22,6 +22,7 @@ const createStartContract = (): Start => ({
getHideWriteControls: jest.fn(),
},
loadFontAwesome: jest.fn(),
+ loadAngularBootstrap: jest.fn(),
});
export const kibanaLegacyPluginMock = {
diff --git a/src/plugins/kibana_legacy/public/notify/index.ts b/src/plugins/kibana_legacy/public/notify/index.ts
index a243059cb1918..d4dcaa77cc47a 100644
--- a/src/plugins/kibana_legacy/public/notify/index.ts
+++ b/src/plugins/kibana_legacy/public/notify/index.ts
@@ -6,5 +6,4 @@
* Side Public License, v 1.
*/
-export * from './toasts';
export * from './lib';
diff --git a/src/plugins/kibana_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md b/src/plugins/kibana_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md
deleted file mode 100644
index de6a51f3927d1..0000000000000
--- a/src/plugins/kibana_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# Toast notifications
-
-Use this service to surface toasts in the bottom-right corner of the screen. After a brief delay, they'll disappear. They're useful for notifying the user of state changes. See [the EUI docs](https://elastic.github.io/eui/) for more information on toasts and their role within the UI.
-
-## Importing the module
-
-```js
-import { toastNotifications } from 'ui/notify';
-```
-
-## Interface
-
-### Adding toasts
-
-For convenience, there are several methods which predefine the appearance of different types of toasts. Use these methods so that the same types of toasts look similar to the user.
-
-#### Default
-
-Neutral toast. Tell the user a change in state has occurred, which is not necessarily good or bad.
-
-```js
-toastNotifications.add('Copied to clipboard');
-```
-
-#### Success
-
-Let the user know that an action was successful, such as saving or deleting an object.
-
-```js
-toastNotifications.addSuccess('Your document was saved');
-```
-
-#### Warning
-
-If something OK or good happened, but perhaps wasn't perfect, show a warning toast.
-
-```js
-toastNotifications.addWarning('Your document was saved, but not its edit history');
-```
-
-#### Danger
-
-When the user initiated an action but the action failed, show them a danger toast.
-
-```js
-toastNotifications.addDanger('An error caused your document to be lost');
-```
-
-### Removing a toast
-
-Toasts will automatically be dismissed after a brief delay, but if for some reason you want to dismiss a toast, you can use the returned toast from one of the `add` methods and then pass it to `remove`.
-
-```js
-const toast = toastNotifications.add('Your document was saved');
-toastNotifications.remove(toast);
-```
-
-### Configuration options
-
-If you want to configure the toast further you can provide an object instead of a string. The properties of this object correspond to the `propTypes` accepted by the `EuiToast` component. Refer to [the EUI docs](https://elastic.github.io/eui/) for info on these `propTypes`.
-
-```js
-toastNotifications.add({
- title: 'Your document was saved',
- text: 'Only you have access to this document',
- color: 'success',
- iconType: 'check',
- 'data-test-subj': 'saveDocumentSuccess',
-});
-```
-
-Because the underlying components are React, you can use JSX to pass in React elements to the `text` prop. This gives you total flexibility over the content displayed within the toast.
-
-```js
-toastNotifications.add({
- title: 'Your document was saved',
- text: (
-
-
- Only you have access to this document. Edit permissions.
-
-
-
-
- ),
-});
-```
-
-## Use in functional tests
-
-Functional tests are commonly used to verify that a user action yielded a successful outcome. If you surface a toast to notify the user of this successful outcome, you can place a `data-test-subj` attribute on the toast and use it to check if the toast exists inside of your functional test. This acts as a proxy for verifying the successful outcome.
-
-```js
-toastNotifications.addSuccess({
- title: 'Your document was saved',
- 'data-test-subj': 'saveDocumentSuccess',
-});
-```
diff --git a/src/plugins/kibana_legacy/public/notify/toasts/index.ts b/src/plugins/kibana_legacy/public/notify/toasts/index.ts
deleted file mode 100644
index cdd7df04548fb..0000000000000
--- a/src/plugins/kibana_legacy/public/notify/toasts/index.ts
+++ /dev/null
@@ -1,9 +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.
- */
-
-export { ToastNotifications } from './toast_notifications';
diff --git a/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.test.ts b/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.test.ts
deleted file mode 100644
index c2c5d9a4fc014..0000000000000
--- a/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.test.ts
+++ /dev/null
@@ -1,76 +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 { notificationServiceMock } from '../../../../../core/public/mocks';
-import { ToastNotifications } from './toast_notifications';
-import { Toast } from 'kibana/public';
-import { BehaviorSubject } from 'rxjs';
-
-describe('ToastNotifications', () => {
- describe('interface', () => {
- function setup() {
- const toastsMock = notificationServiceMock.createStartContract().toasts;
- return { toastNotifications: new ToastNotifications(toastsMock), toastsMock };
- }
-
- describe('add method', () => {
- test('adds a toast', () => {
- const { toastNotifications, toastsMock } = setup();
- toastNotifications.add({});
- expect(toastsMock.add).toHaveBeenCalled();
- });
- });
-
- describe('remove method', () => {
- test('removes a toast', () => {
- const { toastNotifications, toastsMock } = setup();
- const fakeToast = {} as Toast;
- toastNotifications.remove(fakeToast);
- expect(toastsMock.remove).toHaveBeenCalledWith(fakeToast);
- });
- });
-
- describe('onChange method', () => {
- test('callback is called when observable changes', () => {
- const toastsMock = notificationServiceMock.createStartContract().toasts;
- const toasts$ = new BehaviorSubject([]);
- toastsMock.get$.mockReturnValue(toasts$);
- const toastNotifications = new ToastNotifications(toastsMock);
- const onChangeSpy = jest.fn();
- toastNotifications.onChange(onChangeSpy);
- toasts$.next([{ id: 'toast1' }]);
- toasts$.next([]);
- expect(onChangeSpy).toHaveBeenCalledTimes(2);
- });
- });
-
- describe('addSuccess method', () => {
- test('adds a success toast', () => {
- const { toastNotifications, toastsMock } = setup();
- toastNotifications.addSuccess({});
- expect(toastsMock.addSuccess).toHaveBeenCalled();
- });
- });
-
- describe('addWarning method', () => {
- test('adds a warning toast', () => {
- const { toastNotifications, toastsMock } = setup();
- toastNotifications.addWarning({});
- expect(toastsMock.addWarning).toHaveBeenCalled();
- });
- });
-
- describe('addDanger method', () => {
- test('adds a danger toast', () => {
- const { toastNotifications, toastsMock } = setup();
- toastNotifications.addWarning({});
- expect(toastsMock.addWarning).toHaveBeenCalled();
- });
- });
- });
-});
diff --git a/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.ts b/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.ts
deleted file mode 100644
index e7ccbbca07b73..0000000000000
--- a/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.ts
+++ /dev/null
@@ -1,37 +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 { NotificationsSetup, Toast, ToastInput, ErrorToastOptions } from 'kibana/public';
-
-export class ToastNotifications {
- public list: Toast[] = [];
-
- private onChangeCallback?: () => void;
-
- constructor(private readonly toasts: NotificationsSetup['toasts']) {
- toasts.get$().subscribe((list) => {
- this.list = list;
-
- if (this.onChangeCallback) {
- this.onChangeCallback();
- }
- });
- }
-
- public onChange = (callback: () => void) => {
- this.onChangeCallback = callback;
- };
-
- public add = (toastOrTitle: ToastInput) => this.toasts.add(toastOrTitle);
- public remove = (toast: Toast) => this.toasts.remove(toast);
- public addSuccess = (toastOrTitle: ToastInput) => this.toasts.addSuccess(toastOrTitle);
- public addWarning = (toastOrTitle: ToastInput) => this.toasts.addWarning(toastOrTitle);
- public addDanger = (toastOrTitle: ToastInput) => this.toasts.addDanger(toastOrTitle);
- public addError = (error: Error, options: ErrorToastOptions) =>
- this.toasts.addError(error, options);
-}
diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts
index 337fdb80da7e4..f60130d367b58 100644
--- a/src/plugins/kibana_legacy/public/plugin.ts
+++ b/src/plugins/kibana_legacy/public/plugin.ts
@@ -33,6 +33,14 @@ export class KibanaLegacyPlugin {
loadFontAwesome: async () => {
await import('./font_awesome');
},
+ /**
+ * Loads angular bootstrap modules. Should be removed once the last consumer has migrated to EUI
+ * @deprecated
+ */
+ loadAngularBootstrap: async () => {
+ const { initAngularBootstrap } = await import('./angular_bootstrap');
+ initAngularBootstrap();
+ },
/**
* @deprecated
* Just exported for wiring up with dashboard mode, should not be used.
diff --git a/src/plugins/kibana_legacy/public/utils/index.ts b/src/plugins/kibana_legacy/public/utils/index.ts
index db3c0af6c8cb9..94233558b4627 100644
--- a/src/plugins/kibana_legacy/public/utils/index.ts
+++ b/src/plugins/kibana_legacy/public/utils/index.ts
@@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
-export * from './system_api';
// @ts-ignore
export { KbnAccessibleClickProvider } from './kbn_accessible_click';
// @ts-ignore
diff --git a/src/plugins/kibana_legacy/public/utils/system_api.ts b/src/plugins/kibana_legacy/public/utils/system_api.ts
deleted file mode 100644
index d0fe221935ba5..0000000000000
--- a/src/plugins/kibana_legacy/public/utils/system_api.ts
+++ /dev/null
@@ -1,40 +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 { IRequestConfig } from 'angular';
-
-const SYSTEM_REQUEST_HEADER_NAME = 'kbn-system-request';
-const LEGACY_SYSTEM_API_HEADER_NAME = 'kbn-system-api';
-
-/**
- * Adds a custom header designating request as system API
- * @param originalHeaders Object representing set of headers
- * @return Object representing set of headers, with system API header added in
- */
-export function addSystemApiHeader(originalHeaders: Record) {
- const systemApiHeaders = {
- [SYSTEM_REQUEST_HEADER_NAME]: true,
- };
- return {
- ...originalHeaders,
- ...systemApiHeaders,
- };
-}
-
-/**
- * Returns true if request is a system API request; false otherwise
- *
- * @param request Object Request object created by $http service
- * @return true if request is a system API request; false otherwise
- */
-export function isSystemApiRequest(request: IRequestConfig) {
- const { headers } = request;
- return (
- headers && (!!headers[SYSTEM_REQUEST_HEADER_NAME] || !!headers[LEGACY_SYSTEM_API_HEADER_NAME])
- );
-}
diff --git a/src/plugins/timelion/public/plugin.ts b/src/plugins/timelion/public/plugin.ts
index 6f8dbfdcc6704..63ea9a38e2795 100644
--- a/src/plugins/timelion/public/plugin.ts
+++ b/src/plugins/timelion/public/plugin.ts
@@ -19,7 +19,7 @@ import {
AppNavLinkStatus,
} from '../../../core/public';
import { Panel } from './panels/panel';
-import { initAngularBootstrap } from '../../kibana_legacy/public';
+import { KibanaLegacyStart } from '../../kibana_legacy/public';
import { createKbnUrlTracker } from '../../kibana_utils/public';
import { DataPublicPluginStart, esFilters, DataPublicPluginSetup } from '../../data/public';
import { NavigationPublicPluginStart } from '../../navigation/public';
@@ -41,6 +41,7 @@ export interface TimelionPluginStartDependencies {
visualizations: VisualizationsStart;
visTypeTimelion: VisTypeTimelionPluginStart;
savedObjects: SavedObjectsStart;
+ kibanaLegacy: KibanaLegacyStart;
}
/** @internal */
@@ -91,7 +92,6 @@ export class TimelionPlugin
stopUrlTracker();
};
- initAngularBootstrap();
core.application.register({
id: 'timelion',
title: 'Timelion',
@@ -103,6 +103,7 @@ export class TimelionPlugin
visTypeTimelion.isUiEnabled === false ? AppNavLinkStatus.hidden : AppNavLinkStatus.default,
mount: async (params: AppMountParameters) => {
const [coreStart, pluginsStart] = await core.getStartServices();
+ await pluginsStart.kibanaLegacy.loadAngularBootstrap();
this.currentHistory = params.history;
appMounted();
diff --git a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js
index 65e26ddf6e03f..cbc3db6585a7d 100644
--- a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js
+++ b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js
@@ -15,7 +15,7 @@ import { round } from 'lodash';
import { getFieldFormatsRegistry } from '../../../../data/public/test_utils';
import { coreMock } from '../../../../../core/public/mocks';
-import { initAngularBootstrap } from '../../../../kibana_legacy/public';
+import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap';
import { setUiSettings } from '../../../../data/public/services';
import { UI_SETTINGS } from '../../../../data/public/';
import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../../share/public';
@@ -60,10 +60,12 @@ describe('Table Vis - AggTable Directive', function () {
initTableVisLegacyModule(tableVisModule);
};
+ beforeAll(async () => {
+ await initAngularBootstrap();
+ });
beforeEach(() => {
setUiSettings(core.uiSettings);
setFormatService(getFieldFormatsRegistry(core));
- initAngularBootstrap();
initLocalAngular();
angular.mock.module('kibana/table_vis');
angular.mock.inject(($injector, config) => {
diff --git a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js
index 1c6630e30e5f7..ba04b2f449f6d 100644
--- a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js
+++ b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js
@@ -13,11 +13,11 @@ import expect from '@kbn/expect';
import { getFieldFormatsRegistry } from '../../../../data/public/test_utils';
import { coreMock } from '../../../../../core/public/mocks';
-import { initAngularBootstrap } from '../../../../kibana_legacy/public';
import { setUiSettings } from '../../../../data/public/services';
import { setFormatService } from '../../services';
import { getInnerAngular } from '../get_inner_angular';
import { initTableVisLegacyModule } from '../table_vis_legacy_module';
+import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap';
import { tabifiedData } from './tabified_data';
const uiSettings = new Map();
@@ -40,10 +40,12 @@ describe('Table Vis - AggTableGroup Directive', function () {
initTableVisLegacyModule(tableVisModule);
};
+ beforeAll(async () => {
+ await initAngularBootstrap();
+ });
beforeEach(() => {
setUiSettings(core.uiSettings);
setFormatService(getFieldFormatsRegistry(core));
- initAngularBootstrap();
initLocalAngular();
angular.mock.module('kibana/table_vis');
angular.mock.inject(($injector) => {
diff --git a/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts b/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts
index 09fde318ee4df..412dd904a5e87 100644
--- a/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts
+++ b/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts
@@ -16,7 +16,6 @@ import 'angular-recursion';
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
import { CoreStart, IUiSettingsClient, PluginInitializerContext } from 'kibana/public';
import {
- initAngularBootstrap,
PaginateDirectiveProvider,
PaginateControlsDirectiveProvider,
PrivateProvider,
@@ -24,8 +23,6 @@ import {
KbnAccessibleClickProvider,
} from '../../../kibana_legacy/public';
-initAngularBootstrap();
-
const thirdPartyAngularDependencies = ['ngSanitize', 'ui.bootstrap', 'RecursionHelper'];
export function getAngularModule(name: string, core: CoreStart, context: PluginInitializerContext) {
diff --git a/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts b/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts
index 77148803e7978..3feff52f86792 100644
--- a/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts
+++ b/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts
@@ -12,6 +12,7 @@ import $ from 'jquery';
import 'angular-sanitize';
import 'angular-mocks';
+import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap';
import { getAngularModule } from '../get_inner_angular';
import { initTableVisLegacyModule } from '../table_vis_legacy_module';
import { coreMock } from '../../../../../core/public/mocks';
@@ -56,6 +57,10 @@ describe('Table Vis - Paginated table', () => {
const defaultPerPage = 10;
let paginatedTable: any;
+ beforeAll(async () => {
+ await initAngularBootstrap();
+ });
+
const initLocalAngular = () => {
const tableVisModule = getAngularModule(
'kibana/table_vis',
diff --git a/src/plugins/vis_type_table/public/legacy/table_vis_controller.test.ts b/src/plugins/vis_type_table/public/legacy/table_vis_controller.test.ts
index 36a9cc9cce77f..f4a742ea16cb4 100644
--- a/src/plugins/vis_type_table/public/legacy/table_vis_controller.test.ts
+++ b/src/plugins/vis_type_table/public/legacy/table_vis_controller.test.ts
@@ -13,6 +13,7 @@ import $ from 'jquery';
import { getAngularModule } from './get_inner_angular';
import { initTableVisLegacyModule } from './table_vis_legacy_module';
+import { initAngularBootstrap } from '../../../kibana_legacy/public/angular_bootstrap';
import { tableVisLegacyTypeDefinition } from './table_vis_legacy_type';
import { Vis } from '../../../visualizations/public';
import { stubFields } from '../../../data/public/stubs';
@@ -76,6 +77,9 @@ describe('Table Vis - Controller', () => {
initTableVisLegacyModule(tableVisModule);
};
+ beforeAll(async () => {
+ await initAngularBootstrap();
+ });
beforeEach(initLocalAngular);
beforeEach(angular.mock.module('kibana/table_vis'));
diff --git a/src/plugins/vis_type_table/public/legacy/vis_controller.ts b/src/plugins/vis_type_table/public/legacy/vis_controller.ts
index ee446c58c0013..ec198aa96f1f9 100644
--- a/src/plugins/vis_type_table/public/legacy/vis_controller.ts
+++ b/src/plugins/vis_type_table/public/legacy/vis_controller.ts
@@ -56,6 +56,7 @@ export function getTableVisualizationControllerClass(
async initLocalAngular() {
if (!this.tableVisModule) {
const [coreStart, { kibanaLegacy }] = await core.getStartServices();
+ await kibanaLegacy.loadAngularBootstrap();
this.tableVisModule = getAngularModule(innerAngularName, coreStart, context);
initTableVisLegacyModule(this.tableVisModule);
kibanaLegacy.loadFontAwesome();
diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts
index 4525b42b3feb4..ec19e639b91c9 100644
--- a/x-pack/plugins/graph/public/plugin.ts
+++ b/x-pack/plugins/graph/public/plugin.ts
@@ -19,10 +19,7 @@ import {
} from '../../../../src/core/public';
import { Storage } from '../../../../src/plugins/kibana_utils/public';
-import {
- initAngularBootstrap,
- KibanaLegacyStart,
-} from '../../../../src/plugins/kibana_legacy/public';
+import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public';
import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
@@ -77,7 +74,6 @@ export class GraphPlugin
const config = this.initializerContext.config.get();
- initAngularBootstrap();
core.application.register({
id: 'graph',
title: 'Graph',
@@ -88,6 +84,7 @@ export class GraphPlugin
updater$: this.appUpdater$,
mount: async (params: AppMountParameters) => {
const [coreStart, pluginsStart] = await core.getStartServices();
+ await pluginsStart.kibanaLegacy.loadAngularBootstrap();
coreStart.chrome.docTitle.change(
i18n.translate('xpack.graph.pageTitle', { defaultMessage: 'Graph' })
);
diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts
index a5b7d4906b586..9f84165a27ba9 100644
--- a/x-pack/plugins/monitoring/public/plugin.ts
+++ b/x-pack/plugins/monitoring/public/plugin.ts
@@ -93,7 +93,10 @@ export class MonitoringPlugin
category: DEFAULT_APP_CATEGORIES.management,
mount: async (params: AppMountParameters) => {
const [coreStart, pluginsStart] = await core.getStartServices();
- const { AngularApp } = await import('./angular');
+ const [, { AngularApp }] = await Promise.all([
+ pluginsStart.kibanaLegacy.loadAngularBootstrap(),
+ import('./angular'),
+ ]);
const deps: MonitoringStartPluginDependencies = {
navigation: pluginsStart.navigation,
kibanaLegacy: pluginsStart.kibanaLegacy,
From 87066e06b3cfe238b021f786896afdfd74b86303 Mon Sep 17 00:00:00 2001
From: Diana Derevyankina
<54894989+DziyanaDzeraviankina@users.noreply.github.com>
Date: Mon, 12 Jul 2021 17:25:52 +0300
Subject: [PATCH 18/31] [TSVB] Top_hit supports runtime fields (#103401)
* [TSVB] Refactor top-hit aggregation to work with fields instead of _source
* Allow select date strings for top_hit aggregation in table, metric, and markdown
* Fix agg_with handling for top_hit and add some tests
* Refactor get_agg_value and fix type check for _tsvb_chart
* Refactor top_hit.js
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../application/components/aggs/top_hit.js | 37 +++++--
.../components/lib/tick_formatter.js | 2 +-
.../lib/vis_data/helpers/bucket_transform.js | 2 +-
.../lib/vis_data/helpers/get_agg_value.js | 4 +-
.../vis_data/helpers/get_agg_value.test.js | 6 +-
test/functional/apps/visualize/_tsvb_chart.ts | 103 +++++++++++++++---
.../fixtures/kbn_archiver/visualize.json | 3 +-
.../page_objects/visual_builder_page.ts | 47 ++++++++
8 files changed, 168 insertions(+), 36 deletions(-)
diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js
index 546c09cdf34fd..b9ef2d8913574 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import React from 'react';
+import React, { useMemo, useEffect } from 'react';
import { AggRow } from './agg_row';
import { AggSelect } from './agg_select';
import { FieldSelect } from './field_select';
@@ -62,6 +62,7 @@ const getAggWithOptions = (field = {}, fieldTypesRestriction) => {
},
];
case KBN_FIELD_TYPES.STRING:
+ case KBN_FIELD_TYPES.DATE:
return [
{
label: i18n.translate('visTypeTimeseries.topHit.aggWithOptions.concatenate', {
@@ -91,16 +92,18 @@ const getOrderOptions = () => [
},
];
+const AGG_WITH_KEY = 'agg_with';
const ORDER_DATE_RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE];
+const getModelDefaults = () => ({
+ size: 1,
+ order: 'desc',
+ [AGG_WITH_KEY]: 'noop',
+});
+
const TopHitAggUi = (props) => {
const { fields, series, panel } = props;
- const defaults = {
- size: 1,
- agg_with: 'noop',
- order: 'desc',
- };
- const model = { ...defaults, ...props.model };
+ const model = useMemo(() => ({ ...getModelDefaults(), ...props.model }), [props.model]);
const indexPattern = series.override_index_pattern
? series.series_index_pattern
: panel.index_pattern;
@@ -110,7 +113,7 @@ const TopHitAggUi = (props) => {
PANEL_TYPES.METRIC,
PANEL_TYPES.MARKDOWN,
].includes(panel.type)
- ? [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING]
+ ? [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING, KBN_FIELD_TYPES.DATE]
: [KBN_FIELD_TYPES.NUMBER];
const handleChange = createChangeHandler(props.onChange, model);
@@ -124,13 +127,23 @@ const TopHitAggUi = (props) => {
const htmlId = htmlIdGenerator();
const selectedAggWithOption = aggWithOptions.find((option) => {
- return model.agg_with === option.value;
+ return model[AGG_WITH_KEY] === option.value;
});
const selectedOrderOption = orderOptions.find((option) => {
return model.order === option.value;
});
+ useEffect(() => {
+ const defaultFn = aggWithOptions?.[0]?.value;
+ const aggWith = model[AGG_WITH_KEY];
+ if (aggWith && defaultFn && aggWith !== defaultFn && !selectedAggWithOption) {
+ handleChange({
+ [AGG_WITH_KEY]: defaultFn,
+ });
+ }
+ }, [model, selectedAggWithOption, aggWithOptions, handleChange]);
+
return (
{
{
)}
options={aggWithOptions}
selectedOptions={selectedAggWithOption ? [selectedAggWithOption] : []}
- onChange={handleSelectChange('agg_with')}
+ onChange={handleSelectChange(AGG_WITH_KEY)}
singleSelection={{ asPlainText: true }}
+ data-test-subj="topHitAggregateWithComboBox"
/>
@@ -231,6 +245,7 @@ const TopHitAggUi = (props) => {
onChange={handleSelectChange('order_by')}
indexPattern={indexPattern}
fields={fields}
+ data-test-subj="topHitOrderByFieldSelect"
/>
diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js b/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js
index c1d82a182e509..9bccc13d19269 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js
@@ -16,7 +16,7 @@ export const createTickFormatter = (format = '0,0.[00]', template, getConfig = n
const fieldFormats = getFieldFormats();
if (!template) template = '{{value}}';
- const render = handlebars.compile(template, { knownHelpersOnly: true });
+ const render = handlebars.compile(template, { noEscape: true, knownHelpersOnly: true });
let formatter;
if (isDuration(format)) {
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js
index 16e7b9d6072cb..13b890189325c 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js
@@ -111,7 +111,7 @@ export const bucketTransform = {
docs: {
top_hits: {
size: bucket.size,
- _source: { includes: [bucket.field] },
+ fields: [bucket.field],
},
},
},
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js
index 32d17ef6d6cb7..90df3f2675959 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js
@@ -45,10 +45,10 @@ export const getAggValue = (row, metric) => {
}
const hits = get(row, [metric.id, 'docs', 'hits', 'hits'], []);
- const values = hits.map((doc) => get(doc, `_source.${metric.field}`));
+ const values = hits.map((doc) => doc.fields[metric.field]);
const aggWith = (metric.agg_with && aggFns[metric.agg_with]) || aggFns.noop;
- return aggWith(values);
+ return aggWith(values.flat());
case METRIC_TYPES.COUNT:
return get(row, 'doc_count', null);
default:
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js
index a23c57f567563..ecbdd1563c304 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js
@@ -67,11 +67,7 @@ describe('getAggValue', () => {
doc_count: 1,
docs: {
hits: {
- hits: [
- { _source: { example: { value: 25 } } },
- { _source: { example: { value: 25 } } },
- { _source: { example: { value: 25 } } },
- ],
+ hits: [{ fields: { 'example.value': [25, 25, 25] } }],
},
},
},
diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts
index ca310493960f5..49b2ad8f9646a 100644
--- a/test/functional/apps/visualize/_tsvb_chart.ts
+++ b/test/functional/apps/visualize/_tsvb_chart.ts
@@ -24,6 +24,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'timePicker',
'visChart',
'common',
+ 'settings',
]);
describe('visual builder', function describeIndexTests() {
@@ -44,14 +45,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
describe('metric', () => {
+ const { visualBuilder } = PageObjects;
+
beforeEach(async () => {
- await PageObjects.visualBuilder.resetPage();
- await PageObjects.visualBuilder.clickMetric();
- await PageObjects.visualBuilder.checkMetricTabIsPresent();
- await PageObjects.visualBuilder.clickPanelOptions('metric');
- await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
- await PageObjects.visualBuilder.setDropLastBucket(true);
- await PageObjects.visualBuilder.clickDataTab('metric');
+ await visualBuilder.resetPage();
+ await visualBuilder.clickMetric();
+ await visualBuilder.checkMetricTabIsPresent();
+ await visualBuilder.clickPanelOptions('metric');
+ await visualBuilder.setMetricsDataTimerangeMode('Last value');
+ await visualBuilder.setDropLastBucket(true);
+ await visualBuilder.clickDataTab('metric');
});
it('should not have inspector enabled', async () => {
@@ -59,28 +62,98 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should show correct data', async () => {
- const value = await PageObjects.visualBuilder.getMetricValue();
+ const value = await visualBuilder.getMetricValue();
expect(value).to.eql('156');
});
it('should show correct data with Math Aggregation', async () => {
- await PageObjects.visualBuilder.createNewAgg();
- await PageObjects.visualBuilder.selectAggType('math', 1);
- await PageObjects.visualBuilder.fillInVariable();
- await PageObjects.visualBuilder.fillInExpression('params.test + 1');
- const value = await PageObjects.visualBuilder.getMetricValue();
+ await visualBuilder.createNewAgg();
+ await visualBuilder.selectAggType('math', 1);
+ await visualBuilder.fillInVariable();
+ await visualBuilder.fillInExpression('params.test + 1');
+ const value = await visualBuilder.getMetricValue();
expect(value).to.eql('157');
});
it('should populate fields for basic functions', async () => {
- const { visualBuilder } = PageObjects;
-
await visualBuilder.selectAggType('Average');
await visualBuilder.setFieldForAggregation('machine.ram');
const isFieldForAggregationValid = await visualBuilder.checkFieldForAggregationValidity();
expect(isFieldForAggregationValid).to.be(true);
});
+
+ it('should show correct data for Value Count with Entire time range mode', async () => {
+ await visualBuilder.selectAggType('Value Count');
+ await visualBuilder.setFieldForAggregation('machine.ram');
+
+ await visualBuilder.clickPanelOptions('metric');
+ await visualBuilder.setMetricsDataTimerangeMode('Entire time range');
+
+ const value = await visualBuilder.getMetricValue();
+ expect(value).to.eql('13,492');
+ });
+
+ it('should show same data for kibana and string index pattern modes', async () => {
+ await visualBuilder.selectAggType('Max');
+ await visualBuilder.setFieldForAggregation('machine.ram');
+ const kibanaIndexPatternModeValue = await visualBuilder.getMetricValue();
+
+ await visualBuilder.clickPanelOptions('metric');
+ await visualBuilder.switchIndexPatternSelectionMode(false);
+ const stringIndexPatternModeValue = await visualBuilder.getMetricValue();
+
+ expect(kibanaIndexPatternModeValue).to.eql(stringIndexPatternModeValue);
+ expect(kibanaIndexPatternModeValue).to.eql('32,212,254,720');
+ });
+
+ describe('Color rules', () => {
+ beforeEach(async () => {
+ await visualBuilder.selectAggType('Min');
+ await visualBuilder.setFieldForAggregation('machine.ram');
+
+ await visualBuilder.clickPanelOptions('metric');
+ await visualBuilder.setColorRuleOperator('>= greater than or equal');
+ await visualBuilder.setColorRuleValue(0);
+ });
+
+ it('should apply color rules to visualization background', async () => {
+ await visualBuilder.setColorPickerValue('#FFCFDF');
+
+ const backGroundStyle = await visualBuilder.getBackgroundStyle();
+ expect(backGroundStyle).to.eql('background-color: rgb(255, 207, 223);');
+ });
+
+ it('should apply color rules to metric value', async () => {
+ await visualBuilder.setColorPickerValue('#AD7DE6', 1);
+
+ const backGroundStyle = await visualBuilder.getMetricValueStyle();
+ expect(backGroundStyle).to.eql('color: rgb(173, 125, 230);');
+ });
+ });
+
+ describe('Top Hit aggregation', () => {
+ beforeEach(async () => {
+ await visualBuilder.selectAggType('Top Hit');
+ await visualBuilder.setTopHitOrderByField('@timestamp');
+ });
+
+ it('should show correct data for string type field', async () => {
+ await visualBuilder.setFieldForAggregation('machine.os.raw');
+ await visualBuilder.setTopHitAggregateWithOption('Concatenate');
+
+ const value = await visualBuilder.getMetricValue();
+ expect(value).to.eql('win 7');
+ });
+
+ it('should show correct data for runtime field', async () => {
+ await visualBuilder.setFieldForAggregation('hello_world_runtime_field');
+ await visualBuilder.setTopHitAggregateWithOption('Concatenate');
+
+ const value = await visualBuilder.getMetricValue();
+ expect(value).to.eql('hello world');
+ });
+ });
});
describe('gauge', () => {
diff --git a/test/functional/fixtures/kbn_archiver/visualize.json b/test/functional/fixtures/kbn_archiver/visualize.json
index 660da856964b4..225dc0592e87d 100644
--- a/test/functional/fixtures/kbn_archiver/visualize.json
+++ b/test/functional/fixtures/kbn_archiver/visualize.json
@@ -3,6 +3,7 @@
"fieldAttrs": "{\"utc_time\":{\"customLabel\":\"UTC time\"}}",
"fieldFormatMap": "{\"bytes\":{\"id\":\"bytes\"}}",
"fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]",
+ "runtimeFieldMap":"{\"hello_world_runtime_field\":{\"type\":\"keyword\",\"script\":{\"source\":\"emit('hello world')\"}}}",
"timeFieldName": "@timestamp",
"title": "logstash-*"
},
@@ -301,4 +302,4 @@
"references": [],
"type": "index-pattern",
"version": "WzE1LDFd"
-}
\ No newline at end of file
+}
diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts
index 8e28ffab6c9c3..fd89a88658b3a 100644
--- a/test/functional/page_objects/visual_builder_page.ts
+++ b/test/functional/page_objects/visual_builder_page.ts
@@ -575,6 +575,42 @@ export class VisualBuilderPageObject extends FtrService {
await this.testSubjects.existOrFail('euiColorPickerPopover', { timeout: 5000 });
}
+ public async setColorPickerValue(colorHex: string, nth: number = 0): Promise {
+ const picker = await this.find.allByCssSelector('.tvbColorPicker button');
+ await picker[nth].clickMouseButton();
+ await this.checkColorPickerPopUpIsPresent();
+ await this.find.setValue('.euiColorPicker input', colorHex);
+ await this.visChart.waitForVisualizationRenderingStabilized();
+ }
+
+ public async setColorRuleOperator(condition: string): Promise {
+ await this.retry.try(async () => {
+ await this.comboBox.clearInputField('colorRuleOperator');
+ await this.comboBox.set('colorRuleOperator', condition);
+ });
+ }
+
+ public async setColorRuleValue(value: number): Promise {
+ await this.retry.try(async () => {
+ const colorRuleValueInput = await this.find.byCssSelector(
+ '[data-test-subj="colorRuleValue"]'
+ );
+ await colorRuleValueInput.type(value.toString());
+ });
+ }
+
+ public async getBackgroundStyle(): Promise {
+ await this.visChart.waitForVisualizationRenderingStabilized();
+ const visualization = await this.find.byClassName('tvbVis');
+ return await visualization.getAttribute('style');
+ }
+
+ public async getMetricValueStyle(): Promise {
+ await this.visChart.waitForVisualizationRenderingStabilized();
+ const metricValue = await this.find.byCssSelector('[data-test-subj="tsvbMetricValue"]');
+ return await metricValue.getAttribute('style');
+ }
+
public async changePanelPreview(nth: number = 0): Promise {
const prevRenderingCount = await this.visChart.getVisualizationRenderingCount();
const changePreviewBtnArray = await this.testSubjects.findAll('AddActivatePanelBtn');
@@ -680,4 +716,15 @@ export class VisualBuilderPageObject extends FtrService {
const dataTimeRangeMode = await this.testSubjects.find('dataTimeRangeMode');
return await this.comboBox.isOptionSelected(dataTimeRangeMode, value);
}
+
+ public async setTopHitAggregateWithOption(option: string): Promise {
+ await this.comboBox.set('topHitAggregateWithComboBox', option);
+ }
+
+ public async setTopHitOrderByField(timeField: string) {
+ await this.retry.try(async () => {
+ await this.comboBox.clearInputField('topHitOrderByFieldSelect');
+ await this.comboBox.set('topHitOrderByFieldSelect', timeField);
+ });
+ }
}
From a80791aa4ccc2902820591299ce8a89c364d2cd6 Mon Sep 17 00:00:00 2001
From: Jason Stoltzfus
Date: Mon, 12 Jul 2021 10:26:41 -0400
Subject: [PATCH 19/31] Pass locale to calendar (#105134)
---
.../components/analytics/components/analytics_filters.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_filters.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_filters.tsx
index 0c8455e986ae1..dd99d368a0105 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_filters.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_filters.tsx
@@ -80,6 +80,7 @@ export const AnalyticsFilters: React.FC = () => {
'xpack.enterpriseSearch.appSearch.engine.analytics.filters.startDateAriaLabel',
{ defaultMessage: 'Filter by start date' }
)}
+ locale={i18n.getLocale()}
/>
}
endDateControl={
@@ -93,6 +94,7 @@ export const AnalyticsFilters: React.FC = () => {
'xpack.enterpriseSearch.appSearch.engine.analytics.filters.endDateAriaLabel',
{ defaultMessage: 'Filter by end date' }
)}
+ locale={i18n.getLocale()}
/>
}
fullWidth
From d6a36926008b000d006d012edafed5cad6a7f3f6 Mon Sep 17 00:00:00 2001
From: Kaarina Tungseth
Date: Mon, 12 Jul 2021 09:58:39 -0500
Subject: [PATCH 20/31] [DOCS] Adds how to create dashboard drilldowns for Top
N and Table TSVB panels (#104548)
---
docs/user/dashboard/tsvb.asciidoc | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/docs/user/dashboard/tsvb.asciidoc b/docs/user/dashboard/tsvb.asciidoc
index 89da3f7285924..11fe71b7639bb 100644
--- a/docs/user/dashboard/tsvb.asciidoc
+++ b/docs/user/dashboard/tsvb.asciidoc
@@ -148,6 +148,27 @@ The *Markdown* visualization supports Markdown with Handlebar (mustache) syntax
For answers to frequently asked *TSVB* question, review the following.
+[float]
+===== How do I create dashboard drilldowns for Top N and Table visualizations?
+
+You can create dashboard drilldowns that include the specified time range for *Top N* and *Table* visualizations.
+
+. Open the dashboard that you want to link to, then copy the URL.
+
+. Open the dashboard with the *Top N* and *Table* visualization panel, then click *Edit* in the toolbar.
+
+. Open the *Top N* or *Table* panel menu, then select *Edit visualization*.
+
+. Click *Panel options*.
+
+. In the *Item URL* field, enter the URL.
++
+For example `dashboards#/view/f193ca90-c9f4-11eb-b038-dd3270053a27`.
+
+. Click *Save and return*.
+
+. In the toolbar, cick *Save as*, then make sure *Store time with dashboard* is deselected.
+
[float]
===== Why is my TSVB visualization missing data?
From 1aa9459ba2d8c4e736fdae5c0f4f4a59f09c7973 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ece=20=C3=96zalp?=
Date: Mon, 12 Jul 2021 11:07:34 -0400
Subject: [PATCH 21/31] [CTI] converts disabled panel danger to warning
(#104989)
---
.../overview_cti_links/cti_disabled_module.tsx | 4 ++--
.../overview_cti_links/cti_inner_panel.tsx | 12 ++++--------
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx
index 21a4beca72f3b..1600356882c36 100644
--- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx
@@ -21,11 +21,11 @@ export const CtiDisabledModuleComponent = () => {
const danger = useMemo(
() => (
+
{i18n.DANGER_BUTTON}
}
diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_inner_panel.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_inner_panel.tsx
index 08bf0a432f9bb..ddff78608dfb0 100644
--- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_inner_panel.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_inner_panel.tsx
@@ -17,13 +17,9 @@ const ButtonContainer = styled(EuiFlexGroup)`
padding: ${({ theme }) => theme.eui.paddingSizes.s};
`;
-const Title = styled(EuiText)<{ textcolor: 'primary' | 'warning' | 'danger' }>`
+const Title = styled(EuiText)<{ textcolor: 'primary' | 'warning' }>`
color: ${({ theme, textcolor }) =>
- textcolor === 'primary'
- ? theme.eui.euiColorPrimary
- : textcolor === 'warning'
- ? theme.eui.euiColorWarningText
- : theme.eui.euiColorDangerText};
+ textcolor === 'primary' ? theme.eui.euiColorPrimary : theme.eui.euiColorWarningText};
margin-bottom: ${({ theme }) => theme.eui.paddingSizes.m};
`;
@@ -40,12 +36,12 @@ export const CtiInnerPanel = ({
body,
button,
}: {
- color: 'primary' | 'warning' | 'danger';
+ color: 'primary' | 'warning';
title: string;
body: string;
button?: JSX.Element;
}) => {
- const iconType = color === 'primary' ? 'iInCircle' : color === 'warning' ? 'help' : 'alert';
+ const iconType = color === 'primary' ? 'iInCircle' : 'help';
return (
From 0c9777c6020501b6c953b4bca97f41dd858549f1 Mon Sep 17 00:00:00 2001
From: Justin Kambic
Date: Mon, 12 Jul 2021 11:23:57 -0400
Subject: [PATCH 22/31] [Uptime] Refactor page headers to avoid invalid markup
(#104215)
* Refactor monitor and waterfall page headers to avoid rendering invalid markup.
* Update tests.
* Translate page titles.
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../components/monitor/monitor_title.test.tsx | 37 ++++++++++-------
.../components/monitor/monitor_title.tsx | 41 +++++++++----------
.../step_detail/step_detail_container.tsx | 9 ++--
.../step_detail/step_page_title.tsx | 13 ++----
x-pack/plugins/uptime/public/routes.tsx | 33 +++++++++++----
.../uptime/server/lib/requests/get_certs.ts | 1 -
6 files changed, 77 insertions(+), 57 deletions(-)
diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_title.test.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_title.test.tsx
index 4fd6335c3d3ca..726ad235f7f49 100644
--- a/x-pack/plugins/uptime/public/components/monitor/monitor_title.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/monitor_title.test.tsx
@@ -11,7 +11,7 @@ import { screen } from '@testing-library/react';
import { render } from '../../lib/helper/rtl_helpers';
import * as reactRouterDom from 'react-router-dom';
import { Ping } from '../../../common/runtime_types';
-import { MonitorPageTitle } from './monitor_title';
+import { MonitorPageTitle, MonitorPageTitleContent } from './monitor_title';
jest.mock('react-router-dom', () => {
const originalModule = jest.requireActual('react-router-dom');
@@ -77,11 +77,17 @@ describe('MonitorTitle component', () => {
});
it('renders the monitor heading and EnableMonitorAlert toggle', () => {
- render(, {
- state: { monitorStatus: { status: monitorStatusWithName, loading: false } },
- });
- expect(screen.getByRole('heading', { level: 1, name: monitorName })).toBeInTheDocument();
- expect(screen.getByTestId('uptimeDisplayDefineConnector')).toBeInTheDocument();
+ render(
+ <>
+
+
+ >,
+ {
+ state: { monitorStatus: { status: monitorStatusWithName, loading: false } },
+ }
+ );
+ expect(screen.getByText(monitorName));
+ expect(screen.getByRole('switch')).toBeInTheDocument();
});
it('renders the user provided monitorId when the name is not present', () => {
@@ -89,21 +95,24 @@ describe('MonitorTitle component', () => {
render(, {
state: { monitorStatus: { status: defaultMonitorStatus, loading: false } },
});
- expect(screen.getByRole('heading', { level: 1, name: defaultMonitorId })).toBeInTheDocument();
+ expect(screen.getByText(defaultMonitorId));
});
it('renders the url when the monitorId is auto generated and the monitor name is not present', () => {
mockReactRouterDomHooks({ useParamsResponse: { monitorId: autoGeneratedMonitorIdEncoded } });
- render(, {
- state: { monitorStatus: { status: defaultMonitorStatus, loading: false } },
- });
- expect(
- screen.getByRole('heading', { level: 1, name: defaultMonitorStatus.url?.full })
- ).toBeInTheDocument();
+ render(
+
+
+
,
+ {
+ state: { monitorStatus: { status: defaultMonitorStatus, loading: false } },
+ }
+ );
+ expect(screen.getByText(defaultMonitorStatus!.url!.full!));
});
it('renders beta disclaimer for synthetics monitors', () => {
- render(, {
+ render(, {
state: { monitorStatus: { status: defaultBrowserMonitorStatus, loading: false } },
});
const betaLink = screen.getByRole('link', {
diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_title.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_title.tsx
index 2112af0653669..aa68e2aa7fc4b 100644
--- a/x-pack/plugins/uptime/public/components/monitor/monitor_title.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/monitor_title.tsx
@@ -5,15 +5,7 @@
* 2.0.
*/
-import {
- EuiBadge,
- EuiFlexGroup,
- EuiFlexItem,
- EuiSpacer,
- EuiTitle,
- EuiLink,
- EuiText,
-} from '@elastic/eui';
+import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLink, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { useSelector } from 'react-redux';
@@ -40,18 +32,11 @@ const getPageTitle = (monitorId: string, selectedMonitor: Ping | null) => {
return monitorId;
};
-export const MonitorPageTitle: React.FC = () => {
+export const MonitorPageTitleContent: React.FC = () => {
const monitorId = useMonitorId();
-
const selectedMonitor = useSelector(monitorStatusSelector);
-
- const nameOrId = selectedMonitor?.monitor?.name || getPageTitle(monitorId, selectedMonitor);
-
const type = selectedMonitor?.monitor?.type;
const isBrowser = type === 'browser';
-
- useBreadcrumbs([{ text: nameOrId }]);
-
const renderMonitorType = (monitorType: string) => {
switch (monitorType) {
case 'http':
@@ -86,12 +71,13 @@ export const MonitorPageTitle: React.FC = () => {
return '';
}
};
-
return (
<>
-
- {nameOrId}
-
+
+
+
+
+
@@ -118,7 +104,18 @@ export const MonitorPageTitle: React.FC = () => {
)}
-
>
);
};
+
+export const MonitorPageTitle: React.FC = () => {
+ const monitorId = useMonitorId();
+
+ const selectedMonitor = useSelector(monitorStatusSelector);
+
+ const nameOrId = selectedMonitor?.monitor?.name || getPageTitle(monitorId, selectedMonitor);
+
+ useBreadcrumbs([{ text: nameOrId }]);
+
+ return {nameOrId};
+};
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx
index 610107f406306..c24ecd9183865 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx
@@ -16,7 +16,7 @@ import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/
import { useMonitorBreadcrumb } from './use_monitor_breadcrumb';
import { ClientPluginsStart } from '../../../../apps/plugin';
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
-import { StepPageTitle } from './step_page_title';
+import { StepPageTitleContent } from './step_page_title';
import { StepPageNavigation } from './step_page_nav';
import { WaterfallChartContainer } from './waterfall/waterfall_chart_container';
@@ -78,10 +78,11 @@ export const StepDetailContainer: React.FC = ({ checkGroup, stepIndex })
return (
void;
handleNextStep: () => void;
}
-export const StepPageTitle = ({
- stepName,
+
+export const StepPageTitleContent = ({
stepIndex,
totalSteps,
handleNextStep,
@@ -29,11 +29,6 @@ export const StepPageTitle = ({
}: Props) => {
return (
-
-
- {stepName}
-
-
diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx
index e3c558cee2c32..2b0cc4dc5e5c2 100644
--- a/x-pack/plugins/uptime/public/routes.tsx
+++ b/x-pack/plugins/uptime/public/routes.tsx
@@ -23,7 +23,7 @@ import { UptimePage, useUptimeTelemetry } from './hooks';
import { OverviewPageComponent } from './pages/overview';
import { SyntheticsCheckSteps } from './pages/synthetics/synthetics_checks';
import { ClientPluginsStart } from './apps/plugin';
-import { MonitorPageTitle } from './components/monitor/monitor_title';
+import { MonitorPageTitle, MonitorPageTitleContent } from './components/monitor/monitor_title';
import { UptimeDatePicker } from './components/common/uptime_date_picker';
import { useKibana } from '../../../../src/plugins/kibana_react/public';
import { CertRefreshBtn } from './components/certificates/cert_refresh_btn';
@@ -36,10 +36,16 @@ interface RouteProps {
dataTestSubj: string;
title: string;
telemetryId: UptimePage;
- pageHeader?: { pageTitle: string | JSX.Element; rightSideItems?: JSX.Element[] };
+ pageHeader?: {
+ children?: JSX.Element;
+ pageTitle: string | JSX.Element;
+ rightSideItems?: JSX.Element[];
+ };
}
-const baseTitle = 'Uptime - Kibana';
+const baseTitle = i18n.translate('xpack.uptime.routes.baseTitle', {
+ defaultMessage: 'Uptime - Kibana',
+});
export const MONITORING_OVERVIEW_LABEL = i18n.translate('xpack.uptime.overview.heading', {
defaultMessage: 'Monitors',
@@ -47,18 +53,25 @@ export const MONITORING_OVERVIEW_LABEL = i18n.translate('xpack.uptime.overview.h
const Routes: RouteProps[] = [
{
- title: `Monitor | ${baseTitle}`,
+ title: i18n.translate('xpack.uptime.monitorRoute.title', {
+ defaultMessage: 'Monitor | {baseTitle}',
+ values: { baseTitle },
+ }),
path: MONITOR_ROUTE,
component: MonitorPage,
dataTestSubj: 'uptimeMonitorPage',
telemetryId: UptimePage.Monitor,
pageHeader: {
+ children: ,
pageTitle: ,
rightSideItems: [],
},
},
{
- title: `Settings | ${baseTitle}`,
+ title: i18n.translate('xpack.uptime.settingsRoute.title', {
+ defaultMessage: `Settings | {baseTitle}`,
+ values: { baseTitle },
+ }),
path: SETTINGS_ROUTE,
component: SettingsPage,
dataTestSubj: 'uptimeSettingsPage',
@@ -70,7 +83,10 @@ const Routes: RouteProps[] = [
},
},
{
- title: `Certificates | ${baseTitle}`,
+ title: i18n.translate('xpack.uptime.certificatesRoute.title', {
+ defaultMessage: `Certificates | {baseTitle}`,
+ values: { baseTitle },
+ }),
path: CERTIFICATES_ROUTE,
component: CertificatesPage,
dataTestSubj: 'uptimeCertificatesPage',
@@ -81,7 +97,10 @@ const Routes: RouteProps[] = [
},
},
{
- title: baseTitle,
+ title: i18n.translate('xpack.uptime.stepDetailRoute.title', {
+ defaultMessage: 'Synthetics detail | {baseTitle}',
+ values: { baseTitle },
+ }),
path: STEP_DETAIL_ROUTE,
component: StepDetailPage,
dataTestSubj: 'uptimeStepDetailPage',
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts
index 1b20ed9085fef..7639484f51737 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts
@@ -138,7 +138,6 @@ export const getCerts: UMElasticsearchQueryFn = asyn
searchBody.query.bool.filter.push(validityFilters);
}
- // console.log(JSON.stringify(params, null, 2));
const { body: result } = await uptimeEsClient.search({
body: searchBody,
});
From 3e5ed774700336657f25e8f0aef10e3bf17a93a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?=
Date: Mon, 12 Jul 2021 18:55:06 +0300
Subject: [PATCH 23/31] [Osquery] Fix 7.14 live query history view (#105211)
---
.../action_results/action_results_summary.tsx | 22 +------
.../osquery/public/actions/actions_table.tsx | 19 ++++++
.../public/agents/agent_id_to_name.tsx | 37 ++++++++++++
.../osquery/public/agents/agents_table.tsx | 24 ++++++--
.../public/agents/use_agent_details.ts | 36 +++++++++++
.../osquery/public/agents/use_all_agents.ts | 2 +-
.../public/live_queries/form/index.tsx | 2 +-
.../public/routes/live_queries/new/index.tsx | 10 +++-
.../routes/saved_queries/list/index.tsx | 60 ++++++++++---------
.../saved_queries/saved_queries_dropdown.tsx | 41 +++++++++----
.../scheduled_query_group_queries_table.tsx | 2 +-
.../actions/all/query.all_actions.dsl.ts | 23 +++++--
12 files changed, 207 insertions(+), 71 deletions(-)
create mode 100644 x-pack/plugins/osquery/public/agents/agent_id_to_name.tsx
create mode 100644 x-pack/plugins/osquery/public/agents/use_agent_details.ts
diff --git a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx
index d3b0e38a5e033..bf4c97d63d74c 100644
--- a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx
+++ b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx
@@ -8,15 +8,13 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { i18n } from '@kbn/i18n';
-import { EuiLink, EuiInMemoryTable, EuiCodeBlock } from '@elastic/eui';
+import { EuiInMemoryTable, EuiCodeBlock } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
-import { PLUGIN_ID } from '../../../fleet/common';
-import { pagePathGetters } from '../../../fleet/public';
+import { AgentIdToName } from '../agents/agent_id_to_name';
import { useActionResults } from './use_action_results';
import { useAllResults } from '../results/use_all_results';
import { Direction } from '../../common/search_strategy';
-import { useKibana } from '../common/lib/kibana';
interface ActionResultsSummaryProps {
actionId: string;
@@ -35,7 +33,6 @@ const ActionResultsSummaryComponent: React.FC = ({
expirationDate,
agentIds,
}) => {
- const getUrlForApp = useKibana().services.application.getUrlForApp;
// @ts-expect-error update types
const [pageIndex, setPageIndex] = useState(0);
// @ts-expect-error update types
@@ -70,20 +67,7 @@ const ActionResultsSummaryComponent: React.FC = ({
isLive,
});
- const renderAgentIdColumn = useCallback(
- (agentId) => (
-
- {agentId}
-
- ),
- [getUrlForApp]
- );
+ const renderAgentIdColumn = useCallback((agentId) => , []);
const renderRowsColumn = useCallback(
(_, item) => {
diff --git a/x-pack/plugins/osquery/public/actions/actions_table.tsx b/x-pack/plugins/osquery/public/actions/actions_table.tsx
index 0ee928ad8aa14..045c1f67b070d 100644
--- a/x-pack/plugins/osquery/public/actions/actions_table.tsx
+++ b/x-pack/plugins/osquery/public/actions/actions_table.tsx
@@ -9,6 +9,7 @@ import { isArray } from 'lodash';
import { i18n } from '@kbn/i18n';
import { EuiBasicTable, EuiButtonIcon, EuiCodeBlock, formatDate } from '@elastic/eui';
import React, { useState, useCallback, useMemo } from 'react';
+import { useHistory } from 'react-router-dom';
import { useAllActions } from './use_all_actions';
import { Direction } from '../../common/search_strategy';
@@ -27,6 +28,7 @@ const ActionTableResultsButton = React.memo(({ ac
ActionTableResultsButton.displayName = 'ActionTableResultsButton';
const ActionsTableComponent = () => {
+ const { push } = useHistory();
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(20);
@@ -67,6 +69,16 @@ const ActionsTableComponent = () => {
[]
);
+ const handlePlayClick = useCallback(
+ (item) =>
+ push('/live_queries/new', {
+ form: {
+ query: item._source?.data?.query,
+ },
+ }),
+ [push]
+ );
+
const columns = useMemo(
() => [
{
@@ -106,6 +118,11 @@ const ActionsTableComponent = () => {
defaultMessage: 'View details',
}),
actions: [
+ {
+ type: 'icon',
+ icon: 'play',
+ onClick: handlePlayClick,
+ },
{
render: renderActionsColumn,
},
@@ -113,6 +130,7 @@ const ActionsTableComponent = () => {
},
],
[
+ handlePlayClick,
renderActionsColumn,
renderAgentsColumn,
renderCreatedByColumn,
@@ -135,6 +153,7 @@ const ActionsTableComponent = () => {
= ({ agentId }) => {
+ const getUrlForApp = useKibana().services.application.getUrlForApp;
+ const { data } = useAgentDetails({ agentId });
+
+ return (
+
+ {data?.item.local_metadata.host.name ?? agentId}
+
+ );
+};
+
+export const AgentIdToName = React.memo(AgentIdToNameComponent);
diff --git a/x-pack/plugins/osquery/public/agents/agents_table.tsx b/x-pack/plugins/osquery/public/agents/agents_table.tsx
index 7e8f49c051614..53e2ce1d53420 100644
--- a/x-pack/plugins/osquery/public/agents/agents_table.tsx
+++ b/x-pack/plugins/osquery/public/agents/agents_table.tsx
@@ -21,7 +21,12 @@ import {
generateAgentSelection,
} from './helpers';
-import { SELECT_AGENT_LABEL, generateSelectedAgentsMessage } from './translations';
+import {
+ SELECT_AGENT_LABEL,
+ generateSelectedAgentsMessage,
+ ALL_AGENTS_LABEL,
+ AGENT_POLICY_LABEL,
+} from './translations';
import {
AGENT_GROUP_KEY,
@@ -72,8 +77,17 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh
useEffect(() => {
if (agentSelection && !defaultValueInitialized.current && options.length) {
- if (agentSelection.policiesSelected) {
- const policyOptions = find(['label', 'Policy'], options);
+ if (agentSelection.allAgentsSelected) {
+ const allAgentsOptions = find(['label', ALL_AGENTS_LABEL], options);
+
+ if (allAgentsOptions?.options) {
+ setSelectedOptions(allAgentsOptions.options);
+ defaultValueInitialized.current = true;
+ }
+ }
+
+ if (agentSelection.policiesSelected.length) {
+ const policyOptions = find(['label', AGENT_POLICY_LABEL], options);
if (policyOptions) {
const defaultOptions = policyOptions.options?.filter((option) =>
@@ -82,12 +96,12 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh
if (defaultOptions?.length) {
setSelectedOptions(defaultOptions);
+ defaultValueInitialized.current = true;
}
- defaultValueInitialized.current = true;
}
}
}
- }, [agentSelection, options]);
+ }, [agentSelection, options, selectedOptions]);
useEffect(() => {
// update the groups when groups or agents have changed
diff --git a/x-pack/plugins/osquery/public/agents/use_agent_details.ts b/x-pack/plugins/osquery/public/agents/use_agent_details.ts
new file mode 100644
index 0000000000000..1a0663812dec3
--- /dev/null
+++ b/x-pack/plugins/osquery/public/agents/use_agent_details.ts
@@ -0,0 +1,36 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { useQuery } from 'react-query';
+
+import { GetOneAgentResponse, agentRouteService } from '../../../fleet/common';
+import { useErrorToast } from '../common/hooks/use_error_toast';
+import { useKibana } from '../common/lib/kibana';
+
+interface UseAgentDetails {
+ agentId: string;
+}
+
+export const useAgentDetails = ({ agentId }: UseAgentDetails) => {
+ const { http } = useKibana().services;
+ const setErrorToast = useErrorToast();
+ return useQuery(
+ ['agentDetails', agentId],
+ () => http.get(agentRouteService.getInfoPath(agentId)),
+ {
+ enabled: agentId.length > 0,
+ onSuccess: () => setErrorToast(),
+ onError: (error) =>
+ setErrorToast(error as Error, {
+ title: i18n.translate('xpack.osquery.agentDetails.fetchError', {
+ defaultMessage: 'Error while fetching agent details',
+ }),
+ }),
+ }
+ );
+};
diff --git a/x-pack/plugins/osquery/public/agents/use_all_agents.ts b/x-pack/plugins/osquery/public/agents/use_all_agents.ts
index 30ba4d2f57907..cda15cc805437 100644
--- a/x-pack/plugins/osquery/public/agents/use_all_agents.ts
+++ b/x-pack/plugins/osquery/public/agents/use_all_agents.ts
@@ -38,7 +38,7 @@ export const useAllAgents = (
let kuery = `last_checkin_status: online and (${policyFragment})`;
if (searchValue) {
- kuery += `and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`;
+ kuery += ` and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`;
}
return http.get(agentRouteService.getListPath(), {
diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx
index 8654a74fecfb4..658280042696e 100644
--- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx
+++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx
@@ -110,7 +110,7 @@ const LiveQueryFormComponent: React.FC = ({
{
agentSelection: {
agents: [],
- allAgentsSelected: false,
+ allAgentsSelected: true,
platformsSelected: [],
policiesSelected: [],
},
diff --git a/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx b/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx
index 9967eb97cddf2..40614c1f3e1b8 100644
--- a/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx
+++ b/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx
@@ -8,7 +8,7 @@
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useMemo } from 'react';
-import { useLocation } from 'react-router-dom';
+import { useHistory, useLocation } from 'react-router-dom';
import qs from 'query-string';
import { WithHeaderLayout } from '../../../components/layouts';
@@ -19,12 +19,18 @@ import { BetaBadge, BetaBadgeRowWrapper } from '../../../components/beta_badge';
const NewLiveQueryPageComponent = () => {
useBreadcrumbs('live_query_new');
+ const { replace } = useHistory();
const location = useLocation();
const liveQueryListProps = useRouterNavigate('live_queries');
const formDefaultValue = useMemo(() => {
const queryParams = qs.parse(location.search);
+ if (location.state?.form.query) {
+ replace({ state: null });
+ return { query: location.state?.form.query };
+ }
+
if (queryParams?.agentPolicyId) {
return {
agentSelection: {
@@ -37,7 +43,7 @@ const NewLiveQueryPageComponent = () => {
}
return undefined;
- }, [location.search]);
+ }, [location.search, location.state, replace]);
const LeftColumn = useMemo(
() => (
diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx
index 7e8e8e543dfab..8738c06d06597 100644
--- a/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx
+++ b/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx
@@ -16,6 +16,7 @@ import {
import React, { useCallback, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { useHistory } from 'react-router-dom';
import { SavedObject } from 'kibana/public';
import { WithHeaderLayout } from '../../../components/layouts';
@@ -51,6 +52,7 @@ const EditButton = React.memo(EditButtonComponent);
const SavedQueriesPageComponent = () => {
useBreadcrumbs('saved_queries');
+ const { push } = useHistory();
const newQueryLinkProps = useRouterNavigate('saved_queries/new');
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(10);
@@ -59,21 +61,15 @@ const SavedQueriesPageComponent = () => {
const { data } = useSavedQueries({ isLive: true });
- // const handlePlayClick = useCallback(
- // (item) =>
- // push({
- // search: qs.stringify({
- // tab: 'live_query',
- // }),
- // state: {
- // query: {
- // id: item.id,
- // query: item.attributes.query,
- // },
- // },
- // }),
- // [push]
- // );
+ const handlePlayClick = useCallback(
+ (item) =>
+ push('/live_queries/new', {
+ form: {
+ savedQueryId: item.id,
+ },
+ }),
+ [push]
+ );
const renderEditAction = useCallback(
(item: SavedObject<{ name: string }>) => (
@@ -96,45 +92,53 @@ const SavedQueriesPageComponent = () => {
() => [
{
field: 'attributes.id',
- name: 'Query ID',
+ name: i18n.translate('xpack.osquery.savedQueries.table.queryIdColumnTitle', {
+ defaultMessage: 'Query ID',
+ }),
sortable: true,
truncateText: true,
},
{
field: 'attributes.description',
- name: 'Description',
+ name: i18n.translate('xpack.osquery.savedQueries.table.descriptionColumnTitle', {
+ defaultMessage: 'Description',
+ }),
sortable: true,
truncateText: true,
},
{
field: 'attributes.created_by',
- name: 'Created by',
+ name: i18n.translate('xpack.osquery.savedQueries.table.createdByColumnTitle', {
+ defaultMessage: 'Created by',
+ }),
sortable: true,
truncateText: true,
},
{
field: 'attributes.updated_at',
- name: 'Last updated at',
+ name: i18n.translate('xpack.osquery.savedQueries.table.updatedAtColumnTitle', {
+ defaultMessage: 'Last updated at',
+ }),
sortable: (item: SavedObject<{ updated_at: string }>) =>
item.attributes.updated_at ? Date.parse(item.attributes.updated_at) : 0,
truncateText: true,
render: renderUpdatedAt,
},
{
- name: 'Actions',
+ name: i18n.translate('xpack.osquery.savedQueries.table.actionsColumnTitle', {
+ defaultMessage: 'Actions',
+ }),
actions: [
- // {
- // name: 'Live query',
- // description: 'Run live query',
- // type: 'icon',
- // icon: 'play',
- // onClick: handlePlayClick,
- // },
+ {
+ type: 'icon',
+ icon: 'play',
+ onClick: handlePlayClick,
+ },
{ render: renderEditAction },
],
},
],
- [renderEditAction, renderUpdatedAt]
+ [handlePlayClick, renderEditAction, renderUpdatedAt]
);
const onTableChange = useCallback(({ page = {}, sort = {} }) => {
diff --git a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx
index e30954a695b2d..073b56bfd1d4c 100644
--- a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx
+++ b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx
@@ -7,10 +7,11 @@
import { find } from 'lodash/fp';
import { EuiCodeBlock, EuiFormRow, EuiComboBox, EuiText } from '@elastic/eui';
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { SimpleSavedObject } from 'kibana/public';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { useHistory, useLocation } from 'react-router-dom';
import { useSavedQueries } from './use_saved_queries';
@@ -29,19 +30,25 @@ const SavedQueriesDropdownComponent: React.FC = ({
disabled,
onChange,
}) => {
+ const { replace } = useHistory();
+ const location = useLocation();
const [selectedOptions, setSelectedOptions] = useState([]);
const { data } = useSavedQueries({});
- const queryOptions =
- data?.savedObjects?.map((savedQuery) => ({
- label: savedQuery.attributes.id ?? '',
- value: {
- id: savedQuery.attributes.id,
- description: savedQuery.attributes.description,
- query: savedQuery.attributes.query,
- },
- })) ?? [];
+ const queryOptions = useMemo(
+ () =>
+ data?.savedObjects?.map((savedQuery) => ({
+ label: savedQuery.attributes.id ?? '',
+ value: {
+ savedObjectId: savedQuery.id,
+ id: savedQuery.attributes.id,
+ description: savedQuery.attributes.description,
+ query: savedQuery.attributes.query,
+ },
+ })) ?? [],
+ [data?.savedObjects]
+ );
const handleSavedQueryChange = useCallback(
(newSelectedOptions) => {
@@ -73,6 +80,20 @@ const SavedQueriesDropdownComponent: React.FC = ({
[]
);
+ useEffect(() => {
+ const savedQueryId = location.state?.form?.savedQueryId;
+
+ if (savedQueryId) {
+ const savedQueryOption = find(['value.savedObjectId', savedQueryId], queryOptions);
+
+ if (savedQueryOption) {
+ handleSavedQueryChange([savedQueryOption]);
+ }
+
+ replace({ state: null });
+ }
+ }, [handleSavedQueryChange, replace, location.state, queryOptions]);
+
return (
Date: Mon, 12 Jul 2021 11:56:43 -0400
Subject: [PATCH 24/31] [Fleet] Fix enrollment flyout with fleet server from
policy page (#104542)
* [Fleet] Fix enrollment flyout with fleet server from policy page
* Fix tests
* Show enrollment instructions for add agent from fleet server policy
* Fix conditions to show fleet server setup in flyout
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../agent_enrollment_flyout.test.mocks.ts | 1 +
.../agent_enrollment_flyout.test.tsx | 6 ++-
.../agent_enrollment_flyout/index.tsx | 39 ++++++++++++--
.../managed_instructions.tsx | 54 ++++++++++++-------
.../agent_enrollment_flyout/steps.tsx | 22 ++------
.../agent_enrollment_flyout/types.ts | 8 ++-
x-pack/plugins/fleet/public/types/index.ts | 1 +
7 files changed, 87 insertions(+), 44 deletions(-)
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts
index d2e7c4089e88b..5c292187982dc 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts
@@ -11,6 +11,7 @@ jest.mock('../../hooks/use_request', () => {
...module,
useGetSettings: jest.fn(),
sendGetFleetStatus: jest.fn(),
+ sendGetOneAgentPolicy: jest.fn(),
};
});
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx
index f68b1b878c51c..18296134ee1a7 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx
@@ -16,7 +16,7 @@ import { coreMock } from 'src/core/public/mocks';
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
import type { AgentPolicy } from '../../../common';
-import { useGetSettings, sendGetFleetStatus } from '../../hooks/use_request';
+import { useGetSettings, sendGetFleetStatus, sendGetOneAgentPolicy } from '../../hooks/use_request';
import { FleetStatusProvider, ConfigContext } from '../../hooks';
import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page/components';
@@ -79,6 +79,10 @@ describe('', () => {
data: { isReady: true },
});
+ (sendGetOneAgentPolicy as jest.Mock).mockResolvedValue({
+ data: { item: { package_policies: [] } },
+ });
+
(useFleetServerInstructions as jest.Mock).mockReturnValue({
serviceToken: 'test',
getServiceToken: jest.fn(),
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx
index 9b82b2a80b5e1..87911e5d6c2c7 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx
@@ -22,7 +22,9 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { useGetSettings, useUrlModal } from '../../hooks';
+import { useGetSettings, useUrlModal, sendGetOneAgentPolicy, useFleetStatus } from '../../hooks';
+import { FLEET_SERVER_PACKAGE } from '../../constants';
+import type { PackagePolicy } from '../../types';
import { ManagedInstructions } from './managed_instructions';
import { StandaloneInstructions } from './standalone_instructions';
@@ -63,6 +65,30 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({
}
}, [modal, lastModal, settings]);
+ const fleetStatus = useFleetStatus();
+ const [policyId, setSelectedPolicyId] = useState(agentPolicy?.id);
+ const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false);
+
+ useEffect(() => {
+ async function checkPolicyIsFleetServer() {
+ if (policyId && setIsFleetServerPolicySelected) {
+ const agentPolicyRequest = await sendGetOneAgentPolicy(policyId);
+ if (
+ agentPolicyRequest.data?.item &&
+ (agentPolicyRequest.data.item.package_policies as PackagePolicy[]).some(
+ (packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE
+ )
+ ) {
+ setIsFleetServerPolicySelected(true);
+ } else {
+ setIsFleetServerPolicySelected(false);
+ }
+ }
+ }
+
+ checkPolicyIsFleetServer();
+ }, [policyId]);
+
const isLoadingInitialRequest = settings.isLoading && settings.isInitialRequest;
return (
@@ -110,16 +136,23 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({
) : undefined
}
>
- {fleetServerHosts.length === 0 && mode === 'managed' ? null : mode === 'managed' ? (
+ {mode === 'managed' ? (
) : (
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx
index 61f86335cd7f9..8054c48fbbaa8 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx
@@ -11,7 +11,7 @@ import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/st
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { useGetOneEnrollmentAPIKey, useGetSettings, useLink, useFleetStatus } from '../../hooks';
+import { useGetOneEnrollmentAPIKey, useLink, useFleetStatus } from '../../hooks';
import { ManualInstructions } from '../../components/enrollment_instructions';
import {
@@ -56,14 +56,19 @@ const FleetServerMissingRequirements = () => {
};
export const ManagedInstructions = React.memo(
- ({ agentPolicy, agentPolicies, viewDataStep }) => {
+ ({
+ agentPolicy,
+ agentPolicies,
+ viewDataStep,
+ setSelectedPolicyId,
+ isFleetServerPolicySelected,
+ settings,
+ }) => {
const fleetStatus = useFleetStatus();
const [selectedApiKeyId, setSelectedAPIKeyId] = useState();
- const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false);
const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId);
- const settings = useGetSettings();
const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id);
const fleetServerSteps = useMemo(() => {
@@ -88,7 +93,7 @@ export const ManagedInstructions = React.memo(
}, [fleetServerInstructions]);
const steps = useMemo(() => {
- const fleetServerHosts = settings.data?.item?.fleet_server_hosts || [];
+ const fleetServerHosts = settings?.fleet_server_hosts || [];
const baseSteps: EuiContainedStepProps[] = [
DownloadStep(),
!agentPolicy
@@ -96,7 +101,7 @@ export const ManagedInstructions = React.memo(
agentPolicies,
selectedApiKeyId,
setSelectedAPIKeyId,
- setIsFleetServerPolicySelected,
+ setSelectedPolicyId,
})
: AgentEnrollmentKeySelectionStep({ agentPolicy, selectedApiKeyId, setSelectedAPIKeyId }),
];
@@ -121,30 +126,39 @@ export const ManagedInstructions = React.memo(
}, [
agentPolicy,
selectedApiKeyId,
+ setSelectedPolicyId,
setSelectedAPIKeyId,
agentPolicies,
apiKey.data,
fleetServerSteps,
isFleetServerPolicySelected,
- settings.data?.item?.fleet_server_hosts,
+ settings?.fleet_server_hosts,
viewDataStep,
]);
+ if (fleetStatus.isReady && settings?.fleet_server_hosts.length === 0) {
+ return null;
+ }
+
+ if (fleetStatus.isReady) {
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+ }
+
return (
<>
- {fleetStatus.isReady ? (
- <>
-
-
-
-
-
- >
- ) : fleetStatus.missingRequirements?.length === 1 &&
- fleetStatus.missingRequirements[0] === 'fleet_server' ? (
+ {fleetStatus.missingRequirements?.length === 1 &&
+ fleetStatus.missingRequirements[0] === 'fleet_server' ? (
) : (
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx
index 6cffa39628d92..1cfdc45fb7dba 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx
@@ -11,9 +11,8 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import semver from 'semver';
-import type { AgentPolicy, PackagePolicy } from '../../types';
-import { sendGetOneAgentPolicy, useKibanaVersion } from '../../hooks';
-import { FLEET_SERVER_PACKAGE } from '../../constants';
+import type { AgentPolicy } from '../../types';
+import { useKibanaVersion } from '../../hooks';
import { EnrollmentStepAgentPolicy } from './agent_policy_selection';
import { AdvancedAgentAuthenticationSettings } from './advanced_agent_authentication_settings';
@@ -69,13 +68,11 @@ export const AgentPolicySelectionStep = ({
selectedApiKeyId,
setSelectedAPIKeyId,
excludeFleetServer,
- setIsFleetServerPolicySelected,
}: {
agentPolicies?: AgentPolicy[];
setSelectedPolicyId?: (policyId?: string) => void;
selectedApiKeyId?: string;
setSelectedAPIKeyId?: (key?: string) => void;
- setIsFleetServerPolicySelected?: (selected: boolean) => void;
excludeFleetServer?: boolean;
}) => {
const regularAgentPolicies = useMemo(() => {
@@ -92,21 +89,8 @@ export const AgentPolicySelectionStep = ({
if (setSelectedPolicyId) {
setSelectedPolicyId(policyId);
}
- if (policyId && setIsFleetServerPolicySelected) {
- const agentPolicyRequest = await sendGetOneAgentPolicy(policyId);
- if (
- agentPolicyRequest.data?.item &&
- (agentPolicyRequest.data.item.package_policies as PackagePolicy[]).some(
- (packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE
- )
- ) {
- setIsFleetServerPolicySelected(true);
- } else {
- setIsFleetServerPolicySelected(false);
- }
- }
},
- [setIsFleetServerPolicySelected, setSelectedPolicyId]
+ [setSelectedPolicyId]
);
return {
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts
index 9ee514c634655..282a5b243caed 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts
@@ -7,7 +7,7 @@
import type { EuiStepProps } from '@elastic/eui';
-import type { AgentPolicy } from '../../types';
+import type { AgentPolicy, Settings } from '../../types';
export interface BaseProps {
/**
@@ -27,4 +27,10 @@ export interface BaseProps {
* in some way. This is an area for consumers to render a button and text explaining how data can be viewed.
*/
viewDataStep?: EuiStepProps;
+
+ settings?: Settings;
+
+ setSelectedPolicyId?: (policyId?: string) => void;
+
+ isFleetServerPolicySelected?: boolean;
}
diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts
index f21552d68e77b..c91ec42d3e527 100644
--- a/x-pack/plugins/fleet/public/types/index.ts
+++ b/x-pack/plugins/fleet/public/types/index.ts
@@ -27,6 +27,7 @@ export {
PackagePolicyPackage,
Output,
DataStream,
+ Settings,
// API schema - misc setup, status
GetFleetStatusResponse,
// API schemas - Agent policy
From 42c743be8848e08761f76903c4aa1f0b5f8e899a Mon Sep 17 00:00:00 2001
From: Caroline Horn <549577+cchaos@users.noreply.github.com>
Date: Mon, 12 Jul 2021 12:50:41 -0400
Subject: [PATCH 25/31] [Top Menu] Increase size of top menu links to `s`
(#103144)
* Increased non-emphasized header links size from `xs` to `s`
* [Observability] Updating header links to use EuiHeaderLink
* [Spaces Menu] Larger spinner
* [Help Menu] Increase size of links
* [Canvas] Increase size to `s`
---
.../chrome/ui/header/header_help_menu.tsx | 12 +--
.../data/public/ui/filter_bar/filter_bar.tsx | 2 +-
.../index_pattern_table.tsx | 2 +-
.../public/top_nav_menu/top_nav_menu_item.tsx | 2 +-
.../app/RumDashboard/ActionMenu/index.tsx | 60 ++++++--------
.../alerting_popover_flyout.tsx | 1 -
.../anomaly_detection_setup_link.tsx | 1 -
.../shared/apm_header_action_menu/index.tsx | 2 +-
.../function_reference_generator.tsx | 2 +-
.../public/components/help_menu/help_menu.tsx | 4 +-
.../__snapshots__/edit_menu.stories.storyshot | 12 +--
.../edit_menu/edit_menu.component.tsx | 2 +-
.../share_menu.stories.storyshot | 4 +-
.../share_menu/share_menu.component.tsx | 2 +-
.../__snapshots__/view_menu.stories.storyshot | 8 +-
.../view_menu/view_menu.component.tsx | 2 +-
.../file_error_callouts.tsx | 2 +-
.../components/metrics_alert_dropdown.tsx | 7 +-
.../components/alert_dropdown.tsx | 7 +-
.../infra/public/pages/logs/page_content.tsx | 37 ++++-----
.../infra/public/pages/metrics/index.tsx | 43 ++++------
.../anomaly_detection_flyout.tsx | 7 +-
.../nav_control/nav_control_popover.tsx | 2 +-
.../common/header/action_menu_content.tsx | 82 +++++++++----------
.../alerts/toggle_alert_flyout_button.tsx | 7 +-
.../overview/synthetics_callout.test.tsx | 6 --
.../overview/synthetics_callout.tsx | 10 +--
27 files changed, 134 insertions(+), 194 deletions(-)
diff --git a/src/core/public/chrome/ui/header/header_help_menu.tsx b/src/core/public/chrome/ui/header/header_help_menu.tsx
index c6a09c1177a5e..cbf89bba2ca44 100644
--- a/src/core/public/chrome/ui/header/header_help_menu.tsx
+++ b/src/core/public/chrome/ui/header/header_help_menu.tsx
@@ -211,7 +211,7 @@ export class HeaderHelpMenu extends Component {
return (
-
+
}>
-
- {ANALYZE_DATA}
-
-
-
-
-
+ {ANALYZE_MESSAGE}}>
+
- {i18n.translate('xpack.apm.addDataButtonLabel', {
- defaultMessage: 'Add data',
- })}
-
-
-
+ {ANALYZE_DATA}
+
+
+
+ {i18n.translate('xpack.apm.addDataButtonLabel', {
+ defaultMessage: 'Add data',
+ })}
+
+
);
}
diff --git a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx
index ca73f6ddd05b3..4abd36a277311 100644
--- a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx
+++ b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx
@@ -66,7 +66,6 @@ export function AlertingPopoverAndFlyout({
const button = (
-
+
{i18n.translate('xpack.apm.settingsLinkLabel', {
defaultMessage: 'Settings',
})}
diff --git a/x-pack/plugins/canvas/public/components/function_reference_generator/function_reference_generator.tsx b/x-pack/plugins/canvas/public/components/function_reference_generator/function_reference_generator.tsx
index 81532816d9c83..eb394801f549c 100644
--- a/x-pack/plugins/canvas/public/components/function_reference_generator/function_reference_generator.tsx
+++ b/x-pack/plugins/canvas/public/components/function_reference_generator/function_reference_generator.tsx
@@ -29,7 +29,7 @@ export const FunctionReferenceGenerator: FC = ({ functionRegistry }) => {
};
return (
-
+
Generate function reference
);
diff --git a/x-pack/plugins/canvas/public/components/help_menu/help_menu.tsx b/x-pack/plugins/canvas/public/components/help_menu/help_menu.tsx
index 2877ccf41056d..af1850beb5290 100644
--- a/x-pack/plugins/canvas/public/components/help_menu/help_menu.tsx
+++ b/x-pack/plugins/canvas/public/components/help_menu/help_menu.tsx
@@ -46,13 +46,13 @@ export const HelpMenu: FC = ({ functionRegistry }) => {
return (
<>
-
+
{strings.getKeyboardShortcutsLinkLabel()}
{FunctionReferenceGenerator ? (
-
+
) : null}
diff --git a/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/__stories__/__snapshots__/edit_menu.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/__stories__/__snapshots__/edit_menu.stories.storyshot
index cc33ae3526c0c..f2bc9c57cbcc6 100644
--- a/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/__stories__/__snapshots__/edit_menu.stories.storyshot
+++ b/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/__stories__/__snapshots__/edit_menu.stories.storyshot
@@ -9,7 +9,7 @@ exports[`Storyshots components/WorkpadHeader/EditMenu 2 elements selected 1`] =
>
}
isOpen={popoverOpen}
closePopover={closePopover}
diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx
index f3481cab73360..302de15db9f5a 100644
--- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx
+++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx
@@ -7,7 +7,7 @@
import React, { useState, useCallback, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
-import { EuiPopover, EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel } from '@elastic/eui';
+import { EuiPopover, EuiContextMenuItem, EuiContextMenuPanel, EuiHeaderLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { AlertFlyout } from './alert_flyout';
import { useLinkProps } from '../../../hooks/use_link_props';
@@ -83,8 +83,7 @@ export const AlertDropdown = () => {
{
id="xpack.infra.alerting.logs.alertsButton"
defaultMessage="Alerts and rules"
/>
-
+
}
isOpen={popoverOpen}
closePopover={closePopover}
diff --git a/x-pack/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx
index 8175a95f6a064..d8b5667e60d04 100644
--- a/x-pack/plugins/infra/public/pages/logs/page_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui';
+import { EuiHeaderLinks, EuiHeaderLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useContext } from 'react';
import { Route, Switch } from 'react-router-dom';
@@ -78,28 +78,19 @@ export const LogsPageContent: React.FunctionComponent = () => {
{setHeaderActionMenu && (
-
-
-
- {settingsTabTitle}
-
-
-
-
-
-
-
- {ADD_DATA_LABEL}
-
-
-
+
+
+ {settingsTabTitle}
+
+
+
+ {ADD_DATA_LABEL}
+
+
)}
diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx
index 045fcb57ae943..d4845a4dd9e44 100644
--- a/x-pack/plugins/infra/public/pages/metrics/index.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx
@@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import React, { useContext } from 'react';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
-import { EuiErrorBoundary, EuiFlexItem, EuiFlexGroup, EuiButtonEmpty } from '@elastic/eui';
+import { EuiErrorBoundary, EuiHeaderLinks, EuiHeaderLink } from '@elastic/eui';
import { IIndexPattern } from 'src/plugins/data/common';
import { MetricsSourceConfigurationProperties } from '../../../common/metrics_sources';
import { DocumentTitle } from '../../components/document_title';
@@ -86,31 +86,22 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
{setHeaderActionMenu && (
-
-
-
- {settingsTabTitle}
-
-
-
-
-
-
-
-
-
-
- {ADD_DATA_LABEL}
-
-
-
+
+
+ {settingsTabTitle}
+
+
+
+
+ {ADD_DATA_LABEL}
+
+
)}
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout.tsx
index d2cd4f87a5342..4e28fb4202bdc 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout.tsx
@@ -6,7 +6,7 @@
*/
import React, { useState, useCallback } from 'react';
-import { EuiButtonEmpty, EuiFlyout } from '@elastic/eui';
+import { EuiHeaderLink, EuiFlyout } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { FlyoutHome } from './flyout_home';
import { JobSetupScreen } from './job_setup_screen';
@@ -50,8 +50,7 @@ export const AnomalyDetectionFlyout = () => {
return (
<>
- {
id="xpack.infra.ml.anomalyDetectionButton"
defaultMessage="Anomaly detection"
/>
-
+
{showFlyout && (
{
}
return this.getButton(
- }>
+ }>
,
(activeSpace as Space).name
diff --git a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx
index 479a512b7238a..9f00dd2e8f061 100644
--- a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx
+++ b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx
@@ -6,7 +6,7 @@
*/
import React from 'react';
-import { EuiButtonEmpty, EuiHeaderLinks, EuiHeaderSectionItem, EuiToolTip } from '@elastic/eui';
+import { EuiHeaderLinks, EuiToolTip, EuiHeaderLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { useHistory } from 'react-router-dom';
@@ -51,53 +51,45 @@ export function ActionMenuContent(): React.ReactElement {
return (
-
-
+
+
+
+
+
+ {ANALYZE_MESSAGE}}>
+
-
-
-
-
-
-
-
- {ANALYZE_MESSAGE}}>
-
- {ANALYZE_DATA}
-
-
-
-
-
- {ADD_DATA_LABEL}
-
-
+ {ANALYZE_DATA}
+
+
+
+
+ {ADD_DATA_LABEL}
+
);
}
diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx
index 278958bd1987b..22193fe4623d6 100644
--- a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx
@@ -6,7 +6,7 @@
*/
import {
- EuiButtonEmpty,
+ EuiHeaderLink,
EuiContextMenu,
EuiContextMenuPanelDescriptor,
EuiContextMenuPanelItemDescriptor,
@@ -123,8 +123,7 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({
return (
= ({
id="xpack.uptime.alerts.toggleAlertFlyoutButtonText"
defaultMessage="Alerts and rules"
/>
-
+
}
closePopover={() => setIsOpen(false)}
isOpen={isOpen}
diff --git a/x-pack/plugins/uptime/public/components/overview/synthetics_callout.test.tsx b/x-pack/plugins/uptime/public/components/overview/synthetics_callout.test.tsx
index 5f6f9d7a7207e..ec9e5f958ec3a 100644
--- a/x-pack/plugins/uptime/public/components/overview/synthetics_callout.test.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/synthetics_callout.test.tsx
@@ -67,9 +67,6 @@ describe('SyntheticsCallout', () => {
-
`);
});
@@ -128,9 +125,6 @@ describe('SyntheticsCallout', () => {
-
`);
wrapper.find('EuiButton').simulate('click');
diff --git a/x-pack/plugins/uptime/public/components/overview/synthetics_callout.tsx b/x-pack/plugins/uptime/public/components/overview/synthetics_callout.tsx
index fa28e42d7d0c1..4e9c3256f1578 100644
--- a/x-pack/plugins/uptime/public/components/overview/synthetics_callout.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/synthetics_callout.tsx
@@ -5,14 +5,7 @@
* 2.0.
*/
-import {
- EuiButton,
- EuiButtonEmpty,
- EuiCallOut,
- EuiFlexGroup,
- EuiFlexItem,
- EuiSpacer,
-} from '@elastic/eui';
+import { EuiButton, EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -74,7 +67,6 @@ export const SyntheticsCallout = () => {
-
>
);
};
From 4b7b45d69de43a56e5d74d9b4f1ec302cc48396c Mon Sep 17 00:00:00 2001
From: Frank Hassanabad
Date: Mon, 12 Jul 2021 11:18:17 -0600
Subject: [PATCH 26/31] [Security Solutions] Changes cypress tests to use
immutable object data (#105162)
## Summary
In previous pull requests I noticed that there were accidents where Cypress tests were accidentally mutating objects such as rules and mock data since the data is mutable as regular variables. This changes the data to be underneath functions and the functions all return new copies on each invocation. This makes it so that it isn't possible for Cypress tests to accidentally mutate data and mocks between tests. This should also prevent devs from accidentally writing tests that rely on previous tests accidentally mutating data.
I am hoping that this is portable back to 7.14 since it's just test changes and will make it easier to do any other bug fixes without conflicts if tests are updated. If it's not easily portable, then I will mark and update it as only 7.15 backported.
* Changes the mocks underneath `objects/` to use getter functions that return objects.
* Updates the cypress tests
### Checklist
- [x] [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
---
.../detection_alerts/alerts_details.spec.ts | 4 +-
.../integration/cases/attach_timeline.spec.ts | 14 +-
.../cases/connector_options.spec.ts | 48 ++--
.../integration/cases/connectors.spec.ts | 4 +-
.../integration/cases/creation.spec.ts | 10 +-
.../detection_alerts/alerts_details.spec.ts | 4 +-
..._detection_callouts_index_outdated.spec.ts | 8 +-
.../detection_alerts/attach_to_case.spec.ts | 4 +-
.../detection_alerts/closing.spec.ts | 4 +-
.../detection_alerts/cti_enrichments.spec.ts | 8 +-
.../detection_alerts/in_progress.spec.ts | 4 +-
.../investigate_in_timeline.spec.ts | 4 +-
.../missing_privileges_callout.spec.ts | 6 +-
.../detection_alerts/opening.spec.ts | 4 +-
.../detection_rules/custom_query_rule.spec.ts | 86 ++++----
.../event_correlation_rule.spec.ts | 24 +-
.../detection_rules/export_rule.spec.ts | 4 +-
.../indicator_match_rule.spec.ts | 136 +++++++-----
.../integration/detection_rules/links.spec.ts | 4 +-
.../machine_learning_rule.spec.ts | 38 ++--
.../detection_rules/override.spec.ts | 24 +-
.../detection_rules/sorting.spec.ts | 19 +-
.../detection_rules/threshold_rule.spec.ts | 34 +--
.../exceptions/exceptions_modal.spec.ts | 4 +-
.../exceptions/exceptions_table.spec.ts | 14 +-
.../integration/exceptions/from_alert.spec.ts | 8 +-
.../integration/exceptions/from_rule.spec.ts | 8 +-
.../integration/header/search_bar.spec.ts | 6 +-
.../integration/overview/overview.spec.ts | 6 +-
.../timeline_templates/creation.spec.ts | 29 ++-
.../timeline_templates/export.spec.ts | 4 +-
.../integration/timelines/creation.spec.ts | 21 +-
.../integration/timelines/export.spec.ts | 4 +-
.../integration/timelines/notes_tab.spec.ts | 12 +-
.../timelines/open_timeline.spec.ts | 14 +-
.../integration/timelines/query_tab.spec.ts | 12 +-
.../cypress/integration/urls/state.spec.ts | 6 +-
.../security_solution/cypress/objects/case.ts | 50 +++--
.../cypress/objects/connector.ts | 4 +-
.../cypress/objects/exception.ts | 10 +-
.../cypress/objects/filter.ts | 4 +-
.../security_solution/cypress/objects/rule.ts | 205 +++++++++---------
.../cypress/objects/timeline.ts | 30 +--
.../cypress/screens/edit_connector.ts | 10 +-
.../cypress/tasks/create_new_rule.ts | 15 +-
.../mitre/mitre_tactics_techniques.ts | 4 +-
.../detections/mitre/valid_threat_mock.ts | 4 +-
47 files changed, 516 insertions(+), 463 deletions(-)
diff --git a/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts
index f87399a666904..229bbcce87696 100644
--- a/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts
@@ -18,7 +18,7 @@ import { cleanKibana } from '../../tasks/common';
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import { esArchiverCCSLoad, esArchiverCCSUnload } from '../../tasks/es_archiver';
-import { unmappedCCSRule } from '../../objects/rule';
+import { getUnmappedCCSRule } from '../../objects/rule';
import { ALERTS_URL } from '../../urls/navigation';
@@ -29,7 +29,7 @@ describe('Alert details with unmapped fields', () => {
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(unmappedCCSRule);
+ createCustomRuleActivated(getUnmappedCCSRule());
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
expandFirstAlert();
diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/attach_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/attach_timeline.spec.ts
index 29105ce1582cf..e94f7d00f0b37 100644
--- a/x-pack/plugins/security_solution/cypress/integration/cases/attach_timeline.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/cases/attach_timeline.spec.ts
@@ -13,8 +13,8 @@ import {
selectCase,
} from '../../tasks/timeline';
import { DESCRIPTION_INPUT, ADD_COMMENT_INPUT } from '../../screens/create_new_case';
-import { case1 } from '../../objects/case';
-import { timeline } from '../../objects/timeline';
+import { getCase1 } from '../../objects/case';
+import { getTimeline } from '../../objects/timeline';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { cleanKibana } from '../../tasks/common';
import { createCase } from '../../tasks/api_calls/cases';
@@ -23,7 +23,7 @@ describe('attach timeline to case', () => {
context('without cases created', () => {
beforeEach(() => {
cleanKibana();
- createTimeline(timeline).then((response) => {
+ createTimeline(getTimeline()).then((response) => {
cy.wrap(response.body.data.persistTimeline.timeline).as('myTimeline');
});
});
@@ -57,10 +57,10 @@ describe('attach timeline to case', () => {
context('with cases created', () => {
before(() => {
cleanKibana();
- createTimeline(timeline).then((response) =>
+ createTimeline(getTimeline()).then((response) =>
cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId')
);
- createCase(case1).then((response) => cy.wrap(response.body.id).as('caseId'));
+ createCase(getCase1()).then((response) => cy.wrap(response.body.id).as('caseId'));
});
it('attach timeline to an existing case', function () {
@@ -71,7 +71,9 @@ describe('attach timeline to case', () => {
cy.location('origin').then((origin) => {
cy.get(ADD_COMMENT_INPUT).should(
'have.text',
- `[${timeline.title}](${origin}/app/security/timelines?timeline=(id:%27${this.timelineId}%27,isOpen:!t))`
+ `[${getTimeline().title}](${origin}/app/security/timelines?timeline=(id:%27${
+ this.timelineId
+ }%27,isOpen:!t))`
);
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/connector_options.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/connector_options.spec.ts
index 95b555c2acae6..0959f999a4b53 100644
--- a/x-pack/plugins/security_solution/cypress/integration/cases/connector_options.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/cases/connector_options.spec.ts
@@ -7,13 +7,13 @@
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import {
- case1,
- connectorIds,
- mockConnectorsResponse,
- executeResponses,
- ibmResilientConnectorOptions,
- jiraConnectorOptions,
- serviceNowConnectorOpions,
+ getCase1,
+ getConnectorIds,
+ getMockConnectorsResponse,
+ getExecuteResponses,
+ getIbmResilientConnectorOptions,
+ getJiraConnectorOptions,
+ getServiceNowConnectorOptions,
} from '../../objects/case';
import {
createCase,
@@ -30,26 +30,26 @@ import { cleanKibana } from '../../tasks/common';
describe('Cases connector incident fields', () => {
beforeEach(() => {
cleanKibana();
- cy.intercept('GET', '/api/cases/configure/connectors/_find', mockConnectorsResponse);
- cy.intercept('POST', `/api/actions/action/${connectorIds.sn}/_execute`, (req) => {
+ cy.intercept('GET', '/api/cases/configure/connectors/_find', getMockConnectorsResponse());
+ cy.intercept('POST', `/api/actions/action/${getConnectorIds().sn}/_execute`, (req) => {
const response =
req.body.params.subAction === 'getChoices'
- ? executeResponses.servicenow.choices
+ ? getExecuteResponses().servicenow.choices
: { status: 'ok', data: [] };
req.reply(response);
});
- cy.intercept('POST', `/api/actions/action/${connectorIds.jira}/_execute`, (req) => {
+ cy.intercept('POST', `/api/actions/action/${getConnectorIds().jira}/_execute`, (req) => {
const response =
req.body.params.subAction === 'issueTypes'
- ? executeResponses.jira.issueTypes
- : executeResponses.jira.fieldsByIssueType;
+ ? getExecuteResponses().jira.issueTypes
+ : getExecuteResponses().jira.fieldsByIssueType;
req.reply(response);
});
- cy.intercept('POST', `/api/actions/action/${connectorIds.resilient}/_execute`, (req) => {
+ cy.intercept('POST', `/api/actions/action/${getConnectorIds().resilient}/_execute`, (req) => {
const response =
req.body.params.subAction === 'incidentTypes'
- ? executeResponses.resilient.incidentTypes
- : executeResponses.resilient.severity;
+ ? getExecuteResponses().resilient.incidentTypes
+ : getExecuteResponses().resilient.severity;
req.reply(response);
});
});
@@ -57,19 +57,19 @@ describe('Cases connector incident fields', () => {
it('Correct incident fields show when connector is changed', () => {
loginAndWaitForPageWithoutDateRange(CASES_URL);
goToCreateNewCase();
- fillCasesMandatoryfields(case1);
- fillJiraConnectorOptions(jiraConnectorOptions);
- fillServiceNowConnectorOptions(serviceNowConnectorOpions);
- fillIbmResilientConnectorOptions(ibmResilientConnectorOptions);
+ fillCasesMandatoryfields(getCase1());
+ fillJiraConnectorOptions(getJiraConnectorOptions());
+ fillServiceNowConnectorOptions(getServiceNowConnectorOptions());
+ fillIbmResilientConnectorOptions(getIbmResilientConnectorOptions());
createCase();
- cy.get(CONNECTOR_TITLE).should('have.text', ibmResilientConnectorOptions.title);
+ cy.get(CONNECTOR_TITLE).should('have.text', getIbmResilientConnectorOptions().title);
cy.get(CONNECTOR_CARD_DETAILS).should(
'have.text',
`${
- ibmResilientConnectorOptions.title
- }Incident Types: ${ibmResilientConnectorOptions.incidentTypes.join(', ')}Severity: ${
- ibmResilientConnectorOptions.severity
+ getIbmResilientConnectorOptions().title
+ }Incident Types: ${getIbmResilientConnectorOptions().incidentTypes.join(', ')}Severity: ${
+ getIbmResilientConnectorOptions().severity
}`
);
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts
index 9e55067ce4ed4..aa1bd7a5db5cc 100644
--- a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { serviceNowConnector } from '../../objects/case';
+import { getServiceNowConnector } from '../../objects/case';
import { SERVICE_NOW_MAPPING, TOASTER } from '../../screens/configure_cases';
@@ -77,7 +77,7 @@ describe('Cases connectors', () => {
loginAndWaitForPageWithoutDateRange(CASES_URL);
goToEditExternalConnection();
openAddNewConnectorOption();
- addServiceNowConnector(serviceNowConnector);
+ addServiceNowConnector(getServiceNowConnector());
cy.wait('@createConnector').then(({ response }) => {
cy.wrap(response!.statusCode).should('eql', 200);
diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts
index c568aaae664a0..9e3b775156cab 100644
--- a/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { case1 } from '../../objects/case';
+import { getCase1, TestCase } from '../../objects/case';
import {
ALL_CASES_CLOSED_CASES_STATS,
@@ -55,12 +55,12 @@ import { CASES_URL } from '../../urls/navigation';
describe('Cases', () => {
beforeEach(() => {
cleanKibana();
- createTimeline(case1.timeline).then((response) =>
+ createTimeline(getCase1().timeline).then((response) =>
cy
.wrap({
- ...case1,
+ ...getCase1(),
timeline: {
- ...case1.timeline,
+ ...getCase1().timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
})
@@ -86,7 +86,7 @@ describe('Cases', () => {
cy.get(ALL_CASES_TAGS_COUNT).should('have.text', 'Tags2');
cy.get(ALL_CASES_NAME).should('have.text', this.mycase.name);
cy.get(ALL_CASES_REPORTER).should('have.text', this.mycase.reporter);
- (this.mycase as typeof case1).tags.forEach((tag, index) => {
+ (this.mycase as TestCase).tags.forEach((tag, index) => {
cy.get(ALL_CASES_TAGS(index)).should('have.text', tag);
});
cy.get(ALL_CASES_COMMENTS_COUNT).should('have.text', '0');
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts
index d9ca43339d412..825cc7f8081e5 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts
@@ -18,7 +18,7 @@ import { cleanKibana } from '../../tasks/common';
import { esArchiverLoad } from '../../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
-import { unmappedRule } from '../../objects/rule';
+import { getUnmappedRule } from '../../objects/rule';
import { ALERTS_URL } from '../../urls/navigation';
@@ -29,7 +29,7 @@ describe('Alert details with unmapped fields', () => {
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(unmappedRule);
+ createCustomRuleActivated(getUnmappedRule());
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
expandFirstAlert();
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_detection_callouts_index_outdated.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_detection_callouts_index_outdated.spec.ts
index fb0b96c977e32..eaed80c484f60 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_detection_callouts_index_outdated.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_detection_callouts_index_outdated.spec.ts
@@ -7,7 +7,7 @@
import { ROLES } from '../../../common/test';
import { DETECTIONS_RULE_MANAGEMENT_URL, ALERTS_URL } from '../../urls/navigation';
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import { PAGE_TITLE } from '../../screens/common/page';
import {
@@ -77,7 +77,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr
context('On Rule Details page', () => {
beforeEach(() => {
- createCustomRule(newRule);
+ createCustomRule(getNewRule());
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
@@ -127,7 +127,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr
context('On Rule Details page', () => {
beforeEach(() => {
- createCustomRule(newRule);
+ createCustomRule(getNewRule());
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
@@ -177,7 +177,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr
context('On Rule Details page', () => {
beforeEach(() => {
- createCustomRule(newRule);
+ createCustomRule(getNewRule());
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/attach_to_case.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/attach_to_case.spec.ts
index 6cc5d2443e784..e052d1a3272ac 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/attach_to_case.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/attach_to_case.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import { ROLES } from '../../../common/test';
import { waitForAlertsIndexToBeCreated, waitForAlertsPanelToBeLoaded } from '../../tasks/alerts';
@@ -30,7 +30,7 @@ describe('Alerts timeline', () => {
loginAndWaitForPage(ALERTS_URL, ROLES.platform_engineer);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(newRule);
+ createCustomRuleActivated(getNewRule());
refreshPage();
waitForAlertsToPopulate(500);
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/closing.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/closing.spec.ts
index 6ae23733d6434..038bc30c90c1e 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/closing.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/closing.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import {
ALERTS,
ALERTS_COUNT,
@@ -39,7 +39,7 @@ describe('Closing alerts', () => {
loginAndWaitForPage(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(newRule, '1', '100m', 100);
+ createCustomRuleActivated(getNewRule(), '1', '100m', 100);
refreshPage();
waitForAlertsToPopulate(100);
deleteCustomRule();
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts
index b03daf74ce247..522e25590994f 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { newThreatIndicatorRule } from '../../objects/rule';
+import { getNewThreatIndicatorRule } from '../../objects/rule';
import { cleanKibana, reload } from '../../tasks/common';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
@@ -39,7 +39,7 @@ describe('CTI Enrichment', () => {
esArchiverLoad('suspicious_source_event');
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
goToManageAlertsDetectionRules();
- createCustomIndicatorRule(newThreatIndicatorRule);
+ createCustomIndicatorRule(getNewThreatIndicatorRule());
reload();
});
@@ -56,9 +56,9 @@ describe('CTI Enrichment', () => {
it('Displays enrichment matched.* fields on the timeline', () => {
const expectedFields = {
- 'threat.indicator.matched.atomic': newThreatIndicatorRule.atomic,
+ 'threat.indicator.matched.atomic': getNewThreatIndicatorRule().atomic,
'threat.indicator.matched.type': 'indicator_match_rule',
- 'threat.indicator.matched.field': newThreatIndicatorRule.indicatorMappingField,
+ 'threat.indicator.matched.field': getNewThreatIndicatorRule().indicatorMappingField,
};
const fields = Object.keys(expectedFields) as Array;
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/in_progress.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/in_progress.spec.ts
index cb8694d5c35af..890f8a064aa9e 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/in_progress.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/in_progress.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import {
ALERTS,
ALERTS_COUNT,
@@ -36,7 +36,7 @@ describe('Marking alerts as in-progress', () => {
loginAndWaitForPage(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(newRule);
+ createCustomRuleActivated(getNewRule());
refreshPage();
waitForAlertsToPopulate(500);
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/investigate_in_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/investigate_in_timeline.spec.ts
index 115118b6762d9..01a06b3d59266 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/investigate_in_timeline.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/investigate_in_timeline.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import { PROVIDER_BADGE } from '../../screens/timeline';
import {
@@ -27,7 +27,7 @@ describe('Alerts timeline', () => {
loginAndWaitForPage(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(newRule);
+ createCustomRuleActivated(getNewRule());
refreshPage();
waitForAlertsToPopulate(500);
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts
index 20a863e742efd..0db30179284e0 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts
@@ -7,7 +7,7 @@
import { ROLES } from '../../../common/test';
import { DETECTIONS_RULE_MANAGEMENT_URL, ALERTS_URL } from '../../urls/navigation';
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import { PAGE_TITLE } from '../../screens/common/page';
import {
@@ -95,7 +95,7 @@ describe('Detections > Callouts', () => {
context('On Rule Details page', () => {
beforeEach(() => {
- createCustomRule(newRule);
+ createCustomRule(getNewRule());
loadPageAsReadOnlyUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
@@ -145,7 +145,7 @@ describe('Detections > Callouts', () => {
context('On Rule Details page', () => {
beforeEach(() => {
- createCustomRule(newRule);
+ createCustomRule(getNewRule());
loadPageAsPlatformEngineer(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/opening.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/opening.spec.ts
index 6cbc82b93f446..4f78bdac84789 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/opening.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/opening.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import {
ALERTS_COUNT,
SELECTED_ALERTS,
@@ -37,7 +37,7 @@ describe('Opening alerts', () => {
loginAndWaitForPage(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(newRule);
+ createCustomRuleActivated(getNewRule());
refreshPage();
waitForAlertsToPopulate(500);
selectNumberOfAlerts(5);
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts
index 218b1f7745d94..e30cde4989284 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts
@@ -7,11 +7,11 @@
import { formatMitreAttackDescription } from '../../helpers/rules';
import {
- newRule,
- existingRule,
- indexPatterns,
- editedRule,
- newOverrideRule,
+ getNewRule,
+ getExistingRule,
+ getIndexPatterns,
+ getEditedRule,
+ getNewOverrideRule,
} from '../../objects/rule';
import {
ALERT_RULE_METHOD,
@@ -120,19 +120,19 @@ import { activatesRule } from '../../tasks/rule_details';
import { ALERTS_URL } from '../../urls/navigation';
describe('Custom detection rules creation', () => {
- const expectedUrls = newRule.referenceUrls.join('');
- const expectedFalsePositives = newRule.falsePositivesExamples.join('');
- const expectedTags = newRule.tags.join('');
- const expectedMitre = formatMitreAttackDescription(newRule.mitre);
+ const expectedUrls = getNewRule().referenceUrls.join('');
+ const expectedFalsePositives = getNewRule().falsePositivesExamples.join('');
+ const expectedTags = getNewRule().tags.join('');
+ const expectedMitre = formatMitreAttackDescription(getNewRule().mitre);
const expectedNumberOfRules = 1;
beforeEach(() => {
cleanKibana();
- createTimeline(newRule.timeline).then((response) => {
+ createTimeline(getNewRule().timeline).then((response) => {
cy.wrap({
- ...newRule,
+ ...getNewRule(),
timeline: {
- ...newRule.timeline,
+ ...getNewRule().timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
@@ -201,7 +201,7 @@ describe('Custom detection rules creation', () => {
cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
cy.get(DEFINITION_DETAILS).within(() => {
- getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join(''));
+ getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join(''));
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
@@ -209,11 +209,11 @@ describe('Custom detection rules creation', () => {
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
- `${newRule.runsEvery.interval}${newRule.runsEvery.type}`
+ `${getNewRule().runsEvery.interval}${getNewRule().runsEvery.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
- `${newRule.lookBack.interval}${newRule.lookBack.type}`
+ `${getNewRule().lookBack.interval}${getNewRule().lookBack.type}`
);
});
@@ -236,9 +236,9 @@ describe('Custom detection rules deletion and edition', () => {
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
goToManageAlertsDetectionRules();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(newRule, 'rule1');
- createCustomRuleActivated(newOverrideRule, 'rule2');
- createCustomRuleActivated(existingRule, 'rule3');
+ createCustomRuleActivated(getNewRule(), 'rule1');
+ createCustomRuleActivated(getNewOverrideRule(), 'rule2');
+ createCustomRuleActivated(getExistingRule(), 'rule3');
reload();
});
@@ -303,16 +303,18 @@ describe('Custom detection rules deletion and edition', () => {
});
context('Edition', () => {
- const expectedEditedtags = editedRule.tags.join('');
+ const expectedEditedtags = getEditedRule().tags.join('');
const expectedEditedIndexPatterns =
- editedRule.index && editedRule.index.length ? editedRule.index : indexPatterns;
+ getEditedRule().index && getEditedRule().index.length
+ ? getEditedRule().index
+ : getIndexPatterns();
beforeEach(() => {
cleanKibana();
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
goToManageAlertsDetectionRules();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(existingRule, 'rule1');
+ createCustomRuleActivated(getExistingRule(), 'rule1');
reload();
});
@@ -326,7 +328,7 @@ describe('Custom detection rules deletion and edition', () => {
cy.wait('@fetchRuleDetails').then(({ response }) => {
cy.wrap(response!.statusCode).should('eql', 200);
- cy.wrap(response!.body.max_signals).should('eql', existingRule.maxSignals);
+ cy.wrap(response!.body.max_signals).should('eql', getExistingRule().maxSignals);
cy.wrap(response!.body.enabled).should('eql', false);
});
});
@@ -336,25 +338,25 @@ describe('Custom detection rules deletion and edition', () => {
waitForKibana();
// expect define step to populate
- cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.customQuery);
- if (existingRule.index && existingRule.index.length > 0) {
- cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.index.join(''));
+ cy.get(CUSTOM_QUERY_INPUT).should('have.value', getExistingRule().customQuery);
+ if (getExistingRule().index && getExistingRule().index.length > 0) {
+ cy.get(DEFINE_INDEX_INPUT).should('have.text', getExistingRule().index.join(''));
}
goToAboutStepTab();
// expect about step to populate
- cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name);
- cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description);
- cy.get(TAGS_FIELD).should('have.text', existingRule.tags.join(''));
- cy.get(SEVERITY_DROPDOWN).should('have.text', existingRule.severity);
- cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', existingRule.riskScore);
+ cy.get(RULE_NAME_INPUT).invoke('val').should('eql', getExistingRule().name);
+ cy.get(RULE_DESCRIPTION_INPUT).should('have.text', getExistingRule().description);
+ cy.get(TAGS_FIELD).should('have.text', getExistingRule().tags.join(''));
+ cy.get(SEVERITY_DROPDOWN).should('have.text', getExistingRule().severity);
+ cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', getExistingRule().riskScore);
goToScheduleStepTab();
// expect schedule step to populate
- const intervalParts =
- existingRule.interval && existingRule.interval.match(/[0-9]+|[a-zA-Z]+/g);
+ const interval = getExistingRule().interval;
+ const intervalParts = interval != null && interval.match(/[0-9]+|[a-zA-Z]+/g);
if (intervalParts) {
const [amount, unit] = intervalParts;
cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount);
@@ -380,7 +382,7 @@ describe('Custom detection rules deletion and edition', () => {
goToAboutStepTab();
cy.get(TAGS_CLEAR_BUTTON).click({ force: true });
- fillAboutRule(editedRule);
+ fillAboutRule(getEditedRule());
cy.intercept('GET', '/api/detection_engine/rules?id').as('getRule');
@@ -389,30 +391,30 @@ describe('Custom detection rules deletion and edition', () => {
cy.wait('@getRule').then(({ response }) => {
cy.wrap(response!.statusCode).should('eql', 200);
// ensure that editing rule does not modify max_signals
- cy.wrap(response!.body.max_signals).should('eql', existingRule.maxSignals);
+ cy.wrap(response!.body.max_signals).should('eql', getExistingRule().maxSignals);
});
- cy.get(RULE_NAME_HEADER).should('contain', `${editedRule.name}`);
- cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', editedRule.description);
+ cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`);
+ cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getEditedRule().description);
cy.get(ABOUT_DETAILS).within(() => {
- getDetails(SEVERITY_DETAILS).should('have.text', editedRule.severity);
- getDetails(RISK_SCORE_DETAILS).should('have.text', editedRule.riskScore);
+ getDetails(SEVERITY_DETAILS).should('have.text', getEditedRule().severity);
+ getDetails(RISK_SCORE_DETAILS).should('have.text', getEditedRule().riskScore);
getDetails(TAGS_DETAILS).should('have.text', expectedEditedtags);
});
cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
- cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', editedRule.note);
+ cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', getEditedRule().note);
cy.get(DEFINITION_DETAILS).within(() => {
getDetails(INDEX_PATTERNS_DETAILS).should(
'have.text',
expectedEditedIndexPatterns.join('')
);
- getDetails(CUSTOM_QUERY_DETAILS).should('have.text', editedRule.customQuery);
+ getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().customQuery);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
});
- if (editedRule.interval) {
+ if (getEditedRule().interval) {
cy.get(SCHEDULE_DETAILS).within(() => {
- getDetails(RUNS_EVERY_DETAILS).should('have.text', editedRule.interval);
+ getDetails(RUNS_EVERY_DETAILS).should('have.text', getEditedRule().interval);
});
}
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts
index 337e2a8ec5033..677a9b5546494 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts
@@ -6,7 +6,7 @@
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
-import { eqlRule, eqlSequenceRule, indexPatterns } from '../../objects/rule';
+import { getEqlRule, getEqlSequenceRule, getIndexPatterns } from '../../objects/rule';
import {
ALERT_RULE_METHOD,
@@ -78,20 +78,20 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import { ALERTS_URL } from '../../urls/navigation';
describe('Detection rules, EQL', () => {
- const expectedUrls = eqlRule.referenceUrls.join('');
- const expectedFalsePositives = eqlRule.falsePositivesExamples.join('');
- const expectedTags = eqlRule.tags.join('');
- const expectedMitre = formatMitreAttackDescription(eqlRule.mitre);
+ const expectedUrls = getEqlRule().referenceUrls.join('');
+ const expectedFalsePositives = getEqlRule().falsePositivesExamples.join('');
+ const expectedTags = getEqlRule().tags.join('');
+ const expectedMitre = formatMitreAttackDescription(getEqlRule().mitre);
const expectedNumberOfRules = 1;
const expectedNumberOfAlerts = 7;
beforeEach(() => {
cleanKibana();
- createTimeline(eqlRule.timeline).then((response) => {
+ createTimeline(getEqlRule().timeline).then((response) => {
cy.wrap({
- ...eqlRule,
+ ...getEqlRule(),
timeline: {
- ...eqlRule.timeline,
+ ...getEqlRule().timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
@@ -148,7 +148,7 @@ describe('Detection rules, EQL', () => {
cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
cy.get(DEFINITION_DETAILS).within(() => {
- getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join(''));
+ getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join(''));
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Event Correlation');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
@@ -182,11 +182,11 @@ describe('Detection rules, sequence EQL', () => {
beforeEach(() => {
cleanKibana();
- createTimeline(eqlSequenceRule.timeline).then((response) => {
+ createTimeline(getEqlSequenceRule().timeline).then((response) => {
cy.wrap({
- ...eqlSequenceRule,
+ ...getEqlSequenceRule(),
timeline: {
- ...eqlSequenceRule.timeline,
+ ...getEqlSequenceRule().timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts
index 1de636010f967..03086810a8435 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { expectedExportedRule, newRule } from '../../objects/rule';
+import { expectedExportedRule, getNewRule } from '../../objects/rule';
import {
goToManageAlertsDetectionRules,
waitForAlertsIndexToBeCreated,
@@ -28,7 +28,7 @@ describe('Export rules', () => {
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRule(newRule).as('ruleResponse');
+ createCustomRule(getNewRule()).as('ruleResponse');
});
it('Exports a custom rule', function () {
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts
index e1268c52f75d4..07b40df53e2d5 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts
@@ -6,7 +6,7 @@
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
-import { indexPatterns, newThreatIndicatorRule } from '../../objects/rule';
+import { getIndexPatterns, getNewThreatIndicatorRule } from '../../objects/rule';
import {
ALERT_RULE_METHOD,
@@ -109,10 +109,10 @@ import { ALERTS_URL, RULE_CREATION } from '../../urls/navigation';
describe('indicator match', () => {
describe('Detection rules, Indicator Match', () => {
- const expectedUrls = newThreatIndicatorRule.referenceUrls.join('');
- const expectedFalsePositives = newThreatIndicatorRule.falsePositivesExamples.join('');
- const expectedTags = newThreatIndicatorRule.tags.join('');
- const expectedMitre = formatMitreAttackDescription(newThreatIndicatorRule.mitre);
+ const expectedUrls = getNewThreatIndicatorRule().referenceUrls.join('');
+ const expectedFalsePositives = getNewThreatIndicatorRule().falsePositivesExamples.join('');
+ const expectedTags = getNewThreatIndicatorRule().tags.join('');
+ const expectedMitre = formatMitreAttackDescription(getNewThreatIndicatorRule().mitre);
const expectedNumberOfRules = 1;
const expectedNumberOfAlerts = 1;
@@ -134,12 +134,12 @@ describe('indicator match', () => {
describe('Index patterns', () => {
it('Contains a predefined index pattern', () => {
- getIndicatorIndex().should('have.text', indexPatterns.join(''));
+ getIndicatorIndex().should('have.text', getIndexPatterns().join(''));
});
it('Does NOT show invalidation text on initial page load if indicator index pattern is filled out', () => {
getIndicatorIndicatorIndex().type(
- `${newThreatIndicatorRule.indicatorIndexPattern}{enter}`
+ `${getNewThreatIndicatorRule().indicatorIndexPattern}{enter}`
);
getDefineContinueButton().click();
getIndexPatternInvalidationText().should('not.exist');
@@ -148,7 +148,7 @@ describe('indicator match', () => {
it('Shows invalidation text when you try to continue without filling it out', () => {
getIndexPatternClearButton().click();
getIndicatorIndicatorIndex().type(
- `${newThreatIndicatorRule.indicatorIndexPattern}{enter}`
+ `${getNewThreatIndicatorRule().indicatorIndexPattern}{enter}`
);
getDefineContinueButton().click();
getIndexPatternInvalidationText().should('exist');
@@ -195,8 +195,8 @@ describe('indicator match', () => {
describe('Indicator mapping', () => {
beforeEach(() => {
fillIndexAndIndicatorIndexPattern(
- newThreatIndicatorRule.index,
- newThreatIndicatorRule.indicatorIndexPattern
+ getNewThreatIndicatorRule().index,
+ getNewThreatIndicatorRule().indicatorIndexPattern
);
});
@@ -221,8 +221,8 @@ describe('indicator match', () => {
it('Does NOT show invalidation text when there is a valid "index field" and a valid "indicator index field"', () => {
fillIndicatorMatchRow({
- indexField: newThreatIndicatorRule.indicatorMappingField,
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
});
getDefineContinueButton().click();
getIndicatorInvalidationText().should('not.exist');
@@ -231,7 +231,7 @@ describe('indicator match', () => {
it('Shows invalidation text when there is an invalid "index field" and a valid "indicator index field"', () => {
fillIndicatorMatchRow({
indexField: 'non-existent-value',
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
validColumns: 'indicatorField',
});
getDefineContinueButton().click();
@@ -240,7 +240,7 @@ describe('indicator match', () => {
it('Shows invalidation text when there is a valid "index field" and an invalid "indicator index field"', () => {
fillIndicatorMatchRow({
- indexField: newThreatIndicatorRule.indicatorMappingField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: 'non-existent-value',
validColumns: 'indexField',
});
@@ -250,21 +250,21 @@ describe('indicator match', () => {
it('Deletes the first row when you have two rows. Both rows valid rows of "index fields" and valid "indicator index fields". The second row should become the first row', () => {
fillIndicatorMatchRow({
- indexField: newThreatIndicatorRule.indicatorMappingField,
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
indexField: 'agent.name',
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
validColumns: 'indicatorField',
});
getIndicatorDeleteButton().click();
getIndicatorIndexComboField().should('have.text', 'agent.name');
getIndicatorMappingComboField().should(
'have.text',
- newThreatIndicatorRule.indicatorIndexField
+ getNewThreatIndicatorRule().indicatorIndexField
);
getIndicatorIndexComboField(2).should('not.exist');
getIndicatorMappingComboField(2).should('not.exist');
@@ -272,14 +272,14 @@ describe('indicator match', () => {
it('Deletes the first row when you have two rows. Both rows have valid "index fields" and invalid "indicator index fields". The second row should become the first row', () => {
fillIndicatorMatchRow({
- indexField: newThreatIndicatorRule.indicatorMappingField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: 'non-existent-value',
validColumns: 'indexField',
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
- indexField: newThreatIndicatorRule.indicatorMappingField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: 'second-non-existent-value',
validColumns: 'indexField',
});
@@ -292,14 +292,14 @@ describe('indicator match', () => {
it('Deletes the first row when you have two rows. Both rows have valid "indicator index fields" and invalid "index fields". The second row should become the first row', () => {
fillIndicatorMatchRow({
indexField: 'non-existent-value',
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
validColumns: 'indicatorField',
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
indexField: 'second-non-existent-value',
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
validColumns: 'indicatorField',
});
getIndicatorDeleteButton().click();
@@ -310,8 +310,8 @@ describe('indicator match', () => {
it('Deletes the first row of data but not the UI elements and the text defaults back to the placeholder of Search', () => {
fillIndicatorMatchRow({
- indexField: newThreatIndicatorRule.indicatorMappingField,
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
});
getIndicatorDeleteButton().click();
getIndicatorIndexComboField().should('text', 'Search');
@@ -322,8 +322,8 @@ describe('indicator match', () => {
it('Deletes the second row when you have three rows. The first row is valid data, the second row is invalid data, and the third row is valid data. Third row should shift up correctly', () => {
fillIndicatorMatchRow({
- indexField: newThreatIndicatorRule.indicatorMappingField,
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
@@ -335,25 +335,25 @@ describe('indicator match', () => {
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 3,
- indexField: newThreatIndicatorRule.indicatorMappingField,
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
});
getIndicatorDeleteButton(2).click();
getIndicatorIndexComboField(1).should(
'text',
- newThreatIndicatorRule.indicatorMappingField
+ getNewThreatIndicatorRule().indicatorMappingField
);
getIndicatorMappingComboField(1).should(
'text',
- newThreatIndicatorRule.indicatorIndexField
+ getNewThreatIndicatorRule().indicatorIndexField
);
getIndicatorIndexComboField(2).should(
'text',
- newThreatIndicatorRule.indicatorMappingField
+ getNewThreatIndicatorRule().indicatorMappingField
);
getIndicatorMappingComboField(2).should(
'text',
- newThreatIndicatorRule.indicatorIndexField
+ getNewThreatIndicatorRule().indicatorIndexField
);
getIndicatorIndexComboField(3).should('not.exist');
getIndicatorMappingComboField(3).should('not.exist');
@@ -368,17 +368,17 @@ describe('indicator match', () => {
getIndicatorOrButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
- indexField: newThreatIndicatorRule.indicatorMappingField,
- indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
+ indexField: getNewThreatIndicatorRule().indicatorMappingField,
+ indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
});
getIndicatorDeleteButton().click();
getIndicatorIndexComboField().should(
'text',
- newThreatIndicatorRule.indicatorMappingField
+ getNewThreatIndicatorRule().indicatorMappingField
);
getIndicatorMappingComboField().should(
'text',
- newThreatIndicatorRule.indicatorIndexField
+ getNewThreatIndicatorRule().indicatorIndexField
);
getIndicatorIndexComboField(2).should('not.exist');
getIndicatorMappingComboField(2).should('not.exist');
@@ -399,9 +399,9 @@ describe('indicator match', () => {
waitForRulesTableToBeLoaded();
goToCreateNewRule();
selectIndicatorMatchType();
- fillDefineIndicatorMatchRuleAndContinue(newThreatIndicatorRule);
- fillAboutRuleAndContinue(newThreatIndicatorRule);
- fillScheduleRuleAndContinue(newThreatIndicatorRule);
+ fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule());
+ fillAboutRuleAndContinue(getNewThreatIndicatorRule());
+ fillScheduleRuleAndContinue(getNewThreatIndicatorRule());
createAndActivateRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
@@ -417,18 +417,18 @@ describe('indicator match', () => {
cy.get(RULES_TABLE).then(($table) => {
cy.wrap($table.find(RULES_ROW).length).should('eql', 1);
});
- cy.get(RULE_NAME).should('have.text', newThreatIndicatorRule.name);
- cy.get(RISK_SCORE).should('have.text', newThreatIndicatorRule.riskScore);
- cy.get(SEVERITY).should('have.text', newThreatIndicatorRule.severity);
+ cy.get(RULE_NAME).should('have.text', getNewThreatIndicatorRule().name);
+ cy.get(RISK_SCORE).should('have.text', getNewThreatIndicatorRule().riskScore);
+ cy.get(SEVERITY).should('have.text', getNewThreatIndicatorRule().severity);
cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true');
goToRuleDetails();
- cy.get(RULE_NAME_HEADER).should('contain', `${newThreatIndicatorRule.name}`);
- cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newThreatIndicatorRule.description);
+ cy.get(RULE_NAME_HEADER).should('contain', `${getNewThreatIndicatorRule().name}`);
+ cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getNewThreatIndicatorRule().description);
cy.get(ABOUT_DETAILS).within(() => {
- getDetails(SEVERITY_DETAILS).should('have.text', newThreatIndicatorRule.severity);
- getDetails(RISK_SCORE_DETAILS).should('have.text', newThreatIndicatorRule.riskScore);
+ getDetails(SEVERITY_DETAILS).should('have.text', getNewThreatIndicatorRule().severity);
+ getDetails(RISK_SCORE_DETAILS).should('have.text', getNewThreatIndicatorRule().riskScore);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@@ -444,18 +444,20 @@ describe('indicator match', () => {
cy.get(DEFINITION_DETAILS).within(() => {
getDetails(INDEX_PATTERNS_DETAILS).should(
'have.text',
- newThreatIndicatorRule.index.join('')
+ getNewThreatIndicatorRule().index.join('')
);
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', '*:*');
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Indicator Match');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
getDetails(INDICATOR_INDEX_PATTERNS).should(
'have.text',
- newThreatIndicatorRule.indicatorIndexPattern.join('')
+ getNewThreatIndicatorRule().indicatorIndexPattern.join('')
);
getDetails(INDICATOR_MAPPING).should(
'have.text',
- `${newThreatIndicatorRule.indicatorMappingField} MATCHES ${newThreatIndicatorRule.indicatorIndexField}`
+ `${getNewThreatIndicatorRule().indicatorMappingField} MATCHES ${
+ getNewThreatIndicatorRule().indicatorIndexField
+ }`
);
getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*');
});
@@ -463,11 +465,15 @@ describe('indicator match', () => {
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
- `${newThreatIndicatorRule.runsEvery.interval}${newThreatIndicatorRule.runsEvery.type}`
+ `${getNewThreatIndicatorRule().runsEvery.interval}${
+ getNewThreatIndicatorRule().runsEvery.type
+ }`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
- `${newThreatIndicatorRule.lookBack.interval}${newThreatIndicatorRule.lookBack.type}`
+ `${getNewThreatIndicatorRule().lookBack.interval}${
+ getNewThreatIndicatorRule().lookBack.type
+ }`
);
});
@@ -475,13 +481,15 @@ describe('indicator match', () => {
waitForAlertsToPopulate();
cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts);
- cy.get(ALERT_RULE_NAME).first().should('have.text', newThreatIndicatorRule.name);
+ cy.get(ALERT_RULE_NAME).first().should('have.text', getNewThreatIndicatorRule().name);
cy.get(ALERT_RULE_VERSION).first().should('have.text', '1');
cy.get(ALERT_RULE_METHOD).first().should('have.text', 'threat_match');
cy.get(ALERT_RULE_SEVERITY)
.first()
- .should('have.text', newThreatIndicatorRule.severity.toLowerCase());
- cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', newThreatIndicatorRule.riskScore);
+ .should('have.text', getNewThreatIndicatorRule().severity.toLowerCase());
+ cy.get(ALERT_RULE_RISK_SCORE)
+ .first()
+ .should('have.text', getNewThreatIndicatorRule().riskScore);
});
it('Investigate alert in timeline', () => {
@@ -492,7 +500,7 @@ describe('indicator match', () => {
loadPrepackagedTimelineTemplates();
goToManageAlertsDetectionRules();
- createCustomIndicatorRule(newThreatIndicatorRule);
+ createCustomIndicatorRule(getNewThreatIndicatorRule());
reload();
goToRuleDetails();
@@ -502,13 +510,25 @@ describe('indicator match', () => {
cy.get(PROVIDER_BADGE).should('have.length', 3);
cy.get(PROVIDER_BADGE).should(
'have.text',
- `threat.indicator.matched.atomic: "${newThreatIndicatorRule.atomic}"threat.indicator.matched.type: "indicator_match_rule"threat.indicator.matched.field: "${newThreatIndicatorRule.indicatorMappingField}"`
+ `threat.indicator.matched.atomic: "${
+ getNewThreatIndicatorRule().atomic
+ }"threat.indicator.matched.type: "indicator_match_rule"threat.indicator.matched.field: "${
+ getNewThreatIndicatorRule().indicatorMappingField
+ }"`
);
cy.readFile(threatIndicatorPath).then((threatIndicator) => {
cy.get(INDICATOR_MATCH_ROW_RENDER).should(
'have.text',
- `threat.indicator.matched.field${newThreatIndicatorRule.indicatorMappingField}${accessibilityText}matched${newThreatIndicatorRule.indicatorMappingField}${newThreatIndicatorRule.atomic}${accessibilityText}threat.indicator.matched.typeindicator_match_rule${accessibilityText}fromthreat.indicator.event.dataset${threatIndicator.value.source.event.dataset}${accessibilityText}:threat.indicator.event.reference${threatIndicator.value.source.event.reference}(opens in a new tab or window)${accessibilityText}`
+ `threat.indicator.matched.field${
+ getNewThreatIndicatorRule().indicatorMappingField
+ }${accessibilityText}matched${getNewThreatIndicatorRule().indicatorMappingField}${
+ getNewThreatIndicatorRule().atomic
+ }${accessibilityText}threat.indicator.matched.typeindicator_match_rule${accessibilityText}fromthreat.indicator.event.dataset${
+ threatIndicator.value.source.event.dataset
+ }${accessibilityText}:threat.indicator.event.reference${
+ threatIndicator.value.source.event.reference
+ }(opens in a new tab or window)${accessibilityText}`
);
});
});
@@ -519,7 +539,7 @@ describe('indicator match', () => {
cleanKibana();
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
goToManageAlertsDetectionRules();
- createCustomIndicatorRule(newThreatIndicatorRule);
+ createCustomIndicatorRule(getNewThreatIndicatorRule());
reload();
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/links.spec.ts
index fdc4bce677f74..85eb68a6cdfa9 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/links.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/links.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import { RULES_MONIROTING_TABLE, RULE_NAME } from '../../screens/alerts_detection_rules';
import { goToManageAlertsDetectionRules, waitForAlertsIndexToBeCreated } from '../../tasks/alerts';
import { createCustomRuleActivated } from '../../tasks/api_calls/rules';
@@ -19,7 +19,7 @@ describe('Rules talbes links', () => {
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
goToManageAlertsDetectionRules();
waitForAlertsIndexToBeCreated();
- createCustomRuleActivated(newRule, 'rule1');
+ createCustomRuleActivated(getNewRule(), 'rule1');
reload();
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/machine_learning_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/machine_learning_rule.spec.ts
index 2d869b314b67c..e66f8f55be986 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/machine_learning_rule.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/machine_learning_rule.spec.ts
@@ -6,7 +6,7 @@
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
-import { machineLearningRule } from '../../objects/rule';
+import { getMachineLearningRule } from '../../objects/rule';
import {
CUSTOM_RULES_BTN,
@@ -65,10 +65,10 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import { ALERTS_URL } from '../../urls/navigation';
describe('Detection rules, machine learning', () => {
- const expectedUrls = machineLearningRule.referenceUrls.join('');
- const expectedFalsePositives = machineLearningRule.falsePositivesExamples.join('');
- const expectedTags = machineLearningRule.tags.join('');
- const expectedMitre = formatMitreAttackDescription(machineLearningRule.mitre);
+ const expectedUrls = getMachineLearningRule().referenceUrls.join('');
+ const expectedFalsePositives = getMachineLearningRule().falsePositivesExamples.join('');
+ const expectedTags = getMachineLearningRule().tags.join('');
+ const expectedMitre = formatMitreAttackDescription(getMachineLearningRule().mitre);
const expectedNumberOfRules = 1;
beforeEach(() => {
@@ -83,9 +83,9 @@ describe('Detection rules, machine learning', () => {
waitForRulesTableToBeLoaded();
goToCreateNewRule();
selectMachineLearningRuleType();
- fillDefineMachineLearningRuleAndContinue(machineLearningRule);
- fillAboutRuleAndContinue(machineLearningRule);
- fillScheduleRuleAndContinue(machineLearningRule);
+ fillDefineMachineLearningRuleAndContinue(getMachineLearningRule());
+ fillAboutRuleAndContinue(getMachineLearningRule());
+ fillScheduleRuleAndContinue(getMachineLearningRule());
createAndActivateRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
@@ -101,18 +101,18 @@ describe('Detection rules, machine learning', () => {
cy.get(RULES_TABLE).then(($table) => {
cy.wrap($table.find(RULES_ROW).length).should('eql', 1);
});
- cy.get(RULE_NAME).should('have.text', machineLearningRule.name);
- cy.get(RISK_SCORE).should('have.text', machineLearningRule.riskScore);
- cy.get(SEVERITY).should('have.text', machineLearningRule.severity);
+ cy.get(RULE_NAME).should('have.text', getMachineLearningRule().name);
+ cy.get(RISK_SCORE).should('have.text', getMachineLearningRule().riskScore);
+ cy.get(SEVERITY).should('have.text', getMachineLearningRule().severity);
cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true');
goToRuleDetails();
- cy.get(RULE_NAME_HEADER).should('contain', `${machineLearningRule.name}`);
- cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', machineLearningRule.description);
+ cy.get(RULE_NAME_HEADER).should('contain', `${getMachineLearningRule().name}`);
+ cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getMachineLearningRule().description);
cy.get(ABOUT_DETAILS).within(() => {
- getDetails(SEVERITY_DETAILS).should('have.text', machineLearningRule.severity);
- getDetails(RISK_SCORE_DETAILS).should('have.text', machineLearningRule.riskScore);
+ getDetails(SEVERITY_DETAILS).should('have.text', getMachineLearningRule().severity);
+ getDetails(RISK_SCORE_DETAILS).should('have.text', getMachineLearningRule().riskScore);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@@ -125,11 +125,11 @@ describe('Detection rules, machine learning', () => {
cy.get(DEFINITION_DETAILS).within(() => {
getDetails(ANOMALY_SCORE_DETAILS).should(
'have.text',
- machineLearningRule.anomalyScoreThreshold
+ getMachineLearningRule().anomalyScoreThreshold
);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Machine Learning');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
- machineLearningRule.machineLearningJobs.forEach((machineLearningJob, jobIndex) => {
+ getMachineLearningRule().machineLearningJobs.forEach((machineLearningJob, jobIndex) => {
cy.get(MACHINE_LEARNING_JOB_STATUS).eq(jobIndex).should('have.text', 'Stopped');
cy.get(MACHINE_LEARNING_JOB_ID).eq(jobIndex).should('have.text', machineLearningJob);
});
@@ -137,11 +137,11 @@ describe('Detection rules, machine learning', () => {
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
- `${machineLearningRule.runsEvery.interval}${machineLearningRule.runsEvery.type}`
+ `${getMachineLearningRule().runsEvery.interval}${getMachineLearningRule().runsEvery.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
- `${machineLearningRule.lookBack.interval}${machineLearningRule.lookBack.type}`
+ `${getMachineLearningRule().lookBack.interval}${getMachineLearningRule().lookBack.type}`
);
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/override.spec.ts
index a791cc293c1f0..24a56dd563e17 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/override.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/override.spec.ts
@@ -7,9 +7,9 @@
import { formatMitreAttackDescription } from '../../helpers/rules';
import {
- indexPatterns,
- newOverrideRule,
- severitiesOverride,
+ getIndexPatterns,
+ getNewOverrideRule,
+ getSeveritiesOverride,
OverrideRule,
} from '../../objects/rule';
@@ -89,18 +89,18 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import { ALERTS_URL } from '../../urls/navigation';
describe('Detection rules, override', () => {
- const expectedUrls = newOverrideRule.referenceUrls.join('');
- const expectedFalsePositives = newOverrideRule.falsePositivesExamples.join('');
- const expectedTags = newOverrideRule.tags.join('');
- const expectedMitre = formatMitreAttackDescription(newOverrideRule.mitre);
+ const expectedUrls = getNewOverrideRule().referenceUrls.join('');
+ const expectedFalsePositives = getNewOverrideRule().falsePositivesExamples.join('');
+ const expectedTags = getNewOverrideRule().tags.join('');
+ const expectedMitre = formatMitreAttackDescription(getNewOverrideRule().mitre);
beforeEach(() => {
cleanKibana();
- createTimeline(newOverrideRule.timeline).then((response) => {
+ createTimeline(getNewOverrideRule().timeline).then((response) => {
cy.wrap({
- ...newOverrideRule,
+ ...getNewOverrideRule(),
timeline: {
- ...newOverrideRule.timeline,
+ ...getNewOverrideRule().timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
@@ -167,7 +167,7 @@ describe('Detection rules, override', () => {
.eq(severityOverrideIndex + i)
.should(
'have.text',
- `${severity.sourceField}:${severity.sourceValue}${severitiesOverride[i]}`
+ `${severity.sourceField}:${severity.sourceValue}${getSeveritiesOverride()[i]}`
);
});
});
@@ -175,7 +175,7 @@ describe('Detection rules, override', () => {
cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
cy.get(DEFINITION_DETAILS).within(() => {
- getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join(''));
+ getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join(''));
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts
index 7d42ea533a9ae..ef3d3a82d40bd 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts
@@ -39,7 +39,12 @@ import { DEFAULT_RULE_REFRESH_INTERVAL_VALUE } from '../../../common/constants';
import { ALERTS_URL } from '../../urls/navigation';
import { createCustomRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
-import { existingRule, newOverrideRule, newRule, newThresholdRule } from '../../objects/rule';
+import {
+ getExistingRule,
+ getNewOverrideRule,
+ getNewRule,
+ getNewThresholdRule,
+} from '../../objects/rule';
describe('Alerts detection rules', () => {
beforeEach(() => {
@@ -47,10 +52,10 @@ describe('Alerts detection rules', () => {
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlertsIndexToBeCreated();
- createCustomRule(newRule, '1');
- createCustomRule(existingRule, '2');
- createCustomRule(newOverrideRule, '3');
- createCustomRule(newThresholdRule, '4');
+ createCustomRule(getNewRule(), '1');
+ createCustomRule(getExistingRule(), '2');
+ createCustomRule(getNewOverrideRule(), '3');
+ createCustomRule(getNewThresholdRule(), '4');
});
it('Sorts by activated rules', () => {
@@ -90,8 +95,8 @@ describe('Alerts detection rules', () => {
});
it('Pagination updates page number and results', () => {
- createCustomRule({ ...newRule, name: 'Test a rule' }, '5');
- createCustomRule({ ...newRule, name: 'Not same as first rule' }, '6');
+ createCustomRule({ ...getNewRule(), name: 'Test a rule' }, '5');
+ createCustomRule({ ...getNewRule(), name: 'Not same as first rule' }, '6');
goToManageAlertsDetectionRules();
waitForRulesTableToBeLoaded();
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts
index ce00c9b40aead..dba12fb4ab95c 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts
@@ -6,7 +6,12 @@
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
-import { indexPatterns, newRule, newThresholdRule, ThresholdRule } from '../../objects/rule';
+import {
+ getIndexPatterns,
+ getNewRule,
+ getNewThresholdRule,
+ ThresholdRule,
+} from '../../objects/rule';
import {
ALERT_RULE_METHOD,
@@ -84,16 +89,16 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import { ALERTS_URL } from '../../urls/navigation';
describe('Detection rules, threshold', () => {
- const expectedUrls = newThresholdRule.referenceUrls.join('');
- const expectedFalsePositives = newThresholdRule.falsePositivesExamples.join('');
- const expectedTags = newThresholdRule.tags.join('');
- const expectedMitre = formatMitreAttackDescription(newThresholdRule.mitre);
-
- const rule = { ...newThresholdRule };
+ let rule = getNewThresholdRule();
+ const expectedUrls = getNewThresholdRule().referenceUrls.join('');
+ const expectedFalsePositives = getNewThresholdRule().falsePositivesExamples.join('');
+ const expectedTags = getNewThresholdRule().tags.join('');
+ const expectedMitre = formatMitreAttackDescription(getNewThresholdRule().mitre);
beforeEach(() => {
+ rule = getNewThresholdRule();
cleanKibana();
- createTimeline(newThresholdRule.timeline).then((response) => {
+ createTimeline(getNewThresholdRule().timeline).then((response) => {
rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId;
});
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
@@ -149,7 +154,7 @@ describe('Detection rules, threshold', () => {
cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
cy.get(DEFINITION_DETAILS).within(() => {
- getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join(''));
+ getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join(''));
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.customQuery);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Threshold');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
@@ -181,15 +186,14 @@ describe('Detection rules, threshold', () => {
});
it('Preview results of keyword using "host.name"', () => {
- const previewRule: ThresholdRule = { ...newThresholdRule };
- previewRule.index = [...previewRule.index, '.siem-signals*'];
+ rule.index = [...rule.index, '.siem-signals*'];
- createCustomRuleActivated(newRule);
+ createCustomRuleActivated(getNewRule());
goToManageAlertsDetectionRules();
waitForRulesTableToBeLoaded();
goToCreateNewRule();
selectThresholdRuleType();
- fillDefineThresholdRule(previewRule);
+ fillDefineThresholdRule(rule);
previewResults();
cy.get(PREVIEW_HEADER_SUBTITLE).should('have.text', '3 unique hits');
@@ -197,13 +201,13 @@ describe('Detection rules, threshold', () => {
it('Preview results of "ip" using "source.ip"', () => {
const previewRule: ThresholdRule = {
- ...newThresholdRule,
+ ...rule,
thresholdField: 'source.ip',
threshold: '1',
};
previewRule.index = [...previewRule.index, '.siem-signals*'];
- createCustomRuleActivated(newRule);
+ createCustomRuleActivated(getNewRule());
goToManageAlertsDetectionRules();
waitForRulesTableToBeLoaded();
goToCreateNewRule();
diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_modal.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_modal.spec.ts
index a4b929f7d8e1d..7eedc99652f80 100644
--- a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_modal.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_modal.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { newRule } from '../../objects/rule';
+import { getNewRule } from '../../objects/rule';
import { RULE_STATUS } from '../../screens/create_new_rule';
@@ -44,7 +44,7 @@ describe('Exceptions modal', () => {
cleanKibana();
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsIndexToBeCreated();
- createCustomRule(newRule);
+ createCustomRule(getNewRule());
goToManageAlertsDetectionRules();
goToRuleDetails();
diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_table.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_table.spec.ts
index 83277075b35cc..051ebbb9643f6 100644
--- a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_table.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_table.spec.ts
@@ -5,8 +5,12 @@
* 2.0.
*/
-import { exception, exceptionList, expectedExportedExceptionList } from '../../objects/exception';
-import { newRule } from '../../objects/rule';
+import {
+ getException,
+ getExceptionList,
+ expectedExportedExceptionList,
+} from '../../objects/exception';
+import { getNewRule } from '../../objects/rule';
import { RULE_STATUS } from '../../screens/create_new_rule';
@@ -46,7 +50,7 @@ describe('Exceptions Table', () => {
cleanKibana();
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsIndexToBeCreated();
- createCustomRule(newRule);
+ createCustomRule(getNewRule());
goToManageAlertsDetectionRules();
goToRuleDetails();
@@ -56,11 +60,11 @@ describe('Exceptions Table', () => {
// Add a detections exception list
goToExceptionsTab();
- addsExceptionFromRuleSettings(exception);
+ addsExceptionFromRuleSettings(getException());
waitForTheRuleToBeExecuted();
// Create exception list not used by any rules
- createExceptionList(exceptionList).as('exceptionListResponse');
+ createExceptionList(getExceptionList()).as('exceptionListResponse');
goBackToAllRulesTable();
waitForRulesTableToBeLoaded();
diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts
index 4918de7488ddd..8a683aacd5f66 100644
--- a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts
@@ -5,8 +5,8 @@
* 2.0.
*/
-import { exception } from '../../objects/exception';
-import { newRule } from '../../objects/rule';
+import { getException } from '../../objects/exception';
+import { getNewRule } from '../../objects/rule';
import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../../screens/alerts';
import { RULE_STATUS } from '../../screens/create_new_rule';
@@ -43,7 +43,7 @@ describe('From alert', () => {
cleanKibana();
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsIndexToBeCreated();
- createCustomRule(newRule, 'rule_testing', '10s');
+ createCustomRule(getNewRule(), 'rule_testing', '10s');
goToManageAlertsDetectionRules();
goToRuleDetails();
@@ -66,7 +66,7 @@ describe('From alert', () => {
it('Creates an exception and deletes it', () => {
addExceptionFromFirstAlert();
- addsException(exception);
+ addsException(getException());
esArchiverLoad('auditbeat_for_exceptions2');
cy.get(ALERTS_COUNT).should('exist');
diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts
index ea8988456d8b3..8fa0050a36521 100644
--- a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts
@@ -5,8 +5,8 @@
* 2.0.
*/
-import { exception } from '../../objects/exception';
-import { newRule } from '../../objects/rule';
+import { getException } from '../../objects/exception';
+import { getNewRule } from '../../objects/rule';
import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../../screens/alerts';
import { RULE_STATUS } from '../../screens/create_new_rule';
@@ -41,7 +41,7 @@ describe('From rule', () => {
cleanKibana();
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
waitForAlertsIndexToBeCreated();
- createCustomRule(newRule, 'rule_testing', '10s');
+ createCustomRule(getNewRule(), 'rule_testing', '10s');
goToManageAlertsDetectionRules();
goToRuleDetails();
@@ -64,7 +64,7 @@ describe('From rule', () => {
it('Creates an exception and deletes it', () => {
goToExceptionsTab();
- addsExceptionFromRuleSettings(exception);
+ addsExceptionFromRuleSettings(getException());
esArchiverLoad('auditbeat_for_exceptions2');
waitForTheRuleToBeExecuted();
goToAlertsTab();
diff --git a/x-pack/plugins/security_solution/cypress/integration/header/search_bar.spec.ts b/x-pack/plugins/security_solution/cypress/integration/header/search_bar.spec.ts
index d7bef9d67df2f..c02c2bd9ec139 100644
--- a/x-pack/plugins/security_solution/cypress/integration/header/search_bar.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/header/search_bar.spec.ts
@@ -8,7 +8,7 @@
import { loginAndWaitForPage } from '../../tasks/login';
import { openAddFilterPopover, fillAddFilterForm } from '../../tasks/search_bar';
import { GLOBAL_SEARCH_BAR_FILTER_ITEM } from '../../screens/search_bar';
-import { hostIpFilter } from '../../objects/filter';
+import { getHostIpFilter } from '../../objects/filter';
import { HOSTS_URL } from '../../urls/navigation';
import { waitForAllHostsToBeLoaded } from '../../tasks/hosts/all_hosts';
@@ -23,11 +23,11 @@ describe('SearchBar', () => {
it('adds correctly a filter to the global search bar', () => {
openAddFilterPopover();
- fillAddFilterForm(hostIpFilter);
+ fillAddFilterForm(getHostIpFilter());
cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should(
'have.text',
- `${hostIpFilter.key}: ${hostIpFilter.value}`
+ `${getHostIpFilter().key}: ${getHostIpFilter().value}`
);
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/overview/overview.spec.ts b/x-pack/plugins/security_solution/cypress/integration/overview/overview.spec.ts
index 3ff036fa0107f..ca9f83183ab10 100644
--- a/x-pack/plugins/security_solution/cypress/integration/overview/overview.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/overview/overview.spec.ts
@@ -16,7 +16,7 @@ import overviewFixture from '../../fixtures/overview_search_strategy.json';
import emptyInstance from '../../fixtures/empty_instance.json';
import { cleanKibana } from '../../tasks/common';
import { createTimeline, favoriteTimeline } from '../../tasks/api_calls/timelines';
-import { timeline } from '../../objects/timeline';
+import { getTimeline } from '../../objects/timeline';
describe('Overview Page', () => {
before(() => {
@@ -53,7 +53,7 @@ describe('Overview Page', () => {
describe('Favorite Timelines', () => {
it('should appear on overview page', () => {
- createTimeline(timeline)
+ createTimeline(getTimeline())
.then((response) => response.body.data.persistTimeline.timeline.savedObjectId)
.then((timelineId: string) => {
favoriteTimeline({ timelineId, timelineType: 'default' }).then(() => {
@@ -61,7 +61,7 @@ describe('Overview Page', () => {
loginAndWaitForPage(OVERVIEW_URL);
cy.get('[data-test-subj="overview-recent-timelines"]').should(
'contain',
- timeline.title
+ getTimeline().title
);
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_templates/creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_templates/creation.spec.ts
index e2c1d7eef38c3..3930088f8bfdd 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timeline_templates/creation.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timeline_templates/creation.spec.ts
@@ -5,14 +5,13 @@
* 2.0.
*/
-import { timeline } from '../../objects/timeline';
+import { getTimeline } from '../../objects/timeline';
import {
FAVORITE_TIMELINE,
LOCKED_ICON,
NOTES,
NOTES_TAB_BUTTON,
- // NOTES_COUNT,
NOTES_TEXT_AREA,
PIN_EVENT,
TIMELINE_DESCRIPTION,
@@ -61,7 +60,7 @@ describe('Timeline Templates', () => {
openTimelineUsingToggle();
createNewTimelineTemplate();
populateTimeline();
- addFilter(timeline.filter);
+ addFilter(getTimeline().filter);
cy.get(PIN_EVENT).should(
'have.attr',
'aria-label',
@@ -69,21 +68,21 @@ describe('Timeline Templates', () => {
);
cy.get(LOCKED_ICON).should('be.visible');
- addNameToTimeline(timeline.title);
+ addNameToTimeline(getTimeline().title);
cy.wait('@timeline').then(({ response }) => {
const timelineId = response!.body.data.persistTimeline.timeline.savedObjectId;
- addDescriptionToTimeline(timeline.description);
- addNotesToTimeline(timeline.notes);
+ addDescriptionToTimeline(getTimeline().description);
+ addNotesToTimeline(getTimeline().notes);
markAsFavorite();
waitForTimelineChanges();
createNewTimelineTemplate();
closeTimeline();
openTimelineTemplateFromSettings(timelineId);
- cy.contains(timeline.title).should('exist');
- cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description);
+ cy.contains(getTimeline().title).should('exist');
+ cy.get(TIMELINES_DESCRIPTION).first().should('have.text', getTimeline().description);
cy.get(TIMELINES_PINNED_EVENT_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_NOTES_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_FAVORITE).first().should('exist');
@@ -91,30 +90,30 @@ describe('Timeline Templates', () => {
openTimeline(timelineId);
cy.get(FAVORITE_TIMELINE).should('exist');
- cy.get(TIMELINE_TITLE).should('have.text', timeline.title);
- cy.get(TIMELINE_DESCRIPTION).should('have.text', timeline.description);
- cy.get(TIMELINE_QUERY).should('have.text', timeline.query);
+ cy.get(TIMELINE_TITLE).should('have.text', getTimeline().title);
+ cy.get(TIMELINE_DESCRIPTION).should('have.text', getTimeline().description);
+ cy.get(TIMELINE_QUERY).should('have.text', getTimeline().query);
// Comments this assertion until we agreed what to do with the filters.
// cy.get(TIMELINE_FILTER(timeline.filter)).should('exist');
// cy.get(NOTES_COUNT).should('have.text', '1');
cy.get(NOTES_TAB_BUTTON).click();
cy.get(NOTES_TEXT_AREA).should('exist');
- cy.get(NOTES).should('have.text', timeline.notes);
+ cy.get(NOTES).should('have.text', getTimeline().notes);
});
});
it('Create template from timeline', () => {
waitForTimelinesPanelToBeLoaded();
- createTimeline(timeline).then(() => {
+ createTimeline(getTimeline()).then(() => {
expandEventAction();
clickingOnCreateTemplateFromTimelineBtn();
cy.wait('@timeline', { timeout: 100000 }).then(({ request }) => {
expect(request.body.timeline).to.haveOwnProperty('templateTimelineId');
- expect(request.body.timeline).to.haveOwnProperty('description', timeline.description);
+ expect(request.body.timeline).to.haveOwnProperty('description', getTimeline().description);
expect(request.body.timeline.kqlQuery.filterQuery.kuery).to.haveOwnProperty(
'expression',
- timeline.query
+ getTimeline().query
);
cy.get(TIMELINE_FLYOUT_WRAPPER).should('have.css', 'visibility', 'visible');
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_templates/export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_templates/export.spec.ts
index aa0a6c9308a52..5c2d87c9b727f 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timeline_templates/export.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timeline_templates/export.spec.ts
@@ -9,7 +9,7 @@ import { exportTimeline } from '../../tasks/timelines';
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import {
expectedExportedTimelineTemplate,
- timeline as timelineTemplate,
+ getTimeline as getTimelineTemplate,
} from '../../objects/timeline';
import { TIMELINE_TEMPLATES_URL } from '../../urls/navigation';
@@ -20,7 +20,7 @@ describe('Export timelines', () => {
beforeEach(() => {
cleanKibana();
cy.intercept('POST', 'api/timeline/_export?file_name=timelines_export.ndjson').as('export');
- createTimelineTemplate(timelineTemplate).then((response) => {
+ createTimelineTemplate(getTimelineTemplate()).then((response) => {
cy.wrap(response).as('templateResponse');
cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('templateId');
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/creation.spec.ts
index 8a90b67682cb2..4203b9125d155 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timelines/creation.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timelines/creation.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { timeline } from '../../objects/timeline';
+import { getTimeline } from '../../objects/timeline';
import {
LOCKED_ICON,
@@ -64,7 +64,7 @@ describe('Timelines', (): void => {
before(() => {
openTimelineUsingToggle();
- addNameAndDescriptionToTimeline(timeline);
+ addNameAndDescriptionToTimeline(getTimeline());
populateTimeline();
});
@@ -73,8 +73,8 @@ describe('Timelines', (): void => {
});
it('can be added filter', () => {
- addFilter(timeline.filter);
- cy.get(TIMELINE_FILTER(timeline.filter)).should('exist');
+ addFilter(getTimeline().filter);
+ cy.get(TIMELINE_FILTER(getTimeline().filter)).should('exist');
});
it('pins an event', () => {
@@ -89,8 +89,8 @@ describe('Timelines', (): void => {
});
it('can be added notes', () => {
- addNotesToTimeline(timeline.notes);
- cy.get(NOTES_TEXT).should('have.text', timeline.notes);
+ addNotesToTimeline(getTimeline().notes);
+ cy.get(NOTES_TEXT).should('have.text', getTimeline().notes);
});
it('should update timeline after adding eql', () => {
@@ -116,17 +116,20 @@ describe('Create a timeline from a template', () => {
});
it('Should have the same query and open the timeline modal', () => {
- createTimelineTemplate(timeline).then(() => {
+ createTimelineTemplate(getTimeline()).then(() => {
expandEventAction();
cy.intercept('/api/timeline').as('timeline');
clickingOnCreateTimelineFormTemplateBtn();
cy.wait('@timeline', { timeout: 100000 }).then(({ request }) => {
if (request.body && request.body.timeline) {
- expect(request.body.timeline).to.haveOwnProperty('description', timeline.description);
+ expect(request.body.timeline).to.haveOwnProperty(
+ 'description',
+ getTimeline().description
+ );
expect(request.body.timeline.kqlQuery.filterQuery.kuery).to.haveOwnProperty(
'expression',
- timeline.query
+ getTimeline().query
);
cy.get(TIMELINE_FLYOUT_WRAPPER).should('have.css', 'visibility', 'visible');
}
diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/export.spec.ts
index c2bd31c635a70..918a554db5606 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timelines/export.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timelines/export.spec.ts
@@ -10,14 +10,14 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import { TIMELINES_URL } from '../../urls/navigation';
import { createTimeline } from '../../tasks/api_calls/timelines';
-import { expectedExportedTimeline, timeline } from '../../objects/timeline';
+import { expectedExportedTimeline, getTimeline } from '../../objects/timeline';
import { cleanKibana } from '../../tasks/common';
describe('Export timelines', () => {
beforeEach(() => {
cleanKibana();
cy.intercept('POST', '/api/timeline/_export?file_name=timelines_export.ndjson').as('export');
- createTimeline(timeline).then((response) => {
+ createTimeline(getTimeline()).then((response) => {
cy.wrap(response).as('timelineResponse');
cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId');
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/notes_tab.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/notes_tab.spec.ts
index 24309b8fda084..0a784cf952ca6 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timelines/notes_tab.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timelines/notes_tab.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { timelineNonValidQuery } from '../../objects/timeline';
+import { getTimelineNonValidQuery } from '../../objects/timeline';
import {
NOTES_AUTHOR,
@@ -39,7 +39,7 @@ describe('Timeline notes tab', () => {
loginAndWaitForPageWithoutDateRange(TIMELINES_URL);
waitForTimelinesPanelToBeLoaded();
- createTimeline(timelineNonValidQuery)
+ createTimeline(getTimelineNonValidQuery())
.then((response) => response.body.data.persistTimeline.timeline.savedObjectId)
.then((timelineId: string) =>
refreshTimelinesUntilTimeLinePresent(timelineId)
@@ -56,16 +56,16 @@ describe('Timeline notes tab', () => {
});
it('should render mockdown', () => {
cy.intercept('/api/note').as(`updateNote`);
- addNotesToTimeline(timelineNonValidQuery.notes);
+ addNotesToTimeline(getTimelineNonValidQuery().notes);
cy.wait('@updateNote').its('response.statusCode').should('eq', 200);
cy.get(NOTES_TEXT_AREA).should('exist');
});
it('should contain notes', () => {
cy.intercept('/api/note').as(`updateNote`);
- addNotesToTimeline(timelineNonValidQuery.notes);
+ addNotesToTimeline(getTimelineNonValidQuery().notes);
cy.wait('@updateNote').its('response.statusCode').should('eq', 200);
- cy.get(NOTES_TEXT).first().should('have.text', timelineNonValidQuery.notes);
+ cy.get(NOTES_TEXT).first().should('have.text', getTimelineNonValidQuery().notes);
});
it('should be able to render font in bold', () => {
@@ -91,7 +91,7 @@ describe('Timeline notes tab', () => {
it('should render the right author', () => {
cy.intercept('/api/note').as(`updateNote`);
- addNotesToTimeline(timelineNonValidQuery.notes);
+ addNotesToTimeline(getTimelineNonValidQuery().notes);
cy.wait('@updateNote').its('response.statusCode').should('eq', 200);
cy.get(NOTES_AUTHOR).first().should('have.text', text);
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/open_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/open_timeline.spec.ts
index 814631b2af636..5c620a983b2b3 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timelines/open_timeline.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timelines/open_timeline.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { timeline } from '../../objects/timeline';
+import { getTimeline } from '../../objects/timeline';
import { TIMELINE_DESCRIPTION, TIMELINE_TITLE, OPEN_TIMELINE_MODAL } from '../../screens/timeline';
import {
@@ -39,7 +39,7 @@ describe('Open timeline', () => {
loginAndWaitForPageWithoutDateRange(TIMELINES_URL);
waitForTimelinesPanelToBeLoaded();
- createTimeline(timeline)
+ createTimeline(getTimeline())
.then((response) => response.body.data.persistTimeline.timeline.savedObjectId)
.then((timelineId: string) => {
refreshTimelinesUntilTimeLinePresent(timelineId)
@@ -47,7 +47,7 @@ describe('Open timeline', () => {
// request responses and indeterminism since on clicks to activates URL's.
.then(() => cy.wait(1000))
.then(() =>
- addNoteToTimeline(timeline.notes, timelineId).should((response) =>
+ addNoteToTimeline(getTimeline().notes, timelineId).should((response) =>
expect(response.status).to.equal(200)
)
)
@@ -71,11 +71,11 @@ describe('Open timeline', () => {
});
it('should display timeline info - title', () => {
- cy.contains(timeline.title).should('exist');
+ cy.contains(getTimeline().title).should('exist');
});
it('should display timeline info - description', () => {
- cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description);
+ cy.get(TIMELINES_DESCRIPTION).first().should('have.text', getTimeline().description);
});
it('should display timeline info - pinned event count', () => {
@@ -91,11 +91,11 @@ describe('Open timeline', () => {
});
it('should display timeline content - title', () => {
- cy.get(TIMELINE_TITLE).should('have.text', timeline.title);
+ cy.get(TIMELINE_TITLE).should('have.text', getTimeline().title);
});
it('should display timeline content - description', () => {
- cy.get(TIMELINE_DESCRIPTION).should('have.text', timeline.description);
+ cy.get(TIMELINE_DESCRIPTION).should('have.text', getTimeline().description);
});
});
});
diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/query_tab.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/query_tab.spec.ts
index f37a66ac048fb..06891121d6354 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timelines/query_tab.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timelines/query_tab.spec.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { timeline } from '../../objects/timeline';
+import { getTimeline } from '../../objects/timeline';
import {
UNLOCKED_ICON,
@@ -38,7 +38,7 @@ describe('Timeline query tab', () => {
loginAndWaitForPageWithoutDateRange(TIMELINES_URL);
waitForTimelinesPanelToBeLoaded();
- createTimeline(timeline)
+ createTimeline(getTimeline())
.then((response) => response.body.data.persistTimeline.timeline.savedObjectId)
.then((timelineId: string) => {
refreshTimelinesUntilTimeLinePresent(timelineId)
@@ -46,14 +46,14 @@ describe('Timeline query tab', () => {
// request responses and indeterminism since on clicks to activates URL's.
.then(() => cy.wait(1000))
.then(() =>
- addNoteToTimeline(timeline.notes, timelineId).should((response) =>
+ addNoteToTimeline(getTimeline().notes, timelineId).should((response) =>
expect(response.status).to.equal(200)
)
)
.then(() => openTimelineById(timelineId))
.then(() => pinFirstEvent())
.then(() => persistNoteToFirstEvent('event note'))
- .then(() => addFilter(timeline.filter));
+ .then(() => addFilter(getTimeline().filter));
});
});
@@ -63,7 +63,7 @@ describe('Timeline query tab', () => {
});
it('should contain the right query', () => {
- cy.get(TIMELINE_QUERY).should('have.text', `${timeline.query}`);
+ cy.get(TIMELINE_QUERY).should('have.text', `${getTimeline().query}`);
});
it('should be able to add event note', () => {
@@ -71,7 +71,7 @@ describe('Timeline query tab', () => {
});
it('should display timeline filter', () => {
- cy.get(TIMELINE_FILTER(timeline.filter)).should('exist');
+ cy.get(TIMELINE_FILTER(getTimeline().filter)).should('exist');
});
it('should display pinned events', () => {
diff --git a/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts
index 842dd85b42ef8..a72657d78b70d 100644
--- a/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts
@@ -37,7 +37,7 @@ import { addNameToTimeline, closeTimeline, populateTimeline } from '../../tasks/
import { HOSTS_URL } from '../../urls/navigation';
import { ABSOLUTE_DATE_RANGE } from '../../urls/state';
-import { timeline } from '../../objects/timeline';
+import { getTimeline } from '../../objects/timeline';
import { TIMELINE } from '../../screens/create_new_case';
import { cleanKibana } from '../../tasks/common';
@@ -244,7 +244,7 @@ describe('url state', () => {
cy.intercept('PATCH', '/api/timeline').as('timeline');
- addNameToTimeline(timeline.title);
+ addNameToTimeline(getTimeline().title);
cy.wait('@timeline').then(({ response }) => {
closeTimeline();
@@ -256,7 +256,7 @@ describe('url state', () => {
cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).should('not.have.text', 'Updating');
cy.get(TIMELINE).should('be.visible');
cy.get(TIMELINE_TITLE).should('be.visible');
- cy.get(TIMELINE_TITLE).should('have.text', timeline.title);
+ cy.get(TIMELINE_TITLE).should('have.text', getTimeline().title);
});
});
});
diff --git a/x-pack/plugins/security_solution/cypress/objects/case.ts b/x-pack/plugins/security_solution/cypress/objects/case.ts
index 847236688dee7..8bc90c5fa2a3b 100644
--- a/x-pack/plugins/security_solution/cypress/objects/case.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/case.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { CompleteTimeline, timeline } from './timeline';
+import { CompleteTimeline, getTimeline } from './timeline';
export interface TestCase extends TestCaseWithoutTimeline {
timeline: CompleteTimeline;
@@ -43,49 +43,50 @@ export interface IbmResilientConnectorOptions {
incidentTypes: string[];
}
-export const case1: TestCase = {
+export const getCase1 = (): TestCase => ({
name: 'This is the title of the case',
tags: ['Tag1', 'Tag2'],
description: 'This is the case description',
- timeline,
+ timeline: getTimeline(),
reporter: 'elastic',
owner: 'securitySolution',
-};
+});
-export const serviceNowConnector: Connector = {
+export const getServiceNowConnector = (): Connector => ({
connectorName: 'New connector',
URL: 'https://www.test.service-now.com',
username: 'Username Name',
password: 'password',
-};
+});
-export const jiraConnectorOptions: JiraConnectorOptions = {
+export const getJiraConnectorOptions = (): JiraConnectorOptions => ({
issueType: '10006',
priority: 'High',
-};
+});
-export const serviceNowConnectorOpions: ServiceNowconnectorOptions = {
+export const getServiceNowConnectorOptions = (): ServiceNowconnectorOptions => ({
urgency: '2',
severity: '1',
impact: '3',
-};
+});
-export const ibmResilientConnectorOptions: IbmResilientConnectorOptions = {
+export const getIbmResilientConnectorOptions = (): IbmResilientConnectorOptions => ({
title: 'Resilient',
severity: 'Medium',
incidentTypes: ['Communication error (fax; email)', 'Denial of Service'],
-};
+});
export const TIMELINE_CASE_ID = '68248e00-f689-11ea-9ab2-59238b522856';
-export const connectorIds = {
+
+export const getConnectorIds = () => ({
jira: '000e5f86-08b0-4882-adfd-6df981d45c1b',
sn: '93a69ba3-3c31-4b4c-bf86-cc79a090f437',
resilient: 'a6a8dd7f-7e88-48fe-9b9f-70b668da8cbc',
-};
+});
-export const mockConnectorsResponse = [
+export const getMockConnectorsResponse = () => [
{
- id: connectorIds.jira,
+ id: getConnectorIds().jira,
actionTypeId: '.jira',
name: 'Jira',
config: {
@@ -96,7 +97,7 @@ export const mockConnectorsResponse = [
referencedByCount: 0,
},
{
- id: connectorIds.resilient,
+ id: getConnectorIds().resilient,
actionTypeId: '.resilient',
name: 'Resilient',
config: {
@@ -107,7 +108,7 @@ export const mockConnectorsResponse = [
referencedByCount: 0,
},
{
- id: connectorIds.sn,
+ id: getConnectorIds().sn,
actionTypeId: '.servicenow',
name: 'ServiceNow',
config: {
@@ -117,7 +118,8 @@ export const mockConnectorsResponse = [
referencedByCount: 0,
},
];
-export const executeResponses = {
+
+export const getExecuteResponses = () => ({
servicenow: {
choices: {
status: 'ok',
@@ -208,7 +210,7 @@ export const executeResponses = {
{ id: '10006', name: 'Task' },
{ id: '10007', name: 'Sub-task' },
],
- actionId: connectorIds.jira,
+ actionId: getConnectorIds().jira,
},
fieldsByIssueType: {
status: 'ok',
@@ -299,7 +301,7 @@ export const executeResponses = {
timetracking: { allowedValues: [], defaultValue: {} },
labels: { allowedValues: [], defaultValue: {} },
},
- actionId: connectorIds.jira,
+ actionId: getConnectorIds().jira,
},
},
resilient: {
@@ -309,7 +311,7 @@ export const executeResponses = {
{ id: 17, name: 'Communication error (fax; email)' },
{ id: 21, name: 'Denial of Service' },
],
- actionId: connectorIds.resilient,
+ actionId: getConnectorIds().resilient,
},
severity: {
status: 'ok',
@@ -318,7 +320,7 @@ export const executeResponses = {
{ id: 5, name: 'Medium' },
{ id: 6, name: 'High' },
],
- actionId: connectorIds.resilient,
+ actionId: getConnectorIds().resilient,
},
},
-};
+});
diff --git a/x-pack/plugins/security_solution/cypress/objects/connector.ts b/x-pack/plugins/security_solution/cypress/objects/connector.ts
index 2a0f1cc43eff0..a5244583bf494 100644
--- a/x-pack/plugins/security_solution/cypress/objects/connector.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/connector.ts
@@ -14,11 +14,11 @@ export interface EmailConnector {
password: string;
}
-export const emailConnector: EmailConnector = {
+export const getEmailConnector = (): EmailConnector => ({
name: 'Test connector',
from: 'test@example.com',
host: 'example.com',
port: '80',
user: 'username',
password: 'password',
-};
+});
diff --git a/x-pack/plugins/security_solution/cypress/objects/exception.ts b/x-pack/plugins/security_solution/cypress/objects/exception.ts
index 73457f10ccec6..6a934e1ec4651 100644
--- a/x-pack/plugins/security_solution/cypress/objects/exception.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/exception.ts
@@ -20,22 +20,22 @@ export interface ExceptionList {
type: 'detection' | 'endpoint';
}
-export const exceptionList: ExceptionList = {
+export const getExceptionList = (): ExceptionList => ({
description: 'Test exception list description',
list_id: 'test_exception_list',
name: 'Test exception list',
namespace_type: 'single',
tags: ['test tag'],
type: 'detection',
-};
+});
-export const exception: Exception = {
+export const getException = (): Exception => ({
field: 'host.name',
operator: 'is',
values: ['suricata-iowa'],
-};
+});
-export const expectedExportedExceptionList = (exceptionListResponse: Cypress.Response) => {
+export const expectedExportedExceptionList = (exceptionListResponse: Cypress.Response): string => {
const jsonrule = exceptionListResponse.body;
return `{"_version":"${jsonrule._version}","created_at":"${jsonrule.created_at}","created_by":"elastic","description":"${jsonrule.description}","id":"${jsonrule.id}","immutable":false,"list_id":"test_exception_list","name":"Test exception list","namespace_type":"single","os_types":[],"tags":[],"tie_breaker_id":"${jsonrule.tie_breaker_id}","type":"detection","updated_at":"${jsonrule.updated_at}","updated_by":"elastic","version":1}\n"\n""\n{"exception_list_items_details":"{"exported_count":0}\n"}`;
diff --git a/x-pack/plugins/security_solution/cypress/objects/filter.ts b/x-pack/plugins/security_solution/cypress/objects/filter.ts
index b00954de17422..5a69100a4b38a 100644
--- a/x-pack/plugins/security_solution/cypress/objects/filter.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/filter.ts
@@ -10,7 +10,7 @@ export interface SearchBarFilter {
value: string;
}
-export const hostIpFilter: SearchBarFilter = {
+export const getHostIpFilter = (): SearchBarFilter => ({
key: 'host.ip',
value: '1.1.1.1',
-};
+});
diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts
index 3383ef4996ead..a10fa5b0eda78 100644
--- a/x-pack/plugins/security_solution/cypress/objects/rule.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts
@@ -7,8 +7,8 @@
/* eslint-disable @kbn/eslint/no-restricted-paths */
import { rawRules } from '../../server/lib/detection_engine/rules/prepackaged_rules/index';
-import { mockThreatData } from '../../public/detections/mitre/mitre_tactics_techniques';
-import { timeline, CompleteTimeline, indicatorMatchTimelineTemplate } from './timeline';
+import { getMockThreatData } from '../../public/detections/mitre/mitre_tactics_techniques';
+import { getTimeline, CompleteTimeline, getIndicatorMatchTimelineTemplate } from './timeline';
export const totalNumberOfPrebuiltRules = rawRules.length;
@@ -96,7 +96,7 @@ export interface MachineLearningRule {
lookBack: Interval;
}
-export const indexPatterns = [
+export const getIndexPatterns = (): string[] => [
'apm-*-transaction*',
'auditbeat-*',
'endgame-*',
@@ -106,67 +106,69 @@ export const indexPatterns = [
'winlogbeat-*',
];
-const { tactic, technique, subtechnique } = mockThreatData;
-
-const mitre1: Mitre = {
- tactic: `${tactic.name} (${tactic.id})`,
+const getMitre1 = (): Mitre => ({
+ tactic: `${getMockThreatData().tactic.name} (${getMockThreatData().tactic.id})`,
techniques: [
{
- name: `${technique.name} (${technique.id})`,
- subtechniques: [`${subtechnique.name} (${subtechnique.id})`],
+ name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`,
+ subtechniques: [
+ `${getMockThreatData().subtechnique.name} (${getMockThreatData().subtechnique.id})`,
+ ],
},
{
- name: `${technique.name} (${technique.id})`,
+ name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`,
subtechniques: [],
},
],
-};
+});
-const mitre2: Mitre = {
- tactic: `${tactic.name} (${tactic.id})`,
+const getMitre2 = (): Mitre => ({
+ tactic: `${getMockThreatData().tactic.name} (${getMockThreatData().tactic.id})`,
techniques: [
{
- name: `${technique.name} (${technique.id})`,
- subtechniques: [`${subtechnique.name} (${subtechnique.id})`],
+ name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`,
+ subtechniques: [
+ `${getMockThreatData().subtechnique.name} (${getMockThreatData().subtechnique.id})`,
+ ],
},
],
-};
+});
-const severityOverride1: SeverityOverride = {
+const getSeverityOverride1 = (): SeverityOverride => ({
sourceField: 'host.name',
sourceValue: 'host',
-};
+});
-const severityOverride2: SeverityOverride = {
+const getSeverityOverride2 = (): SeverityOverride => ({
sourceField: '@timestamp',
sourceValue: '10/02/2020',
-};
+});
-const severityOverride3: SeverityOverride = {
+const getSeverityOverride3 = (): SeverityOverride => ({
sourceField: 'host.geo.name',
sourceValue: 'atack',
-};
+});
-const severityOverride4: SeverityOverride = {
+const getSeverityOverride4 = (): SeverityOverride => ({
sourceField: 'agent.type',
sourceValue: 'auditbeat',
-};
+});
-const runsEvery: Interval = {
+const getRunsEvery = (): Interval => ({
interval: '1',
timeType: 'Seconds',
type: 's',
-};
+});
-const lookBack: Interval = {
+const getLookBack = (): Interval => ({
interval: '17520',
timeType: 'Hours',
type: 'h',
-};
+});
-export const newRule: CustomRule = {
+export const getNewRule = (): CustomRule => ({
customQuery: 'host.name: *',
- index: indexPatterns,
+ index: getIndexPatterns(),
name: 'New Rule Test',
description: 'The new rule description.',
severity: 'High',
@@ -174,15 +176,15 @@ export const newRule: CustomRule = {
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
- mitre: [mitre1, mitre2],
+ mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
- runsEvery,
- lookBack,
- timeline,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
maxSignals: 100,
-};
+});
-export const unmappedRule: CustomRule = {
+export const getUnmappedRule = (): CustomRule => ({
customQuery: '*:*',
index: ['unmapped*'],
name: 'Rule with unmapped fields',
@@ -192,15 +194,15 @@ export const unmappedRule: CustomRule = {
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
- mitre: [mitre1, mitre2],
+ mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
- runsEvery,
- lookBack,
- timeline,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
maxSignals: 100,
-};
+});
-export const unmappedCCSRule: CustomRule = {
+export const getUnmappedCCSRule = (): CustomRule => ({
customQuery: '*:*',
index: [`${ccsRemoteName}:unmapped*`],
name: 'Rule with unmapped fields',
@@ -210,15 +212,15 @@ export const unmappedCCSRule: CustomRule = {
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
- mitre: [mitre1, mitre2],
+ mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
- runsEvery,
- lookBack,
- timeline,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
maxSignals: 100,
-};
+});
-export const existingRule: CustomRule = {
+export const getExistingRule = (): CustomRule => ({
customQuery: 'host.name: *',
name: 'Rule 1',
description: 'Description for Rule 1',
@@ -231,17 +233,17 @@ export const existingRule: CustomRule = {
falsePositivesExamples: [],
mitre: [],
note: 'This is my note',
- runsEvery,
- lookBack,
- timeline,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
// Please do not change, or if you do, needs
// to be any number other than default value
maxSignals: 500,
-};
+});
-export const newOverrideRule: OverrideRule = {
+export const getNewOverrideRule = (): OverrideRule => ({
customQuery: 'host.name: *',
- index: indexPatterns,
+ index: getIndexPatterns(),
name: 'Override Rule',
description: 'The new rule description.',
severity: 'High',
@@ -249,21 +251,26 @@ export const newOverrideRule: OverrideRule = {
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
- mitre: [mitre1, mitre2],
+ mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
- severityOverride: [severityOverride1, severityOverride2, severityOverride3, severityOverride4],
+ severityOverride: [
+ getSeverityOverride1(),
+ getSeverityOverride2(),
+ getSeverityOverride3(),
+ getSeverityOverride4(),
+ ],
riskOverride: 'destination.port',
nameOverride: 'agent.type',
timestampOverride: '@timestamp',
- runsEvery,
- lookBack,
- timeline,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
maxSignals: 100,
-};
+});
-export const newThresholdRule: ThresholdRule = {
+export const getNewThresholdRule = (): ThresholdRule => ({
customQuery: 'host.name: *',
- index: indexPatterns,
+ index: getIndexPatterns(),
name: 'Threshold Rule',
description: 'The new rule description.',
severity: 'High',
@@ -271,17 +278,17 @@ export const newThresholdRule: ThresholdRule = {
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
- mitre: [mitre1, mitre2],
+ mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
thresholdField: 'host.name',
threshold: '10',
- runsEvery,
- lookBack,
- timeline,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
maxSignals: 100,
-};
+});
-export const machineLearningRule: MachineLearningRule = {
+export const getMachineLearningRule = (): MachineLearningRule => ({
machineLearningJobs: ['linux_anomalous_network_service', 'linux_anomalous_network_activity_ecs'],
anomalyScoreThreshold: '20',
name: 'New ML Rule Test',
@@ -291,52 +298,52 @@ export const machineLearningRule: MachineLearningRule = {
tags: ['ML'],
referenceUrls: ['https://elastic.co/'],
falsePositivesExamples: ['False1'],
- mitre: [mitre1],
+ mitre: [getMitre1()],
note: '# test markdown',
- runsEvery,
- lookBack,
-};
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+});
-export const eqlRule: CustomRule = {
+export const getEqlRule = (): CustomRule => ({
customQuery: 'any where process.name == "which"',
name: 'New EQL Rule',
- index: indexPatterns,
+ index: getIndexPatterns(),
description: 'New EQL rule description.',
severity: 'High',
riskScore: '17',
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
- mitre: [mitre1, mitre2],
+ mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
- runsEvery,
- lookBack,
- timeline,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
maxSignals: 100,
-};
+});
-export const eqlSequenceRule: CustomRule = {
+export const getEqlSequenceRule = (): CustomRule => ({
customQuery:
'sequence with maxspan=30s\
[any where process.name == "which"]\
[any where process.name == "xargs"]',
name: 'New EQL Sequence Rule',
- index: indexPatterns,
+ index: getIndexPatterns(),
description: 'New EQL rule description.',
severity: 'High',
riskScore: '17',
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
- mitre: [mitre1, mitre2],
+ mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
- runsEvery,
- lookBack,
- timeline,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
maxSignals: 100,
-};
+});
-export const newThreatIndicatorRule: ThreatIndicatorRule = {
+export const getNewThreatIndicatorRule = (): ThreatIndicatorRule => ({
name: 'Threat Indicator Rule Test',
description: 'The threat indicator rule description.',
index: ['suspicious-*'],
@@ -345,31 +352,31 @@ export const newThreatIndicatorRule: ThreatIndicatorRule = {
tags: ['test', 'threat'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
- mitre: [mitre1, mitre2],
+ mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
- runsEvery,
- lookBack,
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
indicatorIndexPattern: ['filebeat-*'],
indicatorMappingField: 'myhash.mysha256',
indicatorIndexField: 'threatintel.indicator.file.hash.sha256',
type: 'file',
atomic: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
- timeline: indicatorMatchTimelineTemplate,
+ timeline: getIndicatorMatchTimelineTemplate(),
maxSignals: 100,
-};
+});
-export const duplicatedRuleName = `${newThreatIndicatorRule.name} [Duplicate]`;
+export const duplicatedRuleName = `${getNewThreatIndicatorRule().name} [Duplicate]`;
-export const severitiesOverride = ['Low', 'Medium', 'High', 'Critical'];
+export const getSeveritiesOverride = (): string[] => ['Low', 'Medium', 'High', 'Critical'];
-export const editedRule = {
- ...existingRule,
+export const getEditedRule = (): CustomRule => ({
+ ...getExistingRule(),
severity: 'Medium',
description: 'Edited Rule description',
- tags: [...existingRule.tags, 'edited'],
-};
+ tags: [...getExistingRule().tags, 'edited'],
+});
-export const expectedExportedRule = (ruleResponse: Cypress.Response) => {
+export const expectedExportedRule = (ruleResponse: Cypress.Response): string => {
const jsonrule = ruleResponse.body;
return `{"id":"${jsonrule.id}","updated_at":"${jsonrule.updated_at}","updated_by":"elastic","created_at":"${jsonrule.created_at}","created_by":"elastic","name":"${jsonrule.name}","tags":[],"interval":"100m","enabled":false,"description":"${jsonrule.description}","risk_score":${jsonrule.risk_score},"severity":"${jsonrule.severity}","output_index":".siem-signals-default","author":[],"false_positives":[],"from":"now-17520h","rule_id":"rule_testing","max_signals":100,"risk_score_mapping":[],"severity_mapping":[],"threat":[],"to":"now","references":[],"version":1,"exceptions_list":[],"immutable":false,"type":"query","language":"kuery","index":["exceptions-*"],"query":"${jsonrule.query}","throttle":"no_actions","actions":[]}\n{"exported_count":1,"missing_rules":[],"missing_rules_count":0}\n`;
diff --git a/x-pack/plugins/security_solution/cypress/objects/timeline.ts b/x-pack/plugins/security_solution/cypress/objects/timeline.ts
index 1b66b50605508..c13c1b01ef0ed 100644
--- a/x-pack/plugins/security_solution/cypress/objects/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/timeline.ts
@@ -24,51 +24,51 @@ export interface TimelineFilter {
value?: string;
}
-export const filter: TimelineFilter = {
+export const getFilter = (): TimelineFilter => ({
field: 'host.name',
operator: 'exists',
value: 'exists',
-};
+});
-export const timeline: CompleteTimeline = {
+export const getTimeline = (): CompleteTimeline => ({
title: 'Security Timeline',
description: 'This is the best timeline',
query: 'host.name: *',
notes: 'Yes, the best timeline',
- filter,
-};
+ filter: getFilter(),
+});
-export const indicatorMatchTimelineTemplate: CompleteTimeline = {
- ...timeline,
+export const getIndicatorMatchTimelineTemplate = (): CompleteTimeline => ({
+ ...getTimeline(),
title: 'Generic Threat Match Timeline',
templateTimelineId: '495ad7a7-316e-4544-8a0f-9c098daee76e',
-};
+});
/**
* Timeline query that finds no valid data to cut down on test failures
* or other issues for when we want to test one specific thing and not also
* test the queries happening
*/
-export const timelineNonValidQuery: CompleteTimeline = {
- ...timeline,
+export const getTimelineNonValidQuery = (): CompleteTimeline => ({
+ ...getTimeline(),
query: 'query_to_intentionally_find_nothing: *',
-};
+});
-export const caseTimeline: Timeline = {
+export const caseTimeline = (): Timeline => ({
title: 'SIEM test',
description: 'description',
query: 'host.name: *',
id: '0162c130-78be-11ea-9718-118a926974a4',
-};
+});
-export const expectedExportedTimelineTemplate = (templateResponse: Cypress.Response) => {
+export const expectedExportedTimelineTemplate = (templateResponse: Cypress.Response): string => {
const timelineTemplateBody = templateResponse.body.data.persistTimeline.timeline;
return `{"savedObjectId":"${timelineTemplateBody.savedObjectId}","version":"${timelineTemplateBody.version}","columns":[{"id":"@timestamp"},{"id":"user.name"},{"id":"event.category"},{"id":"event.action"},{"id":"host.name"}],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"expression":"${timelineTemplateBody.kqlQuery.filterQuery.kuery.expression}","kind":"kuery"}}},"dateRange":{"start":"${timelineTemplateBody.dateRange.start}","end":"${timelineTemplateBody.dateRange.end}"},"description":"${timelineTemplateBody.description}","title":"${timelineTemplateBody.title}","templateTimelineVersion":1,"timelineType":"template","created":${timelineTemplateBody.created},"createdBy":"elastic","updated":${timelineTemplateBody.updated},"updatedBy":"elastic","sort":[],"eventNotes":[],"globalNotes":[],"pinnedEventIds":[]}
`;
};
-export const expectedExportedTimeline = (timelineResponse: Cypress.Response) => {
+export const expectedExportedTimeline = (timelineResponse: Cypress.Response): string => {
const timelineBody = timelineResponse.body.data.persistTimeline.timeline;
return `{"savedObjectId":"${timelineBody.savedObjectId}","version":"${timelineBody.version}","columns":[{"id":"@timestamp"},{"id":"user.name"},{"id":"event.category"},{"id":"event.action"},{"id":"host.name"}],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"expression":"${timelineBody.kqlQuery.filterQuery.kuery.expression}","kind":"kuery"}}},"dateRange":{"start":"${timelineBody.dateRange.start}","end":"${timelineBody.dateRange.end}"},"description":"${timelineBody.description}","title":"${timelineBody.title}","created":${timelineBody.created},"createdBy":"elastic","updated":${timelineBody.updated},"updatedBy":"elastic","timelineType":"default","sort":[],"eventNotes":[],"globalNotes":[],"pinnedEventIds":[]}\n`;
diff --git a/x-pack/plugins/security_solution/cypress/screens/edit_connector.ts b/x-pack/plugins/security_solution/cypress/screens/edit_connector.ts
index 5b353983e5a92..598485b167c9f 100644
--- a/x-pack/plugins/security_solution/cypress/screens/edit_connector.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/edit_connector.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { connectorIds } from '../objects/case';
+import { getConnectorIds } from '../objects/case';
export const CONNECTOR_RESILIENT = `[data-test-subj="connector-fields-resilient"]`;
@@ -17,14 +17,16 @@ export const SELECT_INCIDENT_TYPE = `[data-test-subj="incidentTypeComboBox"] inp
export const SELECT_ISSUE_TYPE = `[data-test-subj="issueTypeSelect"]`;
-export const SELECT_JIRA = `[data-test-subj="dropdown-connector-${connectorIds.jira}"]`;
+export const SELECT_JIRA = `[data-test-subj="dropdown-connector-${getConnectorIds().jira}"]`;
export const SELECT_PRIORITY = `[data-test-subj="prioritySelect"]`;
-export const SELECT_RESILIENT = `[data-test-subj="dropdown-connector-${connectorIds.resilient}"]`;
+export const SELECT_RESILIENT = `[data-test-subj="dropdown-connector-${
+ getConnectorIds().resilient
+}"]`;
export const SELECT_SEVERITY = `[data-test-subj="severitySelect"]`;
-export const SELECT_SN = `[data-test-subj="dropdown-connector-${connectorIds.sn}"]`;
+export const SELECT_SN = `[data-test-subj="dropdown-connector-${getConnectorIds().sn}"]`;
export const SELECT_URGENCY = `[data-test-subj="urgencySelect"]`;
diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
index 1b420cd6d1520..d8d91dc9ca624 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts
@@ -5,11 +5,11 @@
* 2.0.
*/
-import { emailConnector, EmailConnector } from '../objects/connector';
+import { getEmailConnector, EmailConnector } from '../objects/connector';
import {
CustomRule,
MachineLearningRule,
- machineLearningRule,
+ getMachineLearningRule,
OverrideRule,
ThreatIndicatorRule,
ThresholdRule,
@@ -397,7 +397,7 @@ export const fillIndexAndIndicatorIndexPattern = (
getIndicatorIndicatorIndex().type(`${indicatorIndex}{enter}`);
};
-export const fillEmailConnectorForm = (connector: EmailConnector = emailConnector) => {
+export const fillEmailConnectorForm = (connector: EmailConnector = getEmailConnector()) => {
cy.get(CONNECTOR_NAME_INPUT).type(connector.name);
cy.get(EMAIL_CONNECTOR_FROM_INPUT).type(connector.from);
cy.get(EMAIL_CONNECTOR_HOST_INPUT).type(connector.host);
@@ -478,9 +478,12 @@ export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRu
cy.get(MACHINE_LEARNING_DROPDOWN_INPUT).type(`${machineLearningJob}{enter}`);
cy.get(MACHINE_LEARNING_DROPDOWN_INPUT).type('{esc}');
});
- cy.get(ANOMALY_THRESHOLD_INPUT).type(`{selectall}${machineLearningRule.anomalyScoreThreshold}`, {
- force: true,
- });
+ cy.get(ANOMALY_THRESHOLD_INPUT).type(
+ `{selectall}${getMachineLearningRule().anomalyScoreThreshold}`,
+ {
+ force: true,
+ }
+ );
getDefineContinueButton().should('exist').click({ force: true });
cy.get(MACHINE_LEARNING_DROPDOWN_INPUT).should('not.exist');
diff --git a/x-pack/plugins/security_solution/public/detections/mitre/mitre_tactics_techniques.ts b/x-pack/plugins/security_solution/public/detections/mitre/mitre_tactics_techniques.ts
index a5da747787ba6..f28311d9c96e7 100644
--- a/x-pack/plugins/security_solution/public/detections/mitre/mitre_tactics_techniques.ts
+++ b/x-pack/plugins/security_solution/public/detections/mitre/mitre_tactics_techniques.ts
@@ -10143,7 +10143,7 @@ export const subtechniquesOptions: MitreSubtechniquesOptions[] = [
*
* Is built alongside and sampled from the data in the file so to always be valid with the most up to date MITRE ATT&CK data
*/
-export const mockThreatData = {
+export const getMockThreatData = () => ({
tactic: {
name: 'Privilege Escalation',
id: 'TA0004',
@@ -10162,4 +10162,4 @@ export const mockThreatData = {
tactics: ['privilege-escalation', 'persistence'],
techniqueId: 'T1546',
},
-};
+});
diff --git a/x-pack/plugins/security_solution/public/detections/mitre/valid_threat_mock.ts b/x-pack/plugins/security_solution/public/detections/mitre/valid_threat_mock.ts
index a7de7494e1116..743b143213c22 100644
--- a/x-pack/plugins/security_solution/public/detections/mitre/valid_threat_mock.ts
+++ b/x-pack/plugins/security_solution/public/detections/mitre/valid_threat_mock.ts
@@ -6,9 +6,9 @@
*/
import { Threats } from '@kbn/securitysolution-io-ts-alerting-types';
-import { mockThreatData } from './mitre_tactics_techniques';
+import { getMockThreatData } from './mitre_tactics_techniques';
-const { tactic, technique, subtechnique } = mockThreatData;
+const { tactic, technique, subtechnique } = getMockThreatData();
const { tactics, ...mockTechnique } = technique;
const { tactics: subtechniqueTactics, ...mockSubtechnique } = subtechnique;
From bb1759e0653ea7af352f1b5c9d7b07a149e77c78 Mon Sep 17 00:00:00 2001
From: liza-mae
Date: Mon, 12 Jul 2021 11:29:37 -0600
Subject: [PATCH 27/31] Fix upgrade tests for 7.14 (#104993)
* Fix upgrade tests for 7.14
* Fix lint issues
* Comment out unused const
* Update uiSettings for non-default space
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../apps/dashboard/dashboard_smoke_tests.ts | 15 ++++++++++++---
.../apps/reporting/reporting_smoke_tests.ts | 2 ++
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts b/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts
index 73819b5bac695..0bc3cd7c2610e 100644
--- a/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts
+++ b/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts
@@ -16,6 +16,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const renderable = getService('renderable');
const dashboardExpect = getService('dashboardExpect');
const PageObjects = getPageObjects(['common', 'header', 'home', 'dashboard', 'timePicker']);
+ const kibanaServer = getService('kibanaServer');
+ const browser = getService('browser');
describe('dashboard smoke tests', function describeIndexTests() {
const spaces = [
@@ -36,6 +38,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
basePath,
});
await PageObjects.header.waitUntilLoadingHasFinished();
+ await kibanaServer.uiSettings.update(
+ {
+ 'visualization:visualize:legacyChartsLibrary': true,
+ 'visualization:visualize:legacyPieChartsLibrary': true,
+ },
+ { space }
+ );
+ await browser.refresh();
});
dashboardTests.forEach(({ name, numPanels }) => {
it('should launch sample ' + name + ' data set dashboard', async () => {
@@ -56,9 +66,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await renderable.waitForRender();
log.debug('Checking pie charts rendered');
await pieChart.expectPieSliceCount(4);
- // https://github.com/elastic/kibana/issues/92887
- // log.debug('Checking area, bar and heatmap charts rendered');
- // await dashboardExpect.seriesElementCount(15);
+ log.debug('Checking area, bar and heatmap charts rendered');
+ await dashboardExpect.seriesElementCount(15);
log.debug('Checking saved searches rendered');
await dashboardExpect.savedSearchRowCount(49);
log.debug('Checking input controls rendered');
diff --git a/x-pack/test/upgrade/apps/reporting/reporting_smoke_tests.ts b/x-pack/test/upgrade/apps/reporting/reporting_smoke_tests.ts
index c00e761d54226..20fc34f77dbf8 100644
--- a/x-pack/test/upgrade/apps/reporting/reporting_smoke_tests.ts
+++ b/x-pack/test/upgrade/apps/reporting/reporting_smoke_tests.ts
@@ -69,6 +69,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
if (type === 'pdf_optimize') {
await testSubjects.click('usePrintLayout');
}
+ const advOpt = await find.byXPath(`//button[descendant::*[text()='Advanced options']]`);
+ await advOpt.click();
const postUrl = await find.byXPath(`//button[descendant::*[text()='Copy POST URL']]`);
await postUrl.click();
const url = await browser.getClipboardValue();
From 3638c5ffb279d9ebf1c255653f35a95a7a0ba943 Mon Sep 17 00:00:00 2001
From: Domenico Andreoli
Date: Mon, 12 Jul 2021 19:50:08 +0200
Subject: [PATCH 28/31] Drop Detection Engine FTR test POC (#104852)
---
.../apps/ccs/ccs_discover.js | 155 ------------------
1 file changed, 155 deletions(-)
diff --git a/x-pack/test/stack_functional_integration/apps/ccs/ccs_discover.js b/x-pack/test/stack_functional_integration/apps/ccs/ccs_discover.js
index a22e4438c7dbd..588ff9a6e9f92 100644
--- a/x-pack/test/stack_functional_integration/apps/ccs/ccs_discover.js
+++ b/x-pack/test/stack_functional_integration/apps/ccs/ccs_discover.js
@@ -5,16 +5,7 @@
* 2.0.
*/
-import fs from 'fs';
-import { resolve } from 'path';
import expect from '@kbn/expect';
-import { Client as EsClient } from '@elastic/elasticsearch';
-import { KbnClient } from '@kbn/test';
-import { EsArchiver } from '@kbn/es-archiver';
-import { CA_CERT_PATH, REPO_ROOT } from '@kbn/dev-utils';
-
-const INTEGRATION_TEST_ROOT = process.env.WORKSPACE || resolve(REPO_ROOT, '../integration-test');
-const ARCHIVE = resolve(INTEGRATION_TEST_ROOT, 'test/es_archives/metricbeat');
export default ({ getService, getPageObjects }) => {
describe('Cross cluster search test in discover', async () => {
@@ -212,151 +203,5 @@ export default ({ getService, getPageObjects }) => {
expect(hitCount).to.be.lessThan(originalHitCount);
});
});
-
- describe('Detection engine', async function () {
- const supertest = getService('supertest');
- const esSupertest = getService('esSupertest');
- const config = getService('config');
-
- const esClient = new EsClient({
- ssl: {
- ca: fs.readFileSync(CA_CERT_PATH, 'utf-8'),
- },
- nodes: [process.env.TEST_ES_URLDATA],
- requestTimeout: config.get('timeouts.esRequestTimeout'),
- });
-
- const kbnClient = new KbnClient({
- log,
- url: process.env.TEST_KIBANA_URLDATA,
- certificateAuthorities: config.get('servers.kibana.certificateAuthorities'),
- uiSettingDefaults: kibanaServer.uiSettings,
- });
-
- const esArchiver = new EsArchiver({
- log,
- client: esClient,
- kbnClient,
- });
-
- let signalsId;
- let dataId;
- let ruleId;
-
- before('Prepare .siem-signal-*', async function () {
- log.info('Create index');
- // visit app/security so to create .siem-signals-* as side effect
- await PageObjects.common.navigateToApp('security', { insertTimestamp: false });
-
- log.info('Create index pattern');
- signalsId = await supertest
- .post('/api/index_patterns/index_pattern')
- .set('kbn-xsrf', 'true')
- .send({
- index_pattern: {
- title: '.siem-signals-*',
- },
- override: true,
- })
- .expect(200)
- .then((res) => JSON.parse(res.text).index_pattern.id);
- log.debug('id: ' + signalsId);
- });
-
- before('Prepare data:metricbeat-*', async function () {
- log.info('Create index');
- await esArchiver.load(ARCHIVE);
-
- log.info('Create index pattern');
- dataId = await supertest
- .post('/api/index_patterns/index_pattern')
- .set('kbn-xsrf', 'true')
- .send({
- index_pattern: {
- title: 'data:metricbeat-*',
- },
- override: true,
- })
- .expect(200)
- .then((res) => JSON.parse(res.text).index_pattern.id);
- log.debug('id: ' + dataId);
- });
-
- before('Add detection rule', async function () {
- ruleId = await supertest
- .post('/api/detection_engine/rules')
- .set('kbn-xsrf', 'true')
- .send({
- description: 'This is the description of the rule',
- risk_score: 17,
- severity: 'low',
- interval: '10s',
- name: 'CCS_Detection_test',
- type: 'query',
- from: 'now-1y',
- index: ['data:metricbeat-*'],
- query: '*:*',
- language: 'kuery',
- enabled: true,
- })
- .expect(200)
- .then((res) => JSON.parse(res.text).id);
- log.debug('id: ' + ruleId);
- });
-
- after('Clean up detection rule', async function () {
- if (ruleId !== undefined) {
- log.debug('id: ' + ruleId);
- await supertest
- .delete('/api/detection_engine/rules?id=' + ruleId)
- .set('kbn-xsrf', 'true')
- .expect(200);
- }
- });
-
- after('Clean up data:metricbeat-*', async function () {
- if (dataId !== undefined) {
- log.info('Delete index pattern');
- log.debug('id: ' + dataId);
- await supertest
- .delete('/api/index_patterns/index_pattern/' + dataId)
- .set('kbn-xsrf', 'true')
- .expect(200);
- }
-
- log.info('Delete index');
- await esArchiver.unload(ARCHIVE);
- });
-
- after('Clean up .siem-signal-*', async function () {
- if (signalsId !== undefined) {
- log.info('Delete index pattern: .siem-signals-*');
- log.debug('id: ' + signalsId);
- await supertest
- .delete('/api/index_patterns/index_pattern/' + signalsId)
- .set('kbn-xsrf', 'true')
- .expect(200);
- }
-
- log.info('Delete index alias: .siem-signals-default');
- await esSupertest
- .delete('/.siem-signals-default-000001/_alias/.siem-signals-default')
- .expect(200);
-
- log.info('Delete index: .siem-signals-default-000001');
- await esSupertest.delete('/.siem-signals-default-000001').expect(200);
- });
-
- it('Should generate alerts based on remote events', async function () {
- log.info('Check if any alert got to .siem-signals-*');
- await PageObjects.common.navigateToApp('discover', { insertTimestamp: false });
- await PageObjects.discover.selectIndexPattern('.siem-signals-*');
- await retry.tryForTime(30000, async () => {
- const hitCount = await PageObjects.discover.getHitCount();
- log.debug('### hit count = ' + hitCount);
- expect(hitCount).to.be('100');
- });
- });
- });
});
};
From afc07c376fe774e88c9096582ffb7a2cd651efc0 Mon Sep 17 00:00:00 2001
From: Constance
Date: Mon, 12 Jul 2021 10:51:38 -0700
Subject: [PATCH 29/31] [Enterprise Search] Update README/description (#105151)
* Update plugin README with beta copy & fancier product info
* Update API dev docs plugin description punctuation
* Update README dev section
- Remote reference to native auth - no longer applicable in 7.14
- Add reference to Getting Started guide (Elastic only)
* Run node scripts/build_plugin_list_docs to update https://www.elastic.co/guide/en/kibana/master/plugin-list.html
* Add link to main Kibana README
- which should link out to their contributing docs
---
docs/developer/plugin-list.asciidoc | 2 +-
x-pack/plugins/enterprise_search/README.md | 22 ++++++++++++++++----
x-pack/plugins/enterprise_search/kibana.json | 2 +-
3 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc
index eee92ba433721..2144fd171ff7a 100644
--- a/docs/developer/plugin-list.asciidoc
+++ b/docs/developer/plugin-list.asciidoc
@@ -393,7 +393,7 @@ security and spaces filtering as well as performing audit logging.
|{kib-repo}blob/{branch}/x-pack/plugins/enterprise_search/README.md[enterpriseSearch]
-|This plugin's goal is to provide a Kibana user interface to the Enterprise Search solution's products (App Search and Workplace Search). In it's current MVP state, the plugin provides the following with the goal of gathering user feedback and raising product awareness:
+|This plugin provides beta Kibana user interfaces for managing the Enterprise Search solution and its products, App Search and Workplace Search.
|{kib-repo}blob/{branch}/x-pack/plugins/event_log/README.md[eventLog]
diff --git a/x-pack/plugins/enterprise_search/README.md b/x-pack/plugins/enterprise_search/README.md
index 0b067e25e32e8..96b0391bbc8da 100644
--- a/x-pack/plugins/enterprise_search/README.md
+++ b/x-pack/plugins/enterprise_search/README.md
@@ -2,16 +2,30 @@
## Overview
-This plugin's goal is to provide a Kibana user interface to the Enterprise Search solution's products (App Search and Workplace Search). In it's current MVP state, the plugin provides the following with the goal of gathering user feedback and raising product awareness:
+This plugin provides beta Kibana user interfaces for managing the Enterprise Search solution and its products, App Search and Workplace Search.
-- **App Search:** A basic engines overview with links into the product.
-- **Workplace Search:** A simple app overview with basic statistics, links to the sources, users (if standard auth), and product settings.
+> :warning: The Kibana interface for Enterprise Search is a beta feature. It is subject to change and is not covered by the same level of support as generally available features. This interface will become the sole management panel for Enterprise Search with the 8.0 release. Until then, the standalone Enterprise Search UI remains available and supported.
+
+### App Search
+
+
+
+Add rich, relevant search to your apps and websites. https://www.elastic.co/app-search/
+
+### Workplace Search
+
+
+
+Unify all your team's content into a personalized search experience. https://www.elastic.co/workplace-search/
## Development
1. When developing locally, Enterprise Search should be running locally alongside Kibana on `localhost:3002`.
2. Update `config/kibana.dev.yml` with `enterpriseSearch.host: 'http://localhost:3002'`
-3. For faster QA/development, run Enterprise Search on [elasticsearch-native auth](https://www.elastic.co/guide/en/app-search/current/security-and-users.html#app-search-self-managed-security-and-user-management-elasticsearch-native-realm) and log in as the `elastic` superuser on Kibana.
+
+Problems? If you're an Elastic Enterprise Search engineer, please reach out to @elastic/enterprise-search-frontend for questions or our in-depth Getting Started developer guide.
+
+Don't forget to read Kibana's [contributing documentation](https://github.com/elastic/kibana/#building-and-running-kibana-andor-contributing-code) and developer guides for more general info on the Kibana ecosystem.
### Kea
diff --git a/x-pack/plugins/enterprise_search/kibana.json b/x-pack/plugins/enterprise_search/kibana.json
index f8b4261114a22..723b24f951434 100644
--- a/x-pack/plugins/enterprise_search/kibana.json
+++ b/x-pack/plugins/enterprise_search/kibana.json
@@ -12,5 +12,5 @@
"name": "Enterprise Search",
"githubTeam": "enterprise-search-frontend"
},
- "description": "Adds dashboards for discovering and managing Enterprise Search products"
+ "description": "Adds dashboards for discovering and managing Enterprise Search products."
}
From a5eadd054dda57025b53d90f17d6d7bb44e4de49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?=
Date: Mon, 12 Jul 2021 19:54:20 +0200
Subject: [PATCH 30/31] [Fleet] Prevent popovers on agent logs page from being
stuck (#105253)
---
.../components/agent_logs/filter_dataset.tsx | 9 ++++++---
.../components/agent_logs/filter_log_level.tsx | 9 ++++++---
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx
index 47174561230ba..18f6a8b565ab9 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { memo, useState, useEffect } from 'react';
+import React, { memo, useState, useEffect, useCallback } from 'react';
import { EuiPopover, EuiFilterButton, EuiFilterSelectItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -22,6 +22,9 @@ export const DatasetFilter: React.FunctionComponent<{
const [isLoading, setIsLoading] = useState(false);
const [datasetValues, setDatasetValues] = useState([AGENT_DATASET]);
+ const togglePopover = useCallback(() => setIsOpen((prevIsOpen) => !prevIsOpen), [setIsOpen]);
+ const closePopover = useCallback(() => setIsOpen(false), [setIsOpen]);
+
useEffect(() => {
const fetchValues = async () => {
setIsLoading(true);
@@ -48,7 +51,7 @@ export const DatasetFilter: React.FunctionComponent<{
button={
setIsOpen(true)}
+ onClick={togglePopover}
isSelected={isOpen}
isLoading={isLoading}
numFilters={datasetValues.length}
@@ -61,7 +64,7 @@ export const DatasetFilter: React.FunctionComponent<{
}
isOpen={isOpen}
- closePopover={() => setIsOpen(false)}
+ closePopover={closePopover}
panelPaddingSize="none"
>
{datasetValues.map((dataset) => (
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx
index 120f21fe68207..b423f3a8a57b3 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { memo, useState, useEffect } from 'react';
+import React, { memo, useState, useEffect, useCallback } from 'react';
import { EuiPopover, EuiFilterButton, EuiFilterSelectItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -33,6 +33,9 @@ export const LogLevelFilter: React.FunctionComponent<{
const [isLoading, setIsLoading] = useState(false);
const [levelValues, setLevelValues] = useState([]);
+ const togglePopover = useCallback(() => setIsOpen((prevIsOpen) => !prevIsOpen), []);
+ const closePopover = useCallback(() => setIsOpen(false), []);
+
useEffect(() => {
const fetchValues = async () => {
setIsLoading(true);
@@ -59,7 +62,7 @@ export const LogLevelFilter: React.FunctionComponent<{
button={
setIsOpen(true)}
+ onClick={togglePopover}
isSelected={isOpen}
isLoading={isLoading}
numFilters={levelValues.length}
@@ -72,7 +75,7 @@ export const LogLevelFilter: React.FunctionComponent<{
}
isOpen={isOpen}
- closePopover={() => setIsOpen(false)}
+ closePopover={closePopover}
panelPaddingSize="none"
>
{levelValues.map((level) => (
From 76f49565c130edfb8a3f72cd807cfc3c73eae4a5 Mon Sep 17 00:00:00 2001
From: Larry Gregory
Date: Mon, 12 Jul 2021 14:18:35 -0400
Subject: [PATCH 31/31] Support authenticating to Elasticsearch via service
account tokens (#102121)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
config/kibana.yml | 4 +
...n-core-server.elasticsearchclientconfig.md | 2 +-
...-plugin-core-server.elasticsearchconfig.md | 3 +-
...elasticsearchconfig.serviceaccounttoken.md | 15 +++
...ore-server.elasticsearchconfig.username.md | 2 +-
...-server.legacyelasticsearchclientconfig.md | 2 +-
docs/setup/settings.asciidoc | 5 +
src/cli/serve/serve.js | 12 +-
.../client/client_config.test.ts | 48 ++++++-
.../elasticsearch/client/client_config.ts | 16 ++-
.../elasticsearch_config.test.ts | 20 +++
.../elasticsearch/elasticsearch_config.ts | 22 ++++
.../legacy/cluster_client.test.ts | 49 +++++++
.../elasticsearch/legacy/cluster_client.ts | 7 +
.../elasticsearch_client_config.test.ts | 122 ++++++++++++++++++
.../legacy/elasticsearch_client_config.ts | 11 +-
src/core/server/server.api.md | 6 +-
.../resources/base/bin/kibana-docker | 1 +
18 files changed, 327 insertions(+), 20 deletions(-)
create mode 100644 docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.serviceaccounttoken.md
diff --git a/config/kibana.yml b/config/kibana.yml
index eefb6bb8bacda..dea9849f17b28 100644
--- a/config/kibana.yml
+++ b/config/kibana.yml
@@ -42,6 +42,10 @@
#elasticsearch.username: "kibana_system"
#elasticsearch.password: "pass"
+# Kibana can also authenticate to Elasticsearch via "service account tokens".
+# If may use this token instead of a username/password.
+# elasticsearch.serviceAccountToken: "my_token"
+
# Enables SSL and paths to the PEM-format SSL certificate and SSL key files, respectively.
# These settings enable SSL for outgoing requests from the Kibana server to the browser.
#server.ssl.enabled: false
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md
index a854e5ddad19a..208e0e0175d71 100644
--- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md
@@ -9,7 +9,7 @@ Configuration options to be used to create a [cluster client](./kibana-plugin-co
Signature:
```typescript
-export declare type ElasticsearchClientConfig = Pick & {
+export declare type ElasticsearchClientConfig = Pick & {
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ClientOptions['pingTimeout'];
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
ssl?: Partial;
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.md
index d87ea63d59b8d..a9ed614ba7552 100644
--- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.md
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.md
@@ -31,10 +31,11 @@ export declare class ElasticsearchConfig
| [pingTimeout](./kibana-plugin-core-server.elasticsearchconfig.pingtimeout.md) | | Duration
| Timeout after which PING HTTP request will be aborted and retried. |
| [requestHeadersWhitelist](./kibana-plugin-core-server.elasticsearchconfig.requestheaderswhitelist.md) | | string[]
| List of Kibana client-side headers to send to Elasticsearch when request scoped cluster client is used. If this is an empty array then \*no\* client-side will be sent. |
| [requestTimeout](./kibana-plugin-core-server.elasticsearchconfig.requesttimeout.md) | | Duration
| Timeout after which HTTP request will be aborted and retried. |
+| [serviceAccountToken](./kibana-plugin-core-server.elasticsearchconfig.serviceaccounttoken.md) | | string
| If Elasticsearch security features are enabled, this setting provides the service account token that the Kibana server users to perform its administrative functions.This is an alternative to specifying a username and password. |
| [shardTimeout](./kibana-plugin-core-server.elasticsearchconfig.shardtimeout.md) | | Duration
| Timeout for Elasticsearch to wait for responses from shards. Set to 0 to disable. |
| [sniffInterval](./kibana-plugin-core-server.elasticsearchconfig.sniffinterval.md) | | false | Duration
| Interval to perform a sniff operation and make sure the list of nodes is complete. If false
then sniffing is disabled. |
| [sniffOnConnectionFault](./kibana-plugin-core-server.elasticsearchconfig.sniffonconnectionfault.md) | | boolean
| Specifies whether the client should immediately sniff for a more current list of nodes when a connection dies. |
| [sniffOnStart](./kibana-plugin-core-server.elasticsearchconfig.sniffonstart.md) | | boolean
| Specifies whether the client should attempt to detect the rest of the cluster when it is first instantiated. |
| [ssl](./kibana-plugin-core-server.elasticsearchconfig.ssl.md) | | Pick<SslConfigSchema, Exclude<keyof SslConfigSchema, 'certificateAuthorities' | 'keystore' | 'truststore'>> & {
certificateAuthorities?: string[];
}
| Set of settings configure SSL connection between Kibana and Elasticsearch that are required when xpack.ssl.verification_mode
in Elasticsearch is set to either certificate
or full
. |
-| [username](./kibana-plugin-core-server.elasticsearchconfig.username.md) | | string
| If Elasticsearch is protected with basic authentication, this setting provides the username that the Kibana server uses to perform its administrative functions. |
+| [username](./kibana-plugin-core-server.elasticsearchconfig.username.md) | | string
| If Elasticsearch is protected with basic authentication, this setting provides the username that the Kibana server uses to perform its administrative functions. Cannot be used in conjunction with serviceAccountToken. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.serviceaccounttoken.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.serviceaccounttoken.md
new file mode 100644
index 0000000000000..5934e83de17a4
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.serviceaccounttoken.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchConfig](./kibana-plugin-core-server.elasticsearchconfig.md) > [serviceAccountToken](./kibana-plugin-core-server.elasticsearchconfig.serviceaccounttoken.md)
+
+## ElasticsearchConfig.serviceAccountToken property
+
+If Elasticsearch security features are enabled, this setting provides the service account token that the Kibana server users to perform its administrative functions.
+
+This is an alternative to specifying a username and password.
+
+Signature:
+
+```typescript
+readonly serviceAccountToken?: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.username.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.username.md
index 14db9f2e36ccf..959870ff43a0f 100644
--- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.username.md
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.username.md
@@ -4,7 +4,7 @@
## ElasticsearchConfig.username property
-If Elasticsearch is protected with basic authentication, this setting provides the username that the Kibana server uses to perform its administrative functions.
+If Elasticsearch is protected with basic authentication, this setting provides the username that the Kibana server uses to perform its administrative functions. Cannot be used in conjunction with serviceAccountToken.
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearchclientconfig.md b/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearchclientconfig.md
index b028a09bee453..a80ebe2fee493 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearchclientconfig.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearchclientconfig.md
@@ -11,7 +11,7 @@
Signature:
```typescript
-export declare type LegacyElasticsearchClientConfig = Pick & Pick & {
+export declare type LegacyElasticsearchClientConfig = Pick & Pick & {
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout'];
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ConfigOptions['requestTimeout'];
sniffInterval?: ElasticsearchConfig['sniffInterval'] | ConfigOptions['sniffInterval'];
diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc
index ba333deeb1609..15abd0fa4ad96 100644
--- a/docs/setup/settings.asciidoc
+++ b/docs/setup/settings.asciidoc
@@ -284,6 +284,11 @@ the username and password that the {kib} server uses to perform maintenance
on the {kib} index at startup. {kib} users still need to authenticate with
{es}, which is proxied through the {kib} server.
+|[[elasticsearch-service-account-token]] `elasticsearch.serviceAccountToken:`
+ | beta[]. If your {es} is protected with basic authentication, this token provides the credentials
+that the {kib} server uses to perform maintenance on the {kib} index at startup. This setting
+is an alternative to `elasticsearch.username` and `elasticsearch.password`.
+
| `enterpriseSearch.host`
| The URL of your Enterprise Search instance
diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js
index ad83965efde33..be949350f7229 100644
--- a/src/cli/serve/serve.js
+++ b/src/cli/serve/serve.js
@@ -68,12 +68,14 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) {
delete extraCliOptions.env;
if (opts.dev) {
- if (!has('elasticsearch.username')) {
- set('elasticsearch.username', 'kibana_system');
- }
+ if (!has('elasticsearch.serviceAccountToken')) {
+ if (!has('elasticsearch.username')) {
+ set('elasticsearch.username', 'kibana_system');
+ }
- if (!has('elasticsearch.password')) {
- set('elasticsearch.password', 'changeme');
+ if (!has('elasticsearch.password')) {
+ set('elasticsearch.password', 'changeme');
+ }
}
if (opts.ssl) {
diff --git a/src/core/server/elasticsearch/client/client_config.test.ts b/src/core/server/elasticsearch/client/client_config.test.ts
index faca79b3aa6fa..7e16339b40235 100644
--- a/src/core/server/elasticsearch/client/client_config.test.ts
+++ b/src/core/server/elasticsearch/client/client_config.test.ts
@@ -204,11 +204,27 @@ describe('parseClientOptions', () => {
);
});
+ it('adds an authorization header if `serviceAccountToken` is set', () => {
+ expect(
+ parseClientOptions(
+ createConfig({
+ serviceAccountToken: 'ABC123',
+ }),
+ false
+ )
+ ).toEqual(
+ expect.objectContaining({
+ headers: expect.objectContaining({
+ authorization: `Bearer ABC123`,
+ }),
+ })
+ );
+ });
+
it('does not add auth to the nodes', () => {
const options = parseClientOptions(
createConfig({
- username: 'user',
- password: 'pass',
+ serviceAccountToken: 'ABC123',
hosts: ['http://node-A:9200'],
}),
true
@@ -252,6 +268,34 @@ describe('parseClientOptions', () => {
]
`);
});
+
+ it('does not add the authorization header even if `serviceAccountToken` is set', () => {
+ expect(
+ parseClientOptions(
+ createConfig({
+ serviceAccountToken: 'ABC123',
+ }),
+ true
+ ).headers
+ ).not.toHaveProperty('authorization');
+ });
+
+ it('does not add auth to the nodes even if `serviceAccountToken` is set', () => {
+ const options = parseClientOptions(
+ createConfig({
+ serviceAccountToken: 'ABC123',
+ hosts: ['http://node-A:9200'],
+ }),
+ true
+ );
+ expect(options.nodes).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "url": "http://node-a:9200/",
+ },
+ ]
+ `);
+ });
});
});
diff --git a/src/core/server/elasticsearch/client/client_config.ts b/src/core/server/elasticsearch/client/client_config.ts
index 3044b277db902..bbbb1ac247b3b 100644
--- a/src/core/server/elasticsearch/client/client_config.ts
+++ b/src/core/server/elasticsearch/client/client_config.ts
@@ -29,6 +29,7 @@ export type ElasticsearchClientConfig = Pick<
| 'hosts'
| 'username'
| 'password'
+ | 'serviceAccountToken'
> & {
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ClientOptions['pingTimeout'];
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
@@ -74,11 +75,16 @@ export function parseClientOptions(
};
}
- if (config.username && config.password && !scoped) {
- clientOptions.auth = {
- username: config.username,
- password: config.password,
- };
+ if (!scoped) {
+ if (config.username && config.password) {
+ clientOptions.auth = {
+ username: config.username,
+ password: config.password,
+ };
+ } else if (config.serviceAccountToken) {
+ // TODO: change once ES client has native support for service account tokens: https://github.com/elastic/elasticsearch-js/issues/1477
+ clientOptions.headers!.authorization = `Bearer ${config.serviceAccountToken}`;
+ }
}
clientOptions.nodes = config.hosts.map((host) => convertHost(host));
diff --git a/src/core/server/elasticsearch/elasticsearch_config.test.ts b/src/core/server/elasticsearch/elasticsearch_config.test.ts
index f8ef1a7a20a83..6e05baac88e34 100644
--- a/src/core/server/elasticsearch/elasticsearch_config.test.ts
+++ b/src/core/server/elasticsearch/elasticsearch_config.test.ts
@@ -41,6 +41,7 @@ test('set correct defaults', () => {
"authorization",
],
"requestTimeout": "PT30S",
+ "serviceAccountToken": undefined,
"shardTimeout": "PT30S",
"sniffInterval": false,
"sniffOnConnectionFault": false,
@@ -377,3 +378,22 @@ test('#username throws if equal to "elastic", only while running from source', (
);
expect(() => config.schema.validate(obj, { dist: true })).not.toThrow();
});
+
+test('serviceAccountToken throws if username is also set', () => {
+ const obj = {
+ username: 'elastic',
+ serviceAccountToken: 'abc123',
+ };
+
+ expect(() => config.schema.validate(obj)).toThrowErrorMatchingInlineSnapshot(
+ `"[serviceAccountToken]: serviceAccountToken cannot be specified when \\"username\\" is also set."`
+ );
+});
+
+test('serviceAccountToken does not throw if username is not set', () => {
+ const obj = {
+ serviceAccountToken: 'abc123',
+ };
+
+ expect(() => config.schema.validate(obj)).not.toThrow();
+});
diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts
index b2b25cda3ac2a..e756d9da867b3 100644
--- a/src/core/server/elasticsearch/elasticsearch_config.ts
+++ b/src/core/server/elasticsearch/elasticsearch_config.ts
@@ -53,6 +53,18 @@ export const configSchema = schema.object({
)
),
password: schema.maybe(schema.string()),
+ serviceAccountToken: schema.maybe(
+ schema.conditional(
+ schema.siblingRef('username'),
+ schema.never(),
+ schema.string(),
+ schema.string({
+ validate: () => {
+ return `serviceAccountToken cannot be specified when "username" is also set.`;
+ },
+ })
+ )
+ ),
requestHeadersWhitelist: schema.oneOf(
[
schema.string({
@@ -272,6 +284,7 @@ export class ElasticsearchConfig {
/**
* If Elasticsearch is protected with basic authentication, this setting provides
* the username that the Kibana server uses to perform its administrative functions.
+ * Cannot be used in conjunction with serviceAccountToken.
*/
public readonly username?: string;
@@ -281,6 +294,14 @@ export class ElasticsearchConfig {
*/
public readonly password?: string;
+ /**
+ * If Elasticsearch security features are enabled, this setting provides the service account
+ * token that the Kibana server users to perform its administrative functions.
+ *
+ * This is an alternative to specifying a username and password.
+ */
+ public readonly serviceAccountToken?: string;
+
/**
* Set of settings configure SSL connection between Kibana and Elasticsearch that
* are required when `xpack.ssl.verification_mode` in Elasticsearch is set to
@@ -314,6 +335,7 @@ export class ElasticsearchConfig {
this.healthCheckDelay = rawConfig.healthCheck.delay;
this.username = rawConfig.username;
this.password = rawConfig.password;
+ this.serviceAccountToken = rawConfig.serviceAccountToken;
this.customHeaders = rawConfig.customHeaders;
const { alwaysPresentCertificate, verificationMode } = rawConfig.ssl;
diff --git a/src/core/server/elasticsearch/legacy/cluster_client.test.ts b/src/core/server/elasticsearch/legacy/cluster_client.test.ts
index 2ce19570677c5..52bc4bd45660e 100644
--- a/src/core/server/elasticsearch/legacy/cluster_client.test.ts
+++ b/src/core/server/elasticsearch/legacy/cluster_client.test.ts
@@ -101,6 +101,30 @@ describe('#callAsInternalUser', () => {
expect(mockEsClientInstance.ping).toHaveBeenLastCalledWith(mockParams);
});
+ test('sets the authorization header when a service account token is configured', async () => {
+ clusterClient = new LegacyClusterClient(
+ { apiVersion: 'es-version', serviceAccountToken: 'ABC123' } as any,
+ logger.get(),
+ 'custom-type'
+ );
+
+ const mockResponse = { data: 'ping' };
+ const mockParams = { param: 'ping' };
+ mockEsClientInstance.ping.mockImplementation(function mockCall(this: any) {
+ return Promise.resolve({
+ context: this,
+ response: mockResponse,
+ });
+ });
+
+ await clusterClient.callAsInternalUser('ping', mockParams);
+
+ expect(mockEsClientInstance.ping).toHaveBeenCalledWith({
+ headers: { authorization: 'Bearer ABC123' },
+ param: 'ping',
+ });
+ });
+
test('correctly deals with nested endpoint', async () => {
const mockResponse = { data: 'authenticate' };
const mockParams = { param: 'authenticate' };
@@ -355,6 +379,31 @@ describe('#asScoped', () => {
);
});
+ test('does not set the authorization header when a service account token is configured', async () => {
+ clusterClient = new LegacyClusterClient(
+ {
+ apiVersion: 'es-version',
+ requestHeadersWhitelist: ['zero'],
+ serviceAccountToken: 'ABC123',
+ } as any,
+ logger.get(),
+ 'custom-type'
+ );
+
+ clusterClient.asScoped(
+ httpServerMock.createRawRequest({ headers: { zero: '0', one: '1', two: '2', three: '3' } })
+ );
+
+ const expectedHeaders = { zero: '0' };
+
+ expect(MockScopedClusterClient).toHaveBeenCalledTimes(1);
+ expect(MockScopedClusterClient).toHaveBeenCalledWith(
+ expect.any(Function),
+ expect.any(Function),
+ expectedHeaders
+ );
+ });
+
test('both scoped and internal API caller fail if cluster client is closed', async () => {
clusterClient.asScoped(
httpServerMock.createRawRequest({ headers: { zero: '0', one: '1', two: '2', three: '3' } })
diff --git a/src/core/server/elasticsearch/legacy/cluster_client.ts b/src/core/server/elasticsearch/legacy/cluster_client.ts
index bdb2ca4d01b3c..6a6765b67da9f 100644
--- a/src/core/server/elasticsearch/legacy/cluster_client.ts
+++ b/src/core/server/elasticsearch/legacy/cluster_client.ts
@@ -147,6 +147,13 @@ export class LegacyClusterClient implements ILegacyClusterClient {
) => {
this.assertIsNotClosed();
+ if (this.config.serviceAccountToken) {
+ clientParams.headers = {
+ ...clientParams.headers,
+ authorization: `Bearer ${this.config.serviceAccountToken}`,
+ };
+ }
+
return await (callAPI.bind(null, this.client) as LegacyAPICaller)(
endpoint,
clientParams,
diff --git a/src/core/server/elasticsearch/legacy/elasticsearch_client_config.test.ts b/src/core/server/elasticsearch/legacy/elasticsearch_client_config.test.ts
index 6239ad270d5b5..a343c0d5d2ad1 100644
--- a/src/core/server/elasticsearch/legacy/elasticsearch_client_config.test.ts
+++ b/src/core/server/elasticsearch/legacy/elasticsearch_client_config.test.ts
@@ -333,6 +333,128 @@ describe('#auth', () => {
});
});
+describe('#serviceAccountToken', () => {
+ it('is set when #auth is true, and a token is provided', () => {
+ expect(
+ parseElasticsearchClientConfig(
+ {
+ apiVersion: 'v7.0.0',
+ customHeaders: { xsrf: 'something' },
+ sniffOnStart: true,
+ sniffOnConnectionFault: true,
+ hosts: ['https://es.local'],
+ requestHeadersWhitelist: [],
+ serviceAccountToken: 'ABC123',
+ },
+ logger.get(),
+ 'custom-type',
+ { auth: true }
+ )
+ ).toMatchInlineSnapshot(`
+ Object {
+ "apiVersion": "v7.0.0",
+ "hosts": Array [
+ Object {
+ "headers": Object {
+ "x-elastic-product-origin": "kibana",
+ "xsrf": "something",
+ },
+ "host": "es.local",
+ "path": "/",
+ "port": "443",
+ "protocol": "https:",
+ "query": null,
+ },
+ ],
+ "keepAlive": true,
+ "log": [Function],
+ "serviceAccountToken": "ABC123",
+ "sniffOnConnectionFault": true,
+ "sniffOnStart": true,
+ }
+ `);
+ });
+
+ it('is not set when #auth is true, and a token is not provided', () => {
+ expect(
+ parseElasticsearchClientConfig(
+ {
+ apiVersion: 'v7.0.0',
+ customHeaders: { xsrf: 'something' },
+ sniffOnStart: true,
+ sniffOnConnectionFault: true,
+ hosts: ['https://es.local'],
+ requestHeadersWhitelist: [],
+ },
+ logger.get(),
+ 'custom-type',
+ { auth: true }
+ )
+ ).toMatchInlineSnapshot(`
+ Object {
+ "apiVersion": "v7.0.0",
+ "hosts": Array [
+ Object {
+ "headers": Object {
+ "x-elastic-product-origin": "kibana",
+ "xsrf": "something",
+ },
+ "host": "es.local",
+ "path": "/",
+ "port": "443",
+ "protocol": "https:",
+ "query": null,
+ },
+ ],
+ "keepAlive": true,
+ "log": [Function],
+ "sniffOnConnectionFault": true,
+ "sniffOnStart": true,
+ }
+ `);
+ });
+
+ it('is not set when #auth is false, and a token is provided', () => {
+ expect(
+ parseElasticsearchClientConfig(
+ {
+ apiVersion: 'v7.0.0',
+ customHeaders: { xsrf: 'something' },
+ sniffOnStart: true,
+ sniffOnConnectionFault: true,
+ hosts: ['https://es.local'],
+ requestHeadersWhitelist: [],
+ serviceAccountToken: 'ABC123',
+ },
+ logger.get(),
+ 'custom-type',
+ { auth: false }
+ )
+ ).toMatchInlineSnapshot(`
+ Object {
+ "apiVersion": "v7.0.0",
+ "hosts": Array [
+ Object {
+ "headers": Object {
+ "x-elastic-product-origin": "kibana",
+ "xsrf": "something",
+ },
+ "host": "es.local",
+ "path": "/",
+ "port": "443",
+ "protocol": "https:",
+ "query": null,
+ },
+ ],
+ "keepAlive": true,
+ "log": [Function],
+ "sniffOnConnectionFault": true,
+ "sniffOnStart": true,
+ }
+ `);
+ });
+});
+
describe('#customHeaders', () => {
test('override the default headers', () => {
const headerKey = Object.keys(DEFAULT_HEADERS)[0];
diff --git a/src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts b/src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts
index d68e7635c57cb..3d81caefad457 100644
--- a/src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts
+++ b/src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts
@@ -35,6 +35,7 @@ export type LegacyElasticsearchClientConfig = Pick & {
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout'];
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ConfigOptions['requestTimeout'];
@@ -61,6 +62,7 @@ interface LegacyElasticsearchClientConfigOverrides {
/** @internal */
type ExtendedConfigOptions = ConfigOptions &
Partial<{
+ serviceAccountToken?: string;
ssl: Partial<{
rejectUnauthorized: boolean;
checkServerIdentity: typeof checkServerIdentity;
@@ -106,9 +108,14 @@ export function parseElasticsearchClientConfig(
esClientConfig.sniffInterval = getDurationAsMs(config.sniffInterval);
}
- const needsAuth = auth !== false && config.username && config.password;
+ const needsAuth =
+ auth !== false && ((config.username && config.password) || config.serviceAccountToken);
if (needsAuth) {
- esClientConfig.httpAuth = `${config.username}:${config.password}`;
+ if (config.username) {
+ esClientConfig.httpAuth = `${config.username}:${config.password}`;
+ } else if (config.serviceAccountToken) {
+ esClientConfig.serviceAccountToken = config.serviceAccountToken;
+ }
}
if (Array.isArray(config.hosts)) {
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index ed55c6e3d09cb..65ea082c9d8a8 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -345,6 +345,7 @@ export const config: {
hosts: Type;
username: Type;
password: Type;
+ serviceAccountToken: Type;
requestHeadersWhitelist: Type;
customHeaders: Type>;
shardTimeout: Type;
@@ -948,7 +949,7 @@ export type ElasticsearchClient = Omit & {
+export type ElasticsearchClientConfig = Pick & {
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ClientOptions['pingTimeout'];
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
ssl?: Partial;
@@ -968,6 +969,7 @@ export class ElasticsearchConfig {
readonly pingTimeout: Duration;
readonly requestHeadersWhitelist: string[];
readonly requestTimeout: Duration;
+ readonly serviceAccountToken?: string;
readonly shardTimeout: Duration;
readonly sniffInterval: false | Duration;
readonly sniffOnConnectionFault: boolean;
@@ -1675,7 +1677,7 @@ export class LegacyClusterClient implements ILegacyClusterClient {
}
// @public @deprecated (undocumented)
-export type LegacyElasticsearchClientConfig = Pick & Pick & {
+export type LegacyElasticsearchClientConfig = Pick & Pick & {
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout'];
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ConfigOptions['requestTimeout'];
sniffInterval?: ElasticsearchConfig['sniffInterval'] | ConfigOptions['sniffInterval'];
diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker
index 39a7665f1ce5e..c7a129418765b 100755
--- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker
+++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker
@@ -51,6 +51,7 @@ kibana_vars=(
elasticsearch.pingTimeout
elasticsearch.requestHeadersWhitelist
elasticsearch.requestTimeout
+ elasticsearch.serviceAccountToken
elasticsearch.shardTimeout
elasticsearch.sniffInterval
elasticsearch.sniffOnConnectionFault