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

Support MDS on DetectorDetails page #719

Closed
Closed
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
17 changes: 15 additions & 2 deletions public/anomaly_detection_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@ import { Main } from './pages/main';
import { Provider } from 'react-redux';
import configureStore from './redux/configureStore';
import { CoreServicesContext } from './components/CoreServices/CoreServices';
import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public';
import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public';

export function renderApp(coreStart: CoreStart, params: AppMountParameters) {
export function renderApp(
coreStart: CoreStart,
params: AppMountParameters,
dataSourceManagement: DataSourceManagementPluginSetup,
dataSource: DataSourcePluginSetup
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like the dataSource object is not used below

) {
const http = coreStart.http;
const store = configureStore(http);

Expand All @@ -29,13 +36,19 @@ 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
dataSourceEnabled={dataSource.dataSourceEnabled}
dataSourceManagement={dataSourceManagement}
setHeaderActionMenu={params.setHeaderActionMenu}
{...props}
/>
</CoreServicesContext.Provider>
)}
/>
Expand Down
2 changes: 2 additions & 0 deletions public/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export type Detector = {
taskState?: DETECTOR_STATE;
taskProgress?: number;
taskError?: string;
dataSourceId?: string;
};

export type DetectorListItem = {
Expand All @@ -218,6 +219,7 @@ export type DetectorListItem = {
lastUpdateTime: number;
enabledTime?: number;
detectorType?: string;
dataSourceId: string;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is dataSourceId optional?

};

export type EntityData = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a tracker for all the TODOs so we don't forget what needs to be done?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tracked here - #721

await dispatch(getDetector(detectorId, '4585f560-d1ef-11ee-aa63-2181676cc573'));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a TODO in the comment so it is clear

}
if (selectedIndices) {
await dispatch(getMappings(selectedIndices));
Expand Down
3 changes: 2 additions & 1 deletion public/pages/DetectorConfig/containers/DetectorConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { getDetector } from '../../../redux/reducers/ad';
import { EuiLoadingSpinner } from '@elastic/eui';
interface DetectorConfigProps extends RouteComponentProps {
detectorId: string;
dataSourceId: string;
onEditFeatures(): void;
onEditDetector(): void;
}
Expand All @@ -32,7 +33,7 @@ export function DetectorConfig(props: DetectorConfigProps) {
);

useEffect(() => {
dispatch(getDetector(props.detectorId));
dispatch(getDetector(props.detectorId, props.dataSourceId));
}, []);

return (
Expand Down
52 changes: 39 additions & 13 deletions public/pages/DetectorDetail/containers/DetectorDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ import {
EuiLoadingSpinner,
EuiButton,
} from '@elastic/eui';
import { CoreStart } from '../../../../../../src/core/public';
import { CoreStart, MountPoint } from '../../../../../../src/core/public';
import { CoreServicesContext } from '../../../components/CoreServices/CoreServices';
import { get, isEmpty } from 'lodash';
import { RouteComponentProps, Switch, Route, Redirect } from 'react-router-dom';
import { RouteComponentProps, Switch, Route, Redirect, useLocation } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { useFetchDetectorInfo } from '../../CreateDetectorSteps/hooks/useFetchDetectorInfo';
import { useHideSideNavBar } from '../../main/hooks/useHideSideNavBar';
Expand Down Expand Up @@ -58,12 +58,16 @@ import {
import { DETECTOR_STATE } from '../../../../server/utils/constants';
import { CatIndex } from '../../../../server/models/types';
import { containsIndex } from '../utils/helpers';
import { DataSourceManagementPluginSetup, DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public';

export interface DetectorRouterProps {
detectorId?: string;
}
interface DetectorDetailProps
extends RouteComponentProps<DetectorRouterProps> {}
interface DetectorDetailProps extends RouteComponentProps<DetectorRouterProps> {
dataSourceEnabled: boolean;
dataSourceManagement: DataSourceManagementPluginSetup;
setActionMenu: (menuMount: MountPoint | undefined) => void;
}

const tabs = [
{
Expand Down Expand Up @@ -103,6 +107,11 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
const core = React.useContext(CoreServicesContext) as CoreStart;
const dispatch = useDispatch();
const detectorId = get(props, 'match.params.detectorId', '') as string;

const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const dataSourceId = queryParams.get('dataSourceId') as string;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does dataSourceId always exist in the query param?


const { detector, hasError, isLoadingDetector, errorMessage } =
useFetchDetectorInfo(detectorId);
const { monitor, fetchMonitorError, isLoadingMonitor } =
Expand Down Expand Up @@ -156,7 +165,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
// detector starts, result index recreated or user switches tabs to re-fetch detector)
useEffect(() => {
const getInitialIndices = async () => {
await dispatch(getIndices('')).catch((error: any) => {
await dispatch(getIndices('', dataSourceId)).catch((error: any) => {
console.error(error);
core.notifications.toasts.addDanger('Error getting all indices');
});
Expand Down Expand Up @@ -201,23 +210,23 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
...detectorDetailModel,
selectedTab: DETECTOR_DETAIL_TABS.CONFIGURATIONS,
});
props.history.push(`/detectors/${detectorId}/configurations`);
props.history.push(`/detectors/${detectorId}/configurations?dataSourceId=${dataSourceId}`);
}, []);

const handleSwitchToHistoricalTab = useCallback(() => {
setDetectorDetailModel({
...detectorDetailModel,
selectedTab: DETECTOR_DETAIL_TABS.HISTORICAL,
});
props.history.push(`/detectors/${detectorId}/historical`);
props.history.push(`/detectors/${detectorId}/historical?dataSourceId=${dataSourceId}`);
}, []);

const handleTabChange = (route: DETECTOR_DETAIL_TABS) => {
setDetectorDetailModel({
...detectorDetailModel,
selectedTab: route,
});
props.history.push(`/detectors/${detectorId}/${route}`);
props.history.push(`/detectors/${detectorId}/${route}?dataSourceId=${dataSourceId}`);
};

const hideMonitorCalloutModal = () => {
Expand Down Expand Up @@ -261,8 +270,8 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
// Await for the start detector call to succeed before displaying toast.
// Don't wait for get detector call; the page will be updated
// via hooks automatically when the new detector info is returned.
await dispatch(startDetector(detectorId));
dispatch(getDetector(detectorId));
await dispatch(startDetector(detectorId, dataSourceId));
dispatch(getDetector(detectorId, dataSourceId));
core.notifications.toasts.addSuccess(
`Successfully started the detector job`
);
Expand All @@ -278,10 +287,10 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
const handleStopAdJob = async (detectorId: string, listener?: Listener) => {
try {
if (isRTJobRunning) {
await dispatch(stopDetector(detectorId));
await dispatch(stopDetector(detectorId, dataSourceId));
}
if (isHistoricalJobRunning) {
await dispatch(stopHistoricalDetector(detectorId));
await dispatch(stopHistoricalDetector(detectorId, dataSourceId));
}
core.notifications.toasts.addSuccess(
`Successfully stopped the ${runningJobsAsString}`
Expand All @@ -302,7 +311,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => {

const handleDelete = useCallback(async (detectorId: string) => {
try {
await dispatch(deleteDetector(detectorId));
await dispatch(deleteDetector(detectorId, dataSourceId));
core.notifications.toasts.addSuccess(`Successfully deleted the detector`);
hideDeleteDetectorModal();
props.history.push('/detectors');
Expand Down Expand Up @@ -350,6 +359,8 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
></EuiCallOut>
) : null;

const DataSourceMenu = props.dataSourceManagement.ui.getDataSourceMenu<DataSourceViewConfig>();

return (
<React.Fragment>
{!isEmpty(detector) && !hasError ? (
Expand All @@ -361,6 +372,18 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
: { ...lightStyles, flexGrow: 'unset' }),
}}
>

{props.dataSourceEnabled && (
<DataSourceMenu
setMenuMountPoint={props.setActionMenu}
componentType={'DataSourceView'}
componentConfig={{
// give a placeholder label for now, will update it once neo team allows empty label field
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably adding a TODO: give a place holder ... here so it is easier to find the location that needs rework

activeOption: [{label: 'labelPlaceHolder', id: dataSourceId}],
fullWidth: true
}}
/>
)}
<EuiFlexGroup
justifyContent="spaceBetween"
style={{ padding: '10px' }}
Expand Down Expand Up @@ -542,6 +565,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
<AnomalyResults
{...resultsProps}
detectorId={detectorId}
dataSourceId={dataSourceId}
onStartDetector={() => handleStartAdJob(detectorId)}
onStopDetector={() => handleStopAdJob(detectorId)}
onSwitchToConfiguration={handleSwitchToConfigurationTab}
Expand All @@ -556,6 +580,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
<HistoricalDetectorResults
{...configProps}
detectorId={detectorId}
dataSourceId={dataSourceId}
/>
)}
/>
Expand All @@ -566,6 +591,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
<DetectorConfig
{...configProps}
detectorId={detectorId}
dataSourceId={dataSourceId}
onEditFeatures={handleEditFeature}
onEditDetector={handleEditDetector}
/>
Expand Down
21 changes: 13 additions & 8 deletions public/pages/DetectorResults/containers/AnomalyHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import { prettifyErrorMessage } from '../../../../server/utils/helpers';

interface AnomalyHistoryProps {
detector: Detector;
dataSourceId: string;
monitor: Monitor | undefined;
isFeatureDataMissing?: boolean;
isHistorical?: boolean;
Expand Down Expand Up @@ -126,6 +127,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
props.isHistorical && props.detector?.detectionDateRange
? props.detector.detectionDateRange.endTime
: moment().valueOf();
const dataSourceId = props.dataSourceId;
const [dateRange, setDateRange] = useState<DateRange>({
startDate: initialStartDate,
endDate: initialEndDate,
Expand Down Expand Up @@ -223,7 +225,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
taskId.current,
modelId
);
return dispatch(searchResults(params, resultIndex, true));
return dispatch(searchResults(params, resultIndex, dataSourceId, true));
})
: [];

Expand Down Expand Up @@ -252,7 +254,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
modelId
);
const anomalySummaryResponse = await dispatch(
searchResults(anomalySummaryQuery, resultIndex, true)
searchResults(anomalySummaryQuery, resultIndex, dataSourceId, true)
);
allPureAnomalies.push(parsePureAnomalies(anomalySummaryResponse));
}
Expand All @@ -275,7 +277,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
taskId.current,
modelId
);
return dispatch(searchResults(params, resultIndex, true));
return dispatch(searchResults(params, resultIndex, dataSourceId, true));
}
);

Expand Down Expand Up @@ -308,7 +310,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
modelId
);
const bucketizedAnomalyResultResponse = await dispatch(
searchResults(bucketizedAnomalyResultsQuery, resultIndex, true)
searchResults(bucketizedAnomalyResultsQuery, resultIndex, dataSourceId, true)
);
allBucketizedAnomalyResults.push(
parseBucketizedAnomalyResults(bucketizedAnomalyResultResponse)
Expand Down Expand Up @@ -408,7 +410,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
);
const detectorResultResponse = props.isHistorical
? await dispatch(
getDetectorResults(taskId.current, params, true, resultIndex, true)
getDetectorResults(taskId.current, dataSourceId, params, true, resultIndex, true)
).catch((error: any) => {
setIsLoading(false);
setIsLoadingAnomalyResults(false);
Expand All @@ -417,6 +419,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
: await dispatch(
getDetectorResults(
props.detector.id,
dataSourceId,
params,
false,
resultIndex,
Expand Down Expand Up @@ -536,6 +539,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
const result = await dispatch(
getTopAnomalyResults(
detectorId,
dataSourceId,
get(props, 'isHistorical', false),
query
)
Expand All @@ -553,7 +557,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
props.isHistorical,
taskId.current
);
const result = await dispatch(searchResults(query, resultIndex, true));
const result = await dispatch(searchResults(query, resultIndex, dataSourceId, true));
topEntityAnomalySummaries = parseTopEntityAnomalySummaryResults(
result,
isMultiCategory
Expand All @@ -572,7 +576,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
props.isHistorical,
taskId.current
);
return dispatch(searchResults(entityResultQuery, resultIndex, true));
return dispatch(searchResults(entityResultQuery, resultIndex, dataSourceId, true));
}
);

Expand Down Expand Up @@ -644,6 +648,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
return dispatch(
getDetectorResults(
props.isHistorical ? taskId.current : props.detector?.id,
dataSourceId,
params,
props.isHistorical ? true : false,
resultIndex,
Expand Down Expand Up @@ -722,7 +727,7 @@ export const AnomalyHistory = (props: AnomalyHistoryProps) => {
heatmapCell.entityList
);

const result = await dispatch(searchResults(query, resultIndex, true));
const result = await dispatch(searchResults(query, resultIndex, dataSourceId, true));

// Gets top child entities as an Entity[][],
// where each entry in the array is a unique combination of entity values
Expand Down
Loading
Loading