From cc9dafe3ee8546b34bea91d17feaaf8a9ffb3b88 Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Mon, 6 Feb 2023 12:56:53 -0800 Subject: [PATCH] Update error handling in viz container (#247) * update error handling in viz container Signed-off-by: Shenoy Pratik * fixed comments & minor changes Signed-off-by: Shenoy Pratik --------- Signed-off-by: Shenoy Pratik --- common/types/custom_panels.ts | 5 + .../custom_panels/custom_panel_view.tsx | 20 ++-- .../custom_panels/helpers/utils.tsx | 40 +++++--- .../visualization_container.scss | 6 ++ .../visualization_container.tsx | 98 +++++++++++++------ .../visualization_flyout.scss | 6 ++ .../visualization_flyout.tsx | 74 +++++++++++--- yarn.lock | 2 +- 8 files changed, 186 insertions(+), 65 deletions(-) diff --git a/common/types/custom_panels.ts b/common/types/custom_panels.ts index e397b06076..ca75e41def 100644 --- a/common/types/custom_panels.ts +++ b/common/types/custom_panels.ts @@ -45,3 +45,8 @@ export interface pplResponse { size: number; status: number; } + +export interface VizContainerError { + errorMessage: string; + errorDetails?: string; +} diff --git a/public/components/custom_panels/custom_panel_view.tsx b/public/components/custom_panels/custom_panel_view.tsx index be6ab6ae90..bf83eec695 100644 --- a/public/components/custom_panels/custom_panel_view.tsx +++ b/public/components/custom_panels/custom_panel_view.tsx @@ -37,7 +37,11 @@ import { CREATE_PANEL_MESSAGE, CUSTOM_PANELS_API_PREFIX, } from '../../../common/constants/custom_panels'; -import { SavedVisualizationType, VisualizationType } from '../../../common/types/custom_panels'; +import { + SavedVisualizationType, + VisualizationType, + VizContainerError, +} from '../../../common/types/custom_panels'; import { PanelGrid } from './panel_modules/panel_grid'; import { getCustomModal } from './helpers/modal_containers'; import PPLService from '../../services/requests/ppl'; @@ -60,6 +64,7 @@ import { } from '../common/search/autocomplete_logic'; import { AddVisualizationPopover } from './helpers/add_visualization_popover'; import { DeleteModal } from '../common/helpers/delete_modal'; +import _ from 'lodash'; /* * "CustomPanelsView" module used to render an Operational Panel @@ -336,12 +341,15 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { const visData: SavedVisualizationType = await fetchVisualizationById( http, visualizationId, - (value: string) => setToast(value, 'danger') + (error: VizContainerError) => setToast(error.errorMessage, 'danger') ); - const moreIndices = parseForIndices(visData.query); - for (let j = 0; j < moreIndices.length; j++) { - if (!indices.includes(moreIndices[j])) { - indices.push(moreIndices[j]); + + if (!_.isEmpty(visData)) { + const moreIndices = parseForIndices(visData.query); + for (let j = 0; j < moreIndices.length; j++) { + if (!indices.includes(moreIndices[j])) { + indices.push(moreIndices[j]); + } } } } diff --git a/public/components/custom_panels/helpers/utils.tsx b/public/components/custom_panels/helpers/utils.tsx index 47c9459678..04745e0423 100644 --- a/public/components/custom_panels/helpers/utils.tsx +++ b/public/components/custom_panels/helpers/utils.tsx @@ -15,7 +15,11 @@ import { PPL_DATE_FORMAT, PPL_INDEX_REGEX } from '../../../../common/constants/s import PPLService from '../../../services/requests/ppl'; import { CoreStart } from '../../../../../../src/core/public'; import { CUSTOM_PANELS_API_PREFIX } from '../../../../common/constants/custom_panels'; -import { VisualizationType, SavedVisualizationType } from '../../../../common/types/custom_panels'; +import { + VisualizationType, + SavedVisualizationType, + VizContainerError, +} from '../../../../common/types/custom_panels'; import { Visualization } from '../../visualizations/visualization'; import { getVizContainerProps } from '../../../components/visualizations/charts/helpers'; import { QueryManager } from '../../../../common/query_manager'; @@ -134,20 +138,21 @@ const pplServiceRequestor = async ( type: string, setVisualizationData: React.Dispatch>, setIsLoading: React.Dispatch>, - setIsError: React.Dispatch> + setIsError: React.Dispatch> ) => { await pplService .fetch({ query: finalQuery, format: 'viz' }) .then((res) => { - if (res === undefined) setIsError('Please check the validity of PPL Filter'); + if (res === undefined) + setIsError({ errorMessage: 'Please check the validity of PPL Filter' }); setVisualizationData(res); }) .catch((error: Error) => { const errorMessage = JSON.parse(error.body.message); - setIsError( - errorMessage.error.reason + '. ' + errorMessage.error.details || - 'Issue in fetching visualization' - ); + setIsError({ + errorMessage: errorMessage.error.reason || 'Issue in fetching visualization', + errorDetails: errorMessage.error.details, + }); console.error(error.body); }) .finally(() => { @@ -159,7 +164,7 @@ const pplServiceRequestor = async ( export const fetchVisualizationById = async ( http: CoreStart['http'], savedVisualizationId: string, - setIsError: (value: string) => void + setIsError: (error: VizContainerError) => void ) => { let savedVisualization = {} as SavedVisualizationType; await http @@ -168,7 +173,9 @@ export const fetchVisualizationById = async ( savedVisualization = res.visualization; }) .catch((err) => { - setIsError(`Could not locate saved visualization id:${savedVisualizationId}`); + setIsError({ + errorMessage: `Could not locate saved visualization id: ${savedVisualizationId}`, + }); console.error('Issue in fetching the saved Visualization by Id', err); }); return savedVisualization; @@ -183,19 +190,19 @@ export const getQueryResponse = ( endTime: string, setVisualizationData: React.Dispatch>, setIsLoading: React.Dispatch>, - setIsError: React.Dispatch>, + setIsError: React.Dispatch>, filterQuery = '', timestampField = 'timestamp' ) => { setIsLoading(true); - setIsError(''); + setIsError({} as VizContainerError); let finalQuery = ''; try { finalQuery = queryAccumulator(query, timestampField, startTime, endTime, filterQuery); } catch (error) { const errorMessage = 'Issue in building final query'; - setIsError(errorMessage); + setIsError({ errorMessage: errorMessage }); console.error(errorMessage, error); setIsLoading(false); return; @@ -218,13 +225,14 @@ export const renderSavedVisualization = async ( setVisualizationData: React.Dispatch>, setVisualizationMetaData: React.Dispatch>, setIsLoading: React.Dispatch>, - setIsError: React.Dispatch> + setIsError: React.Dispatch> ) => { setIsLoading(true); - setIsError(''); + setIsError({} as VizContainerError); let visualization = {} as SavedVisualizationType; let updatedVisualizationQuery = ''; + visualization = await fetchVisualizationById(http, savedVisualizationId, setIsError); if (_.isEmpty(visualization)) { @@ -307,11 +315,11 @@ export const renderCatalogVisualization = async ( setVisualizationData: React.Dispatch>, setVisualizationMetaData: React.Dispatch>, setIsLoading: React.Dispatch>, - setIsError: React.Dispatch>, + setIsError: React.Dispatch>, spanResolution?: string ) => { setIsLoading(true); - setIsError(''); + setIsError({} as VizContainerError); const visualizationType = 'line'; const visualizationTimeField = '@timestamp'; diff --git a/public/components/custom_panels/panel_modules/visualization_container/visualization_container.scss b/public/components/custom_panels/panel_modules/visualization_container/visualization_container.scss index 6d14d72882..0130b340fb 100644 --- a/public/components/custom_panels/panel_modules/visualization_container/visualization_container.scss +++ b/public/components/custom_panels/panel_modules/visualization_container/visualization_container.scss @@ -21,6 +21,12 @@ } } +.viz-error-btn { + height: 26px; + line-height: 26px; + font-size: 0.8rem; +} + .visualization-div { width: 100%; height: 90%; diff --git a/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx b/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx index 12eaa49771..6d6001feec 100644 --- a/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx +++ b/public/components/custom_panels/panel_modules/visualization_container/visualization_container.tsx @@ -33,6 +33,8 @@ import { renderSavedVisualization, } from '../../helpers/utils'; import './visualization_container.scss'; +import { VizContainerError } from '../../../../../common/types/custom_panels'; +import _ from 'lodash'; /* * Visualization container - This module is a placeholder to add visualizations in react-grid-layout @@ -99,42 +101,66 @@ export const VisualizationContainer = ({ const [visualizationMetaData, setVisualizationMetaData] = useState(); const [visualizationData, setVisualizationData] = useState([]); const [isLoading, setIsLoading] = useState(true); - const [isError, setIsError] = useState(''); + const [isError, setIsError] = useState({} as VizContainerError); const onActionsMenuClick = () => setIsPopoverOpen((currPopoverOpen) => !currPopoverOpen); const closeActionsMenu = () => setIsPopoverOpen(false); const [isModalVisible, setIsModalVisible] = useState(false); + const [modalContent, setModalContent] = useState(<>); const closeModal = () => setIsModalVisible(false); - const showModal = () => setIsModalVisible(true); + const showModal = (modalType: string) => { + if (modalType === 'catalogModal') + setModalContent( + + + +

{visualizationMetaData.name}

+
+
- let modal; + + This PPL Query is generated in runtime from selected data source + + + {visualizationMetaData.query} + + - if (isModalVisible) { - modal = ( - - - -

{visualizationMetaData.name}

-
-
+ + + Close + + +
+ ); + else + setModalContent( + + + +

{isError.errorMessage}

+
+
- - This PPL Query is generated in runtime from selected data source - - - {visualizationMetaData.query} - - + + Error Details + + + {isError.errorDetails} + + - - - Close - - -
- ); - } + + + Close + + +
+ ); + + setIsModalVisible(true); + }; let popoverPanel = [ { closeActionsMenu(); - showModal(); + showModal('catalogModal'); }} > View query @@ -228,13 +254,25 @@ export const VisualizationContainer = ({
{isLoading ? ( - ) : isError !== '' ? ( + ) : !_.isEmpty(isError) ? (
-

{isError}

+

{isError.errorMessage}

+ {isError.hasOwnProperty('errorDetails') && isError.errorDetails !== '' ? ( + showModal('errorModal')} + size="s" + > + See error details + + ) : ( + <> + )}
) : ( displayVisualization(visualizationMetaData, visualizationData, visualizationType) @@ -301,7 +339,7 @@ export const VisualizationContainer = ({
{memoisedVisualizationBox} - {modal} + {isModalVisible && modalContent} ); }; diff --git a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.scss b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.scss index bec49407b7..60b8f7d43b 100644 --- a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.scss +++ b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.scss @@ -27,3 +27,9 @@ height: 3vh; max-width: 500px; } + +.viz-error-btn { + height: 26px; + line-height: 26px; + font-size: 0.8rem; +} diff --git a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx index 290765b801..3c999e52cd 100644 --- a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx +++ b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx @@ -9,6 +9,7 @@ import { EuiButton, EuiButtonIcon, EuiCallOut, + EuiCodeBlock, EuiDatePicker, EuiDatePickerRange, EuiFlexGroup, @@ -19,6 +20,11 @@ import { EuiFormRow, EuiIcon, EuiLoadingChart, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, EuiSelect, EuiSelectOption, EuiSpacer, @@ -27,7 +33,7 @@ import { EuiToolTip, ShortDate, } from '@elastic/eui'; -import _ from 'lodash'; +import _, { isError } from 'lodash'; import React, { useEffect, useState } from 'react'; import { FlyoutContainers } from '../../../common/flyout_containers'; import { displayVisualization, getQueryResponse, isDateValid } from '../../helpers/utils'; @@ -39,6 +45,7 @@ import { pplResponse, SavedVisualizationType, VisualizationType, + VizContainerError, } from '../../../../../common/types/custom_panels'; import './visualization_flyout.scss'; import { uiSettingsService } from '../../../../../common/utils'; @@ -101,7 +108,7 @@ export const VisaulizationFlyout = ({ const [previewData, setPreviewData] = useState({} as pplResponse); const [previewArea, setPreviewArea] = useState(<>); const [previewLoading, setPreviewLoading] = useState(false); - const [isPreviewError, setIsPreviewError] = useState(''); + const [isPreviewError, setIsPreviewError] = useState({} as VizContainerError); const [savedVisualizations, setSavedVisualizations] = useState([]); const [visualizationOptions, setVisualizationOptions] = useState([]); const [selectValue, setSelectValue] = useState(''); @@ -110,6 +117,38 @@ export const VisaulizationFlyout = ({ const startDate = convertDateTime(start, true, false); const endDate = convertDateTime(end, false, false); + const [isModalVisible, setIsModalVisible] = useState(false); + const [modalContent, setModalContent] = useState(<>); + + const closeModal = () => setIsModalVisible(false); + const showModal = (modalType: string) => { + setModalContent( + + + +

{isPreviewError.errorMessage}

+
+
+ + + Error Details + + + {isPreviewError.errorDetails} + + + + + + Close + + +
+ ); + + setIsModalVisible(true); + }; + const isInputValid = () => { if (!isDateValid(convertDateTime(start), convertDateTime(end, false), setToast, 'left')) { return false; @@ -325,13 +364,21 @@ export const VisaulizationFlyout = ({ {previewLoading ? ( - ) : isPreviewError !== '' ? ( -
+ ) : !_.isEmpty(isPreviewError) ? ( +
-

{isPreviewError}

+

{isPreviewError.errorMessage}

+ {isPreviewError.hasOwnProperty('errorDetails') && + isPreviewError.errorDetails !== '' ? ( + showModal('errorModal')} size="s"> + See error details + + ) : ( + <> + )}
) : (
@@ -366,12 +413,15 @@ export const VisaulizationFlyout = ({ }, []); return ( - + <> + + {isModalVisible && modalContent} + ); }; diff --git a/yarn.lock b/yarn.lock index 73159fe7b9..61402a1bfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -817,7 +817,7 @@ date-fns@^1.27.2: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== -debug@^2.6.9, debug@^3.1.0, debug@^4.0.1, debug@^4.1.1, debug@^4.3.4: +debug@^2.6.9, debug@^3.1.0, debug@^4.0.1, debug@^4.1.1: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==