From f0e66c03070fc37f93254ae3faa39b9affbee1da Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 4 Aug 2020 15:13:53 -0400 Subject: [PATCH 1/7] handle group id in url for anomaly detection --- .../job_filter_bar/job_filter_bar.js | 20 ++++++++++--------- .../jobs/jobs_list/components/utils.d.ts | 3 ++- .../jobs/jobs_list/components/utils.js | 14 +++++++++++-- 3 files changed, 25 insertions(+), 12 deletions(-) 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 b274a8d572adb..04d7a5649c8af 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,7 @@ import React, { Component, Fragment } from 'react'; import { ml } from '../../../../services/ml_api_service'; import { JobGroup } from '../job_group'; -import { getSelectedJobIdFromUrl, clearSelectedJobIdFromUrl } from '../utils'; +import { getGroupQueryText, getSelectedJobIdFromUrl, clearSelectedJobIdFromUrl } from '../utils'; import { EuiSearchBar, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -54,15 +54,17 @@ export class JobFilterBar extends Component { componentDidMount() { // If job id is selected in url, filter table to that id - const selectedId = getSelectedJobIdFromUrl(window.location.href); - if (selectedId !== undefined) { + const urlValues = getSelectedJobIdFromUrl(window.location.href); + const defaultQueryText = urlValues.isGroup ? getGroupQueryText(urlValues.id) : urlValues.id; + + if (defaultQueryText !== undefined) { this.setState( { - selectedId, + defaultQueryText, }, () => { // trigger onChange with query for job id to trigger table filter - const query = EuiSearchBar.Query.parse(selectedId); + const query = EuiSearchBar.Query.parse(defaultQueryText); this.onChange({ query }); } ); @@ -87,7 +89,7 @@ export class JobFilterBar extends Component { }; render() { - const { error, selectedId } = this.state; + const { error, defaultQueryText } = this.state; const filters = [ { type: 'field_value_toggle_group', @@ -147,7 +149,7 @@ export class JobFilterBar extends Component { return ( - {selectedId === undefined && ( + {defaultQueryText === undefined && ( )} - {selectedId !== undefined && ( + {defaultQueryText !== undefined && ( Date: Tue, 4 Aug 2020 17:15:25 -0400 Subject: [PATCH 2/7] filter analytics list by group id. --- .../analytics_list/analytics_list.tsx | 19 +++++++++++-------- .../job_filter_bar/job_filter_bar.js | 4 ++-- .../jobs/jobs_list/components/utils.d.ts | 2 +- .../jobs/jobs_list/components/utils.js | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 90e24f6da5d0a..8c536386acab6 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -38,7 +38,10 @@ import { getTaskStateBadge, getJobTypeBadge, useColumns } from './use_columns'; import { ExpandedRow } from './expanded_row'; import { AnalyticStatsBarStats, StatsBar } from '../../../../../components/stats_bar'; import { CreateAnalyticsButton } from '../create_analytics_button'; -import { getSelectedJobIdFromUrl } from '../../../../../jobs/jobs_list/components/utils'; +import { + getSelectedIdFromUrl, + getGroupQueryText, +} from '../../../../../jobs/jobs_list/components/utils'; import { SourceSelection } from '../source_selection'; function getItemIdToExpandedRowMap( @@ -99,16 +102,16 @@ export const DataFrameAnalyticsList: FC = ({ // Query text/job_id based on url but only after getAnalytics is done first // selectedJobIdFromUrlInitialized makes sure the query is only run once since analytics is being refreshed constantly - const [selectedJobIdFromUrlInitialized, setSelectedJobIdFromUrlInitialized] = useState(false); + const [selectedIdFromUrlInitialized, setSelectedIdFromUrlInitialized] = useState(false); useEffect(() => { - if (selectedJobIdFromUrlInitialized === false && analytics.length > 0) { - const selectedJobIdFromUrl = getSelectedJobIdFromUrl(window.location.href); - if (selectedJobIdFromUrl !== undefined) { - setSelectedJobIdFromUrlInitialized(true); - setSearchQueryText(selectedJobIdFromUrl); + if (selectedIdFromUrlInitialized === false && analytics.length > 0) { + const { id: selectedIdFromUrl, isGroup } = getSelectedIdFromUrl(window.location.href); + if (selectedIdFromUrl !== undefined) { + setSelectedIdFromUrlInitialized(true); + setSearchQueryText(isGroup ? getGroupQueryText(selectedIdFromUrl) : selectedIdFromUrl); } } - }, [selectedJobIdFromUrlInitialized, analytics]); + }, [selectedIdFromUrlInitialized, analytics]); // Subscribe to the refresh observable to trigger reloading the analytics list. useRefreshAnalyticsList({ 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 04d7a5649c8af..1a5ff9e146532 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,7 @@ import React, { Component, Fragment } from 'react'; import { ml } from '../../../../services/ml_api_service'; import { JobGroup } from '../job_group'; -import { getGroupQueryText, getSelectedJobIdFromUrl, clearSelectedJobIdFromUrl } from '../utils'; +import { getGroupQueryText, getSelectedIdFromUrl, clearSelectedJobIdFromUrl } from '../utils'; import { EuiSearchBar, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -54,7 +54,7 @@ export class JobFilterBar extends Component { componentDidMount() { // If job id is selected in url, filter table to that id - const urlValues = getSelectedJobIdFromUrl(window.location.href); + const urlValues = getSelectedIdFromUrl(window.location.href); const defaultQueryText = urlValues.isGroup ? getGroupQueryText(urlValues.id) : urlValues.id; 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 1d332a3c6c18a..bb88da549ac8b 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 @@ -3,6 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -export function getSelectedJobIdFromUrl(str: string): { id: string; isGroup: boolean }; +export function getSelectedIdFromUrl(str: string): { id: string; isGroup: boolean }; export function getGroupQueryText(str: 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 a0df4082c1bb6..68d765ed6adb6 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 @@ -370,7 +370,7 @@ function getUrlVars(url) { return vars; } -export function getSelectedJobIdFromUrl(url) { +export function getSelectedIdFromUrl(url) { const result = {}; if (typeof url === 'string') { const isGroup = url.includes('groupId'); From 0e4f2ed8ba51799eac0b710c5cd3e849828af5de Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 5 Aug 2020 14:58:28 -0400 Subject: [PATCH 3/7] handle list of groupIds --- .../components/analytics_list/analytics_list.tsx | 5 +++-- .../components/job_filter_bar/job_filter_bar.js | 6 +++++- .../jobs/jobs_list/components/utils.d.ts | 4 ++-- .../application/jobs/jobs_list/components/utils.js | 11 +++++------ .../ml/public/application/util/get_job_id_url.ts | 13 +++++++++++++ 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 8c536386acab6..4fe3c5d1c0f99 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -105,8 +105,9 @@ export const DataFrameAnalyticsList: FC = ({ const [selectedIdFromUrlInitialized, setSelectedIdFromUrlInitialized] = useState(false); useEffect(() => { if (selectedIdFromUrlInitialized === false && analytics.length > 0) { - const { id: selectedIdFromUrl, isGroup } = getSelectedIdFromUrl(window.location.href); - if (selectedIdFromUrl !== undefined) { + const { ids, isGroup } = getSelectedIdFromUrl(window.location.href); + const selectedIdFromUrl = ids ?? ids[0]; + if (selectedIdFromUrl) { setSelectedIdFromUrlInitialized(true); setSearchQueryText(isGroup ? getGroupQueryText(selectedIdFromUrl) : selectedIdFromUrl); } 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 1a5ff9e146532..ad2f188f1e370 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 @@ -54,8 +54,12 @@ export class JobFilterBar extends Component { componentDidMount() { // If job id is selected in url, filter table to that id + let defaultQueryText; const urlValues = getSelectedIdFromUrl(window.location.href); - const defaultQueryText = urlValues.isGroup ? getGroupQueryText(urlValues.id) : urlValues.id; + + if (urlValues.ids !== undefined) { + defaultQueryText = urlValues.isGroup ? getGroupQueryText(urlValues.ids) : urlValues.ids[0]; + } if (defaultQueryText !== undefined) { this.setState( 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 bb88da549ac8b..04710e1d4de97 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 @@ -3,6 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -export function getSelectedIdFromUrl(str: string): { id: string; isGroup: boolean }; -export function getGroupQueryText(str: string): string; +export function getSelectedIdFromUrl(str: string): { ids: string[]; isGroup: boolean }; +export function getGroupQueryText(arr: 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 68d765ed6adb6..3f6190d341ac7 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 @@ -369,32 +369,31 @@ function getUrlVars(url) { }); return vars; } - export function getSelectedIdFromUrl(url) { const result = {}; if (typeof url === 'string') { - const isGroup = url.includes('groupId'); + const isGroup = url.includes('groupIds'); url = decodeURIComponent(url); if (url.includes('mlManagement') && (url.includes('jobId') || isGroup)) { const urlParams = getUrlVars(url); const decodedJson = rison.decode(urlParams.mlManagement); - result.id = isGroup ? decodedJson.groupId : decodedJson.jobId; + result.ids = isGroup ? decodedJson.groupIds : [decodedJson.jobId]; result.isGroup = isGroup; } } return result; } -export function getGroupQueryText(groupId) { - return `groups:(${groupId})`; +export function getGroupQueryText(groupIds) { + return `groups:(${groupIds.join(' or ')})`; } export function clearSelectedJobIdFromUrl(url) { if (typeof url === 'string') { url = decodeURIComponent(url); - if (url.includes('mlManagement') && url.includes('jobId')) { + if (url.includes('mlManagement') && (url.includes('jobId') || url.includes('groupIds'))) { const urlParams = getUrlVars(url); const clearedParams = `ml#/jobs?_g=${urlParams._g}`; window.history.replaceState({}, document.title, clearedParams); diff --git a/x-pack/plugins/ml/public/application/util/get_job_id_url.ts b/x-pack/plugins/ml/public/application/util/get_job_id_url.ts index a6ca575f21b50..388958febd4d2 100644 --- a/x-pack/plugins/ml/public/application/util/get_job_id_url.ts +++ b/x-pack/plugins/ml/public/application/util/get_job_id_url.ts @@ -18,3 +18,16 @@ export function getJobIdUrl(tabId: string, jobId: string): string { return `${basePath.get()}/app/ml#/${tabId}${url}`; } + +export function getGroupIdsUrl(tabId: string, groupIds: string[]): string { + // Create url for filtering by group id(s) for kibana management table + // format: mlManagement=(groupIds:!(first_group_id,second_group_id)) + const settings = { + groupIds, + }; + const encoded = rison.encode(settings); + const url = `?mlManagement=${encoded}`; + const basePath = getBasePath(); + + return `${basePath.get()}/app/ml#/${tabId}${url}`; +} From 148ab2aa15dff2d238df831095f8e6e7ade4a9f7 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 5 Aug 2020 17:24:53 -0400 Subject: [PATCH 4/7] ensure analytics can handle jobid in url. rename util function --- .../analytics_list/analytics_list.tsx | 9 ++--- .../components/analytics_list/use_columns.tsx | 4 +-- .../components/jobs_list/job_description.js | 17 +++++++--- .../components/jobs_list/jobs_list.js | 8 +++-- .../jobs/jobs_list/components/utils.js | 2 +- .../public/application/util/get_job_id_url.ts | 33 ------------------- .../application/util/get_selected_ids_url.ts | 29 ++++++++++++++++ 7 files changed, 55 insertions(+), 47 deletions(-) delete mode 100644 x-pack/plugins/ml/public/application/util/get_job_id_url.ts create mode 100644 x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 4fe3c5d1c0f99..521f783a05976 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -105,11 +105,12 @@ export const DataFrameAnalyticsList: FC = ({ const [selectedIdFromUrlInitialized, setSelectedIdFromUrlInitialized] = useState(false); useEffect(() => { if (selectedIdFromUrlInitialized === false && analytics.length > 0) { - const { ids, isGroup } = getSelectedIdFromUrl(window.location.href); - const selectedIdFromUrl = ids ?? ids[0]; - if (selectedIdFromUrl) { + const urlValues = getSelectedIdFromUrl(window.location.href); + + if (urlValues.ids !== undefined) { + const queryText = urlValues.isGroup ? getGroupQueryText(urlValues.ids) : urlValues.ids[0]; setSelectedIdFromUrlInitialized(true); - setSearchQueryText(isGroup ? getGroupQueryText(selectedIdFromUrl) : selectedIdFromUrl); + setSearchQueryText(queryText); } } }, [selectedIdFromUrlInitialized, analytics]); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx index 1b115496c2091..74f592ae27409 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx @@ -19,7 +19,7 @@ import { EuiLink, RIGHT_ALIGNMENT, } from '@elastic/eui'; -import { getJobIdUrl } from '../../../../../util/get_job_id_url'; +import { getSelectedIdsUrl } from '../../../../../util/get_selected_ids_url'; import { getAnalysisType, DataFrameAnalyticsId } from '../../../../common'; import { @@ -137,7 +137,7 @@ export const progressColumn = { }; export const getDFAnalyticsJobIdLink = (item: DataFrameAnalyticsListRow) => ( - {item.id} + {item.id} ); export const useColumns = ( diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js index dbaa1ff59a487..de4c8e279bdab 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js @@ -8,19 +8,28 @@ import PropTypes from 'prop-types'; import React from 'react'; import { JobGroup } from '../job_group'; +import { getSelectedIdsUrl } from '../../../../util/get_selected_ids_url'; -export function JobDescription({ job }) { +export function JobDescription({ job, isManagementTable }) { return (
{job.description}   - {job.groups.map((group) => ( - - ))} + {job.groups.map((group) => { + if (isManagementTable === true) { + return ( + + + + ); + } + return ; + })}
); } JobDescription.propTypes = { job: PropTypes.object.isRequired, + isManagementTable: PropTypes.bool, }; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 23b68551ca0f5..8754fa7b97785 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -14,7 +14,7 @@ import { toLocaleString } from '../../../../util/string_utils'; import { ResultLinks, actionsMenuContent } from '../job_actions'; import { JobDescription } from './job_description'; import { JobIcon } from '../../../../components/job_message_icon'; -import { getJobIdUrl } from '../../../../util/get_job_id_url'; +import { getSelectedIdsUrl } from '../../../../util/get_selected_ids_url'; import { TIME_FORMAT } from '../../../../../../common/constants/time_format'; import { EuiBadge, EuiBasicTable, EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; @@ -71,7 +71,7 @@ export class JobsList extends Component { return id; } - return {id}; + return {id}; } getPageOfJobs(index, size, sortField, sortDirection) { @@ -189,7 +189,9 @@ export class JobsList extends Component { sortable: true, field: 'description', 'data-test-subj': 'mlJobListColumnDescription', - render: (description, item) => , + render: (description, item) => ( + + ), textOnly: true, width: '20%', }, 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 3f6190d341ac7..8d5bfbeacfa6d 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 @@ -379,7 +379,7 @@ export function getSelectedIdFromUrl(url) { const urlParams = getUrlVars(url); const decodedJson = rison.decode(urlParams.mlManagement); - result.ids = isGroup ? decodedJson.groupIds : [decodedJson.jobId]; + result.ids = isGroup ? decodedJson.groupIds : decodedJson.jobId; result.isGroup = isGroup; } } diff --git a/x-pack/plugins/ml/public/application/util/get_job_id_url.ts b/x-pack/plugins/ml/public/application/util/get_job_id_url.ts deleted file mode 100644 index 388958febd4d2..0000000000000 --- a/x-pack/plugins/ml/public/application/util/get_job_id_url.ts +++ /dev/null @@ -1,33 +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 rison from 'rison-node'; - -import { getBasePath } from './dependency_cache'; - -export function getJobIdUrl(tabId: string, jobId: string): string { - // Create url for filtering by job id for kibana management table - const settings = { - jobId, - }; - const encoded = rison.encode(settings); - const url = `?mlManagement=${encoded}`; - const basePath = getBasePath(); - - return `${basePath.get()}/app/ml#/${tabId}${url}`; -} - -export function getGroupIdsUrl(tabId: string, groupIds: string[]): string { - // Create url for filtering by group id(s) for kibana management table - // format: mlManagement=(groupIds:!(first_group_id,second_group_id)) - const settings = { - groupIds, - }; - const encoded = rison.encode(settings); - const url = `?mlManagement=${encoded}`; - const basePath = getBasePath(); - - return `${basePath.get()}/app/ml#/${tabId}${url}`; -} diff --git a/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts b/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts new file mode 100644 index 0000000000000..a3323e2d2bca7 --- /dev/null +++ b/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts @@ -0,0 +1,29 @@ +/* + * 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 rison from 'rison-node'; + +import { getBasePath } from './dependency_cache'; + +export enum TAB_IDS { + DATA_FRAME_ANALYTICS = 'data_frame_analytics', + ANOMALY_DETECTION = 'jobs', +} + +export function getSelectedIdsUrl( + tabId: string, + ids: string | string[], + isGroup: boolean = false +): string { + // Create url for filtering by job id or group ids for kibana management table + const settings = { + [isGroup ? 'groupIds' : 'jobId']: Array.isArray(ids) ? ids : [ids], + }; + const encoded = rison.encode(settings); + const url = `?mlManagement=${encoded}`; + const basePath = getBasePath(); + + return `${basePath.get()}/app/ml#/${tabId}${url}`; +} From 3e9c17b9e4153525c3dce853498f6f17a7027673 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Thu, 6 Aug 2020 12:02:01 -0400 Subject: [PATCH 5/7] add tests for getSelectedIdFromUrl and getGroupQueryText --- .../jobs/jobs_list/components/utils.test.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.test.ts diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.test.ts b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.test.ts new file mode 100644 index 0000000000000..7671928ec1eaa --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.test.ts @@ -0,0 +1,47 @@ +/* + * 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 { getGroupQueryText, getSelectedIdFromUrl } from './utils'; + +describe('ML - Jobs List utils', () => { + const jobId = 'test_job_id_1'; + const jobIdUrl = `http://localhost:5601/aql/app/ml#/jobs?mlManagement=(jobId:!(${jobId}))`; + const groupIdOne = 'test_group_id_1'; + const groupIdTwo = 'test_group_id_2'; + const groupIdsUrl = `http://localhost:5601/aql/app/ml#/jobs?mlManagement=(groupIds:!(${groupIdOne},${groupIdTwo}))`; + const groupIdUrl = `http://localhost:5601/aql/app/ml#/jobs?mlManagement=(groupIds:!(${groupIdOne}))`; + + describe('getSelectedIdFromUrl', () => { + it('should get selected job id from the url', () => { + const actual = getSelectedIdFromUrl(jobIdUrl); + expect(actual).toStrictEqual({ ids: [jobId], isGroup: false }); + }); + + it('should get selected group ids from the url', () => { + const expected = { ids: [groupIdOne, groupIdTwo], isGroup: true }; + const actual = getSelectedIdFromUrl(groupIdsUrl); + expect(actual).toStrictEqual(expected); + }); + + it('should get selected group id from the url', () => { + const expected = { ids: [groupIdOne], isGroup: true }; + const actual = getSelectedIdFromUrl(groupIdUrl); + expect(actual).toStrictEqual(expected); + }); + }); + + describe('getGroupQueryText', () => { + it('should get query string for selected group ids', () => { + const actual = getGroupQueryText([groupIdOne, groupIdTwo]); + expect(actual).toBe(`groups:(${groupIdOne} or ${groupIdTwo})`); + }); + + it('should get query string for selected group id', () => { + const actual = getGroupQueryText([groupIdOne]); + expect(actual).toBe(`groups:(${groupIdOne})`); + }); + }); +}); From 812cd04c507953771404b4a5addf041fcba198a2 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Mon, 10 Aug 2020 10:34:11 -0400 Subject: [PATCH 6/7] keep groupIds as array of strings and jobId as single string --- .../analytics_list/analytics_list.tsx | 14 ++++++---- .../components/analytics_list/use_columns.tsx | 4 +-- .../job_filter_bar/job_filter_bar.js | 8 ++++-- .../components/jobs_list/job_description.js | 4 +-- .../components/jobs_list/jobs_list.js | 4 +-- .../jobs/jobs_list/components/utils.d.ts | 2 +- .../jobs/jobs_list/components/utils.js | 12 +++++--- .../application/util/get_selected_ids_url.ts | 28 +++++++++++++------ 8 files changed, 48 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 521f783a05976..0652ec5f8acb1 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -105,13 +105,17 @@ export const DataFrameAnalyticsList: FC = ({ const [selectedIdFromUrlInitialized, setSelectedIdFromUrlInitialized] = useState(false); useEffect(() => { if (selectedIdFromUrlInitialized === false && analytics.length > 0) { - const urlValues = getSelectedIdFromUrl(window.location.href); + const { jobId, groupIds } = getSelectedIdFromUrl(window.location.href); + let queryText = ''; - if (urlValues.ids !== undefined) { - const queryText = urlValues.isGroup ? getGroupQueryText(urlValues.ids) : urlValues.ids[0]; - setSelectedIdFromUrlInitialized(true); - setSearchQueryText(queryText); + if (groupIds !== undefined) { + queryText = getGroupQueryText(groupIds); + } else if (jobId !== undefined) { + queryText = jobId; } + + setSelectedIdFromUrlInitialized(true); + setSearchQueryText(queryText); } }, [selectedIdFromUrlInitialized, analytics]); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx index 74f592ae27409..b49ebcbad47ac 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx @@ -19,7 +19,7 @@ import { EuiLink, RIGHT_ALIGNMENT, } from '@elastic/eui'; -import { getSelectedIdsUrl } from '../../../../../util/get_selected_ids_url'; +import { getJobIdUrl } from '../../../../../util/get_selected_ids_url'; import { getAnalysisType, DataFrameAnalyticsId } from '../../../../common'; import { @@ -137,7 +137,7 @@ export const progressColumn = { }; export const getDFAnalyticsJobIdLink = (item: DataFrameAnalyticsListRow) => ( - {item.id} + {item.id} ); export const useColumns = ( 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 ad2f188f1e370..6eb7b00e5620c 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 @@ -55,10 +55,12 @@ export class JobFilterBar extends Component { componentDidMount() { // If job id is selected in url, filter table to that id let defaultQueryText; - const urlValues = getSelectedIdFromUrl(window.location.href); + const { jobId, groupIds } = getSelectedIdFromUrl(window.location.href); - if (urlValues.ids !== undefined) { - defaultQueryText = urlValues.isGroup ? getGroupQueryText(urlValues.ids) : urlValues.ids[0]; + if (groupIds !== undefined) { + defaultQueryText = getGroupQueryText(groupIds); + } else if (jobId !== undefined) { + defaultQueryText = jobId; } if (defaultQueryText !== undefined) { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js index de4c8e279bdab..efa4940cebb11 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js @@ -8,7 +8,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { JobGroup } from '../job_group'; -import { getSelectedIdsUrl } from '../../../../util/get_selected_ids_url'; +import { getGroupIdsUrl } from '../../../../util/get_selected_ids_url'; export function JobDescription({ job, isManagementTable }) { return ( @@ -18,7 +18,7 @@ export function JobDescription({ job, isManagementTable }) { {job.groups.map((group) => { if (isManagementTable === true) { return ( - + ); diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 8754fa7b97785..17b87f04d162f 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -14,7 +14,7 @@ import { toLocaleString } from '../../../../util/string_utils'; import { ResultLinks, actionsMenuContent } from '../job_actions'; import { JobDescription } from './job_description'; import { JobIcon } from '../../../../components/job_message_icon'; -import { getSelectedIdsUrl } from '../../../../util/get_selected_ids_url'; +import { getJobIdUrl } from '../../../../util/get_selected_ids_url'; import { TIME_FORMAT } from '../../../../../../common/constants/time_format'; import { EuiBadge, EuiBasicTable, EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; @@ -71,7 +71,7 @@ export class JobsList extends Component { return id; } - return {id}; + return {id}; } getPageOfJobs(index, size, sortField, sortDirection) { 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 04710e1d4de97..a201b0812e4e9 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 @@ -3,6 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -export function getSelectedIdFromUrl(str: string): { ids: string[]; isGroup: boolean }; +export function getSelectedIdFromUrl(str: string): { groupIds: string[] } | { jobId: string }; export function getGroupQueryText(arr: 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 8d5bfbeacfa6d..913727bda67df 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 @@ -369,18 +369,22 @@ function getUrlVars(url) { }); return vars; } + export function getSelectedIdFromUrl(url) { const result = {}; if (typeof url === 'string') { const isGroup = url.includes('groupIds'); - url = decodeURIComponent(url); - if (url.includes('mlManagement') && (url.includes('jobId') || isGroup)) { + + if (url.includes('mlManagement')) { const urlParams = getUrlVars(url); const decodedJson = rison.decode(urlParams.mlManagement); - result.ids = isGroup ? decodedJson.groupIds : decodedJson.jobId; - result.isGroup = isGroup; + if (isGroup) { + result.groupIds = decodedJson.groupIds; + } else { + result.jobId = decodedJson.jobId; + } } } return result; diff --git a/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts b/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts index a3323e2d2bca7..0f53d93e584f8 100644 --- a/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts +++ b/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import rison from 'rison-node'; - import { getBasePath } from './dependency_cache'; export enum TAB_IDS { @@ -12,18 +11,29 @@ export enum TAB_IDS { ANOMALY_DETECTION = 'jobs', } -export function getSelectedIdsUrl( - tabId: string, - ids: string | string[], - isGroup: boolean = false -): string { +function getSelectedIdsUrl(tabId, settings: any): string { // Create url for filtering by job id or group ids for kibana management table - const settings = { - [isGroup ? 'groupIds' : 'jobId']: Array.isArray(ids) ? ids : [ids], - }; const encoded = rison.encode(settings); const url = `?mlManagement=${encoded}`; const basePath = getBasePath(); return `${basePath.get()}/app/ml#/${tabId}${url}`; } + +// Create url for filtering by group ids for kibana management table +export function getGroupIdsUrl(tabId: string, ids: string[]): string { + const settings = { + groupIds: ids, + }; + + return getSelectedIdsUrl(tabId, settings); +} + +// Create url for filtering by job id for kibana management table +export function getJobIdUrl(tabId: string, id: string): string { + const settings = { + jobId: id, + }; + + return getSelectedIdsUrl(tabId, settings); +} From e4d47f121161821f9e057dee06348a22f79748d3 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Mon, 10 Aug 2020 13:18:40 -0400 Subject: [PATCH 7/7] fix tests and update types --- .../components/analytics_list/use_columns.tsx | 4 ++-- .../jobs_list/components/jobs_list/job_description.js | 4 ++-- .../jobs/jobs_list/components/jobs_list/jobs_list.js | 4 ++-- .../application/jobs/jobs_list/components/utils.d.ts | 3 ++- .../application/jobs/jobs_list/components/utils.test.ts | 8 ++++---- .../ml/public/application/util/get_selected_ids_url.ts | 6 +++--- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx index b49ebcbad47ac..781891e658de4 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx @@ -19,7 +19,7 @@ import { EuiLink, RIGHT_ALIGNMENT, } from '@elastic/eui'; -import { getJobIdUrl } from '../../../../../util/get_selected_ids_url'; +import { getJobIdUrl, TAB_IDS } from '../../../../../util/get_selected_ids_url'; import { getAnalysisType, DataFrameAnalyticsId } from '../../../../common'; import { @@ -137,7 +137,7 @@ export const progressColumn = { }; export const getDFAnalyticsJobIdLink = (item: DataFrameAnalyticsListRow) => ( - {item.id} + {item.id} ); export const useColumns = ( diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js index efa4940cebb11..a5469357ba1a1 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_description.js @@ -8,7 +8,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { JobGroup } from '../job_group'; -import { getGroupIdsUrl } from '../../../../util/get_selected_ids_url'; +import { getGroupIdsUrl, TAB_IDS } from '../../../../util/get_selected_ids_url'; export function JobDescription({ job, isManagementTable }) { return ( @@ -18,7 +18,7 @@ export function JobDescription({ job, isManagementTable }) { {job.groups.map((group) => { if (isManagementTable === true) { return ( - + ); diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 17b87f04d162f..f90bbf3cf3fe6 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -14,7 +14,7 @@ import { toLocaleString } from '../../../../util/string_utils'; import { ResultLinks, actionsMenuContent } from '../job_actions'; import { JobDescription } from './job_description'; import { JobIcon } from '../../../../components/job_message_icon'; -import { getJobIdUrl } from '../../../../util/get_selected_ids_url'; +import { getJobIdUrl, TAB_IDS } from '../../../../util/get_selected_ids_url'; import { TIME_FORMAT } from '../../../../../../common/constants/time_format'; import { EuiBadge, EuiBasicTable, EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; @@ -71,7 +71,7 @@ export class JobsList extends Component { return id; } - return {id}; + return {id}; } getPageOfJobs(index, size, sortField, sortDirection) { 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 a201b0812e4e9..cf4fad9513de5 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 @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -export function getSelectedIdFromUrl(str: string): { groupIds: string[] } | { jobId: string }; + +export function getSelectedIdFromUrl(str: string): { groupIds?: string[]; jobId?: string }; export function getGroupQueryText(arr: string[]): string; export function clearSelectedJobIdFromUrl(str: string): void; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.test.ts b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.test.ts index 7671928ec1eaa..e4c3c21c5a54a 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.test.ts +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.test.ts @@ -8,7 +8,7 @@ import { getGroupQueryText, getSelectedIdFromUrl } from './utils'; describe('ML - Jobs List utils', () => { const jobId = 'test_job_id_1'; - const jobIdUrl = `http://localhost:5601/aql/app/ml#/jobs?mlManagement=(jobId:!(${jobId}))`; + const jobIdUrl = `http://localhost:5601/aql/app/ml#/jobs?mlManagement=(jobId:${jobId})`; const groupIdOne = 'test_group_id_1'; const groupIdTwo = 'test_group_id_2'; const groupIdsUrl = `http://localhost:5601/aql/app/ml#/jobs?mlManagement=(groupIds:!(${groupIdOne},${groupIdTwo}))`; @@ -17,17 +17,17 @@ describe('ML - Jobs List utils', () => { describe('getSelectedIdFromUrl', () => { it('should get selected job id from the url', () => { const actual = getSelectedIdFromUrl(jobIdUrl); - expect(actual).toStrictEqual({ ids: [jobId], isGroup: false }); + expect(actual).toStrictEqual({ jobId }); }); it('should get selected group ids from the url', () => { - const expected = { ids: [groupIdOne, groupIdTwo], isGroup: true }; + const expected = { groupIds: [groupIdOne, groupIdTwo] }; const actual = getSelectedIdFromUrl(groupIdsUrl); expect(actual).toStrictEqual(expected); }); it('should get selected group id from the url', () => { - const expected = { ids: [groupIdOne], isGroup: true }; + const expected = { groupIds: [groupIdOne] }; const actual = getSelectedIdFromUrl(groupIdUrl); expect(actual).toStrictEqual(expected); }); diff --git a/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts b/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts index 0f53d93e584f8..806626577008e 100644 --- a/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts +++ b/x-pack/plugins/ml/public/application/util/get_selected_ids_url.ts @@ -11,7 +11,7 @@ export enum TAB_IDS { ANOMALY_DETECTION = 'jobs', } -function getSelectedIdsUrl(tabId, settings: any): string { +function getSelectedIdsUrl(tabId: TAB_IDS, settings: { [key: string]: string | string[] }): string { // Create url for filtering by job id or group ids for kibana management table const encoded = rison.encode(settings); const url = `?mlManagement=${encoded}`; @@ -21,7 +21,7 @@ function getSelectedIdsUrl(tabId, settings: any): string { } // Create url for filtering by group ids for kibana management table -export function getGroupIdsUrl(tabId: string, ids: string[]): string { +export function getGroupIdsUrl(tabId: TAB_IDS, ids: string[]): string { const settings = { groupIds: ids, }; @@ -30,7 +30,7 @@ export function getGroupIdsUrl(tabId: string, ids: string[]): string { } // Create url for filtering by job id for kibana management table -export function getJobIdUrl(tabId: string, id: string): string { +export function getJobIdUrl(tabId: TAB_IDS, id: string): string { const settings = { jobId: id, };