From 485f2dba26870e127392afe0c2107027131e7e40 Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Tue, 4 Aug 2020 15:29:55 -0700 Subject: [PATCH] Improve error handling when retrieving all detectors (#267) --- .../Dashboard/Container/DashboardOverview.tsx | 34 ++++++++----- .../DetectorsList/containers/List/List.tsx | 50 +++++++++++-------- public/pages/DetectorsList/utils/constants.ts | 2 + public/redux/reducers/ad.ts | 6 ++- server/routes/ad.ts | 19 ++++--- 5 files changed, 70 insertions(+), 41 deletions(-) diff --git a/public/pages/Dashboard/Container/DashboardOverview.tsx b/public/pages/Dashboard/Container/DashboardOverview.tsx index aff4a629..427215f5 100644 --- a/public/pages/Dashboard/Container/DashboardOverview.tsx +++ b/public/pages/Dashboard/Container/DashboardOverview.tsx @@ -32,6 +32,8 @@ import { } from '@elastic/eui'; //@ts-ignore import chrome from 'ui/chrome'; +// @ts-ignore +import { toastNotifications } from 'ui/notify'; import { AnomalousDetectorsList } from '../Components/AnomalousDetectorsList'; import { GET_ALL_DETECTORS_QUERY_PARAMS, @@ -54,6 +56,8 @@ export function DashboardOverview() { const allDetectorList = adState.detectorList; + const errorGettingDetectors = adState.errorMessage; + const [isLoadingDetectors, setIsLoadingDetectors] = useState(true); const [currentDetectors, setCurrentDetectors] = useState( @@ -69,7 +73,7 @@ export function DashboardOverview() { [key: string]: DetectorListItem; }) => { const detectorNames = Object.values(detectorsIdMap).map( - detectorListItem => { + (detectorListItem) => { return detectorListItem.name; } ); @@ -85,7 +89,7 @@ export function DashboardOverview() { const handleDetectorsFilterChange = ( options: EuiComboBoxOptionProps[] ): void => { - const selectedNames = options.map(option => option.label); + const selectedNames = options.map((option) => option.label); setSelectedDetectorsName(selectedNames); setAllDetectorsSelected(isEmpty(selectedNames)); @@ -103,7 +107,7 @@ export function DashboardOverview() { options: EuiComboBoxOptionProps[] ): void => { const selectedStates = options.map( - option => option.label as DETECTOR_STATE + (option) => option.label as DETECTOR_STATE ); setSelectedDetectorStates(selectedStates); setAllDetectorStatesSelected(isEmpty(selectedStates)); @@ -122,7 +126,7 @@ export function DashboardOverview() { const handleIndicesFilterChange = ( options: EuiComboBoxOptionProps[] ): void => { - const selectedIndices = options.map(option => option.label); + const selectedIndices = options.map((option) => option.label); setSelectedIndices(selectedIndices); setAllIndicesSelected(isEmpty(selectedIndices)); }; @@ -138,13 +142,13 @@ export function DashboardOverview() { } else { detectorsToFilter = cloneDeep( Object.values(allDetectorList) - ).filter(detectorItem => selectedNameList.includes(detectorItem.name)); + ).filter((detectorItem) => selectedNameList.includes(detectorItem.name)); } let filteredDetectorItemsByNamesAndIndex = detectorsToFilter; if (!allIndicesSelected) { filteredDetectorItemsByNamesAndIndex = detectorsToFilter.filter( - detectorItem => + (detectorItem) => selectedIndexList.includes(detectorItem.indices.toString()) ); } @@ -152,7 +156,7 @@ export function DashboardOverview() { let finalFilteredDetectors = filteredDetectorItemsByNamesAndIndex; if (!allDetectorStatesSelected) { finalFilteredDetectors = filteredDetectorItemsByNamesAndIndex.filter( - detectorItem => selectedStateList.includes(detectorItem.curState) + (detectorItem) => selectedStateList.includes(detectorItem.curState) ); } @@ -161,12 +165,7 @@ export function DashboardOverview() { const intializeDetectors = async () => { setIsLoadingDetectors(true); - try { - await dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); - } catch (error) { - console.log('Error is found during getting detector list', error); - } - setIsLoadingDetectors(false); + dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); dispatch(getIndices('')); dispatch(getAliases('')); }; @@ -175,6 +174,14 @@ export function DashboardOverview() { intializeDetectors(); }, []); + useEffect(() => { + if (errorGettingDetectors) { + console.error(errorGettingDetectors); + toastNotifications.addDanger('Unable to get all detectors'); + setIsLoadingDetectors(false); + } + }, [errorGettingDetectors]); + useEffect(() => { chrome.breadcrumbs.set([ BREADCRUMBS.ANOMALY_DETECTOR, @@ -184,6 +191,7 @@ export function DashboardOverview() { useEffect(() => { setCurrentDetectors(Object.values(allDetectorList)); + setIsLoadingDetectors(false); }, [allDetectorList]); useEffect(() => { diff --git a/public/pages/DetectorsList/containers/List/List.tsx b/public/pages/DetectorsList/containers/List/List.tsx index c4211b1f..f96b9784 100644 --- a/public/pages/DetectorsList/containers/List/List.tsx +++ b/public/pages/DetectorsList/containers/List/List.tsx @@ -75,7 +75,10 @@ import { getDetectorsToDisplay, } from '../../../utils/helpers'; import { staticColumn } from '../../utils/tableUtils'; -import { DETECTOR_ACTION } from '../../utils/constants'; +import { + DETECTOR_ACTION, + SINGLE_DETECTOR_ERROR_MSG, +} from '../../utils/constants'; import { getTitleWithCount, Listener } from '../../../../utils/utils'; import { ListActions } from '../../components/ListActions/ListActions'; import { searchMonitors } from '../../../../redux/reducers/alerting'; @@ -117,6 +120,9 @@ export const DetectorList = (props: ListProps) => { const dispatch = useDispatch(); const allDetectors = useSelector((state: AppState) => state.ad.detectorList); const allMonitors = useSelector((state: AppState) => state.alerting.monitors); + const errorGettingDetectors = useSelector( + (state: AppState) => state.ad.errorMessage + ); const elasticsearchState = useSelector( (state: AppState) => state.elasticsearch ); @@ -171,6 +177,17 @@ export const DetectorList = (props: ListProps) => { getInitialMonitors(); }, []); + useEffect(() => { + if ( + errorGettingDetectors && + errorGettingDetectors !== SINGLE_DETECTOR_ERROR_MSG + ) { + console.error(errorGettingDetectors); + toastNotifications.addDanger('Unable to get all detectors'); + setIsLoadingFinalDetectors(false); + } + }, [errorGettingDetectors]); + // Updating displayed indices (initializing to first 20 for now) const visibleIndices = get(elasticsearchState, 'indices', []) as CatIndex[]; const visibleAliases = get(elasticsearchState, 'aliases', []) as IndexAlias[]; @@ -256,14 +273,7 @@ export const DetectorList = (props: ListProps) => { }, [confirmModalState.isRequestingToClose, isLoading]); const getUpdatedDetectors = async () => { - try { - dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); - } catch (error) { - toastNotifications.addDanger( - `Error is found while getting detector list: ${error}` - ); - setIsLoadingFinalDetectors(false); - } + dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); }; const handlePageChange = (pageNumber: number) => { @@ -306,7 +316,7 @@ export const DetectorList = (props: ListProps) => { const sanitizedQuery = sanitizeSearchText(searchValue); setIndexQuery(sanitizedQuery); await dispatch(getPrioritizedIndices(sanitizedQuery)); - setState(state => ({ + setState((state) => ({ ...state, page: 0, })); @@ -321,8 +331,8 @@ export const DetectorList = (props: ListProps) => { states = options.length == 0 ? ALL_DETECTOR_STATES - : options.map(option => option.label as DETECTOR_STATE); - setState(state => ({ + : options.map((option) => option.label as DETECTOR_STATE); + setState((state) => ({ ...state, page: 0, selectedDetectorStates: states, @@ -335,7 +345,7 @@ export const DetectorList = (props: ListProps) => { indices = options.length == 0 ? ALL_INDICES - : options.map(option => option.label).slice(0, MAX_SELECTED_INDICES); + : options.map((option) => option.label).slice(0, MAX_SELECTED_INDICES); setState({ ...state, @@ -345,7 +355,7 @@ export const DetectorList = (props: ListProps) => { }; const handleResetFilter = () => { - setState(state => ({ + setState((state) => ({ ...state, queryParams: { ...state.queryParams, @@ -443,7 +453,7 @@ export const DetectorList = (props: ListProps) => { const validIds = getDetectorsForAction( selectedDetectorsForAction, DETECTOR_ACTION.START - ).map(detector => detector.id); + ).map((detector) => detector.id); const promises = validIds.map(async (id: string) => { return dispatch(startDetector(id)); }); @@ -453,7 +463,7 @@ export const DetectorList = (props: ListProps) => { 'All selected detectors have been started successfully' ); }) - .catch(error => { + .catch((error) => { toastNotifications.addDanger( `Error starting all selected detectors: ${error}` ); @@ -468,7 +478,7 @@ export const DetectorList = (props: ListProps) => { const validIds = getDetectorsForAction( selectedDetectorsForAction, DETECTOR_ACTION.STOP - ).map(detector => detector.id); + ).map((detector) => detector.id); const promises = validIds.map(async (id: string) => { return dispatch(stopDetector(id)); }); @@ -479,7 +489,7 @@ export const DetectorList = (props: ListProps) => { ); if (listener) listener.onSuccess(); }) - .catch(error => { + .catch((error) => { toastNotifications.addDanger( `Error stopping all selected detectors: ${error}` ); @@ -498,7 +508,7 @@ export const DetectorList = (props: ListProps) => { const validIds = getDetectorsForAction( selectedDetectorsForAction, DETECTOR_ACTION.DELETE - ).map(detector => detector.id); + ).map((detector) => detector.id); const promises = validIds.map(async (id: string) => { return dispatch(deleteDetector(id)); }); @@ -508,7 +518,7 @@ export const DetectorList = (props: ListProps) => { 'All selected detectors have been deleted successfully' ); }) - .catch(error => { + .catch((error) => { toastNotifications.addDanger( `Error deleting all selected detectors: ${error}` ); diff --git a/public/pages/DetectorsList/utils/constants.ts b/public/pages/DetectorsList/utils/constants.ts index 7e7844dd..b2979207 100644 --- a/public/pages/DetectorsList/utils/constants.ts +++ b/public/pages/DetectorsList/utils/constants.ts @@ -18,3 +18,5 @@ export enum DETECTOR_ACTION { STOP, DELETE, } + +export const SINGLE_DETECTOR_ERROR_MSG = 'Not Found'; diff --git a/public/redux/reducers/ad.ts b/public/redux/reducers/ad.ts index 9ff9e9ac..762e8dd7 100644 --- a/public/redux/reducers/ad.ts +++ b/public/redux/reducers/ad.ts @@ -192,9 +192,10 @@ const reducer = handleActions( ), totalDetectors: action.result.data.response.totalDetectors, }), - FAILURE: (state: Detectors): Detectors => ({ + FAILURE: (state: Detectors, action: APIErrorAction): Detectors => ({ ...state, requesting: false, + errorMessage: action.error, }), }, [UPDATE_DETECTOR]: { @@ -214,9 +215,10 @@ const reducer = handleActions( }, }, }), - FAILURE: (state: Detectors): Detectors => ({ + FAILURE: (state: Detectors, action: APIErrorAction): Detectors => ({ ...state, requesting: false, + errorMessage: action.error, }), }, diff --git a/server/routes/ad.ts b/server/routes/ad.ts index 053fce72..3ecd5d62 100644 --- a/server/routes/ad.ts +++ b/server/routes/ad.ts @@ -460,13 +460,17 @@ const getDetectors = async ( ); return detectorStateResp; } catch (err) { - console.log( - 'Anomaly detector - Unable to retrieve detector state', - err + console.log('Error getting detector profile ', err); + return Promise.reject( + new Error('Error retrieving all detector states') ); } }); - const detectorStateResponses = await Promise.all(detectorStatePromises); + const detectorStateResponses = await Promise.all( + detectorStatePromises + ).catch((err) => { + throw err; + }); const finalDetectorStates = getFinalDetectorStates( detectorStateResponses, finalDetectors @@ -484,12 +488,15 @@ const getDetectors = async ( }); return detectorResp; } catch (err) { - console.log('Anomaly detector - Unable to get detector job', err); + console.log('Error getting detector ', err); + return Promise.reject(new Error('Error retrieving all detectors')); } }); const detectorsWithJobResponses = await Promise.all( detectorsWithJobPromises - ); + ).catch((err) => { + throw err; + }); const finalDetectorsWithJob = getDetectorsWithJob( detectorsWithJobResponses );