Skip to content

Commit 0942c6e

Browse files
jackiehanyanggithub-actions[bot]
authored andcommitted
Support MDS on List, Detail, Dashboard, Overview pages (#722)
* update snapshots Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add snpshot Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add data source client Signed-off-by: Jackie Han <jkhanjob@gmail.com> * test * neo List Detectors page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add opensearch_dashboards.json file Signed-off-by: Jackie Han <jkhanjob@gmail.com> * neo List Detectors page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * test Signed-off-by: Jackie Han <jkhanjob@gmail.com> * Support MDS on DetectorDetails page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * change 4/7 Signed-off-by: Jackie Han <jkhanjob@gmail.com> * version change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * list detector list change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * Support MDS on List, Detail, Dashboard, Overview pages Signed-off-by: Jackie Han <jkhanjob@gmail.com> * change version back to 3.0 Signed-off-by: Jackie Han <jkhanjob@gmail.com> * revert version change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make dataSourceId optional in DetectorListItem type Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update imports Signed-off-by: Jackie Han <jkhanjob@gmail.com> * remove used function Signed-off-by: Jackie Han <jkhanjob@gmail.com> * cleanup Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add getter and setter for dataSource plugin Signed-off-by: Jackie Han <jkhanjob@gmail.com> * read dataSourceId from the url instead of passing props Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments and run prettier Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make getDataSourceManagementPlugin() optional Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add comment Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make dataSourceId type safe Signed-off-by: Jackie Han <jkhanjob@gmail.com> --------- Signed-off-by: Jackie Han <jkhanjob@gmail.com> (cherry picked from commit ee5504f)
1 parent 437b0ed commit 0942c6e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1447
-561
lines changed

opensearch_dashboards.json

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"configPath": [
66
"anomaly_detection_dashboards"
77
],
8+
"optionalPlugins": ["dataSource","dataSourceManagement"],
89
"requiredPlugins": [
910
"opensearchDashboardsUtils",
1011
"expressions",

public/anomaly_detection_app.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters) {
2929
} else {
3030
require('@elastic/charts/dist/theme_only_light.css');
3131
}
32+
3233
ReactDOM.render(
3334
<Provider store={store}>
3435
<Router>
3536
<Route
3637
render={(props) => (
3738
<CoreServicesContext.Provider value={coreStart}>
38-
<Main {...props} />
39+
<Main
40+
setHeaderActionMenu={params.setHeaderActionMenu}
41+
{...props}
42+
/>
3943
</CoreServicesContext.Provider>
4044
)}
4145
/>

public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx

+17-4
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,18 @@ import {
8181
} from '../utils/constants';
8282
import { HeatmapCell } from './AnomalyHeatmapChart';
8383
import { ANOMALY_AGG, MIN_END_TIME, MAX_END_TIME } from '../../utils/constants';
84-
import { MAX_HISTORICAL_AGG_RESULTS } from '../../../utils/constants';
84+
import {
85+
DATA_SOURCE_ID,
86+
MAX_HISTORICAL_AGG_RESULTS,
87+
} from '../../../utils/constants';
8588
import { searchResults } from '../../../redux/reducers/anomalyResults';
8689
import {
8790
DAY_IN_MILLI_SECS,
8891
WEEK_IN_MILLI_SECS,
8992
DETECTOR_STATE,
9093
} from '../../../../server/utils/constants';
9194
import { ENTITY_COLORS } from '../../DetectorResults/utils/constants';
95+
import { useLocation } from 'react-router-dom';
9296

9397
interface AnomalyDetailsChartProps {
9498
onDateRangeChange(
@@ -118,6 +122,9 @@ interface AnomalyDetailsChartProps {
118122
export const AnomalyDetailsChart = React.memo(
119123
(props: AnomalyDetailsChartProps) => {
120124
const dispatch = useDispatch();
125+
const location = useLocation();
126+
const dataSourceId =
127+
new URLSearchParams(location.search).get(DATA_SOURCE_ID) || '';
121128
const [showAlertsFlyout, setShowAlertsFlyout] = useState<boolean>(false);
122129
const [alertAnnotations, setAlertAnnotations] = useState<any[]>([]);
123130
const [isLoadingAlerts, setIsLoadingAlerts] = useState<boolean>(false);
@@ -174,7 +181,9 @@ export const AnomalyDetailsChart = React.memo(
174181
zoomRange.endDate,
175182
taskId
176183
);
177-
dispatch(searchResults(anomalyDataRangeQuery, resultIndex, true))
184+
dispatch(
185+
searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)
186+
)
178187
.then((response: any) => {
179188
// Only retrieve buckets that are in the anomaly results range. This is so
180189
// we don't show aggregate results for where there is no data at all
@@ -193,7 +202,9 @@ export const AnomalyDetailsChart = React.memo(
193202
taskId,
194203
selectedAggId
195204
);
196-
dispatch(searchResults(historicalAggQuery, resultIndex, true))
205+
dispatch(
206+
searchResults(historicalAggQuery, resultIndex, dataSourceId, true)
207+
)
197208
.then((response: any) => {
198209
const aggregatedAnomalies = parseHistoricalAggregatedAnomalies(
199210
response,
@@ -229,7 +240,9 @@ export const AnomalyDetailsChart = React.memo(
229240
zoomRange.endDate,
230241
taskId
231242
);
232-
dispatch(searchResults(anomalyDataRangeQuery, resultIndex, true))
243+
dispatch(
244+
searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)
245+
)
233246
.then((response: any) => {
234247
const dataStartDate = get(
235248
response,

public/pages/CreateDetectorSteps/hooks/useFetchDetectorInfo.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import { getMappings } from '../../../redux/reducers/opensearch';
2121
// 1. Get detector
2222
// 2. Gets index mapping
2323
export const useFetchDetectorInfo = (
24-
detectorId: string
24+
detectorId: string,
25+
dataSourceId: string
2526
): {
2627
detector: Detector;
2728
hasError: boolean;
@@ -43,7 +44,8 @@ export const useFetchDetectorInfo = (
4344
useEffect(() => {
4445
const fetchDetector = async () => {
4546
if (!detector) {
46-
await dispatch(getDetector(detectorId));
47+
// hardcoding the datasource id for now, will update it later when working on create page
48+
await dispatch(getDetector(detectorId, dataSourceId));
4749
}
4850
if (selectedIndices) {
4951
await dispatch(getMappings(selectedIndices));

public/pages/Dashboard/Components/AnomaliesDistribution.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import { get, isEmpty } from 'lodash';
3333
import { AD_DOC_FIELDS } from '../../../../server/utils/constants';
3434
import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../utils/constants';
3535
import { searchResults } from '../../../redux/reducers/anomalyResults';
36+
import { useLocation } from 'react-router-dom';
37+
import { DATA_SOURCE_ID } from '../../../utils/constants';
3638
export interface AnomaliesDistributionChartProps {
3739
selectedDetectors: DetectorListItem[];
3840
}
@@ -41,6 +43,9 @@ export const AnomaliesDistributionChart = (
4143
props: AnomaliesDistributionChartProps
4244
) => {
4345
const dispatch = useDispatch();
46+
const location = useLocation();
47+
const dataSourceId =
48+
new URLSearchParams(location.search).get(DATA_SOURCE_ID) || '';
4449

4550
const [anomalyDistribution, setAnomalyDistribution] = useState(
4651
[] as object[]
@@ -66,6 +71,7 @@ export const AnomaliesDistributionChart = (
6671
await getAnomalyDistributionForDetectorsByTimeRange(
6772
searchResults,
6873
props.selectedDetectors,
74+
dataSourceId,
6975
timeRange,
7076
dispatch,
7177
0,

public/pages/Dashboard/Components/AnomaliesLiveChart.tsx

+13-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,14 @@ import {
5151
getLatestAnomalyResultsForDetectorsByTimeRange,
5252
getLatestAnomalyResultsByTimeRange,
5353
} from '../utils/utils';
54-
import { MAX_ANOMALIES, SPACE_STR } from '../../../utils/constants';
54+
import {
55+
DATA_SOURCE_ID,
56+
MAX_ANOMALIES,
57+
SPACE_STR,
58+
} from '../../../utils/constants';
5559
import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../utils/constants';
5660
import { searchResults } from '../../../redux/reducers/anomalyResults';
61+
import { useLocation } from 'react-router-dom';
5762

5863
export interface AnomaliesLiveChartProps {
5964
selectedDetectors: DetectorListItem[];
@@ -68,6 +73,9 @@ const MAX_LIVE_DETECTORS = 10;
6873

6974
export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => {
7075
const dispatch = useDispatch();
76+
const location = useLocation();
77+
const dataSourceId =
78+
new URLSearchParams(location.search).get(DATA_SOURCE_ID) || '';
7179

7280
const [liveTimeRange, setLiveTimeRange] = useState<LiveTimeRangeState>({
7381
startDateTime: moment().subtract(31, 'minutes'),
@@ -102,7 +110,8 @@ export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => {
102110
1,
103111
true,
104112
ALL_CUSTOM_AD_RESULT_INDICES,
105-
false
113+
false,
114+
dataSourceId
106115
);
107116
} catch (err) {
108117
console.log(
@@ -126,7 +135,8 @@ export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => {
126135
MAX_LIVE_DETECTORS,
127136
false,
128137
ALL_CUSTOM_AD_RESULT_INDICES,
129-
false
138+
false,
139+
dataSourceId
130140
);
131141
setLiveAnomalyData(latestLiveAnomalyResult);
132142

public/pages/Dashboard/Container/DashboardOverview.tsx

+99-11
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
* GitHub history for details.
1010
*/
1111

12-
import React, { Fragment, useState, useEffect } from 'react';
12+
import React, { Fragment, useState, useEffect, useMemo } from 'react';
1313
import { AnomaliesLiveChart } from '../Components/AnomaliesLiveChart';
1414
import { AnomaliesDistributionChart } from '../Components/AnomaliesDistribution';
15+
import queryString from 'querystring';
1516

1617
import { useDispatch, useSelector } from 'react-redux';
1718
import { get, isEmpty, cloneDeep } from 'lodash';
@@ -29,27 +30,53 @@ import {
2930
} from '@elastic/eui';
3031
import { AnomalousDetectorsList } from '../Components/AnomalousDetectorsList';
3132
import {
32-
GET_ALL_DETECTORS_QUERY_PARAMS,
3333
ALL_DETECTORS_MESSAGE,
3434
ALL_DETECTOR_STATES_MESSAGE,
3535
ALL_INDICES_MESSAGE,
3636
} from '../utils/constants';
3737
import { AppState } from '../../../redux/reducers';
38-
import { CatIndex, IndexAlias } from '../../../../server/models/types';
39-
import { getVisibleOptions } from '../../utils/helpers';
38+
import {
39+
CatIndex,
40+
IndexAlias,
41+
MDSQueryParams,
42+
} from '../../../../server/models/types';
43+
import {
44+
getAllDetectorsQueryParamsWithDataSourceId,
45+
getVisibleOptions,
46+
} from '../../utils/helpers';
4047
import { BREADCRUMBS } from '../../../utils/constants';
4148
import { DETECTOR_STATE } from '../../../../server/utils/constants';
42-
import { getDetectorStateOptions } from '../../DetectorsList/utils/helpers';
49+
import {
50+
getDetectorStateOptions,
51+
getURLQueryParams,
52+
} from '../../DetectorsList/utils/helpers';
4353
import { DashboardHeader } from '../Components/utils/DashboardHeader';
4454
import { EmptyDashboard } from '../Components/EmptyDashboard/EmptyDashboard';
4555
import {
4656
prettifyErrorMessage,
4757
NO_PERMISSIONS_KEY_WORD,
4858
} from '../../../../server/utils/helpers';
4959
import { CoreServicesContext } from '../../../components/CoreServices/CoreServices';
50-
import { CoreStart } from '../../../../../../src/core/public';
60+
import { CoreStart, MountPoint } from '../../../../../../src/core/public';
61+
import { DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public';
62+
import {
63+
getDataSourceManagementPlugin,
64+
getDataSourcePlugin,
65+
getNotifications,
66+
getSavedObjectsClient,
67+
} from '../../../services';
68+
import { RouteComponentProps } from 'react-router-dom';
69+
70+
interface OverviewProps extends RouteComponentProps {
71+
setActionMenu: (menuMount: MountPoint | undefined) => void;
72+
}
73+
74+
interface MDSOverviewState {
75+
queryParams: MDSQueryParams;
76+
selectedDataSourceId: string;
77+
}
5178

52-
export function DashboardOverview() {
79+
export function DashboardOverview(props: OverviewProps) {
5380
const core = React.useContext(CoreServicesContext) as CoreStart;
5481
const dispatch = useDispatch();
5582
const adState = useSelector((state: AppState) => state.ad);
@@ -58,13 +85,23 @@ export function DashboardOverview() {
5885
const errorGettingDetectors = adState.errorMessage;
5986
const isLoadingDetectors = adState.requesting;
6087

88+
const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false;
89+
6190
const [currentDetectors, setCurrentDetectors] = useState(
6291
Object.values(allDetectorList)
6392
);
6493
const [allDetectorsSelected, setAllDetectorsSelected] = useState(true);
6594
const [selectedDetectorsName, setSelectedDetectorsName] = useState(
6695
[] as string[]
6796
);
97+
const queryParams = getURLQueryParams(props.location);
98+
const [MDSOverviewState, setMDSOverviewState] = useState<MDSOverviewState>({
99+
queryParams,
100+
selectedDataSourceId: queryParams.dataSourceId
101+
? queryParams.dataSourceId
102+
: '',
103+
});
104+
68105
const getDetectorOptions = (detectorsIdMap: {
69106
[key: string]: DetectorListItem;
70107
}) => {
@@ -108,6 +145,20 @@ export function DashboardOverview() {
108145
setAllDetectorStatesSelected(isEmpty(selectedStates));
109146
};
110147

148+
const handleDataSourceChange = ([event]) => {
149+
const dataSourceId = event?.id;
150+
if (!dataSourceId) {
151+
getNotifications().toasts.addDanger(
152+
prettifyErrorMessage('Unable to set data source.')
153+
);
154+
} else {
155+
setMDSOverviewState({
156+
queryParams: dataSourceId,
157+
selectedDataSourceId: dataSourceId,
158+
});
159+
}
160+
};
161+
111162
const opensearchState = useSelector((state: AppState) => state.opensearch);
112163

113164
const [selectedIndices, setSelectedIndices] = useState([] as string[]);
@@ -157,14 +208,28 @@ export function DashboardOverview() {
157208
};
158209

159210
const intializeDetectors = async () => {
160-
dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS));
161-
dispatch(getIndices(''));
162-
dispatch(getAliases(''));
211+
dispatch(
212+
getDetectorList(
213+
getAllDetectorsQueryParamsWithDataSourceId(
214+
MDSOverviewState.selectedDataSourceId
215+
)
216+
)
217+
);
218+
dispatch(getIndices('', MDSOverviewState.selectedDataSourceId));
219+
dispatch(getAliases('', MDSOverviewState.selectedDataSourceId));
163220
};
164221

165222
useEffect(() => {
223+
const { history, location } = props;
224+
const updatedParams = {
225+
dataSourceId: MDSOverviewState.selectedDataSourceId,
226+
};
227+
history.replace({
228+
...location,
229+
search: queryString.stringify(updatedParams),
230+
});
166231
intializeDetectors();
167-
}, []);
232+
}, [MDSOverviewState]);
168233

169234
useEffect(() => {
170235
if (errorGettingDetectors) {
@@ -197,9 +262,32 @@ export function DashboardOverview() {
197262
);
198263
}, [selectedDetectorsName, selectedIndices, selectedDetectorStates]);
199264

265+
let renderDataSourceComponent = null;
266+
if (dataSourceEnabled) {
267+
const DataSourceMenu =
268+
getDataSourceManagementPlugin()?.ui.getDataSourceMenu<DataSourceSelectableConfig>();
269+
renderDataSourceComponent = useMemo(() => {
270+
return (
271+
<DataSourceMenu
272+
setMenuMountPoint={props.setActionMenu}
273+
componentType={'DataSourceSelectable'}
274+
componentConfig={{
275+
fullWidth: false,
276+
activeOption: [{ id: MDSOverviewState.selectedDataSourceId }],
277+
savedObjects: getSavedObjectsClient(),
278+
notifications: getNotifications(),
279+
onSelectedDataSources: (dataSources) =>
280+
handleDataSourceChange(dataSources),
281+
}}
282+
/>
283+
);
284+
}, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]);
285+
}
286+
200287
return (
201288
<div style={{ height: '1200px' }}>
202289
<Fragment>
290+
{dataSourceEnabled && renderDataSourceComponent}
203291
<DashboardHeader hasDetectors={totalRealtimeDetectors > 0} />
204292
{isLoadingDetectors ? (
205293
<div>

0 commit comments

Comments
 (0)