From b08594ad5c2cdbd04acfd3805f1a5d244b9f71ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Fri, 18 Sep 2020 12:53:42 +0100 Subject: [PATCH 01/40] [APM] Bug: Set minimum scale to 1% for the Transaction error rate chart (#77861) --- .../shared/charts/ErroneousTransactionsRateChart/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/shared/charts/ErroneousTransactionsRateChart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/ErroneousTransactionsRateChart/index.tsx index e806f556347f1..e64357c085209 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/ErroneousTransactionsRateChart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/ErroneousTransactionsRateChart/index.tsx @@ -6,6 +6,7 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; +import { max } from 'lodash'; import React, { useCallback } from 'react'; import { useParams } from 'react-router-dom'; import { asPercent } from '../../../../../common/utils/formatters'; @@ -56,6 +57,7 @@ export function ErroneousTransactionsRateChart() { ); const errorRates = data?.erroneousTransactionsRate || []; + const maxRate = max(errorRates.map((errorRate) => errorRate.y)); return ( @@ -70,7 +72,7 @@ export function ErroneousTransactionsRateChart() { Date: Fri, 18 Sep 2020 14:02:37 +0200 Subject: [PATCH 02/40] [APM] Align APM severity levels with ML (#77818) --- x-pack/plugins/apm/common/alert_types.ts | 37 ++++++ .../apm/common/anomaly_detection.test.ts | 39 ------ .../plugins/apm/common/anomaly_detection.ts | 84 ++----------- .../apm/common/service_health_status.ts | 79 ++++++++++++ x-pack/plugins/apm/kibana.json | 12 +- .../SelectAnomalySeverity.tsx | 119 ++++++------------ .../index.tsx | 15 ++- .../ServiceMap/Popover/AnomalyDetection.tsx | 13 +- .../app/ServiceMap/cytoscapeOptions.ts | 33 ++--- .../ServiceList/HealthBadge.tsx | 20 +-- .../ServiceList/__test__/List.test.js | 16 ++- .../__test__/__snapshots__/List.test.js.snap | 10 +- .../app/ServiceOverview/ServiceList/index.tsx | 43 +++---- .../__test__/ServiceOverview.test.tsx | 7 +- .../ServiceOverview.test.tsx.snap | 8 +- .../components/app/ServiceOverview/index.tsx | 3 - ...transaction_duration_anomaly_alert_type.ts | 28 ++++- .../lib/service_map/get_service_anomalies.ts | 10 +- .../transform_service_map_responses.test.ts | 3 + .../get_services/get_services_items_stats.ts | 4 +- x-pack/plugins/ml/common/index.ts | 2 + x-pack/plugins/ml/kibana.json | 3 + .../__snapshots__/service_maps.snap | 36 ++++++ .../trial/tests/service_maps/service_maps.ts | 2 + .../trial/tests/services/top_services.ts | 18 +-- 25 files changed, 362 insertions(+), 282 deletions(-) delete mode 100644 x-pack/plugins/apm/common/anomaly_detection.test.ts create mode 100644 x-pack/plugins/apm/common/service_health_status.ts diff --git a/x-pack/plugins/apm/common/alert_types.ts b/x-pack/plugins/apm/common/alert_types.ts index 15a3c642faf32..a234226d18034 100644 --- a/x-pack/plugins/apm/common/alert_types.ts +++ b/x-pack/plugins/apm/common/alert_types.ts @@ -5,6 +5,8 @@ */ import { i18n } from '@kbn/i18n'; +import { ValuesType } from 'utility-types'; +import { ANOMALY_SEVERITY, ANOMALY_THRESHOLD } from '../../ml/common'; export enum AlertType { ErrorCount = 'apm.error_rate', // ErrorRate was renamed to ErrorCount but the key is kept as `error_rate` for backwards-compat. @@ -55,6 +57,41 @@ export const ALERT_TYPES_CONFIG = { }, }; +export const ANOMALY_ALERT_SEVERITY_TYPES = [ + { + type: ANOMALY_SEVERITY.CRITICAL, + label: i18n.translate('xpack.apm.alerts.anomalySeverity.criticalLabel', { + defaultMessage: 'critical', + }), + threshold: ANOMALY_THRESHOLD.CRITICAL, + }, + { + type: ANOMALY_SEVERITY.MAJOR, + label: i18n.translate('xpack.apm.alerts.anomalySeverity.majorLabel', { + defaultMessage: 'major', + }), + threshold: ANOMALY_THRESHOLD.MAJOR, + }, + { + type: ANOMALY_SEVERITY.MINOR, + label: i18n.translate('xpack.apm.alerts.anomalySeverity.minor', { + defaultMessage: 'minor', + }), + threshold: ANOMALY_THRESHOLD.MINOR, + }, + { + type: ANOMALY_SEVERITY.WARNING, + label: i18n.translate('xpack.apm.alerts.anomalySeverity.warningLabel', { + defaultMessage: 'warning', + }), + threshold: ANOMALY_THRESHOLD.WARNING, + }, +] as const; + +export type AnomalyAlertSeverityType = ValuesType< + typeof ANOMALY_ALERT_SEVERITY_TYPES +>['type']; + // Server side registrations // x-pack/plugins/apm/server/lib/alerts/.ts // x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts diff --git a/x-pack/plugins/apm/common/anomaly_detection.test.ts b/x-pack/plugins/apm/common/anomaly_detection.test.ts deleted file mode 100644 index 21963b5300f83..0000000000000 --- a/x-pack/plugins/apm/common/anomaly_detection.test.ts +++ /dev/null @@ -1,39 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getSeverity, Severity } from './anomaly_detection'; - -describe('getSeverity', () => { - describe('when score is undefined', () => { - it('returns undefined', () => { - expect(getSeverity(undefined)).toEqual(undefined); - }); - }); - - describe('when score < 25', () => { - it('returns warning', () => { - expect(getSeverity(10)).toEqual(Severity.warning); - }); - }); - - describe('when score is between 25 and 50', () => { - it('returns minor', () => { - expect(getSeverity(40)).toEqual(Severity.minor); - }); - }); - - describe('when score is between 50 and 75', () => { - it('returns major', () => { - expect(getSeverity(60)).toEqual(Severity.major); - }); - }); - - describe('when score is 75 or more', () => { - it('returns critical', () => { - expect(getSeverity(100)).toEqual(Severity.critical); - }); - }); -}); diff --git a/x-pack/plugins/apm/common/anomaly_detection.ts b/x-pack/plugins/apm/common/anomaly_detection.ts index 5d80ee6381267..dc5731e88083c 100644 --- a/x-pack/plugins/apm/common/anomaly_detection.ts +++ b/x-pack/plugins/apm/common/anomaly_detection.ts @@ -5,89 +5,31 @@ */ import { i18n } from '@kbn/i18n'; -import { EuiTheme } from '../../../legacy/common/eui_styled_components'; +import { ANOMALY_SEVERITY } from '../../ml/common'; +import { + getSeverityType, + getSeverityColor as mlGetSeverityColor, +} from '../../ml/common'; +import { ServiceHealthStatus } from './service_health_status'; export interface ServiceAnomalyStats { transactionType?: string; anomalyScore?: number; actualValue?: number; jobId?: string; + healthStatus: ServiceHealthStatus; } -export enum Severity { - critical = 'critical', - major = 'major', - minor = 'minor', - warning = 'warning', -} - -// TODO: Replace with `getSeverity` from: -// https://github.com/elastic/kibana/blob/0f964f66916480f2de1f4b633e5afafc08cf62a0/x-pack/plugins/ml/common/util/anomaly_utils.ts#L129 -export function getSeverity(score?: number) { - if (typeof score !== 'number') { - return undefined; - } else if (score < 25) { - return Severity.warning; - } else if (score >= 25 && score < 50) { - return Severity.minor; - } else if (score >= 50 && score < 75) { - return Severity.major; - } else if (score >= 75) { - return Severity.critical; - } else { - return undefined; +export function getSeverity(score: number | undefined) { + if (score === undefined) { + return ANOMALY_SEVERITY.UNKNOWN; } -} -export function getSeverityColor(theme: EuiTheme, severity?: Severity) { - switch (severity) { - case Severity.warning: - return theme.eui.euiColorVis0; - case Severity.minor: - case Severity.major: - return theme.eui.euiColorVis5; - case Severity.critical: - return theme.eui.euiColorVis9; - default: - return; - } + return getSeverityType(score); } -export function getSeverityLabel(severity?: Severity) { - switch (severity) { - case Severity.critical: - return i18n.translate( - 'xpack.apm.servicesTable.serviceHealthStatus.critical', - { - defaultMessage: 'Critical', - } - ); - - case Severity.major: - case Severity.minor: - return i18n.translate( - 'xpack.apm.servicesTable.serviceHealthStatus.warning', - { - defaultMessage: 'Warning', - } - ); - - case Severity.warning: - return i18n.translate( - 'xpack.apm.servicesTable.serviceHealthStatus.healthy', - { - defaultMessage: 'Healthy', - } - ); - - default: - return i18n.translate( - 'xpack.apm.servicesTable.serviceHealthStatus.unknown', - { - defaultMessage: 'Unknown', - } - ); - } +export function getSeverityColor(score: number) { + return mlGetSeverityColor(score); } export const ML_ERRORS = { diff --git a/x-pack/plugins/apm/common/service_health_status.ts b/x-pack/plugins/apm/common/service_health_status.ts new file mode 100644 index 0000000000000..468f06ab97af8 --- /dev/null +++ b/x-pack/plugins/apm/common/service_health_status.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { ANOMALY_SEVERITY } from '../../ml/common'; + +import { EuiTheme } from '../../../legacy/common/eui_styled_components'; + +export enum ServiceHealthStatus { + healthy = 'healthy', + critical = 'critical', + warning = 'warning', + unknown = 'unknown', +} + +export function getServiceHealthStatus({ + severity, +}: { + severity: ANOMALY_SEVERITY; +}) { + switch (severity) { + case ANOMALY_SEVERITY.CRITICAL: + case ANOMALY_SEVERITY.MAJOR: + return ServiceHealthStatus.critical; + + case ANOMALY_SEVERITY.MINOR: + case ANOMALY_SEVERITY.WARNING: + return ServiceHealthStatus.warning; + + case ANOMALY_SEVERITY.LOW: + return ServiceHealthStatus.healthy; + + case ANOMALY_SEVERITY.UNKNOWN: + return ServiceHealthStatus.unknown; + } +} + +export function getServiceHealthStatusColor( + theme: EuiTheme, + status: ServiceHealthStatus +) { + switch (status) { + case ServiceHealthStatus.healthy: + return theme.eui.euiColorVis0; + case ServiceHealthStatus.warning: + return theme.eui.euiColorVis5; + case ServiceHealthStatus.critical: + return theme.eui.euiColorVis9; + case ServiceHealthStatus.unknown: + return theme.eui.euiColorMediumShade; + } +} + +export function getServiceHealthStatusLabel(status: ServiceHealthStatus) { + switch (status) { + case ServiceHealthStatus.critical: + return i18n.translate('xpack.apm.serviceHealthStatus.critical', { + defaultMessage: 'Critical', + }); + + case ServiceHealthStatus.warning: + return i18n.translate('xpack.apm.serviceHealthStatus.warning', { + defaultMessage: 'Warning', + }); + + case ServiceHealthStatus.healthy: + return i18n.translate('xpack.apm.serviceHealthStatus.healthy', { + defaultMessage: 'Healthy', + }); + + case ServiceHealthStatus.unknown: + return i18n.translate('xpack.apm.serviceHealthStatus.unknown', { + defaultMessage: 'Unknown', + }); + } +} diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 8aa4417580337..bdef0f9786a3f 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -23,13 +23,19 @@ ], "server": true, "ui": true, - "configPath": ["xpack", "apm"], - "extraPublicDirs": ["public/style/variables"], + "configPath": [ + "xpack", + "apm" + ], + "extraPublicDirs": [ + "public/style/variables" + ], "requiredBundles": [ "kibanaReact", "kibanaUtils", "observability", "home", - "maps" + "maps", + "ml" ] } diff --git a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx index 5bddfc67200b1..468d08339431c 100644 --- a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx +++ b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx @@ -5,105 +5,60 @@ */ import React from 'react'; -import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiHealth, EuiSpacer, EuiSuperSelect, EuiText } from '@elastic/eui'; +import { getSeverityColor } from '../../../../common/anomaly_detection'; import { - getSeverityColor, - Severity, -} from '../../../../common/anomaly_detection'; -import { useTheme } from '../../../hooks/useTheme'; + AnomalyAlertSeverityType, + ANOMALY_ALERT_SEVERITY_TYPES, +} from '../../../../common/alert_types'; -type SeverityScore = 0 | 25 | 50 | 75; -const ANOMALY_SCORES: SeverityScore[] = [0, 25, 50, 75]; - -const anomalyScoreSeverityMap: { - [key in SeverityScore]: { label: string; severity: Severity }; -} = { - 0: { - label: i18n.translate('xpack.apm.alerts.anomalySeverity.warningLabel', { - defaultMessage: 'warning', - }), - severity: Severity.warning, - }, - 25: { - label: i18n.translate('xpack.apm.alerts.anomalySeverity.minorLabel', { - defaultMessage: 'minor', - }), - severity: Severity.minor, - }, - 50: { - label: i18n.translate('xpack.apm.alerts.anomalySeverity.majorLabel', { - defaultMessage: 'major', - }), - severity: Severity.major, - }, - 75: { - label: i18n.translate('xpack.apm.alerts.anomalySeverity.criticalLabel', { - defaultMessage: 'critical', - }), - severity: Severity.critical, - }, -}; - -export function AnomalySeverity({ - severityScore, -}: { - severityScore: SeverityScore; -}) { - const theme = useTheme(); - const { label, severity } = anomalyScoreSeverityMap[severityScore]; - const defaultColor = theme.eui.euiColorMediumShade; - const color = getSeverityColor(theme, severity) || defaultColor; +export function AnomalySeverity({ type }: { type: AnomalyAlertSeverityType }) { + const selectedOption = ANOMALY_ALERT_SEVERITY_TYPES.find( + (option) => option.type === type + )!; return ( - - {label} + + {selectedOption.label} ); } -const getOption = (value: SeverityScore) => { - return { - value: value.toString(10), - inputDisplay: , - dropdownDisplay: ( - <> - - - -

- -

-
- - ), - }; -}; - interface Props { - onChange: (value: SeverityScore) => void; - value: SeverityScore; + onChange: (value: AnomalyAlertSeverityType) => void; + value: AnomalyAlertSeverityType; } export function SelectAnomalySeverity({ onChange, value }: Props) { - const options = ANOMALY_SCORES.map((anomalyScore) => getOption(anomalyScore)); - return ( { - const selectedAnomalyScore = parseInt( - selectedValue, - 10 - ) as SeverityScore; - onChange(selectedAnomalyScore); + options={ANOMALY_ALERT_SEVERITY_TYPES.map((option) => ({ + value: option.type, + inputDisplay: , + dropdownDisplay: ( + <> + + + +

+ +

+
+ + ), + }))} + valueOfSelected={value} + onChange={(selectedValue: AnomalyAlertSeverityType) => { + onChange(selectedValue); }} /> ); diff --git a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/index.tsx index fb4cda56fce04..ca1f55e9d391a 100644 --- a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/index.tsx @@ -7,6 +7,7 @@ import { useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { ANOMALY_SEVERITY } from '../../../../../ml/common'; import { ALERT_TYPES_CONFIG } from '../../../../common/alert_types'; import { useEnvironments } from '../../../hooks/useEnvironments'; import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes'; @@ -34,7 +35,11 @@ interface Params { serviceName: string; transactionType: string; environment: string; - anomalyScore: 0 | 25 | 50 | 75; + anomalySeverityType: + | ANOMALY_SEVERITY.CRITICAL + | ANOMALY_SEVERITY.MAJOR + | ANOMALY_SEVERITY.MINOR + | ANOMALY_SEVERITY.WARNING; } interface Props { @@ -67,7 +72,7 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { transactionType, serviceName, environment: urlParams.environment || ENVIRONMENT_ALL.value, - anomalyScore: 75, + anomalySeverityType: ANOMALY_SEVERITY.CRITICAL, }; const params = { @@ -84,7 +89,7 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { onChange={(e) => setAlertParams('environment', e.target.value)} />, } + value={} title={i18n.translate( 'xpack.apm.transactionDurationAnomalyAlertTrigger.anomalySeverity', { @@ -93,9 +98,9 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { )} > { - setAlertParams('anomalyScore', value); + setAlertParams('anomalySeverityType', value); }} /> , diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx index 5699d0b56219b..c1192f5f18274 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx @@ -14,6 +14,10 @@ import { EuiIconTip, EuiHealth, } from '@elastic/eui'; +import { + getServiceHealthStatus, + getServiceHealthStatusColor, +} from '../../../../../common/service_health_status'; import { useTheme } from '../../../../hooks/useTheme'; import { fontSize, px } from '../../../../style/variables'; import { asInteger, asDuration } from '../../../../utils/formatters'; @@ -22,7 +26,6 @@ import { popoverWidth } from '../cytoscapeOptions'; import { TRANSACTION_REQUEST } from '../../../../../common/transaction_types'; import { getSeverity, - getSeverityColor, ServiceAnomalyStats, } from '../../../../../common/anomaly_detection'; @@ -59,13 +62,15 @@ export function AnomalyDetection({ serviceName, serviceAnomalyStats }: Props) { const theme = useTheme(); const anomalyScore = serviceAnomalyStats?.anomalyScore; - const anomalySeverity = getSeverity(anomalyScore); + const severity = getSeverity(anomalyScore); const actualValue = serviceAnomalyStats?.actualValue; const mlJobId = serviceAnomalyStats?.jobId; const transactionType = serviceAnomalyStats?.transactionType ?? TRANSACTION_REQUEST; const hasAnomalyDetectionScore = anomalyScore !== undefined; + const healthStatus = getServiceHealthStatus({ severity }); + return ( <>
@@ -81,7 +86,9 @@ export function AnomalyDetection({ serviceName, serviceAnomalyStats }: Props) { - + {ANOMALY_DETECTION_SCORE_METRIC} diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index 1ac7157cc2aad..61ac9bd7cd54c 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -5,26 +5,26 @@ */ import cytoscape from 'cytoscape'; import { CSSProperties } from 'react'; +import { + getServiceHealthStatusColor, + ServiceHealthStatus, +} from '../../../../common/service_health_status'; import { SERVICE_NAME, SPAN_DESTINATION_SERVICE_RESOURCE, } from '../../../../common/elasticsearch_fieldnames'; import { EuiTheme } from '../../../../../observability/public'; import { defaultIcon, iconForNode } from './icons'; -import { - getSeverity, - getSeverityColor, - ServiceAnomalyStats, - Severity, -} from '../../../../common/anomaly_detection'; +import { ServiceAnomalyStats } from '../../../../common/anomaly_detection'; export const popoverWidth = 280; -function getNodeSeverity(el: cytoscape.NodeSingular) { +function getServiceAnomalyStats(el: cytoscape.NodeSingular) { const serviceAnomalyStats: ServiceAnomalyStats | undefined = el.data( 'serviceAnomalyStats' ); - return getSeverity(serviceAnomalyStats?.anomalyScore); + + return serviceAnomalyStats; } function getBorderColorFn( @@ -32,10 +32,11 @@ function getBorderColorFn( ): cytoscape.Css.MapperFunction { return (el: cytoscape.NodeSingular) => { const hasAnomalyDetectionJob = el.data('serviceAnomalyStats') !== undefined; - const nodeSeverity = getNodeSeverity(el); + const anomalyStats = getServiceAnomalyStats(el); if (hasAnomalyDetectionJob) { - return ( - getSeverityColor(theme, nodeSeverity) || theme.eui.euiColorMediumShade + return getServiceHealthStatusColor( + theme, + anomalyStats?.healthStatus ?? ServiceHealthStatus.unknown ); } if (el.hasClass('primary') || el.selected()) { @@ -49,8 +50,8 @@ const getBorderStyle: cytoscape.Css.MapperFunction< cytoscape.NodeSingular, cytoscape.Css.LineStyle > = (el: cytoscape.NodeSingular) => { - const nodeSeverity = getNodeSeverity(el); - if (nodeSeverity === Severity.critical) { + const status = getServiceAnomalyStats(el)?.healthStatus; + if (status === ServiceHealthStatus.critical) { return 'double'; } else { return 'solid'; @@ -58,11 +59,11 @@ const getBorderStyle: cytoscape.Css.MapperFunction< }; function getBorderWidth(el: cytoscape.NodeSingular) { - const nodeSeverity = getNodeSeverity(el); + const status = getServiceAnomalyStats(el)?.healthStatus; - if (nodeSeverity === Severity.minor || nodeSeverity === Severity.major) { + if (status === ServiceHealthStatus.warning) { return 4; - } else if (nodeSeverity === Severity.critical) { + } else if (status === ServiceHealthStatus.critical) { return 8; } else { return 4; diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx index 94353080bc7d5..c6be0a352ef66 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx @@ -6,20 +6,22 @@ import React from 'react'; import { EuiBadge } from '@elastic/eui'; import { - getSeverityColor, - getSeverityLabel, - Severity, -} from '../../../../../common/anomaly_detection'; + getServiceHealthStatusColor, + getServiceHealthStatusLabel, + ServiceHealthStatus, +} from '../../../../../common/service_health_status'; import { useTheme } from '../../../../hooks/useTheme'; -export function HealthBadge({ severity }: { severity?: Severity }) { +export function HealthBadge({ + healthStatus, +}: { + healthStatus: ServiceHealthStatus; +}) { const theme = useTheme(); - const unknownColor = theme.eui.euiColorLightShade; - return ( - - {getSeverityLabel(severity)} + + {getServiceHealthStatusLabel(healthStatus)} ); } diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js index 519d74827097b..7c306c16cca1f 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js @@ -9,6 +9,7 @@ import { shallow } from 'enzyme'; import { ServiceList, SERVICE_COLUMNS } from '../index'; import props from './props.json'; import { mockMoment } from '../../../../../utils/testHelpers'; +import { ServiceHealthStatus } from '../../../../../../common/service_health_status'; describe('ServiceOverview -> List', () => { beforeAll(() => { @@ -52,25 +53,28 @@ describe('ServiceOverview -> List', () => { describe('without ML data', () => { it('does not render health column', () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); const columns = wrapper.props().columns; - expect(columns[0].field).not.toBe('severity'); + expect(columns[0].field).not.toBe('healthStatus'); }); }); describe('with ML data', () => { it('renders health column', () => { const wrapper = shallow( - + ({ + ...item, + healthStatus: ServiceHealthStatus.warning, + }))} + /> ); const columns = wrapper.props().columns; - expect(columns[0].field).toBe('severity'); + expect(columns[0].field).toBe('healthStatus'); }); }); }); diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap index da3f6ae89940a..e6a9823f3ee28 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap @@ -1,6 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ServiceOverview -> List renders columns correctly 1`] = ``; +exports[`ServiceOverview -> List renders columns correctly 1`] = ` + +`; exports[`ServiceOverview -> List renders empty state 1`] = ` List renders empty state 1`] = ` } initialPageSize={50} initialSortDirection="desc" - initialSortField="severity" + initialSortField="healthStatus" items={Array []} sortFn={[Function]} /> @@ -106,7 +110,7 @@ exports[`ServiceOverview -> List renders with data 1`] = ` } initialPageSize={50} initialSortDirection="desc" - initialSortField="severity" + initialSortField="healthStatus" items={ Array [ Object { diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx index ce256137481cb..4c7c0824a7c49 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx @@ -10,6 +10,7 @@ import React from 'react'; import styled from 'styled-components'; import { ValuesType } from 'utility-types'; import { orderBy } from 'lodash'; +import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { asPercent } from '../../../../../common/utils/formatters'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ServiceListAPIResponse } from '../../../../../server/lib/services/get_services'; @@ -20,14 +21,12 @@ import { ManagedTable, ITableColumn } from '../../../shared/ManagedTable'; import { EnvironmentBadge } from '../../../shared/EnvironmentBadge'; import { TransactionOverviewLink } from '../../../shared/Links/apm/TransactionOverviewLink'; import { AgentIcon } from '../../../shared/AgentIcon'; -import { Severity } from '../../../../../common/anomaly_detection'; import { HealthBadge } from './HealthBadge'; import { ServiceListMetric } from './ServiceListMetric'; interface Props { items: ServiceListAPIResponse['items']; noItemsMessage?: React.ReactNode; - displayHealthStatus: boolean; } type ServiceListItem = ValuesType; @@ -53,14 +52,18 @@ const AppLink = styled(TransactionOverviewLink)` export const SERVICE_COLUMNS: Array> = [ { - field: 'severity', + field: 'healthStatus', name: i18n.translate('xpack.apm.servicesTable.healthColumnLabel', { defaultMessage: 'Health', }), width: px(unit * 6), sortable: true, - render: (_, { severity }) => { - return ; + render: (_, { healthStatus }) => { + return ( + + ); }, }, { @@ -172,40 +175,38 @@ export const SERVICE_COLUMNS: Array> = [ }, ]; -const SEVERITY_ORDER = [ - Severity.warning, - Severity.minor, - Severity.major, - Severity.critical, +const SERVICE_HEALTH_STATUS_ORDER = [ + ServiceHealthStatus.unknown, + ServiceHealthStatus.healthy, + ServiceHealthStatus.warning, + ServiceHealthStatus.critical, ]; -export function ServiceList({ - items, - displayHealthStatus, - noItemsMessage, -}: Props) { +export function ServiceList({ items, noItemsMessage }: Props) { + const displayHealthStatus = items.some((item) => 'healthStatus' in item); + const columns = displayHealthStatus ? SERVICE_COLUMNS - : SERVICE_COLUMNS.filter((column) => column.field !== 'severity'); + : SERVICE_COLUMNS.filter((column) => column.field !== 'healthStatus'); return ( { - // For severity, sort items by severity first, then by TPM + // For healthStatus, sort items by healthStatus first, then by TPM - return sortField === 'severity' + return sortField === 'healthStatus' ? orderBy( itemsToSort, [ (item) => { - return item.severity - ? SEVERITY_ORDER.indexOf(item.severity) + return item.healthStatus + ? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus) : -1; }, (item) => item.transactionsPerMinute?.value ?? 0, diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx index e4ba1e36378d9..d8c8f25616560 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx @@ -10,6 +10,7 @@ import { merge } from 'lodash'; import React, { FunctionComponent, ReactChild } from 'react'; import { MemoryRouter } from 'react-router-dom'; import { createKibanaReactContext } from 'src/plugins/kibana_react/public'; +import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { ServiceOverview } from '..'; import { EuiThemeProvider } from '../../../../../../observability/public'; import { ApmPluginContextValue } from '../../../../context/ApmPluginContext'; @@ -114,7 +115,7 @@ describe('Service Overview -> View', () => { errorsPerMinute: 200, avgResponseTime: 300, environments: ['test', 'dev'], - severity: 1, + healthStatus: ServiceHealthStatus.warning, }, { serviceName: 'My Go Service', @@ -123,7 +124,7 @@ describe('Service Overview -> View', () => { errorsPerMinute: 500, avgResponseTime: 600, environments: [], - severity: 10, + severity: ServiceHealthStatus.healthy, }, ], }); @@ -252,7 +253,7 @@ describe('Service Overview -> View', () => { errorsPerMinute: 200, avgResponseTime: 300, environments: ['test', 'dev'], - severity: 1, + healthStatus: ServiceHealthStatus.warning, }, ], }); diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap index b56f7d6820274..dfc0cc8637ff1 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap @@ -153,8 +153,8 @@ NodeList [ > - Unknown + Warning @@ -435,7 +435,7 @@ NodeList [ > 'severity' in item); - return ( <> @@ -134,7 +132,6 @@ export function ServiceOverview() { option.type === alertParams.anomalySeverityType + ); + + if (!selectedOption) { + throw new Error( + `Anomaly alert severity type ${alertParams.anomalySeverityType} is not supported.` + ); + } + + const threshold = selectedOption.threshold; + if (mlJobIds.length === 0) { return {}; } @@ -96,7 +118,7 @@ export function registerTransactionDurationAnomalyAlertType({ { range: { record_score: { - gte: alertParams.anomalyScore, + gte: threshold, }, }, }, diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts index ed8ae923e6e6c..da087b4c1911a 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import Boom from 'boom'; +import { getServiceHealthStatus } from '../../../common/service_health_status'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { PromiseReturnType } from '../../../typings/common'; import { @@ -12,6 +13,7 @@ import { } from '../../../common/transaction_types'; import { ServiceAnomalyStats, + getSeverity, ML_ERRORS, } from '../../../common/anomaly_detection'; import { getMlJobsWithAPMGroup } from '../anomaly_detection/get_ml_jobs_with_apm_group'; @@ -130,13 +132,19 @@ function transformResponseToServiceAnomalies( response.aggregations?.services.buckets ?? [] ).reduce( (statsByServiceName, { key: serviceName, top_score: topScoreAgg }) => { + const anomalyScore = topScoreAgg.hits.hits[0]?.sort?.[0]; + + const severity = getSeverity(anomalyScore); + const healthStatus = getServiceHealthStatus({ severity }); + return { ...statsByServiceName, [serviceName]: { transactionType: topScoreAgg.hits.hits[0]?._source?.by_field_value, - anomalyScore: topScoreAgg.hits.hits[0]?.sort?.[0], + anomalyScore, actualValue: topScoreAgg.hits.hits[0]?._source?.actual?.[0], jobId: topScoreAgg.hits.hits[0]?._source?.job_id, + healthStatus, }, }; }, diff --git a/x-pack/plugins/apm/server/lib/service_map/transform_service_map_responses.test.ts b/x-pack/plugins/apm/server/lib/service_map/transform_service_map_responses.test.ts index e529198e717d3..f30b80feda302 100644 --- a/x-pack/plugins/apm/server/lib/service_map/transform_service_map_responses.test.ts +++ b/x-pack/plugins/apm/server/lib/service_map/transform_service_map_responses.test.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ServiceHealthStatus } from '../../../common/service_health_status'; + import { AGENT_NAME, SERVICE_ENVIRONMENT, @@ -43,6 +45,7 @@ const anomalies = { actualValue: 10000, anomalyScore: 50, jobId: 'apm-test-1234-ml-module-name', + healthStatus: ServiceHealthStatus.warning, }, }, }; diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts index e7e18cbff1c15..17799203fe73b 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { getServiceHealthStatus } from '../../../../common/service_health_status'; import { EventOutcome } from '../../../../common/event_outcome'; import { getSeverity } from '../../../../common/anomaly_detection'; import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; @@ -413,10 +414,11 @@ export const getHealthStatuses = async ( const stats = anomalies.serviceAnomalies[serviceName]; const severity = getSeverity(stats.anomalyScore); + const healthStatus = getServiceHealthStatus({ severity }); return { serviceName, - severity, + healthStatus, }; }); }; diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index 791a7de48f36f..9a415ac0718b3 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -5,3 +5,5 @@ */ export { SearchResponse7 } from './types/es_client'; +export { ANOMALY_SEVERITY, ANOMALY_THRESHOLD } from './constants/anomalies'; +export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; diff --git a/x-pack/plugins/ml/kibana.json b/x-pack/plugins/ml/kibana.json index 2c5dbe108ab1e..1cd52079b4e39 100644 --- a/x-pack/plugins/ml/kibana.json +++ b/x-pack/plugins/ml/kibana.json @@ -35,5 +35,8 @@ "dashboard", "savedObjects", "home" + ], + "extraPublicDirs": [ + "common" ] } diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap b/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap index 1424ca42539c0..199a49dce8f9e 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap +++ b/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap @@ -77,6 +77,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -104,6 +105,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -130,6 +132,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -156,6 +159,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -209,6 +213,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -255,6 +260,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -301,6 +307,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -407,6 +414,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -465,6 +473,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -491,6 +500,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -504,6 +514,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -523,6 +534,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -548,6 +560,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -573,6 +586,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -605,6 +619,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -687,6 +702,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -734,6 +750,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -748,6 +765,7 @@ Array [ "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -806,6 +824,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -833,6 +852,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -859,6 +879,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -885,6 +906,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -938,6 +960,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -984,6 +1007,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -1030,6 +1054,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -1136,6 +1161,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -1194,6 +1220,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -1220,6 +1247,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -1233,6 +1261,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -1252,6 +1281,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -1277,6 +1307,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -1302,6 +1333,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -1334,6 +1366,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -1416,6 +1449,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, @@ -1463,6 +1497,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -1477,6 +1512,7 @@ Object { "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts index be3301964bd3c..11b5ca71e64e7 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts @@ -165,6 +165,7 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) "serviceAnomalyStats": Object { "actualValue": 3933482.1764705875, "anomalyScore": 2.6101702751482714, + "healthStatus": "healthy", "jobId": "apm-testing-d457-high_mean_transaction_duration", "transactionType": "request", }, @@ -179,6 +180,7 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) "serviceAnomalyStats": Object { "actualValue": 684716.5813953485, "anomalyScore": 0.20498907719907372, + "healthStatus": "healthy", "jobId": "apm-production-229a-high_mean_transaction_duration", "transactionType": "request", }, diff --git a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts index d08064f6aa70e..c93816dfb48b9 100644 --- a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts @@ -45,24 +45,24 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.body.items.length).to.be.greaterThan(0); }); - it('some items have severity set', () => { + it('some items have a health status set', () => { // Under the assumption that the loaded archive has // at least one APM ML job, and the time range is longer - // than 15m, at least one items should have severity set. - // Note that we currently have a bug where healthy services - // report as unknown (so without any severity status): + // than 15m, at least one items should have a health status + // set. Note that we currently have a bug where healthy + // services report as unknown (so without any health status): // https://github.com/elastic/kibana/issues/77083 - const severityScores = response.body.items.map((item: any) => item.severity); + const healthStatuses = response.body.items.map((item: any) => item.healthStatus); - expect(severityScores.filter(Boolean).length).to.be.greaterThan(0); + expect(healthStatuses.filter(Boolean).length).to.be.greaterThan(0); - expectSnapshot(severityScores).toMatchInline(` + expectSnapshot(healthStatuses).toMatchInline(` Array [ undefined, undefined, - "warning", - "warning", + "healthy", + "healthy", undefined, undefined, undefined, From 5a31dce92d086bfe199334e6723a59c897feb14a Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Fri, 18 Sep 2020 08:16:57 -0400 Subject: [PATCH 03/40] Upgrade and consolidate prop-types and create-react-class (#77803) --- package.json | 4 +-- packages/kbn-i18n/package.json | 2 +- packages/kbn-ui-framework/package.json | 2 +- x-pack/package.json | 4 +-- yarn.lock | 34 ++++++-------------------- 5 files changed, 14 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index d258d21413fe4..fb1d7e50d7d22 100644 --- a/package.json +++ b/package.json @@ -308,7 +308,7 @@ "@types/pegjs": "^0.10.1", "@types/pngjs": "^3.3.2", "@types/podium": "^1.0.0", - "@types/prop-types": "^15.5.3", + "@types/prop-types": "^15.7.3", "@types/reach__router": "^1.2.6", "@types/react": "^16.9.36", "@types/react-dom": "^16.9.8", @@ -444,7 +444,7 @@ "pngjs": "^3.4.0", "postcss": "^7.0.32", "prettier": "^2.1.1", - "prop-types": "15.6.0", + "prop-types": "^15.7.2", "proxyquire": "1.8.0", "react-grid-layout": "^0.16.2", "react-markdown": "^4.3.1", diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index 680c789bc9d9d..eccdff9060cbe 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -27,7 +27,7 @@ "intl-format-cache": "^2.1.0", "intl-messageformat": "^2.2.0", "intl-relativeformat": "^2.1.0", - "prop-types": "^15.6.2", + "prop-types": "^15.7.2", "react": "^16.12.0", "react-intl": "^2.8.0" } diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index a2151ca3381bc..363f97522a901 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -18,7 +18,7 @@ "classnames": "2.2.6", "focus-trap-react": "^3.1.1", "lodash": "^4.17.15", - "prop-types": "15.6.0", + "prop-types": "^15.7.2", "react": "^16.12.0", "react-ace": "^5.9.0", "react-color": "^2.13.8", diff --git a/x-pack/package.json b/x-pack/package.json index 9a1d424da4a1d..86fb6bec6ba89 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -107,7 +107,7 @@ "@types/papaparse": "^5.0.3", "@types/pngjs": "^3.3.2", "@types/pretty-ms": "^5.0.0", - "@types/prop-types": "^15.5.3", + "@types/prop-types": "^15.7.3", "@types/proper-lockfile": "^3.0.1", "@types/puppeteer": "^1.20.1", "@types/react": "^16.9.36", @@ -354,7 +354,7 @@ "papaparse": "^5.2.0", "pdfmake": "^0.1.65", "pngjs": "3.4.0", - "prop-types": "^15.6.0", + "prop-types": "^15.7.2", "proper-lockfile": "^3.2.0", "puid": "1.0.7", "puppeteer-core": "^1.19.0", diff --git a/yarn.lock b/yarn.lock index fa5f6ff135670..ffca5adcc562f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4388,10 +4388,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg== -"@types/prop-types@^15.5.3": - version "15.5.9" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.9.tgz#f2d14df87b0739041bc53a7d75e3d77d726a3ec0" - integrity sha512-Nha5b+jmBI271jdTMwrHiNXM+DvThjHOfyZtMX9kj/c/LUj2xiLHsG/1L3tJ8DjAoQN48cHwUwtqBotjyXaSdQ== +"@types/prop-types@^15.7.3": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== "@types/proper-lockfile@^3.0.1": version "3.0.1" @@ -9590,7 +9590,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@^15.5.1: +create-react-class@^15.5.1, create-react-class@^15.5.2: version "15.6.3" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== @@ -9599,15 +9599,6 @@ create-react-class@^15.5.1: loose-envify "^1.3.1" object-assign "^4.1.1" -create-react-class@^15.5.2: - version "15.6.2" - resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.2.tgz#cf1ed15f12aad7f14ef5f2dfe05e6c42f91ef02a" - integrity sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co= - dependencies: - fbjs "^0.8.9" - loose-envify "^1.3.1" - object-assign "^4.1.1" - create-react-context@^0.1.5: version "0.1.6" resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.1.6.tgz#0f425931d907741127acc6e31acb4f9015dd9fdc" @@ -22793,15 +22784,6 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" - integrity sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY= - dependencies: - fbjs "^0.8.16" - loose-envify "^1.3.1" - object-assign "^4.1.1" - prop-types@15.7.2, prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -28338,9 +28320,9 @@ typescript@4.0.2, typescript@^3.0.1, typescript@^3.0.3, typescript@^3.2.2, types integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== ua-parser-js@^0.7.18: - version "0.7.21" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" - integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== + version "0.7.22" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3" + integrity sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" From 613509d81a8799c34fdd81df00d3019c48adec9d Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 18 Sep 2020 08:54:08 -0400 Subject: [PATCH 04/40] Improve home screen for limited-access users (#77665) --- .../__snapshots__/home.test.js.snap | 2 + .../public/application/components/home.js | 5 +- .../__snapshots__/manage_data.test.tsx.snap | 3 + .../manage_data/manage_data.test.tsx | 5 + .../components/manage_data/manage_data.tsx | 52 +++---- .../solution_panel.test.tsx.snap | 1 + .../solutions_section/solution_panel.tsx | 1 + .../feature_catalogue_registry.test.ts | 34 +++++ .../feature_catalogue_registry.ts | 7 +- src/plugins/management/public/plugin.ts | 7 +- test/functional/page_objects/home_page.ts | 8 ++ .../server/plugin.ts | 5 +- .../plugins/snapshot_restore/server/plugin.ts | 1 + .../home/feature_controls/home_security.ts | 130 ++++++++++++++++++ .../apps/home/feature_controls/index.ts | 14 ++ x-pack/test/functional/apps/home/index.ts | 14 ++ .../security_and_spaces/tests/catalogue.ts | 10 +- .../security_only/tests/catalogue.ts | 10 +- .../spaces_only/tests/catalogue.ts | 10 +- 19 files changed, 287 insertions(+), 32 deletions(-) create mode 100644 x-pack/test/functional/apps/home/feature_controls/home_security.ts create mode 100644 x-pack/test/functional/apps/home/feature_controls/index.ts create mode 100644 x-pack/test/functional/apps/home/index.ts diff --git a/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap index 1b10756c2975c..bf1e8c8f0b401 100644 --- a/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap @@ -164,6 +164,7 @@ exports[`home directories should not render directory entry when showOnHomePage {stackManagement ? ( - + `; + +exports[`ManageData render empty without any features 1`] = ``; diff --git a/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx b/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx index 5d00370caf2cc..0e86bf7dd3d84 100644 --- a/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx +++ b/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx @@ -88,4 +88,9 @@ describe('ManageData', () => { ); expect(component).toMatchSnapshot(); }); + + test('render empty without any features', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); }); diff --git a/src/plugins/home/public/application/components/manage_data/manage_data.tsx b/src/plugins/home/public/application/components/manage_data/manage_data.tsx index 0dfb4f949f0c7..85f1bc04f353b 100644 --- a/src/plugins/home/public/application/components/manage_data/manage_data.tsx +++ b/src/plugins/home/public/application/components/manage_data/manage_data.tsx @@ -36,31 +36,37 @@ export const ManageData: FC = ({ addBasePath, features }) => ( <> {features.length > 1 &&