From 2d361f018e0776c237d03b84ca8aa24615d16d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Tue, 22 Dec 2020 09:25:30 +0100 Subject: [PATCH] [APM] Fix broken link to ML when time range is not set (#85976) (#86227) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../ServiceMap/Popover/AnomalyDetection.tsx | 6 +- .../Settings/anomaly_detection/jobs_list.tsx | 12 +-- .../Links/MachineLearningLinks/MLJobLink.tsx | 40 --------- .../MachineLearningLinks/MLLink.test.tsx | 24 ----- .../MLManageJobsLink.test.tsx | 21 +++++ .../{MLLink.tsx => MLManageJobsLink.tsx} | 41 ++++----- ...k.test.tsx => MLSingleMetricLink.test.tsx} | 10 +-- .../MLSingleMetricLink.tsx | 90 +++++++++++++++++++ .../useTimeSeriesExplorerHref.ts | 53 ----------- .../charts/transaction_charts/ml_header.tsx | 6 +- 10 files changed, 145 insertions(+), 158 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx delete mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx create mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLManageJobsLink.test.tsx rename x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/{MLLink.tsx => MLManageJobsLink.tsx} (57%) rename x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/{MLJobLink.test.tsx => MLSingleMetricLink.test.tsx} (91%) create mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLSingleMetricLink.tsx delete mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts 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 36c0c9f37f9c71..97c76b4139c8a4 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 @@ -21,7 +21,7 @@ import { import { useTheme } from '../../../../hooks/use_theme'; import { fontSize, px } from '../../../../style/variables'; import { asInteger, asDuration } from '../../../../../common/utils/formatters'; -import { MLJobLink } from '../../../shared/Links/MachineLearningLinks/MLJobLink'; +import { MLSingleMetricLink } from '../../../shared/Links/MachineLearningLinks/MLSingleMetricLink'; import { popoverWidth } from '../cytoscape_options'; import { TRANSACTION_REQUEST } from '../../../../../common/transaction_types'; import { @@ -108,14 +108,14 @@ export function AnomalyDetection({ serviceName, serviceAnomalyStats }: Props) { )} {mlJobId && ( - {ANOMALY_DETECTION_LINK} - + )} diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx index 8d6a0740a8a080..acc2550930b8e6 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx @@ -19,8 +19,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { ITableColumn, ManagedTable } from '../../../shared/ManagedTable'; import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; -import { MLJobLink } from '../../../shared/Links/MachineLearningLinks/MLJobLink'; -import { MLLink } from '../../../shared/Links/MachineLearningLinks/MLLink'; +import { MLSingleMetricLink } from '../../../shared/Links/MachineLearningLinks/MLSingleMetricLink'; +import { MLManageJobsLink } from '../../../shared/Links/MachineLearningLinks/MLManageJobsLink'; import { getEnvironmentLabel } from '../../../../../common/environment_filter_values'; import { LegacyJobsCallout } from './legacy_jobs_callout'; import { AnomalyDetectionApiResponse } from './index'; @@ -44,14 +44,14 @@ const columns: Array> = [ { defaultMessage: 'Action' } ), render: (jobId: string) => ( - + {i18n.translate( 'xpack.apm.settings.anomalyDetection.jobList.mlJobLinkText', { defaultMessage: 'View job in ML', } )} - + ), }, ]; @@ -97,14 +97,14 @@ export function JobsList({ data, status, onAddEnvironments }: Props) { defaultMessage="To add anomaly detection to a new environment, create a machine learning job. Existing machine learning jobs can be managed in {mlJobsLink}." values={{ mlJobsLink: ( - + {i18n.translate( 'xpack.apm.settings.anomalyDetection.jobList.mlDescriptionText.mlJobsLinkText', { defaultMessage: 'Machine Learning', } )} - + ), }} /> diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx deleted file mode 100644 index 887ac2ff6bbb9e..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx +++ /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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { ReactNode } from 'react'; -import { EuiLink } from '@elastic/eui'; -import { useTimeSeriesExplorerHref } from './useTimeSeriesExplorerHref'; - -interface Props { - children?: ReactNode; - jobId: string; - external?: boolean; - serviceName?: string; - transactionType?: string; -} - -export function MLJobLink({ - jobId, - serviceName, - transactionType, - external, - children, -}: Props) { - const href = useTimeSeriesExplorerHref({ - jobId, - serviceName, - transactionType, - }); - - return ( - - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx deleted file mode 100644 index c453de709a5d27..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx +++ /dev/null @@ -1,24 +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 { Location } from 'history'; -import React from 'react'; -import { getRenderedHref } from '../../../../utils/testHelpers'; -import { MLLink } from './MLLink'; - -test('MLLink produces the correct URL', async () => { - const href = await getRenderedHref( - () => , - { - search: - '?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', - } as Location - ); - - expect(href).toMatchInlineSnapshot( - `"/app/ml/jobs?_a=(jobs:(queryText:'id:(something)%20groups:(apm)'))&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-5h,to:now-2h))"` - ); -}); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLManageJobsLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLManageJobsLink.test.tsx new file mode 100644 index 00000000000000..21ca9a3ab53bc3 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLManageJobsLink.test.tsx @@ -0,0 +1,21 @@ +/* + * 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 { Location } from 'history'; +import React from 'react'; +import { getRenderedHref } from '../../../../utils/testHelpers'; +import { MLManageJobsLink } from './MLManageJobsLink'; + +test('MLManageJobsLink', async () => { + const href = await getRenderedHref(() => , { + search: + '?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0', + } as Location); + + expect(href).toMatchInlineSnapshot( + `"/app/ml/jobs?_a=(jobs:(queryText:'groups:(apm)'))&_g=(refreshInterval:(pause:!t,value:0),time:(from:now-5h,to:now-2h))"` + ); +}); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLManageJobsLink.tsx similarity index 57% rename from x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLManageJobsLink.tsx index 7bf017fb239e3c..3406ce0b1e9d29 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLManageJobsLink.tsx @@ -6,51 +6,44 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; +import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/common'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useMlHref, ML_PAGES } from '../../../../../../ml/public'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; - -interface MlRisonData { - ml?: { - jobIds: string[]; - }; -} +import { TimePickerRefreshInterval } from '../../DatePicker/typings'; interface Props { - query?: MlRisonData; - path?: string; children?: React.ReactNode; external?: boolean; } -export function MLLink({ children, path = '', query = {}, external }: Props) { +export function MLManageJobsLink({ children, external }: Props) { const { core, plugins: { ml }, } = useApmPluginContext(); - let jobIds: string[] = []; - if (query.ml?.jobIds) { - jobIds = query.ml.jobIds; - } const { urlParams } = useUrlParams(); - const { rangeFrom, rangeTo, refreshInterval, refreshPaused } = urlParams; - // default to link to ML Anomaly Detection jobs management page + const timePickerRefreshIntervalDefaults = core.uiSettings.get( + UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS + ); + + const { + // hardcoding a custom default of 1 hour since the default kibana timerange of 15 minutes is shorter than the ML interval + rangeFrom = 'now-1h', + rangeTo = 'now', + refreshInterval = timePickerRefreshIntervalDefaults.value, + refreshPaused = timePickerRefreshIntervalDefaults.pause, + } = urlParams; + const mlADLink = useMlHref(ml, core.http.basePath.get(), { page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, pageState: { - jobId: jobIds, groupIds: ['apm'], globalState: { - time: - rangeFrom !== undefined && rangeTo !== undefined - ? { from: rangeFrom, to: rangeTo } - : undefined, - refreshInterval: - refreshPaused !== undefined && refreshInterval !== undefined - ? { pause: refreshPaused, value: refreshInterval } - : undefined, + time: { from: rangeFrom, to: rangeTo }, + refreshInterval: { pause: refreshPaused, value: refreshInterval }, }, }, }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLSingleMetricLink.test.tsx similarity index 91% rename from x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLSingleMetricLink.test.tsx index b13b1f89da352a..146f216170ae06 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLSingleMetricLink.test.tsx @@ -7,13 +7,13 @@ import { Location } from 'history'; import React from 'react'; import { getRenderedHref } from '../../../../utils/testHelpers'; -import { MLJobLink } from './MLJobLink'; +import { MLSingleMetricLink } from './MLSingleMetricLink'; -describe('MLJobLink', () => { +describe('MLSingleMetricLink', () => { it('should produce the correct URL with jobId', async () => { const href = await getRenderedHref( () => ( - + ), { search: @@ -28,7 +28,7 @@ describe('MLJobLink', () => { it('should produce the correct URL with jobId, serviceName, and transactionType', async () => { const href = await getRenderedHref( () => ( - { it('correctly encodes time range values', async () => { const href = await getRenderedHref( () => ( - + ); +} + +export function useSingleMetricHref({ + jobId, + serviceName, + transactionType, +}: { + jobId: string; + serviceName?: string; + transactionType?: string; +}) { + const { + core, + plugins: { ml }, + } = useApmPluginContext(); + const { urlParams } = useUrlParams(); + + const timePickerRefreshIntervalDefaults = core.uiSettings.get( + UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS + ); + + const { + // hardcoding a custom default of 1 hour since the default kibana timerange of 15 minutes is shorter than the ML interval + rangeFrom = 'now-1h', + rangeTo = 'now', + refreshInterval = timePickerRefreshIntervalDefaults.value, + refreshPaused = timePickerRefreshIntervalDefaults.pause, + } = urlParams; + + const entities = + serviceName && transactionType + ? { + entities: { + 'service.name': serviceName, + 'transaction.type': transactionType, + }, + } + : {}; + + const href = useMlHref(ml, core.http.basePath.get(), { + page: ML_PAGES.SINGLE_METRIC_VIEWER, + pageState: { + jobIds: [jobId], + timeRange: { from: rangeFrom, to: rangeTo }, + refreshInterval: { pause: refreshPaused, value: refreshInterval }, + ...entities, + }, + }); + + return href; +} diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts deleted file mode 100644 index eabef034bf3d99..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts +++ /dev/null @@ -1,53 +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 { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import { useMlHref } from '../../../../../../ml/public'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; - -export function useTimeSeriesExplorerHref({ - jobId, - serviceName, - transactionType, -}: { - jobId: string; - serviceName?: string; - transactionType?: string; -}) { - // default to link to ML Anomaly Detection jobs management page - const { - core, - plugins: { ml }, - } = useApmPluginContext(); - const { urlParams } = useUrlParams(); - const { rangeFrom, rangeTo, refreshInterval, refreshPaused } = urlParams; - - const timeRange = - rangeFrom !== undefined && rangeTo !== undefined - ? { from: rangeFrom, to: rangeTo } - : undefined; - const mlAnomalyDetectionHref = useMlHref(ml, core.http.basePath.get(), { - page: 'timeseriesexplorer', - pageState: { - jobIds: [jobId], - timeRange, - refreshInterval: - refreshPaused !== undefined && refreshInterval !== undefined - ? { pause: refreshPaused, value: refreshInterval } - : undefined, - ...(serviceName && transactionType - ? { - entities: { - 'service.name': serviceName, - 'transaction.type': transactionType, - }, - } - : {}), - }, - }); - - return mlAnomalyDetectionHref; -} diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx index f0569ea1a07521..d125af70268cb7 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { MLJobLink } from '../../Links/MachineLearningLinks/MLJobLink'; +import { MLSingleMetricLink } from '../../Links/MachineLearningLinks/MLSingleMetricLink'; interface Props { hasValidMlLicense?: boolean; @@ -78,7 +78,7 @@ export function MLHeader({ hasValidMlLicense, mlJobId }: Props) { } )}{' '} - + );