From a1250632c3b855a1e085b5c195abb3bc951935f8 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 20 Sep 2023 09:09:51 -0700 Subject: [PATCH] Inspector: Show transformation query errors (#73344) --- .../components/Inspector/InspectContent.tsx | 32 ++++++++++++++++--- .../components/Inspector/PanelInspector.tsx | 10 +++--- .../dashboard/components/Inspector/hooks.ts | 8 ++--- .../PanelEditor/usePanelLatestData.ts | 8 +++-- .../features/inspector/InspectErrorTab.tsx | 2 +- .../features/query/state/PanelQueryRunner.ts | 2 +- 6 files changed, 43 insertions(+), 19 deletions(-) diff --git a/public/app/features/dashboard/components/Inspector/InspectContent.tsx b/public/app/features/dashboard/components/Inspector/InspectContent.tsx index 4bd881c62ad..9841466210d 100644 --- a/public/app/features/dashboard/components/Inspector/InspectContent.tsx +++ b/public/app/features/dashboard/components/Inspector/InspectContent.tsx @@ -1,7 +1,16 @@ import { isEmpty } from 'lodash'; import React, { useState } from 'react'; -import { CoreApp, DataSourceApi, formattedValueToString, getValueFormat, PanelData, PanelPlugin } from '@grafana/data'; +import { + CoreApp, + DataSourceApi, + formattedValueToString, + getValueFormat, + PanelData, + PanelPlugin, + LoadingState, + DataQueryError, +} from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { Drawer, Tab, TabsBar } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; @@ -51,10 +60,7 @@ export const InspectContent = ({ return null; } - let errors = data?.errors; - if (!errors?.length && data?.error) { - errors = [data.error]; - } + let errors = getErrors(data); // Validate that the active tab is actually valid and allowed let activeTab = currentTab; @@ -114,6 +120,22 @@ export const InspectContent = ({ ); }; +// This will combine +function getErrors(data: PanelData | undefined): DataQueryError[] { + let errors = data?.errors ?? []; + if (data?.error && !errors.includes(data.error)) { + errors = [data.error, ...errors]; + } + if (!errors.length && data?.state === LoadingState.Error) { + return [ + { + message: 'Error loading data', + }, + ]; + } + return errors; +} + function formatStats(data: PanelData) { const { request } = data; diff --git a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx index 9aa2309e02d..57a31c2a540 100644 --- a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx +++ b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx @@ -28,16 +28,16 @@ export interface ConnectedProps { export type Props = OwnProps & ConnectedProps; const PanelInspectorUnconnected = ({ panel, dashboard, plugin }: Props) => { + const location = useLocation(); + const defaultTab = new URLSearchParams(location.search).get('inspectTab') as InspectTab; const [dataOptions, setDataOptions] = useState({ - withTransforms: false, + withTransforms: defaultTab === InspectTab.Error, withFieldConfig: true, }); - const location = useLocation(); - const { data, isLoading, error } = usePanelLatestData(panel, dataOptions, false); + const { data, isLoading, hasError } = usePanelLatestData(panel, dataOptions, true); const metaDs = useDatasourceMetadata(data); - const tabs = useInspectTabs(panel, dashboard, plugin, error, metaDs); - const defaultTab = new URLSearchParams(location.search).get('inspectTab') as InspectTab; + const tabs = useInspectTabs(panel, dashboard, plugin, hasError, metaDs); const onClose = () => { locationService.partial({ diff --git a/public/app/features/dashboard/components/Inspector/hooks.ts b/public/app/features/dashboard/components/Inspector/hooks.ts index 3e4291f3d65..59cc4a2284e 100644 --- a/public/app/features/dashboard/components/Inspector/hooks.ts +++ b/public/app/features/dashboard/components/Inspector/hooks.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import useAsync from 'react-use/lib/useAsync'; -import { DataQueryError, DataSourceApi, PanelData, PanelPlugin } from '@grafana/data'; +import { DataSourceApi, PanelData, PanelPlugin } from '@grafana/data'; import { getDataSourceSrv } from '@grafana/runtime'; import { t } from 'app/core/internationalization'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; @@ -42,7 +42,7 @@ export const useInspectTabs = ( panel: PanelModel, dashboard: DashboardModel, plugin: PanelPlugin | undefined | null, - error?: DataQueryError, + hasError?: boolean, metaDs?: DataSourceApi ) => { return useMemo(() => { @@ -58,7 +58,7 @@ export const useInspectTabs = ( tabs.push({ label: t('dashboard.inspect.json-tab', 'JSON'), value: InspectTab.JSON }); - if (error && error.message) { + if (hasError) { tabs.push({ label: t('dashboard.inspect.error-tab', 'Error'), value: InspectTab.Error }); } @@ -66,5 +66,5 @@ export const useInspectTabs = ( tabs.push({ label: t('dashboard.inspect.query-tab', 'Query'), value: InspectTab.Query }); } return tabs; - }, [plugin, metaDs, dashboard, error]); + }, [plugin, metaDs, dashboard, hasError]); }; diff --git a/public/app/features/dashboard/components/PanelEditor/usePanelLatestData.ts b/public/app/features/dashboard/components/PanelEditor/usePanelLatestData.ts index 5d2f455de61..360b00c1a8a 100644 --- a/public/app/features/dashboard/components/PanelEditor/usePanelLatestData.ts +++ b/public/app/features/dashboard/components/PanelEditor/usePanelLatestData.ts @@ -1,14 +1,14 @@ import { useEffect, useRef, useState } from 'react'; import { Unsubscribable } from 'rxjs'; -import { DataQueryError, LoadingState, PanelData } from '@grafana/data'; +import { LoadingState, PanelData } from '@grafana/data'; import { GetDataOptions } from '../../../query/state/PanelQueryRunner'; import { PanelModel } from '../../state'; interface UsePanelLatestData { data?: PanelData; - error?: DataQueryError; + hasError: boolean; isLoading: boolean; hasSeries: boolean; } @@ -63,8 +63,10 @@ export const usePanelLatestData = ( return { data: latestData, - error: latestData && latestData.error, isLoading: latestData?.state === LoadingState.Loading, hasSeries: latestData ? !!latestData.series : false, + hasError: Boolean( + latestData && (latestData.error || latestData?.errors?.length || latestData.state === LoadingState.Error) + ), }; }; diff --git a/public/app/features/inspector/InspectErrorTab.tsx b/public/app/features/inspector/InspectErrorTab.tsx index 97d516aad9b..554d79b89dc 100644 --- a/public/app/features/inspector/InspectErrorTab.tsx +++ b/public/app/features/inspector/InspectErrorTab.tsx @@ -67,7 +67,7 @@ export const InspectErrorTab = ({ errors }: InspectErrorTabProps) => { return ( <> {errors.map((error, index) => ( - + {renderError(error)} ))} diff --git a/public/app/features/query/state/PanelQueryRunner.ts b/public/app/features/query/state/PanelQueryRunner.ts index 7ffe08a09ba..79f9a50d00a 100644 --- a/public/app/features/query/state/PanelQueryRunner.ts +++ b/public/app/features/query/state/PanelQueryRunner.ts @@ -225,7 +225,7 @@ export class PanelQueryRunner { return of({ ...data, state: LoadingState.Error, - error: toDataQueryError(err), + errors: [toDataQueryError(err)], }); }) );