Skip to content

Commit ef54fed

Browse files
qn895darnautov
andauthored
[7.x] [ML] Extend MlUrlGenerator to support other ML pages (#75696) (#76561)
Co-authored-by: Dima Arnautov <[email protected]>
1 parent 95aeaa9 commit ef54fed

File tree

22 files changed

+982
-208
lines changed

22 files changed

+982
-208
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export const ML_APP_URL_GENERATOR = 'ML_APP_URL_GENERATOR';
8+
9+
export const ML_PAGES = {
10+
ANOMALY_DETECTION_JOBS_MANAGE: 'jobs',
11+
ANOMALY_EXPLORER: 'explorer',
12+
SINGLE_METRIC_VIEWER: 'timeseriesexplorer',
13+
DATA_FRAME_ANALYTICS_JOBS_MANAGE: 'data_frame_analytics',
14+
DATA_FRAME_ANALYTICS_EXPLORATION: 'data_frame_analytics/exploration',
15+
/**
16+
* Page: Data Visualizer
17+
*/
18+
DATA_VISUALIZER: 'datavisualizer',
19+
/**
20+
* Page: Data Visualizer
21+
* Open data visualizer by selecting a Kibana index pattern or saved search
22+
*/
23+
DATA_VISUALIZER_INDEX_SELECT: 'datavisualizer_index_select',
24+
/**
25+
* Page: Data Visualizer
26+
* Open data visualizer by importing data from a log file
27+
*/
28+
DATA_VISUALIZER_FILE: 'filedatavisualizer',
29+
/**
30+
* Page: Data Visualizer
31+
* Open index data visualizer viewer page
32+
*/
33+
DATA_VISUALIZER_INDEX_VIEWER: 'jobs/new_job/datavisualizer',
34+
ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE: `jobs/new_job/step/job_type`,
35+
SETTINGS: 'settings',
36+
CALENDARS_MANAGE: 'settings/calendars_list',
37+
FILTER_LISTS_MANAGE: 'settings/filter_lists',
38+
} as const;
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { RefreshInterval, TimeRange } from '../../../../../src/plugins/data/common/query';
8+
import { JobId } from '../../../reporting/common/types';
9+
import { ML_PAGES } from '../constants/ml_url_generator';
10+
11+
type OptionalPageState = object | undefined;
12+
13+
export type MLPageState<PageType, PageState> = PageState extends OptionalPageState
14+
? { page: PageType; pageState?: PageState }
15+
: PageState extends object
16+
? { page: PageType; pageState: PageState }
17+
: { page: PageType };
18+
19+
export const ANALYSIS_CONFIG_TYPE = {
20+
OUTLIER_DETECTION: 'outlier_detection',
21+
REGRESSION: 'regression',
22+
CLASSIFICATION: 'classification',
23+
} as const;
24+
25+
type DataFrameAnalyticsType = typeof ANALYSIS_CONFIG_TYPE[keyof typeof ANALYSIS_CONFIG_TYPE];
26+
27+
export interface MlCommonGlobalState {
28+
time?: TimeRange;
29+
}
30+
export interface MlCommonAppState {
31+
[key: string]: any;
32+
}
33+
34+
export interface MlIndexBasedSearchState {
35+
index?: string;
36+
savedSearchId?: string;
37+
}
38+
39+
export interface MlGenericUrlPageState extends MlIndexBasedSearchState {
40+
globalState?: MlCommonGlobalState;
41+
appState?: MlCommonAppState;
42+
[key: string]: any;
43+
}
44+
45+
export interface MlGenericUrlState {
46+
page:
47+
| typeof ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER
48+
| typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE;
49+
pageState: MlGenericUrlPageState;
50+
}
51+
52+
export interface AnomalyDetectionQueryState {
53+
jobId?: JobId;
54+
groupIds?: string[];
55+
}
56+
57+
export type AnomalyDetectionUrlState = MLPageState<
58+
typeof ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
59+
AnomalyDetectionQueryState | undefined
60+
>;
61+
export interface ExplorerAppState {
62+
mlExplorerSwimlane: {
63+
selectedType?: string;
64+
selectedLanes?: string[];
65+
selectedTimes?: number[];
66+
showTopFieldValues?: boolean;
67+
viewByFieldName?: string;
68+
viewByPerPage?: number;
69+
viewByFromPage?: number;
70+
};
71+
mlExplorerFilter: {
72+
influencersFilterQuery?: unknown;
73+
filterActive?: boolean;
74+
filteredFields?: string[];
75+
queryString?: string;
76+
};
77+
query?: any;
78+
}
79+
export interface ExplorerGlobalState {
80+
ml: { jobIds: JobId[] };
81+
time?: TimeRange;
82+
refreshInterval?: RefreshInterval;
83+
}
84+
85+
export interface ExplorerUrlPageState {
86+
/**
87+
* Job IDs
88+
*/
89+
jobIds: JobId[];
90+
/**
91+
* Optionally set the time range in the time picker.
92+
*/
93+
timeRange?: TimeRange;
94+
/**
95+
* Optionally set the refresh interval.
96+
*/
97+
refreshInterval?: RefreshInterval;
98+
/**
99+
* Optionally set the query.
100+
*/
101+
query?: any;
102+
/**
103+
* Optional state for the swim lane
104+
*/
105+
mlExplorerSwimlane?: ExplorerAppState['mlExplorerSwimlane'];
106+
mlExplorerFilter?: ExplorerAppState['mlExplorerFilter'];
107+
}
108+
109+
export type ExplorerUrlState = MLPageState<typeof ML_PAGES.ANOMALY_EXPLORER, ExplorerUrlPageState>;
110+
111+
export interface TimeSeriesExplorerGlobalState {
112+
ml: {
113+
jobIds: JobId[];
114+
};
115+
time?: TimeRange;
116+
refreshInterval?: RefreshInterval;
117+
}
118+
119+
export interface TimeSeriesExplorerAppState {
120+
zoom?: {
121+
from?: string;
122+
to?: string;
123+
};
124+
mlTimeSeriesExplorer?: {
125+
detectorIndex?: number;
126+
entities?: Record<string, string>;
127+
};
128+
query?: any;
129+
}
130+
131+
export interface TimeSeriesExplorerPageState
132+
extends Pick<TimeSeriesExplorerAppState, 'zoom' | 'query'>,
133+
Pick<TimeSeriesExplorerGlobalState, 'refreshInterval'> {
134+
jobIds: JobId[];
135+
timeRange?: TimeRange;
136+
detectorIndex?: number;
137+
entities?: Record<string, string>;
138+
}
139+
140+
export type TimeSeriesExplorerUrlState = MLPageState<
141+
typeof ML_PAGES.SINGLE_METRIC_VIEWER,
142+
TimeSeriesExplorerPageState
143+
>;
144+
145+
export interface DataFrameAnalyticsQueryState {
146+
jobId?: JobId | JobId[];
147+
groupIds?: string[];
148+
}
149+
150+
export type DataFrameAnalyticsUrlState = MLPageState<
151+
typeof ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
152+
DataFrameAnalyticsQueryState | undefined
153+
>;
154+
155+
export interface DataVisualizerUrlState {
156+
page:
157+
| typeof ML_PAGES.DATA_VISUALIZER
158+
| typeof ML_PAGES.DATA_VISUALIZER_FILE
159+
| typeof ML_PAGES.DATA_VISUALIZER_INDEX_SELECT;
160+
}
161+
162+
export interface DataFrameAnalyticsExplorationQueryState {
163+
ml: {
164+
jobId: JobId;
165+
analysisType: DataFrameAnalyticsType;
166+
};
167+
}
168+
169+
export type DataFrameAnalyticsExplorationUrlState = MLPageState<
170+
typeof ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION,
171+
{
172+
jobId: JobId;
173+
analysisType: DataFrameAnalyticsType;
174+
}
175+
>;
176+
177+
/**
178+
* Union type of ML URL state based on page
179+
*/
180+
export type MlUrlGeneratorState =
181+
| AnomalyDetectionUrlState
182+
| ExplorerUrlState
183+
| TimeSeriesExplorerUrlState
184+
| DataFrameAnalyticsUrlState
185+
| DataFrameAnalyticsExplorationUrlState
186+
| DataVisualizerUrlState
187+
| MlGenericUrlState;

x-pack/plugins/ml/public/application/contexts/kibana/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export { useNavigateToPath, NavigateToPath } from './use_navigate_to_path';
99
export { useUiSettings } from './use_ui_settings_context';
1010
export { useTimefilter } from './use_timefilter';
1111
export { useNotifications } from './use_notifications_context';
12+
export { useMlUrlGenerator } from './use_create_url';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { useMlKibana } from './kibana_context';
8+
import { ML_APP_URL_GENERATOR } from '../../../../common/constants/ml_url_generator';
9+
10+
export const useMlUrlGenerator = () => {
11+
const {
12+
services: {
13+
share: {
14+
urlGenerators: { getUrlGenerator },
15+
},
16+
},
17+
} = useMlKibana();
18+
19+
return getUrlGenerator(ML_APP_URL_GENERATOR);
20+
};

x-pack/plugins/ml/public/application/contexts/kibana/use_navigate_to_path.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,19 @@ export const useNavigateToPath = () => {
2121

2222
const location = useLocation();
2323

24-
return useMemo(
25-
() => (path: string | undefined, preserveSearch = false) => {
26-
navigateToUrl(
27-
getUrlForApp(PLUGIN_ID, {
28-
path: `${path}${preserveSearch === true ? location.search : ''}`,
29-
})
30-
);
31-
},
32-
[location]
33-
);
24+
return useMemo(() => {
25+
return (path: string | undefined, preserveSearch = false) => {
26+
if (path === undefined) return;
27+
const modifiedPath = `${path}${preserveSearch === true ? location.search : ''}`;
28+
/**
29+
* Handle urls generated by MlUrlGenerator where '/app/ml' is automatically prepended
30+
*/
31+
const url = modifiedPath.includes('/app/ml')
32+
? modifiedPath
33+
: getUrlForApp(PLUGIN_ID, {
34+
path: modifiedPath,
35+
});
36+
navigateToUrl(url);
37+
};
38+
}, [location]);
3439
};

x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/back_to_list_panel/back_to_list_panel.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@
77
import React, { FC, Fragment } from 'react';
88
import { EuiCard, EuiIcon } from '@elastic/eui';
99
import { i18n } from '@kbn/i18n';
10-
import { useNavigateToPath } from '../../../../../contexts/kibana';
10+
import { useMlKibana, useMlUrlGenerator } from '../../../../../contexts/kibana';
11+
import { ML_PAGES } from '../../../../../../../common/constants/ml_url_generator';
1112

1213
export const BackToListPanel: FC = () => {
13-
const navigateToPath = useNavigateToPath();
14+
const urlGenerator = useMlUrlGenerator();
15+
const {
16+
services: {
17+
application: { navigateToUrl },
18+
},
19+
} = useMlKibana();
1420

1521
const redirectToAnalyticsManagementPage = async () => {
16-
await navigateToPath('/data_frame_analytics');
22+
const url = await urlGenerator.createUrl({ page: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE });
23+
await navigateToUrl(url);
1724
};
1825

1926
return (

x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/view_results_panel/view_results_panel.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,27 @@
77
import React, { FC, Fragment } from 'react';
88
import { EuiCard, EuiIcon } from '@elastic/eui';
99
import { i18n } from '@kbn/i18n';
10-
import { useNavigateToPath } from '../../../../../contexts/kibana';
11-
import { getResultsUrl } from '../../../analytics_management/components/analytics_list/common';
10+
import { useMlUrlGenerator } from '../../../../../contexts/kibana';
1211
import { ANALYSIS_CONFIG_TYPE } from '../../../../common/analytics';
13-
12+
import { ML_PAGES } from '../../../../../../../common/constants/ml_url_generator';
13+
import { useNavigateToPath } from '../../../../../contexts/kibana';
1414
interface Props {
1515
jobId: string;
1616
analysisType: ANALYSIS_CONFIG_TYPE;
1717
}
1818

1919
export const ViewResultsPanel: FC<Props> = ({ jobId, analysisType }) => {
20+
const urlGenerator = useMlUrlGenerator();
2021
const navigateToPath = useNavigateToPath();
2122

22-
const redirectToAnalyticsManagementPage = async () => {
23-
const path = getResultsUrl(jobId, analysisType);
23+
const redirectToAnalyticsExplorationPage = async () => {
24+
const path = await urlGenerator.createUrl({
25+
page: ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION,
26+
pageState: {
27+
jobId,
28+
analysisType,
29+
},
30+
});
2431
await navigateToPath(path);
2532
};
2633

@@ -38,7 +45,7 @@ export const ViewResultsPanel: FC<Props> = ({ jobId, analysisType }) => {
3845
defaultMessage: 'View results for the analytics job.',
3946
}
4047
)}
41-
onClick={redirectToAnalyticsManagementPage}
48+
onClick={redirectToAnalyticsExplorationPage}
4249
data-test-subj="analyticsWizardViewResultsCard"
4350
/>
4451
</Fragment>

x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { ExplorerChartsData } from './explorer_charts/explorer_charts_container_
2121
import { EXPLORER_ACTION } from './explorer_constants';
2222
import { AppStateSelectedCells, TimeRangeBounds } from './explorer_utils';
2323
import { explorerReducer, getExplorerDefaultState, ExplorerState } from './reducers';
24+
import { ExplorerAppState } from '../../../common/types/ml_url_generator';
2425

2526
export const ALLOW_CELL_RANGE_SELECTION = true;
2627

@@ -49,24 +50,6 @@ const explorerState$: Observable<ExplorerState> = explorerFilteredAction$.pipe(
4950
shareReplay(1)
5051
);
5152

52-
export interface ExplorerAppState {
53-
mlExplorerSwimlane: {
54-
selectedType?: string;
55-
selectedLanes?: string[];
56-
selectedTimes?: number[];
57-
showTopFieldValues?: boolean;
58-
viewByFieldName?: string;
59-
viewByPerPage?: number;
60-
viewByFromPage?: number;
61-
};
62-
mlExplorerFilter: {
63-
influencersFilterQuery?: unknown;
64-
filterActive?: boolean;
65-
filteredFields?: string[];
66-
queryString?: string;
67-
};
68-
}
69-
7053
const explorerAppState$: Observable<ExplorerAppState> = explorerState$.pipe(
7154
map(
7255
(state: ExplorerState): ExplorerAppState => {

x-pack/plugins/ml/public/application/routing/router.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ const MlRoutes: FC<{
7575
pageDeps: PageDependencies;
7676
}> = ({ pageDeps }) => {
7777
const navigateToPath = useNavigateToPath();
78-
7978
return (
8079
<>
8180
{Object.entries(routes).map(([name, routeFactory]) => {

x-pack/plugins/ml/public/application/routing/routes/explorer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import { useSelectedCells } from '../../explorer/hooks/use_selected_cells';
2222
import { mlJobService } from '../../services/job_service';
2323
import { ml } from '../../services/ml_api_service';
2424
import { useExplorerData } from '../../explorer/actions';
25-
import { ExplorerAppState, explorerService } from '../../explorer/explorer_dashboard_service';
25+
import { ExplorerAppState } from '../../../../common/types/ml_url_generator';
26+
import { explorerService } from '../../explorer/explorer_dashboard_service';
2627
import { getDateFormatTz } from '../../explorer/explorer_utils';
2728
import { useJobSelection } from '../../components/job_selector/use_job_selection';
2829
import { useShowCharts } from '../../components/controls/checkbox_showcharts';

0 commit comments

Comments
 (0)