Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] Support MDS on List, Detail, Dashboard, Overview pages #727

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"configPath": [
"anomaly_detection_dashboards"
],
"optionalPlugins": ["dataSource","dataSourceManagement"],
"requiredPlugins": [
"opensearchDashboardsUtils",
"expressions",
Expand Down
6 changes: 5 additions & 1 deletion public/anomaly_detection_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters) {
} else {
require('@elastic/charts/dist/theme_only_light.css');
}

ReactDOM.render(
<Provider store={store}>
<Router>
<Route
render={(props) => (
<CoreServicesContext.Provider value={coreStart}>
<Main {...props} />
<Main
setHeaderActionMenu={params.setHeaderActionMenu}
{...props}
/>
</CoreServicesContext.Provider>
)}
/>
Expand Down
21 changes: 17 additions & 4 deletions public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,18 @@ import {
} from '../utils/constants';
import { HeatmapCell } from './AnomalyHeatmapChart';
import { ANOMALY_AGG, MIN_END_TIME, MAX_END_TIME } from '../../utils/constants';
import { MAX_HISTORICAL_AGG_RESULTS } from '../../../utils/constants';
import {
DATA_SOURCE_ID,
MAX_HISTORICAL_AGG_RESULTS,
} from '../../../utils/constants';
import { searchResults } from '../../../redux/reducers/anomalyResults';
import {
DAY_IN_MILLI_SECS,
WEEK_IN_MILLI_SECS,
DETECTOR_STATE,
} from '../../../../server/utils/constants';
import { ENTITY_COLORS } from '../../DetectorResults/utils/constants';
import { useLocation } from 'react-router-dom';

interface AnomalyDetailsChartProps {
onDateRangeChange(
Expand Down Expand Up @@ -118,6 +122,9 @@ interface AnomalyDetailsChartProps {
export const AnomalyDetailsChart = React.memo(
(props: AnomalyDetailsChartProps) => {
const dispatch = useDispatch();
const location = useLocation();
const dataSourceId =
new URLSearchParams(location.search).get(DATA_SOURCE_ID) || '';
const [showAlertsFlyout, setShowAlertsFlyout] = useState<boolean>(false);
const [alertAnnotations, setAlertAnnotations] = useState<any[]>([]);
const [isLoadingAlerts, setIsLoadingAlerts] = useState<boolean>(false);
Expand Down Expand Up @@ -174,7 +181,9 @@ export const AnomalyDetailsChart = React.memo(
zoomRange.endDate,
taskId
);
dispatch(searchResults(anomalyDataRangeQuery, resultIndex, true))
dispatch(
searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)
)
.then((response: any) => {
// Only retrieve buckets that are in the anomaly results range. This is so
// we don't show aggregate results for where there is no data at all
Expand All @@ -193,7 +202,9 @@ export const AnomalyDetailsChart = React.memo(
taskId,
selectedAggId
);
dispatch(searchResults(historicalAggQuery, resultIndex, true))
dispatch(
searchResults(historicalAggQuery, resultIndex, dataSourceId, true)
)
.then((response: any) => {
const aggregatedAnomalies = parseHistoricalAggregatedAnomalies(
response,
Expand Down Expand Up @@ -229,7 +240,9 @@ export const AnomalyDetailsChart = React.memo(
zoomRange.endDate,
taskId
);
dispatch(searchResults(anomalyDataRangeQuery, resultIndex, true))
dispatch(
searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)
)
.then((response: any) => {
const dataStartDate = get(
response,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import { getMappings } from '../../../redux/reducers/opensearch';
// 1. Get detector
// 2. Gets index mapping
export const useFetchDetectorInfo = (
detectorId: string
detectorId: string,
dataSourceId: string
): {
detector: Detector;
hasError: boolean;
Expand All @@ -43,7 +44,8 @@ export const useFetchDetectorInfo = (
useEffect(() => {
const fetchDetector = async () => {
if (!detector) {
await dispatch(getDetector(detectorId));
// hardcoding the datasource id for now, will update it later when working on create page
await dispatch(getDetector(detectorId, dataSourceId));
}
if (selectedIndices) {
await dispatch(getMappings(selectedIndices));
Expand Down
6 changes: 6 additions & 0 deletions public/pages/Dashboard/Components/AnomaliesDistribution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { get, isEmpty } from 'lodash';
import { AD_DOC_FIELDS } from '../../../../server/utils/constants';
import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../utils/constants';
import { searchResults } from '../../../redux/reducers/anomalyResults';
import { useLocation } from 'react-router-dom';
import { DATA_SOURCE_ID } from '../../../utils/constants';
export interface AnomaliesDistributionChartProps {
selectedDetectors: DetectorListItem[];
}
Expand All @@ -41,6 +43,9 @@ export const AnomaliesDistributionChart = (
props: AnomaliesDistributionChartProps
) => {
const dispatch = useDispatch();
const location = useLocation();
const dataSourceId =
new URLSearchParams(location.search).get(DATA_SOURCE_ID) || '';

const [anomalyDistribution, setAnomalyDistribution] = useState(
[] as object[]
Expand All @@ -66,6 +71,7 @@ export const AnomaliesDistributionChart = (
await getAnomalyDistributionForDetectorsByTimeRange(
searchResults,
props.selectedDetectors,
dataSourceId,
timeRange,
dispatch,
0,
Expand Down
16 changes: 13 additions & 3 deletions public/pages/Dashboard/Components/AnomaliesLiveChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,14 @@ import {
getLatestAnomalyResultsForDetectorsByTimeRange,
getLatestAnomalyResultsByTimeRange,
} from '../utils/utils';
import { MAX_ANOMALIES, SPACE_STR } from '../../../utils/constants';
import {
DATA_SOURCE_ID,
MAX_ANOMALIES,
SPACE_STR,
} from '../../../utils/constants';
import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../utils/constants';
import { searchResults } from '../../../redux/reducers/anomalyResults';
import { useLocation } from 'react-router-dom';

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

export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => {
const dispatch = useDispatch();
const location = useLocation();
const dataSourceId =
new URLSearchParams(location.search).get(DATA_SOURCE_ID) || '';

const [liveTimeRange, setLiveTimeRange] = useState<LiveTimeRangeState>({
startDateTime: moment().subtract(31, 'minutes'),
Expand Down Expand Up @@ -102,7 +110,8 @@ export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => {
1,
true,
ALL_CUSTOM_AD_RESULT_INDICES,
false
false,
dataSourceId
);
} catch (err) {
console.log(
Expand All @@ -126,7 +135,8 @@ export const AnomaliesLiveChart = (props: AnomaliesLiveChartProps) => {
MAX_LIVE_DETECTORS,
false,
ALL_CUSTOM_AD_RESULT_INDICES,
false
false,
dataSourceId
);
setLiveAnomalyData(latestLiveAnomalyResult);

Expand Down
110 changes: 99 additions & 11 deletions public/pages/Dashboard/Container/DashboardOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
* GitHub history for details.
*/

import React, { Fragment, useState, useEffect } from 'react';
import React, { Fragment, useState, useEffect, useMemo } from 'react';
import { AnomaliesLiveChart } from '../Components/AnomaliesLiveChart';
import { AnomaliesDistributionChart } from '../Components/AnomaliesDistribution';
import queryString from 'querystring';

import { useDispatch, useSelector } from 'react-redux';
import { get, isEmpty, cloneDeep } from 'lodash';
Expand All @@ -29,27 +30,53 @@ import {
} from '@elastic/eui';
import { AnomalousDetectorsList } from '../Components/AnomalousDetectorsList';
import {
GET_ALL_DETECTORS_QUERY_PARAMS,
ALL_DETECTORS_MESSAGE,
ALL_DETECTOR_STATES_MESSAGE,
ALL_INDICES_MESSAGE,
} from '../utils/constants';
import { AppState } from '../../../redux/reducers';
import { CatIndex, IndexAlias } from '../../../../server/models/types';
import { getVisibleOptions } from '../../utils/helpers';
import {
CatIndex,
IndexAlias,
MDSQueryParams,
} from '../../../../server/models/types';
import {
getAllDetectorsQueryParamsWithDataSourceId,
getVisibleOptions,
} from '../../utils/helpers';
import { BREADCRUMBS } from '../../../utils/constants';
import { DETECTOR_STATE } from '../../../../server/utils/constants';
import { getDetectorStateOptions } from '../../DetectorsList/utils/helpers';
import {
getDetectorStateOptions,
getURLQueryParams,
} from '../../DetectorsList/utils/helpers';
import { DashboardHeader } from '../Components/utils/DashboardHeader';
import { EmptyDashboard } from '../Components/EmptyDashboard/EmptyDashboard';
import {
prettifyErrorMessage,
NO_PERMISSIONS_KEY_WORD,
} from '../../../../server/utils/helpers';
import { CoreServicesContext } from '../../../components/CoreServices/CoreServices';
import { CoreStart } from '../../../../../../src/core/public';
import { CoreStart, MountPoint } from '../../../../../../src/core/public';
import { DataSourceSelectableConfig } from '../../../../../../src/plugins/data_source_management/public';
import {
getDataSourceManagementPlugin,
getDataSourcePlugin,
getNotifications,
getSavedObjectsClient,
} from '../../../services';
import { RouteComponentProps } from 'react-router-dom';

interface OverviewProps extends RouteComponentProps {
setActionMenu: (menuMount: MountPoint | undefined) => void;
}

interface MDSOverviewState {
queryParams: MDSQueryParams;
selectedDataSourceId: string;
}

export function DashboardOverview() {
export function DashboardOverview(props: OverviewProps) {
const core = React.useContext(CoreServicesContext) as CoreStart;
const dispatch = useDispatch();
const adState = useSelector((state: AppState) => state.ad);
Expand All @@ -58,13 +85,23 @@ export function DashboardOverview() {
const errorGettingDetectors = adState.errorMessage;
const isLoadingDetectors = adState.requesting;

const dataSourceEnabled = getDataSourcePlugin()?.dataSourceEnabled || false;

const [currentDetectors, setCurrentDetectors] = useState(
Object.values(allDetectorList)
);
const [allDetectorsSelected, setAllDetectorsSelected] = useState(true);
const [selectedDetectorsName, setSelectedDetectorsName] = useState(
[] as string[]
);
const queryParams = getURLQueryParams(props.location);
const [MDSOverviewState, setMDSOverviewState] = useState<MDSOverviewState>({
queryParams,
selectedDataSourceId: queryParams.dataSourceId
? queryParams.dataSourceId
: '',
});

const getDetectorOptions = (detectorsIdMap: {
[key: string]: DetectorListItem;
}) => {
Expand Down Expand Up @@ -108,6 +145,20 @@ export function DashboardOverview() {
setAllDetectorStatesSelected(isEmpty(selectedStates));
};

const handleDataSourceChange = ([event]) => {
const dataSourceId = event?.id;
if (!dataSourceId) {
getNotifications().toasts.addDanger(
prettifyErrorMessage('Unable to set data source.')
);
} else {
setMDSOverviewState({
queryParams: dataSourceId,
selectedDataSourceId: dataSourceId,
});
}
};

const opensearchState = useSelector((state: AppState) => state.opensearch);

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

const intializeDetectors = async () => {
dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS));
dispatch(getIndices(''));
dispatch(getAliases(''));
dispatch(
getDetectorList(
getAllDetectorsQueryParamsWithDataSourceId(
MDSOverviewState.selectedDataSourceId
)
)
);
dispatch(getIndices('', MDSOverviewState.selectedDataSourceId));
dispatch(getAliases('', MDSOverviewState.selectedDataSourceId));
};

useEffect(() => {
const { history, location } = props;
const updatedParams = {
dataSourceId: MDSOverviewState.selectedDataSourceId,
};
history.replace({
...location,
search: queryString.stringify(updatedParams),
});
intializeDetectors();
}, []);
}, [MDSOverviewState]);

useEffect(() => {
if (errorGettingDetectors) {
Expand Down Expand Up @@ -197,9 +262,32 @@ export function DashboardOverview() {
);
}, [selectedDetectorsName, selectedIndices, selectedDetectorStates]);

let renderDataSourceComponent = null;
if (dataSourceEnabled) {
const DataSourceMenu =
getDataSourceManagementPlugin()?.ui.getDataSourceMenu<DataSourceSelectableConfig>();
renderDataSourceComponent = useMemo(() => {
return (
<DataSourceMenu
setMenuMountPoint={props.setActionMenu}
componentType={'DataSourceSelectable'}
componentConfig={{
fullWidth: false,
activeOption: [{ id: MDSOverviewState.selectedDataSourceId }],
savedObjects: getSavedObjectsClient(),
notifications: getNotifications(),
onSelectedDataSources: (dataSources) =>
handleDataSourceChange(dataSources),
}}
/>
);
}, [getSavedObjectsClient(), getNotifications(), props.setActionMenu]);
}

return (
<div style={{ height: '1200px' }}>
<Fragment>
{dataSourceEnabled && renderDataSourceComponent}
<DashboardHeader hasDetectors={totalRealtimeDetectors > 0} />
{isLoadingDetectors ? (
<div>
Expand Down
Loading
Loading