From 00642701e3e10332bb90afcbb139bfdb74e0f03e Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 21 Sep 2020 15:10:21 -0500 Subject: [PATCH 01/19] [ML] Adds ability to pass multiple jobIds to job management url --- .../plugins/ml/common/types/ml_url_generator.ts | 2 +- .../components/job_filter_bar/job_filter_bar.js | 9 +++++++-- .../jobs/jobs_list/components/utils.d.ts | 1 + .../jobs/jobs_list/components/utils.js | 15 +++++++++++++-- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/ml/common/types/ml_url_generator.ts b/x-pack/plugins/ml/common/types/ml_url_generator.ts index d176c22bdbb62..beab1e274fe48 100644 --- a/x-pack/plugins/ml/common/types/ml_url_generator.ts +++ b/x-pack/plugins/ml/common/types/ml_url_generator.ts @@ -55,7 +55,7 @@ export type MlGenericUrlState = MLPageState< >; export interface AnomalyDetectionQueryState { - jobId?: JobId; + jobId?: JobId | string[]; groupIds?: string[]; globalState?: MlCommonGlobalState; } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.js index 6eb7b00e5620c..08373542c1234 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.js @@ -9,7 +9,12 @@ import React, { Component, Fragment } from 'react'; import { ml } from '../../../../services/ml_api_service'; import { JobGroup } from '../job_group'; -import { getGroupQueryText, getSelectedIdFromUrl, clearSelectedJobIdFromUrl } from '../utils'; +import { + getGroupQueryText, + getSelectedIdFromUrl, + clearSelectedJobIdFromUrl, + getJobQueryText, +} from '../utils'; import { EuiSearchBar, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -60,7 +65,7 @@ export class JobFilterBar extends Component { if (groupIds !== undefined) { defaultQueryText = getGroupQueryText(groupIds); } else if (jobId !== undefined) { - defaultQueryText = jobId; + defaultQueryText = getJobQueryText(jobId); } if (defaultQueryText !== undefined) { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts index cf4fad9513de5..75d6b149fda08 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts @@ -6,4 +6,5 @@ export function getSelectedIdFromUrl(str: string): { groupIds?: string[]; jobId?: string }; export function getGroupQueryText(arr: string[]): string; +export function getJobQueryText(arr: string | string[]): string; export function clearSelectedJobIdFromUrl(str: string): void; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index fd0789c9bc103..75eaf46e3b441 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -268,6 +268,7 @@ export function deleteJobs(jobs, finish = () => {}) { } export function filterJobs(jobs, clauses) { + console.log('jobs, clauses', jobs, clauses); if (clauses.length === 0) { return jobs; } @@ -309,8 +310,13 @@ export function filterJobs(jobs, clauses) { } else { // filter other clauses, i.e. the toggle group buttons if (Array.isArray(c.value)) { - // the groups value is an array of group ids - js = jobs.filter((job) => jobProperty(job, c.field).some((g) => c.value.indexOf(g) >= 0)); + // if it's an array of job ids + if (c.field === 'id') { + js = jobs.filter((job) => c.value.indexOf(jobProperty(job, c.field)) >= 0); + } else { + // the groups value is an array of group ids + js = jobs.filter((job) => jobProperty(job, c.field).some((g) => c.value.indexOf(g) >= 0)); + } } else { js = jobs.filter((job) => jobProperty(job, c.field) === c.value); } @@ -353,6 +359,7 @@ function jobProperty(job, prop) { job_state: 'jobState', datafeed_state: 'datafeedState', groups: 'groups', + id: 'id', }; return job[propMap[prop]]; } @@ -389,6 +396,10 @@ export function getGroupQueryText(groupIds) { return `groups:(${groupIds.join(' or ')})`; } +export function getJobQueryText(jobIds) { + return Array.isArray(jobIds) ? `id:(${jobIds.join(' OR ')})` : jobIds; +} + export function clearSelectedJobIdFromUrl(url) { if (typeof url === 'string') { url = decodeURIComponent(url); From 357c42be7d2b6a2171a2db7b72be1514d374096c Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 21 Sep 2020 15:20:48 -0500 Subject: [PATCH 02/19] [ML][APM] Update links to jobs management page for MLLink and LegacyJobsCallout --- .../anomaly_detection/legacy_jobs_callout.tsx | 39 +++++++++++--- .../MachineLearningLinks/MLLink.test.tsx | 2 +- .../Links/MachineLearningLinks/MLLink.tsx | 51 ++++++++++++++----- 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx index 54053097ab02e..3467908b7ce89 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx @@ -5,12 +5,41 @@ */ import { EuiCallOut, EuiButton } from '@elastic/eui'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; export function LegacyJobsCallout() { - const { core } = useApmPluginContext(); + const { + core, + plugins: { ml }, + } = useApmPluginContext(); + + const [mlADLink, setMlADLink] = useState( + core.http.basePath.prepend('/app/ml/jobs') + ); + + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + if (ml?.urlGenerator !== undefined) { + const href = await ml.urlGenerator.createUrl({ + page: 'jobs', + pageState: { + jobId: 'high_mean_response_time', + }, + }); + if (!isCancelled) { + setMlADLink(href); + } + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [ml?.urlGenerator]); + return ( - + {i18n.translate( 'xpack.apm.settings.anomaly_detection.legacy_jobs.button', { defaultMessage: 'Review jobs' } 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 index da345e35c10b1..f8887913bce65 100644 --- 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 @@ -21,6 +21,6 @@ test('MLLink produces the correct URL', async () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml#/some/path?_g=(ml:(jobIds:!(something)),refreshInterval:(pause:!t,value:0),time:(from:now-5h,to:now-2h))"` + `"/basepath/app/ml/some/path?_g=(ml:(jobIds:!(something)),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/MLLink.tsx index d8ec212515c6f..3901a4f10a27f 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx @@ -5,12 +5,10 @@ */ import { EuiLink } from '@elastic/eui'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; -import rison, { RisonValue } from 'rison-node'; -import url from 'url'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; -import { getTimepickerRisonData, TimepickerRisonData } from '../rison_helpers'; +import { getTimepickerRisonData } from '../rison_helpers'; interface MlRisonData { ml?: { @@ -29,23 +27,48 @@ export function MLLink({ children, path = '', query = {}, external }: Props) { const { core } = useApmPluginContext(); const location = useLocation(); - const risonQuery: MlRisonData & TimepickerRisonData = getTimepickerRisonData( - location.search + const { + plugins: { ml }, + } = useApmPluginContext(); + + // default to link to ML Anomaly Detection jobs management page + const [mlADLink, setMlADLink] = useState( + core.http.basePath.prepend('/app/ml/jobs') ); - if (query.ml) { - risonQuery.ml = query.ml; - } + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + const { time, refreshInterval } = getTimepickerRisonData(location.search); + let jobIds: string[] = []; + if (query.ml?.jobIds) { + jobIds = query.ml.jobIds; + } - const href = url.format({ - pathname: core.http.basePath.prepend('/app/ml'), - hash: `${path}?_g=${rison.encode(risonQuery as RisonValue)}`, - }); + if (ml?.urlGenerator !== undefined) { + const href = await ml.urlGenerator.createUrl({ + page: 'jobs', + pageState: { + jobId: jobIds, + timeRange: time, + refreshInterval, + }, + }); + if (!isCancelled) { + setMlADLink(href); + } + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [ml?.urlGenerator, location.search, query]); return ( From ec58132d5007be71e0fecd72a6e3a40f00a6e516 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 21 Sep 2020 16:06:11 -0500 Subject: [PATCH 03/19] [ML][APM] Update useTimeSeriesExplorerHref --- .../useTimeSeriesExplorerHref.ts | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) 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 index 3b60962d797ed..355b373354189 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import querystring from 'querystring'; import { useLocation } from 'react-router-dom'; -import rison from 'rison-node'; -import url from 'url'; +import { useEffect, useState } from 'react'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; import { getTimepickerRisonData } from '../rison_helpers'; +import { RefreshInterval } from '../../../../../../../../src/plugins/data/common/query'; export function useTimeSeriesExplorerHref({ jobId, @@ -20,42 +19,49 @@ export function useTimeSeriesExplorerHref({ serviceName?: string; transactionType?: string; }) { - const { core } = useApmPluginContext(); + const { + core, + plugins: { ml }, + } = useApmPluginContext(); const location = useLocation(); - const { time, refreshInterval } = getTimepickerRisonData(location.search); - const search = querystring.stringify( - { - _g: rison.encode({ - ml: { jobIds: [jobId] }, - time, - refreshInterval, - }), - ...(serviceName && transactionType - ? { - _a: rison.encode({ - mlTimeSeriesExplorer: { - entities: { - 'service.name': serviceName, - 'transaction.type': transactionType, - }, - zoom: time, - }, - }), - } - : null), - }, - undefined, - undefined, - { - encodeURIComponent(str: string) { - return str; - }, - } + // default to link to ML Anomaly Detection jobs management page + const [mlADUrl, setMlADLink] = useState( + core.http.basePath.prepend('/app/ml/jobs') ); - return url.format({ - pathname: core.http.basePath.prepend('/app/ml'), - hash: url.format({ pathname: '/timeseriesexplorer', search }), - }); + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + const { time, refreshInterval } = getTimepickerRisonData(location.search); + if (ml?.urlGenerator !== undefined) { + const href = await ml.urlGenerator.createUrl({ + page: 'timeseriesexplorer', + pageState: { + jobIds: [jobId], + timeRange: time, + refreshInterval: refreshInterval as RefreshInterval, + zoom: time, + ...(serviceName && transactionType + ? { + entities: { + 'service.name': serviceName, + 'transaction.type': transactionType, + }, + } + : null), + }, + }); + if (!isCancelled) { + setMlADLink(href); + } + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [ml?.urlGenerator, location.search, jobId, serviceName, transactionType]); + + return mlADUrl; } From d9ed2a5d417891c0b1aa3e20cce5edea01e0a056 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 21 Sep 2020 16:42:35 -0500 Subject: [PATCH 04/19] [ML][APM] Update tests --- .../shared/Links/MachineLearningLinks/MLJobLink.test.tsx | 4 ++-- .../useTimeSeriesExplorerHref.test.ts | 2 +- .../context/ApmPluginContext/MockApmPluginContext.tsx | 9 ++++++++- 3 files changed, 11 insertions(+), 4 deletions(-) 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/MLJobLink.test.tsx index 9ba4aab0e23d9..f341724bef76d 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx @@ -22,7 +22,7 @@ describe('MLJobLink', () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))"` + `"/basepath/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))"` ); }); it('should produce the correct URL with jobId, serviceName, and transactionType', async () => { @@ -41,7 +41,7 @@ describe('MLJobLink', () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request),zoom:(from:now/w,to:now-4h)))"` + `"/basepath/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request),zoom:(from:now/w,to:now-4h)))"` ); }); }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts index 66f3903ba849b..40bf8829537b8 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts @@ -28,7 +28,7 @@ describe('useTimeSeriesExplorerHref', () => { }); expect(href).toMatchInlineSnapshot( - `"/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request),zoom:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z')))"` + `"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request),zoom:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z')))"` ); }); }); diff --git a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx index 48206572932b1..227e4d4a1cc7a 100644 --- a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx +++ b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx @@ -78,10 +78,17 @@ const mockConfig: ConfigSchema = { }, }; +const mockPlugin = { + ml: { + urlGenerator: { + createUrl: jest.fn, + }, + }, +}; export const mockApmPluginContextValue = { config: mockConfig, core: mockCore, - plugins: {}, + plugins: mockPlugin, }; export function MockApmPluginContextWrapper({ From 6696c0646654fac070369c1a4a72b8f851486253 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 21 Sep 2020 18:28:21 -0500 Subject: [PATCH 05/19] [ML][APM] Move test from useTimeSeriesExplorerHref to MLJobLink.test.tsx --- .../app/Home/__snapshots__/Home.test.tsx.snap | 16 +++++++-- .../MachineLearningLinks/MLJobLink.test.tsx | 24 ++++++++++--- .../MachineLearningLinks/MLLink.test.tsx | 4 +-- .../useTimeSeriesExplorerHref.test.ts | 34 ------------------- .../useTimeSeriesExplorerHref.ts | 9 +++-- .../ApmPluginContext/MockApmPluginContext.tsx | 2 +- x-pack/plugins/apm/public/plugin.ts | 3 ++ x-pack/plugins/ml/public/plugin.ts | 24 ++++++++++--- 8 files changed, 61 insertions(+), 55 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts diff --git a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap index 9706895b164a6..61ec70c0c92ca 100644 --- a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap @@ -51,7 +51,13 @@ exports[`Home component should render services 1`] = ` "get$": [Function], }, }, - "plugins": Object {}, + "plugins": Object { + "ml": Object { + "urlGenerator": Object { + "createUrl": [Function], + }, + }, + }, } } > @@ -112,7 +118,13 @@ exports[`Home component should render traces 1`] = ` "get$": [Function], }, }, - "plugins": Object {}, + "plugins": Object { + "ml": Object { + "urlGenerator": Object { + "createUrl": [Function], + }, + }, + }, } } > 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/MLJobLink.test.tsx index f341724bef76d..98aed3cc0f0d7 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx @@ -21,9 +21,7 @@ describe('MLJobLink', () => { } as Location ); - expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))"` - ); + expect(href).toMatchInlineSnapshot(`""`); }); it('should produce the correct URL with jobId, serviceName, and transactionType', async () => { const href = await getRenderedHref( @@ -40,8 +38,24 @@ describe('MLJobLink', () => { } as Location ); - expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request),zoom:(from:now/w,to:now-4h)))"` + expect(href).toMatchInlineSnapshot(`""`); + }); + + it('correctly encodes time range values', async () => { + const href = await getRenderedHref( + () => ( + + ), + { + search: + '?rangeFrom=2020-07-29T17:27:29.000Z&rangeTo=2020-07-29T18:45:00.000Z&refreshInterval=10000&refreshPaused=true', + } as Location ); + + expect(href).toMatchInlineSnapshot(`""`); }); }); 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 index f8887913bce65..1b30d97f4d140 100644 --- 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 @@ -20,7 +20,5 @@ test('MLLink produces the correct URL', async () => { } as Location ); - expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml/some/path?_g=(ml:(jobIds:!(something)),refreshInterval:(pause:!t,value:0),time:(from:now-5h,to:now-2h))"` - ); + expect(href).toMatchInlineSnapshot(`""`); }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts deleted file mode 100644 index 40bf8829537b8..0000000000000 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts +++ /dev/null @@ -1,34 +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 { useTimeSeriesExplorerHref } from './useTimeSeriesExplorerHref'; - -jest.mock('../../../../hooks/useApmPluginContext', () => ({ - useApmPluginContext: () => ({ - core: { http: { basePath: { prepend: (url: string) => url } } }, - }), -})); - -jest.mock('react-router-dom', () => ({ - useLocation: () => ({ - search: - '?rangeFrom=2020-07-29T17:27:29.000Z&rangeTo=2020-07-29T18:45:00.000Z&refreshInterval=10000&refreshPaused=true', - }), -})); - -describe('useTimeSeriesExplorerHref', () => { - it('correctly encodes time range values', async () => { - const href = useTimeSeriesExplorerHref({ - jobId: 'apm-production-485b-high_mean_transaction_duration', - serviceName: 'opbeans-java', - transactionType: 'request', - }); - - expect(href).toMatchInlineSnapshot( - `"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request),zoom:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z')))"` - ); - }); -}); 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 index 355b373354189..f7f18cf24cebd 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts @@ -19,16 +19,15 @@ export function useTimeSeriesExplorerHref({ serviceName?: string; transactionType?: string; }) { + // default to link to ML Anomaly Detection jobs management page const { core, plugins: { ml }, } = useApmPluginContext(); - const location = useLocation(); - - // default to link to ML Anomaly Detection jobs management page - const [mlADUrl, setMlADLink] = useState( + const [mlADLink, setMlADLink] = useState( core.http.basePath.prepend('/app/ml/jobs') ); + const location = useLocation(); useEffect(() => { let isCancelled = false; @@ -63,5 +62,5 @@ export function useTimeSeriesExplorerHref({ }; }, [ml?.urlGenerator, location.search, jobId, serviceName, transactionType]); - return mlADUrl; + return mlADLink; } diff --git a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx index 227e4d4a1cc7a..d3a2b5f05aa10 100644 --- a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx +++ b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx @@ -81,7 +81,7 @@ const mockConfig: ConfigSchema = { const mockPlugin = { ml: { urlGenerator: { - createUrl: jest.fn, + createUrl: (params: any) => '', }, }, }; diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index ab3f1026a92dd..c44ade92969de 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -36,12 +36,14 @@ import { featureCatalogueEntry } from './featureCatalogueEntry'; import { toggleAppLinkInNav } from './toggleAppLinkInNav'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { registerApmAlerts } from './components/alerting/register_apm_alerts'; +import { MlPluginSetup, MlPluginStart } from '../../ml/public'; export type ApmPluginSetup = void; export type ApmPluginStart = void; export interface ApmPluginSetupDeps { alerts?: AlertingPluginPublicSetup; + ml?: MlPluginSetup; data: DataPublicPluginSetup; features: FeaturesPluginSetup; home?: HomePublicPluginSetup; @@ -52,6 +54,7 @@ export interface ApmPluginSetupDeps { export interface ApmPluginStartDeps { alerts?: AlertingPluginPublicStart; + ml?: MlPluginStart; data: DataPublicPluginStart; home: void; licensing: void; diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index d8e0c7274b549..a7967e0ed66dc 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -15,7 +15,7 @@ import { import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; import { ManagementSetup } from 'src/plugins/management/public'; -import { SharePluginSetup, SharePluginStart } from 'src/plugins/share/public'; +import { SharePluginSetup, SharePluginStart, UrlGeneratorContract } from 'src/plugins/share/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { DataPublicPluginStart } from 'src/plugins/data/public'; @@ -34,9 +34,10 @@ import { registerFeature } from './register_feature'; import { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import { registerMlUiActions } from './ui_actions'; import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public'; -import { registerUrlGenerator } from './ml_url_generator'; +import { MlUrlGenerator } from './ml_url_generator'; import { isFullLicense, isMlEnabled } from '../common/license'; import { registerEmbeddables } from './embeddables'; +import { ML_APP_URL_GENERATOR } from '../common/constants/ml_url_generator'; export interface MlStartDependencies { data: DataPublicPluginStart; @@ -62,6 +63,7 @@ export type MlCoreSetup = CoreSetup; export class MlPlugin implements Plugin { private appUpdater = new BehaviorSubject(() => ({})); + private urlGenerator: undefined | UrlGeneratorContract; constructor(private initializerContext: PluginInitializerContext) {} @@ -111,6 +113,15 @@ export class MlPlugin implements Plugin { if (pluginsSetup.home) { registerFeature(pluginsSetup.home); } + if (pluginsSetup.share) { + const baseUrl = core.http.basePath.prepend('/app/ml'); + this.urlGenerator = pluginsSetup.share.urlGenerators.registerUrlGenerator( + new MlUrlGenerator({ + appBasePath: baseUrl, + useHash: core.uiSettings.get('state:storeInSessionStorage'), + }) + ); + } const { capabilities } = coreStart.application; @@ -129,7 +140,6 @@ export class MlPlugin implements Plugin { } registerEmbeddables(pluginsSetup.embeddable, core); registerMlUiActions(pluginsSetup.uiActions, core); - registerUrlGenerator(pluginsSetup.share, core); } else if (managementApp) { managementApp.disable(); } @@ -144,7 +154,9 @@ export class MlPlugin implements Plugin { } }); - return {}; + return { + urlGenerator: this.urlGenerator, + }; } start(core: CoreStart, deps: any) { @@ -154,7 +166,9 @@ export class MlPlugin implements Plugin { http: core.http, i18n: core.i18n, }); - return {}; + return { + urlGenerator: this.urlGenerator, + }; } public stop() {} From a8971907f29d42bc08beff40e0e97900d3df4112 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 21 Sep 2020 18:49:03 -0500 Subject: [PATCH 06/19] [ML][Infra] Update ML links in infra to non-hash paths --- .../logging/log_analysis_results/analyze_in_ml_button.tsx | 4 ++-- x-pack/plugins/infra/public/hooks/use_link_props.test.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx index 3e54920160c53..c265876522767 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx @@ -58,7 +58,7 @@ export const getOverallAnomalyExplorerLinkDescriptor = ( return { app: 'ml', - hash: '/explorer', + pathname: '/explorer', search: { _g }, }; }; @@ -89,7 +89,7 @@ export const getEntitySpecificSingleMetricViewerLink = ( return { app: 'ml', - hash: '/timeseriesexplorer', + pathname: '/timeseriesexplorer', search: { _g, _a }, }; }; diff --git a/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx b/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx index d93cc44c45623..8c1647bd79798 100644 --- a/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx +++ b/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx @@ -129,7 +129,7 @@ describe('useLinkProps hook', () => { it('Provides the correct props with hash options', () => { const { result } = renderUseLinkPropsHook({ app: 'ml', - hash: '/explorer', + pathname: '/explorer', search: { type: 'host', id: 'some-id', @@ -137,7 +137,7 @@ describe('useLinkProps hook', () => { }, }); expect(result.current.href).toBe( - '/test-basepath/s/test-space/app/ml#/explorer?type=host&id=some-id&count=12345' + '/test-basepath/s/test-space/app/ml/explorer?type=host&id=some-id&count=12345' ); expect(result.current.onClick).toBeDefined(); }); @@ -145,7 +145,7 @@ describe('useLinkProps hook', () => { it('Provides the correct props with more complex encoding', () => { const { result } = renderUseLinkPropsHook({ app: 'ml', - hash: '/explorer', + pathname: '/explorer', search: { type: 'host + host', name: 'this name has spaces and ** and %', @@ -155,7 +155,7 @@ describe('useLinkProps hook', () => { }, }); expect(result.current.href).toBe( - '/test-basepath/s/test-space/app/ml#/explorer?type=host%20%2B%20host&name=this%20name%20has%20spaces%20and%20**%20and%20%25&id=some-id&count=12345&animals=dog,cat,bear' + '/test-basepath/s/test-space/app/ml/explorer?type=host%20%2B%20host&name=this%20name%20has%20spaces%20and%20**%20and%20%25&id=some-id&count=12345&animals=dog,cat,bear' ); expect(result.current.onClick).toBeDefined(); }); From dcf92e00349a64ee2ab150fa9c5d22730d4a2abf Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 21 Sep 2020 19:41:40 -0500 Subject: [PATCH 07/19] [ML] Move MlUrlGenerator registration outside of licensing block for security solution --- x-pack/plugins/ml/public/plugin.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index a7967e0ed66dc..e73071046535e 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -104,25 +104,25 @@ export class MlPlugin implements Plugin { }); const managementApp = registerManagementSection(pluginsSetup.management, core); + if (pluginsSetup.share) { + const baseUrl = core.http.basePath.prepend('/app/ml'); + this.urlGenerator = pluginsSetup.share.urlGenerators.registerUrlGenerator( + new MlUrlGenerator({ + appBasePath: baseUrl, + useHash: core.uiSettings.get('state:storeInSessionStorage'), + }) + ); + } const licensing = pluginsSetup.licensing.license$.pipe(take(1)); licensing.subscribe(async (license) => { const [coreStart] = await core.getStartServices(); + if (isMlEnabled(license)) { // add ML to home page if (pluginsSetup.home) { registerFeature(pluginsSetup.home); } - if (pluginsSetup.share) { - const baseUrl = core.http.basePath.prepend('/app/ml'); - this.urlGenerator = pluginsSetup.share.urlGenerators.registerUrlGenerator( - new MlUrlGenerator({ - appBasePath: baseUrl, - useHash: core.uiSettings.get('state:storeInSessionStorage'), - }) - ); - } - const { capabilities } = coreStart.application; // register ML for the index pattern management no data screen. From 76ffb79908240bea6797270c9ebedebb38bd3596 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 21 Sep 2020 19:42:39 -0500 Subject: [PATCH 08/19] [ML][Security] Update ml links in security --- .../ml_popover/jobs_table/jobs_table.tsx | 124 +++++++++++++----- .../description_step/ml_job_description.tsx | 30 ++++- .../plugins/security_solution/public/types.ts | 3 + 3 files changed, 121 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx index 1e9e689dcd6ff..13448f67400c8 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx @@ -22,7 +22,7 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; -import { useBasePath } from '../../../lib/kibana'; +import { useBasePath, useKibana } from '../../../lib/kibana'; import * as i18n from './translations'; import { JobSwitch } from './job_switch'; import { SecurityJob } from '../types'; @@ -36,6 +36,52 @@ JobNameWrapper.displayName = 'JobNameWrapper'; // TODO: Use SASS mixin @include EuiTextTruncate when we switch from styled components const truncateThreshold = 200; +interface JobNameProps { + id: string; + description: string; + basePath: string; +} + +const JobName = ({ id, description, basePath }: JobNameProps) => { + const [jobUrl, setJobUrl] = useState(`${basePath}/app/ml/jobs`); + const { + services: { ml }, + } = useKibana(); + + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + if (ml?.urlGenerator !== undefined) { + const href = await ml.urlGenerator.createUrl({ + page: 'jobs', + pageState: { + jobId: id, + }, + }); + if (!isCancelled) { + setJobUrl(href); + } + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [ml?.urlGenerator, id]); + + return ( + + + {id} + + + {description.length > truncateThreshold + ? `${description.substring(0, truncateThreshold)}...` + : description} + + + ); +}; const getJobsTableColumns = ( isLoading: boolean, onJobStateChange: (job: SecurityJob, latestTimestampMs: number, enable: boolean) => Promise, @@ -44,20 +90,7 @@ const getJobsTableColumns = ( { name: i18n.COLUMN_JOB_NAME, render: ({ id, description }: SecurityJob) => ( - - - {id} - - - {description.length > truncateThreshold - ? `${description.substring(0, truncateThreshold)}...` - : description} - - + ), }, { @@ -141,22 +174,49 @@ export const JobsTable = React.memo(JobsTableComponent); JobsTable.displayName = 'JobsTable'; -export const NoItemsMessage = React.memo(({ basePath }: { basePath: string }) => ( - {i18n.NO_ITEMS_TEXT}} - titleSize="xs" - actions={ - - {i18n.CREATE_CUSTOM_JOB} - - } - /> -)); +export const NoItemsMessage = React.memo(({ basePath }: { basePath: string }) => { + const { + services: { ml }, + } = useKibana(); + + const [createNewAnomalyDetectionJoUrl, setCreateNewAnomalyDetectionJoUrl] = useState( + `${basePath}/app/ml/jobs/new_job/step/index_or_search` + ); + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + if (ml?.urlGenerator !== undefined) { + const href = await ml.urlGenerator.createUrl({ + page: 'jobs/new_job/step/index_or_search', + }); + if (!isCancelled) { + setCreateNewAnomalyDetectionJoUrl(href); + } + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [ml?.urlGenerator]); + + return ( + {i18n.NO_ITEMS_TEXT}} + titleSize="xs" + actions={ + + {i18n.CREATE_CUSTOM_JOB} + + } + /> + ); +}); NoItemsMessage.displayName = 'NoItemsMessage'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx index 414f6f2c2d3bb..d4a3758aa9090 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { EuiBadge, EuiIcon, EuiLink, EuiToolTip } from '@elastic/eui'; @@ -72,9 +72,31 @@ const Wrapper = styled.div` const MlJobDescriptionComponent: React.FC<{ jobId: string }> = ({ jobId }) => { const { jobs } = useSecurityJobs(false); - const jobUrl = useKibana().services.application.getUrlForApp( - `ml#/jobs?mlManagement=(jobId:${encodeURI(jobId)})` - ); + const { + services: { http, ml }, + } = useKibana(); + const [jobUrl, setJobUrl] = useState(http.basePath.prepend('/app/ml/jobs')); + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + if (ml?.urlGenerator !== undefined) { + const href = await ml.urlGenerator.createUrl({ + page: 'jobs', + pageState: { + jobId: [jobId], + }, + }); + if (!isCancelled) { + setJobUrl(href); + } + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [ml?.urlGenerator, jobId]); + const job = jobs.find(({ id }) => id === jobId); const jobIdSpan = {jobId}; diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 62069484dd8bd..ef40d34104b72 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -23,12 +23,14 @@ import { SecurityPluginSetup } from '../../security/public'; import { AppFrontendLibs } from './common/lib/lib'; import { ResolverPluginSetup } from './resolver/types'; import { Inspect } from '../common/search_strategy'; +import { MlPluginSetup, MlPluginStart } from '../../ml/public'; export interface SetupPlugins { home?: HomePublicPluginSetup; security: SecurityPluginSetup; triggers_actions_ui: TriggersActionsSetup; usageCollection?: UsageCollectionSetup; + ml?: MlPluginSetup; } export interface StartPlugins { @@ -40,6 +42,7 @@ export interface StartPlugins { newsfeed?: NewsfeedStart; triggers_actions_ui: TriggersActionsStart; uiActions: UiActionsStart; + ml?: MlPluginStart; } export type StartServices = CoreStart & From cdd50455b23094bf33c2f96e7a9e5e9fde8602aa Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 22 Sep 2020 11:46:52 -0500 Subject: [PATCH 09/19] [ML][APM] Update test snapshots --- .../Links/MachineLearningLinks/MLJobLink.test.tsx | 12 +++++++++--- .../Links/MachineLearningLinks/MLLink.test.tsx | 4 +++- .../useTimeSeriesExplorerHref.ts | 6 +++--- .../ApmPluginContext/MockApmPluginContext.tsx | 8 +++++--- .../ml/public/ml_url_generator/ml_url_generator.ts | 2 +- x-pack/plugins/ml/public/plugin.ts | 10 ++-------- x-pack/plugins/ml/public/shared.ts | 1 + 7 files changed, 24 insertions(+), 19 deletions(-) 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/MLJobLink.test.tsx index 98aed3cc0f0d7..01336a0e8f0ce 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx @@ -21,7 +21,9 @@ describe('MLJobLink', () => { } as Location ); - expect(href).toMatchInlineSnapshot(`""`); + expect(href).toMatchInlineSnapshot( + `"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now%2Fw,to:now-4h))&_a=(mlTimeSeriesExplorer:(),zoom:(from:now%2Fw,to:now-4h))"` + ); }); it('should produce the correct URL with jobId, serviceName, and transactionType', async () => { const href = await getRenderedHref( @@ -38,7 +40,9 @@ describe('MLJobLink', () => { } as Location ); - expect(href).toMatchInlineSnapshot(`""`); + expect(href).toMatchInlineSnapshot( + `"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now%2Fw,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request)),zoom:(from:now%2Fw,to:now-4h))"` + ); }); it('correctly encodes time range values', async () => { @@ -56,6 +60,8 @@ describe('MLJobLink', () => { } as Location ); - expect(href).toMatchInlineSnapshot(`""`); + expect(href).toMatchInlineSnapshot( + `"/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request)),zoom:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))"` + ); }); }); 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 index 1b30d97f4d140..d89802df7fc22 100644 --- 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 @@ -20,5 +20,7 @@ test('MLLink produces the correct URL', async () => { } as Location ); - expect(href).toMatchInlineSnapshot(`""`); + expect(href).toMatchInlineSnapshot( + `"/app/ml/jobs?mlManagement=(groupIds:!(apm),jobId:!(something))"` + ); }); 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 index f7f18cf24cebd..bf596def49884 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts @@ -24,7 +24,7 @@ export function useTimeSeriesExplorerHref({ core, plugins: { ml }, } = useApmPluginContext(); - const [mlADLink, setMlADLink] = useState( + const [mlAnomalyDetectionHref, setMlAnomalyDetectionHref] = useState( core.http.basePath.prepend('/app/ml/jobs') ); const location = useLocation(); @@ -52,7 +52,7 @@ export function useTimeSeriesExplorerHref({ }, }); if (!isCancelled) { - setMlADLink(href); + setMlAnomalyDetectionHref(href); } } }; @@ -62,5 +62,5 @@ export function useTimeSeriesExplorerHref({ }; }, [ml?.urlGenerator, location.search, jobId, serviceName, transactionType]); - return mlADLink; + return mlAnomalyDetectionHref; } diff --git a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx index d3a2b5f05aa10..1933005091110 100644 --- a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx +++ b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx @@ -9,6 +9,7 @@ import { ApmPluginContext, ApmPluginContextValue } from '.'; import { ConfigSchema } from '../..'; import { UI_SETTINGS } from '../../../../../../src/plugins/data/common'; import { createCallApmApi } from '../../services/rest/createCallApmApi'; +import { MlUrlGenerator } from '../../../../ml/public'; const uiSettings: Record = { [UI_SETTINGS.TIMEPICKER_QUICK_RANGES]: [ @@ -80,9 +81,10 @@ const mockConfig: ConfigSchema = { const mockPlugin = { ml: { - urlGenerator: { - createUrl: (params: any) => '', - }, + urlGenerator: new MlUrlGenerator({ + appBasePath: '/app/ml', + useHash: false, + }), }, }; export const mockApmPluginContextValue = { diff --git a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts index abec5cc2b7d1e..b050d5626f882 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts @@ -106,7 +106,7 @@ export function registerUrlGenerator( core: CoreSetup ) { const baseUrl = core.http.basePath.prepend('/app/ml'); - share.urlGenerators.registerUrlGenerator( + return share.urlGenerators.registerUrlGenerator( new MlUrlGenerator({ appBasePath: baseUrl, useHash: core.uiSettings.get('state:storeInSessionStorage'), diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 13ca2fbeaf2b0..8ff3e030eede7 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -34,7 +34,7 @@ import { registerFeature } from './register_feature'; import { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import { registerMlUiActions } from './ui_actions'; import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public'; -import { MlUrlGenerator } from './ml_url_generator'; +import { registerUrlGenerator } from './ml_url_generator'; import { isFullLicense, isMlEnabled } from '../common/license'; import { registerEmbeddables } from './embeddables'; import { ML_APP_URL_GENERATOR } from '../common/constants/ml_url_generator'; @@ -105,13 +105,7 @@ export class MlPlugin implements Plugin { const managementApp = registerManagementSection(pluginsSetup.management, core); if (pluginsSetup.share) { - const baseUrl = core.http.basePath.prepend('/app/ml'); - this.urlGenerator = pluginsSetup.share.urlGenerators.registerUrlGenerator( - new MlUrlGenerator({ - appBasePath: baseUrl, - useHash: core.uiSettings.get('state:storeInSessionStorage'), - }) - ); + this.urlGenerator = registerUrlGenerator(pluginsSetup.share, core); } const licensing = pluginsSetup.licensing.license$.pipe(take(1)); diff --git a/x-pack/plugins/ml/public/shared.ts b/x-pack/plugins/ml/public/shared.ts index ec884bfac5351..90311467ab956 100644 --- a/x-pack/plugins/ml/public/shared.ts +++ b/x-pack/plugins/ml/public/shared.ts @@ -21,3 +21,4 @@ export * from './application/formatters/metric_change_description'; export * from './application/components/data_grid'; export * from './application/data_frame_analytics/common'; export * from './application/util/date_utils'; +export * from './ml_url_generator'; From b9258f28dbce8fc0b9babbe4ca8f75f6fb456d92 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 22 Sep 2020 12:15:59 -0500 Subject: [PATCH 10/19] [ML][APM] Update snapshots --- .../app/Home/__snapshots__/Home.test.tsx.snap | 14 +++- .../__snapshots__/Stackframe.test.tsx.snap | 66 +++++++++---------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap index 61ec70c0c92ca..945508067775d 100644 --- a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap @@ -53,8 +53,13 @@ exports[`Home component should render services 1`] = ` }, "plugins": Object { "ml": Object { - "urlGenerator": Object { + "urlGenerator": MlUrlGenerator { "createUrl": [Function], + "id": "ML_APP_URL_GENERATOR", + "params": Object { + "appBasePath": "/app/ml", + "useHash": false, + }, }, }, }, @@ -120,8 +125,13 @@ exports[`Home component should render traces 1`] = ` }, "plugins": Object { "ml": Object { - "urlGenerator": Object { + "urlGenerator": MlUrlGenerator { "createUrl": [Function], + "id": "ML_APP_URL_GENERATOR", + "params": Object { + "appBasePath": "/app/ml", + "useHash": false, + }, }, }, }, diff --git a/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap index a5f8c40876540..94c7f97105e67 100644 --- a/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap @@ -238,8 +238,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -2021127760, - "componentId": "sc-fzoLsD", + "baseHash": 1028616872, + "componentId": "sc-fzpans", "isStatic": false, "rules": Array [ " @@ -254,7 +254,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-fzoLsD", + "styledComponentId": "sc-fzpans", "target": "span", "toString": [Function], "warnTooManyClasses": [Function], @@ -444,8 +444,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": -1923298833, + "componentId": "sc-AxmLO", "isStatic": false, "rules": Array [ " @@ -462,7 +462,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxmLO", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -474,8 +474,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": 1882630949, - "componentId": "sc-AxheI", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -500,7 +500,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxheI", + "styledComponentId": "sc-Axmtr", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], @@ -669,8 +669,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": -1923298833, + "componentId": "sc-AxmLO", "isStatic": false, "rules": Array [ " @@ -687,7 +687,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxmLO", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -699,8 +699,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": 1882630949, - "componentId": "sc-AxheI", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -725,7 +725,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxheI", + "styledComponentId": "sc-Axmtr", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], @@ -895,8 +895,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": -1923298833, + "componentId": "sc-AxmLO", "isStatic": false, "rules": Array [ " @@ -913,7 +913,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxmLO", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -925,8 +925,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": 1882630949, - "componentId": "sc-AxheI", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -951,7 +951,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxheI", + "styledComponentId": "sc-Axmtr", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], @@ -1131,8 +1131,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": -1923298833, + "componentId": "sc-AxmLO", "isStatic": false, "rules": Array [ " @@ -1149,7 +1149,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxmLO", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -1161,8 +1161,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": 1882630949, - "componentId": "sc-AxheI", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -1187,7 +1187,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxheI", + "styledComponentId": "sc-Axmtr", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], @@ -1384,8 +1384,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": -1923298833, + "componentId": "sc-AxmLO", "isStatic": false, "rules": Array [ " @@ -1402,7 +1402,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxmLO", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -1414,8 +1414,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": 1882630949, - "componentId": "sc-AxheI", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -1440,7 +1440,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxheI", + "styledComponentId": "sc-Axmtr", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], From 02cb4c666300e986e9d94880d93205c41c0fb1fd Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 22 Sep 2020 13:09:20 -0500 Subject: [PATCH 11/19] [ML][Security solution] Update tests --- .../ml_popover/jobs_table/jobs_table.test.tsx | 61 +++++++++++++------ .../common/lib/kibana/kibana_react.mock.ts | 7 +++ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx index dfcabe7b5aedf..6d22adbe5928b 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx @@ -10,12 +10,30 @@ import { JobsTableComponent } from './jobs_table'; import { mockSecurityJobs } from '../api.mock'; import { cloneDeep } from 'lodash/fp'; import { SecurityJob } from '../types'; +import { createKibanaContextProviderMock } from '../../../lib/kibana/kibana_react.mock'; +import { render, waitForElement } from '@testing-library/react'; jest.mock('../../../lib/kibana'); +const MockKibanaContextProvider = createKibanaContextProviderMock(); + +export async function getRenderedHref(Component: React.FC, selector: string) { + const el = render( + + + + ); + + await waitForElement(() => el.container.querySelector(selector)); + + const a = el.container.querySelector(selector); + return a ? a.getAttribute('href') : ''; +} + describe('JobsTableComponent', () => { let securityJobs: SecurityJob[]; let onJobStateChangeMock = jest.fn(); + beforeEach(() => { securityJobs = cloneDeep(mockSecurityJobs); onJobStateChangeMock = jest.fn(); @@ -32,31 +50,33 @@ describe('JobsTableComponent', () => { expect(wrapper).toMatchSnapshot(); }); - test('should render the hyperlink which points specifically to the job id', () => { - const wrapper = mount( - - ); - expect(wrapper.find('[data-test-subj="jobs-table-link"]').first().props().href).toEqual( - '/test/base/path/app/ml#/jobs?mlManagement=(jobId:linux_anomalous_network_activity_ecs)' + test('should render the hyperlink which points specifically to the job id', async () => { + const href = await getRenderedHref( + () => ( + + ), + '[data-test-subj="jobs-table-link"]' ); + expect(href).toEqual('/app/ml/jobs?mlManagement=(jobId:linux_anomalous_network_activity_ecs)'); }); - test('should render the hyperlink with URI encodings which points specifically to the job id', () => { + test('should render the hyperlink with URI encodings which points specifically to the job id', async () => { securityJobs[0].id = 'job id with spaces'; - const wrapper = mount( - - ); - expect(wrapper.find('[data-test-subj="jobs-table-link"]').first().props().href).toEqual( - '/test/base/path/app/ml#/jobs?mlManagement=(jobId:job%20id%20with%20spaces)' + const href = await getRenderedHref( + () => ( + + ), + '[data-test-subj="jobs-table-link"]' ); + expect(href).toEqual("/app/ml/jobs?mlManagement=(jobId:'job%20id%20with%20spaces')"); }); test('should call onJobStateChange when the switch is clicked to be true/open', () => { @@ -67,6 +87,7 @@ describe('JobsTableComponent', () => { onJobStateChange={onJobStateChangeMock} /> ); + wrapper .find('button[data-test-subj="job-switch"]') .first() diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index c026b65853a4c..914ebd0af951d 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -30,6 +30,7 @@ import { } from '../../../../common/constants'; import { StartServices } from '../../../types'; import { createSecuritySolutionStorageMock } from '../../mock/mock_local_storage'; +import { MlUrlGenerator } from '../../../../../ml/public'; const mockUiSettings: Record = { [DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' }, @@ -82,6 +83,12 @@ export const createStartServicesMock = (): StartServices => { data, security, storage, + ml: { + urlGenerator: new MlUrlGenerator({ + appBasePath: '/app/ml', + useHash: false, + }), + }, } as unknown) as StartServices; return services; From d37abef58123acfc382288f2d374969221fdb738 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 22 Sep 2020 14:19:27 -0500 Subject: [PATCH 12/19] [ML] Update MLLink to include globalState --- .../shared/Links/MachineLearningLinks/MLLink.test.tsx | 2 +- .../shared/Links/MachineLearningLinks/MLLink.tsx | 7 ++++--- .../public/application/jobs/jobs_list/components/utils.js | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) 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 index d89802df7fc22..cfaf52edfe751 100644 --- 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 @@ -21,6 +21,6 @@ test('MLLink produces the correct URL', async () => { ); expect(href).toMatchInlineSnapshot( - `"/app/ml/jobs?mlManagement=(groupIds:!(apm),jobId:!(something))"` + `"/app/ml/jobs?mlManagement=(groupIds:!(apm),jobId:!(something))&_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/MLLink.tsx index f336713019a07..c2bb5e1832851 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx @@ -35,7 +35,6 @@ export function MLLink({ children, path = '', query = {}, external }: Props) { const [mlADLink, setMlADLink] = useState( core.http.basePath.prepend('/app/ml/jobs') ); - useEffect(() => { let isCancelled = false; const generateLink = async () => { @@ -51,8 +50,10 @@ export function MLLink({ children, path = '', query = {}, external }: Props) { pageState: { jobId: jobIds, groupIds: ['apm'], - timeRange: time, - refreshInterval, + globalState: { + time, + refreshInterval, + }, }, }); if (!isCancelled) { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index 75eaf46e3b441..c1f6d75637ed4 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -268,7 +268,6 @@ export function deleteJobs(jobs, finish = () => {}) { } export function filterJobs(jobs, clauses) { - console.log('jobs, clauses', jobs, clauses); if (clauses.length === 0) { return jobs; } From 9be734dca3f34ac0b673fd161a95aff679e29785 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 22 Sep 2020 14:29:48 -0500 Subject: [PATCH 13/19] [ML] Update useTimeSeriesExplorerHref --- .../Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index bf596def49884..fefbcb2d14775 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts @@ -48,7 +48,7 @@ export function useTimeSeriesExplorerHref({ 'transaction.type': transactionType, }, } - : null), + : {}), }, }); if (!isCancelled) { From 55c25a4b0ffde42f890780cecc083a275b4819cd Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Thu, 24 Sep 2020 13:17:27 -0500 Subject: [PATCH 14/19] [ML] Update apm and security_solution to use useMlHref hook --- .../app/Home/__snapshots__/Home.test.tsx.snap | 2 + .../anomaly_detection/legacy_jobs_callout.tsx | 34 +++-------- .../Links/MachineLearningLinks/MLLink.tsx | 56 +++++++------------ .../useTimeSeriesExplorerHref.ts | 55 +++++++----------- .../ApmPluginContext/MockApmPluginContext.tsx | 1 + .../ml/public/ml_url_generator/index.ts | 2 + .../ml/public/ml_url_generator/use_ml_href.ts | 34 +++++++++++ x-pack/plugins/security_solution/kibana.json | 2 +- .../ml_popover/jobs_table/jobs_table.tsx | 51 ++++------------- 9 files changed, 97 insertions(+), 140 deletions(-) create mode 100644 x-pack/plugins/ml/public/ml_url_generator/use_ml_href.ts diff --git a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap index 945508067775d..00be0b37a0e82 100644 --- a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap @@ -34,6 +34,7 @@ exports[`Home component should render services 1`] = ` }, "http": Object { "basePath": Object { + "get": [Function], "prepend": [Function], }, }, @@ -106,6 +107,7 @@ exports[`Home component should render traces 1`] = ` }, "http": Object { "basePath": Object { + "get": [Function], "prepend": [Function], }, }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx index 3467908b7ce89..1844e5754cfba 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/legacy_jobs_callout.tsx @@ -5,40 +5,22 @@ */ import { EuiCallOut, EuiButton } from '@elastic/eui'; -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; +import { useMlHref } from '../../../../../../ml/public'; export function LegacyJobsCallout() { const { core, plugins: { ml }, } = useApmPluginContext(); - - const [mlADLink, setMlADLink] = useState( - core.http.basePath.prepend('/app/ml/jobs') - ); - - useEffect(() => { - let isCancelled = false; - const generateLink = async () => { - if (ml?.urlGenerator !== undefined) { - const href = await ml.urlGenerator.createUrl({ - page: 'jobs', - pageState: { - jobId: 'high_mean_response_time', - }, - }); - if (!isCancelled) { - setMlADLink(href); - } - } - }; - generateLink(); - return () => { - isCancelled = true; - }; - }, [ml?.urlGenerator]); + const mlADLink = useMlHref(ml, core.http.basePath.get(), { + page: 'jobs', + pageState: { + jobId: 'high_mean_response_time', + }, + }); return ( { - let isCancelled = false; - const generateLink = async () => { - const { time, refreshInterval } = getTimepickerRisonData(location.search); - let jobIds: string[] = []; - if (query.ml?.jobIds) { - jobIds = query.ml.jobIds; - } + let jobIds: string[] = []; + if (query.ml?.jobIds) { + jobIds = query.ml.jobIds; + } + const { time, refreshInterval } = getTimepickerRisonData(location.search); - if (ml?.urlGenerator !== undefined) { - const href = await ml.urlGenerator.createUrl({ - page: 'jobs', - pageState: { - jobId: jobIds, - groupIds: ['apm'], - globalState: { - time, - refreshInterval, - }, - }, - }); - if (!isCancelled) { - setMlADLink(href); - } - } - }; - generateLink(); - return () => { - isCancelled = true; - }; - }, [ml?.urlGenerator, location.search, query]); + // default to link to ML Anomaly Detection jobs management page + const mlADLink = useMlHref(ml, core.http.basePath.get(), { + page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, + pageState: { + jobId: jobIds, + groupIds: ['apm'], + globalState: { + time, + refreshInterval, + }, + }, + }); return ( { - let isCancelled = false; - const generateLink = async () => { - const { time, refreshInterval } = getTimepickerRisonData(location.search); - if (ml?.urlGenerator !== undefined) { - const href = await ml.urlGenerator.createUrl({ - page: 'timeseriesexplorer', - pageState: { - jobIds: [jobId], - timeRange: time, - refreshInterval: refreshInterval as RefreshInterval, - zoom: time, - ...(serviceName && transactionType - ? { - entities: { - 'service.name': serviceName, - 'transaction.type': transactionType, - }, - } - : {}), - }, - }); - if (!isCancelled) { - setMlAnomalyDetectionHref(href); - } - } - }; - generateLink(); - return () => { - isCancelled = true; - }; - }, [ml?.urlGenerator, location.search, jobId, serviceName, transactionType]); + const mlAnomalyDetectionHref = useMlHref(ml, core.http.basePath.get(), { + page: 'timeseriesexplorer', + pageState: { + jobIds: [jobId], + timeRange: time, + refreshInterval: refreshInterval as RefreshInterval, + zoom: time, + ...(serviceName && transactionType + ? { + entities: { + 'service.name': serviceName, + 'transaction.type': transactionType, + }, + } + : {}), + }, + }); return mlAnomalyDetectionHref; } diff --git a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx index 1933005091110..65f6dca179e71 100644 --- a/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx +++ b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx @@ -55,6 +55,7 @@ const mockCore = { http: { basePath: { prepend: (path: string) => `/basepath${path}`, + get: () => `/basepath`, }, }, i18n: { diff --git a/x-pack/plugins/ml/public/ml_url_generator/index.ts b/x-pack/plugins/ml/public/ml_url_generator/index.ts index 1579b187278c4..bd9e58654828b 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/index.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/index.ts @@ -4,3 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ export { MlUrlGenerator, registerUrlGenerator } from './ml_url_generator'; +export { useMlHref } from './use_ml_href'; +export { ML_PAGES } from '../../common/constants/ml_url_generator'; diff --git a/x-pack/plugins/ml/public/ml_url_generator/use_ml_href.ts b/x-pack/plugins/ml/public/ml_url_generator/use_ml_href.ts new file mode 100644 index 0000000000000..8e5a6ef64e59f --- /dev/null +++ b/x-pack/plugins/ml/public/ml_url_generator/use_ml_href.ts @@ -0,0 +1,34 @@ +/* + * 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 { useEffect, useState } from 'react'; +import { MlPluginStart } from '../index'; +import { MlUrlGeneratorState } from '../../common/types/ml_url_generator'; +export const useMlHref = ( + ml: MlPluginStart | undefined, + basePath: string, + params: MlUrlGeneratorState +) => { + const [mlLink, setMlLink] = useState(`${basePath}/app/ml/${params.page}`); + + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + if (ml?.urlGenerator !== undefined) { + const href = await ml.urlGenerator.createUrl(params); + if (!isCancelled) { + setMlLink(href); + } + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [ml?.urlGenerator, params]); + + return mlLink; +}; diff --git a/x-pack/plugins/security_solution/kibana.json b/x-pack/plugins/security_solution/kibana.json index 7b5c3b5337c02..ddaafec5d8a7d 100644 --- a/x-pack/plugins/security_solution/kibana.json +++ b/x-pack/plugins/security_solution/kibana.json @@ -31,5 +31,5 @@ ], "server": true, "ui": true, - "requiredBundles": ["esUiShared", "ingestManager", "kibanaUtils", "kibanaReact", "lists"] + "requiredBundles": ["esUiShared", "ingestManager", "kibanaUtils", "kibanaReact", "lists", "ml"] } diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx index 13448f67400c8..5e3045efe1f4d 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.tsx @@ -26,6 +26,7 @@ import { useBasePath, useKibana } from '../../../lib/kibana'; import * as i18n from './translations'; import { JobSwitch } from './job_switch'; import { SecurityJob } from '../types'; +import { useMlHref, ML_PAGES } from '../../../../../../ml/public'; const JobNameWrapper = styled.div` margin: 5px 0; @@ -43,31 +44,16 @@ interface JobNameProps { } const JobName = ({ id, description, basePath }: JobNameProps) => { - const [jobUrl, setJobUrl] = useState(`${basePath}/app/ml/jobs`); const { services: { ml }, } = useKibana(); - useEffect(() => { - let isCancelled = false; - const generateLink = async () => { - if (ml?.urlGenerator !== undefined) { - const href = await ml.urlGenerator.createUrl({ - page: 'jobs', - pageState: { - jobId: id, - }, - }); - if (!isCancelled) { - setJobUrl(href); - } - } - }; - generateLink(); - return () => { - isCancelled = true; - }; - }, [ml?.urlGenerator, id]); + const jobUrl = useMlHref(ml, basePath, { + page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, + pageState: { + jobId: id, + }, + }); return ( @@ -179,26 +165,9 @@ export const NoItemsMessage = React.memo(({ basePath }: { basePath: string }) => services: { ml }, } = useKibana(); - const [createNewAnomalyDetectionJoUrl, setCreateNewAnomalyDetectionJoUrl] = useState( - `${basePath}/app/ml/jobs/new_job/step/index_or_search` - ); - useEffect(() => { - let isCancelled = false; - const generateLink = async () => { - if (ml?.urlGenerator !== undefined) { - const href = await ml.urlGenerator.createUrl({ - page: 'jobs/new_job/step/index_or_search', - }); - if (!isCancelled) { - setCreateNewAnomalyDetectionJoUrl(href); - } - } - }; - generateLink(); - return () => { - isCancelled = true; - }; - }, [ml?.urlGenerator]); + const createNewAnomalyDetectionJoUrl = useMlHref(ml, basePath, { + page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX, + }); return ( Date: Mon, 28 Sep 2020 11:43:37 -0500 Subject: [PATCH 15/19] [ML] Update APM to use useUrlParams hook, update security solution hook --- .../Links/MachineLearningLinks/MLLink.tsx | 18 ++++++----- .../useTimeSeriesExplorerHref.ts | 21 ++++++++----- .../description_step/ml_job_description.tsx | 31 +++++-------------- 3 files changed, 32 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx index 97ca18ea13db6..5fbcd475cb47b 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx @@ -6,10 +6,9 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { useLocation } from 'react-router-dom'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; -import { getTimepickerRisonData } from '../rison_helpers'; import { useMlHref, ML_PAGES } from '../../../../../../ml/public'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; interface MlRisonData { ml?: { @@ -25,8 +24,6 @@ interface Props { } export function MLLink({ children, path = '', query = {}, external }: Props) { - const location = useLocation(); - const { core, plugins: { ml }, @@ -36,7 +33,8 @@ export function MLLink({ children, path = '', query = {}, external }: Props) { if (query.ml?.jobIds) { jobIds = query.ml.jobIds; } - const { time, refreshInterval } = getTimepickerRisonData(location.search); + const { urlParams } = useUrlParams(); + const { rangeFrom, rangeTo, refreshInterval, refreshPaused } = urlParams; // default to link to ML Anomaly Detection jobs management page const mlADLink = useMlHref(ml, core.http.basePath.get(), { @@ -45,8 +43,14 @@ export function MLLink({ children, path = '', query = {}, external }: Props) { jobId: jobIds, groupIds: ['apm'], globalState: { - time, - refreshInterval, + time: + rangeFrom !== undefined && rangeTo !== undefined + ? { from: rangeFrom, to: rangeTo } + : undefined, + refreshInterval: + refreshPaused !== undefined && refreshInterval !== undefined + ? { pause: refreshPaused, value: refreshInterval } + : undefined, }, }, }); 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 index c5fb461864573..a758f266b4417 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts @@ -4,11 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useLocation } from 'react-router-dom'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; -import { getTimepickerRisonData } from '../rison_helpers'; -import { RefreshInterval } from '../../../../../../../../src/plugins/data/common/query'; import { useMlHref } from '../../../../../../ml/public'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; export function useTimeSeriesExplorerHref({ jobId, @@ -24,16 +22,23 @@ export function useTimeSeriesExplorerHref({ core, plugins: { ml }, } = useApmPluginContext(); - const location = useLocation(); - const { time, refreshInterval } = getTimepickerRisonData(location.search); + 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: time, - refreshInterval: refreshInterval as RefreshInterval, - zoom: time, + timeRange, + refreshInterval: + refreshPaused !== undefined && refreshInterval !== undefined + ? { pause: refreshPaused, value: refreshInterval } + : undefined, + zoom: timeRange, ...(serviceName && transactionType ? { entities: { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx index d4a3758aa9090..e25da49cf7e62 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import styled from 'styled-components'; import { EuiBadge, EuiIcon, EuiLink, EuiToolTip } from '@elastic/eui'; -import { MlSummaryJob } from '../../../../../../ml/public'; +import { ML_PAGES, MlSummaryJob, useMlHref } from '../../../../../../ml/public'; import { isJobStarted } from '../../../../../common/machine_learning/helpers'; import { useSecurityJobs } from '../../../../common/components/ml_popover/hooks/use_security_jobs'; import { useKibana } from '../../../../common/lib/kibana'; @@ -75,27 +75,12 @@ const MlJobDescriptionComponent: React.FC<{ jobId: string }> = ({ jobId }) => { const { services: { http, ml }, } = useKibana(); - const [jobUrl, setJobUrl] = useState(http.basePath.prepend('/app/ml/jobs')); - useEffect(() => { - let isCancelled = false; - const generateLink = async () => { - if (ml?.urlGenerator !== undefined) { - const href = await ml.urlGenerator.createUrl({ - page: 'jobs', - pageState: { - jobId: [jobId], - }, - }); - if (!isCancelled) { - setJobUrl(href); - } - } - }; - generateLink(); - return () => { - isCancelled = true; - }; - }, [ml?.urlGenerator, jobId]); + const jobUrl = useMlHref(ml, http.basePath.get(), { + page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, + pageState: { + jobId: [jobId], + }, + }); const job = jobs.find(({ id }) => id === jobId); From 7e66d56cc37ab24729962bc47d80636c23e24c1c Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 29 Sep 2020 10:20:42 -0500 Subject: [PATCH 16/19] [ML] Update tests, fix duplicate imports --- .../shared/Links/MachineLearningLinks/MLLink.test.tsx | 4 +--- x-pack/plugins/apm/public/utils/testHelpers.tsx | 5 ++++- .../components/ml_popover/jobs_table/jobs_table.test.tsx | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) 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 index cfaf52edfe751..be00364cab92e 100644 --- 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 @@ -11,9 +11,7 @@ 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', diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx index a69288f7bd4f9..971455fde3946 100644 --- a/x-pack/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx @@ -24,6 +24,7 @@ import { ESSearchRequest, } from '../../typings/elasticsearch'; import { MockApmPluginContextWrapper } from '../context/ApmPluginContext/MockApmPluginContext'; +import { UrlParamsProvider } from '../context/UrlParamsContext'; const originalConsoleWarn = console.warn; // eslint-disable-line no-console /** @@ -67,7 +68,9 @@ export async function getRenderedHref(Component: React.FC, location: Location) { const el = render( - + + + ); diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx index 7a0d225555448..5eed5c5cd2c6c 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx @@ -6,13 +6,12 @@ import { shallow, mount } from 'enzyme'; import React from 'react'; -import { waitFor } from '@testing-library/react'; +import { render, waitForElement, waitFor } from '@testing-library/react'; import { JobsTableComponent } from './jobs_table'; import { mockSecurityJobs } from '../api.mock'; import { cloneDeep } from 'lodash/fp'; import { SecurityJob } from '../types'; import { createKibanaContextProviderMock } from '../../../lib/kibana/kibana_react.mock'; -import { render, waitForElement } from '@testing-library/react'; jest.mock('../../../lib/kibana'); From 4903e358472dbc859037df113678a6f474e24d39 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 29 Sep 2020 13:42:46 -0500 Subject: [PATCH 17/19] [ML] Update imports, remove ml exports to shared cause it's not needed [ML] Add import --- x-pack/plugins/ml/public/index.ts | 1 + x-pack/plugins/ml/public/plugin.ts | 10 +++++++--- x-pack/plugins/ml/public/register_helper.ts | 1 - x-pack/plugins/ml/public/shared.ts | 1 - 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/ml/public/index.ts b/x-pack/plugins/ml/public/index.ts index c43df1e1a3d2c..81e10588a3845 100755 --- a/x-pack/plugins/ml/public/index.ts +++ b/x-pack/plugins/ml/public/index.ts @@ -41,6 +41,7 @@ export type { // Static exports export { getSeverityColor, getSeverityType } from '../common/util/anomaly_utils'; export { ANOMALY_SEVERITY } from '../common'; +export { useMlHref, ML_PAGES, MlUrlGenerator } from './ml_url_generator'; // Bundled shared exports // Exported this way so the code doesn't end up in ML's page load bundle diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index ef1da2b11655c..1f98de380312a 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -16,7 +16,11 @@ import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; import type { ManagementSetup } from 'src/plugins/management/public'; -import type { SharePluginSetup, SharePluginStart } from 'src/plugins/share/public'; +import type { + SharePluginSetup, + SharePluginStart, + UrlGeneratorContract, +} from 'src/plugins/share/public'; import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import type { DataPublicPluginStart } from 'src/plugins/data/public'; import type { HomePublicPluginSetup } from 'src/plugins/home/public'; @@ -34,6 +38,8 @@ import type { SecurityPluginSetup } from '../../security/public'; import { PLUGIN_ICON_SOLUTION, PLUGIN_ID } from '../common/constants/app'; import { setDependencyCache } from './application/util/dependency_cache'; +import { ML_APP_URL_GENERATOR } from '../common/constants/ml_url_generator'; +import { registerUrlGenerator } from './ml_url_generator'; export interface MlStartDependencies { data: DataPublicPluginStart; @@ -114,7 +120,6 @@ export class MlPlugin implements Plugin { registerFeature, registerManagementSection, registerMlUiActions, - registerUrlGenerator, MlCardState, } = await import('./register_helper'); @@ -123,7 +128,6 @@ export class MlPlugin implements Plugin { if (pluginsSetup.home) { registerFeature(pluginsSetup.home); } - const { capabilities } = coreStart.application; // register ML for the index pattern management no data screen. diff --git a/x-pack/plugins/ml/public/register_helper.ts b/x-pack/plugins/ml/public/register_helper.ts index 97574e296d1eb..b24ec44363775 100644 --- a/x-pack/plugins/ml/public/register_helper.ts +++ b/x-pack/plugins/ml/public/register_helper.ts @@ -12,4 +12,3 @@ export { registerEmbeddables } from './embeddables'; export { registerFeature } from './register_feature'; export { registerManagementSection } from './application/management'; export { registerMlUiActions } from './ui_actions'; -export { registerUrlGenerator } from './ml_url_generator'; diff --git a/x-pack/plugins/ml/public/shared.ts b/x-pack/plugins/ml/public/shared.ts index 90311467ab956..ec884bfac5351 100644 --- a/x-pack/plugins/ml/public/shared.ts +++ b/x-pack/plugins/ml/public/shared.ts @@ -21,4 +21,3 @@ export * from './application/formatters/metric_change_description'; export * from './application/components/data_grid'; export * from './application/data_frame_analytics/common'; export * from './application/util/date_utils'; -export * from './ml_url_generator'; From 5e09d954bdea3da56c692d31feef276c5ba2fdc6 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 30 Sep 2020 15:40:36 -0500 Subject: [PATCH 18/19] [ML] Update snapshot --- .../__snapshots__/Stackframe.test.tsx.snap | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap index 94c7f97105e67..a5f8c40876540 100644 --- a/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap @@ -238,8 +238,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": 1028616872, - "componentId": "sc-fzpans", + "baseHash": -2021127760, + "componentId": "sc-fzoLsD", "isStatic": false, "rules": Array [ " @@ -254,7 +254,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-fzpans", + "styledComponentId": "sc-fzoLsD", "target": "span", "toString": [Function], "warnTooManyClasses": [Function], @@ -444,8 +444,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1923298833, - "componentId": "sc-AxmLO", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -462,7 +462,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxmLO", + "styledComponentId": "sc-Axmtr", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -474,8 +474,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": 1882630949, + "componentId": "sc-AxheI", "isStatic": false, "rules": Array [ " @@ -500,7 +500,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxheI", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], @@ -669,8 +669,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1923298833, - "componentId": "sc-AxmLO", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -687,7 +687,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxmLO", + "styledComponentId": "sc-Axmtr", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -699,8 +699,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": 1882630949, + "componentId": "sc-AxheI", "isStatic": false, "rules": Array [ " @@ -725,7 +725,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxheI", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], @@ -895,8 +895,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1923298833, - "componentId": "sc-AxmLO", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -913,7 +913,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxmLO", + "styledComponentId": "sc-Axmtr", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -925,8 +925,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": 1882630949, + "componentId": "sc-AxheI", "isStatic": false, "rules": Array [ " @@ -951,7 +951,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxheI", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], @@ -1131,8 +1131,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1923298833, - "componentId": "sc-AxmLO", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -1149,7 +1149,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxmLO", + "styledComponentId": "sc-Axmtr", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -1161,8 +1161,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": 1882630949, + "componentId": "sc-AxheI", "isStatic": false, "rules": Array [ " @@ -1187,7 +1187,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxheI", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], @@ -1384,8 +1384,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1923298833, - "componentId": "sc-AxmLO", + "baseHash": -1474970742, + "componentId": "sc-Axmtr", "isStatic": false, "rules": Array [ " @@ -1402,7 +1402,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-AxmLO", + "styledComponentId": "sc-Axmtr", "target": "code", "toString": [Function], "warnTooManyClasses": [Function], @@ -1414,8 +1414,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "baseHash": -1474970742, - "componentId": "sc-Axmtr", + "baseHash": 1882630949, + "componentId": "sc-AxheI", "isStatic": false, "rules": Array [ " @@ -1440,7 +1440,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`] "foldedComponentIds": Array [], "render": [Function], "shouldForwardProp": undefined, - "styledComponentId": "sc-Axmtr", + "styledComponentId": "sc-AxheI", "target": "pre", "toString": [Function], "warnTooManyClasses": [Function], From 1c390c169993ec2f6d687a52752a46db98cb8f27 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 30 Sep 2020 16:00:15 -0500 Subject: [PATCH 19/19] [ML] Fix warnings for jobs_table.test.tsx --- .../ml_popover/jobs_table/jobs_table.test.tsx | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx index 5eed5c5cd2c6c..156475f63aa65 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx @@ -6,28 +6,21 @@ import { shallow, mount } from 'enzyme'; import React from 'react'; -import { render, waitForElement, waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import { JobsTableComponent } from './jobs_table'; import { mockSecurityJobs } from '../api.mock'; import { cloneDeep } from 'lodash/fp'; import { SecurityJob } from '../types'; -import { createKibanaContextProviderMock } from '../../../lib/kibana/kibana_react.mock'; jest.mock('../../../lib/kibana'); -const MockKibanaContextProvider = createKibanaContextProviderMock(); - export async function getRenderedHref(Component: React.FC, selector: string) { - const el = render( - - - - ); + const el = render(); - await waitForElement(() => el.container.querySelector(selector)); + await waitFor(() => el.container.querySelector(selector)); const a = el.container.querySelector(selector); - return a ? a.getAttribute('href') : ''; + return a?.getAttribute('href') ?? ''; } describe('JobsTableComponent', () => { @@ -61,7 +54,9 @@ describe('JobsTableComponent', () => { ), '[data-test-subj="jobs-table-link"]' ); - expect(href).toEqual('/app/ml/jobs?mlManagement=(jobId:linux_anomalous_network_activity_ecs)'); + await waitFor(() => + expect(href).toEqual('/app/ml/jobs?mlManagement=(jobId:linux_anomalous_network_activity_ecs)') + ); }); test('should render the hyperlink with URI encodings which points specifically to the job id', async () => { @@ -76,7 +71,9 @@ describe('JobsTableComponent', () => { ), '[data-test-subj="jobs-table-link"]' ); - expect(href).toEqual("/app/ml/jobs?mlManagement=(jobId:'job%20id%20with%20spaces')"); + await waitFor(() => + expect(href).toEqual("/app/ml/jobs?mlManagement=(jobId:'job%20id%20with%20spaces')") + ); }); test('should call onJobStateChange when the switch is clicked to be true/open', async () => { @@ -99,7 +96,7 @@ describe('JobsTableComponent', () => { }); }); - test('should have a switch when it is not in the loading state', () => { + test('should have a switch when it is not in the loading state', async () => { const wrapper = mount( { onJobStateChange={onJobStateChangeMock} /> ); - expect(wrapper.find('[data-test-subj="job-switch"]').exists()).toBe(true); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="job-switch"]').exists()).toBe(true); + }); }); - test('should not have a switch when it is in the loading state', () => { + test('should not have a switch when it is in the loading state', async () => { const wrapper = mount( { onJobStateChange={onJobStateChangeMock} /> ); - expect(wrapper.find('[data-test-subj="job-switch"]').exists()).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="job-switch"]').exists()).toBe(false); + }); }); });