Alerting: List V2 - Improve error handling (#106282)
* Add error badge to the DataSourceSection component * Fix lint * Update translations * Fix error propagation from featureDiscoveryApi * Pass errors to GMA loader
This commit is contained in:
@@ -59,24 +59,28 @@ export const featureDiscoveryApi = alertingApi.injectEndpoints({
|
||||
return { error: new Error(`Missing data source configuration for ${rulesSourceIdentifier}`) };
|
||||
}
|
||||
|
||||
const features = await discoverFeaturesByUid(dataSourceSettings.uid);
|
||||
try {
|
||||
const features = await discoverFeaturesByUid(dataSourceSettings.uid);
|
||||
|
||||
const rulerConfig = features.features.rulerApiEnabled
|
||||
? ({
|
||||
dataSourceName: dataSourceSettings.name,
|
||||
dataSourceUid: dataSourceSettings.uid,
|
||||
apiVersion: features.application === PromApplication.Cortex ? 'legacy' : 'config',
|
||||
} satisfies RulerDataSourceConfig)
|
||||
: undefined;
|
||||
const rulerConfig = features.features.rulerApiEnabled
|
||||
? ({
|
||||
dataSourceName: dataSourceSettings.name,
|
||||
dataSourceUid: dataSourceSettings.uid,
|
||||
apiVersion: features.application === PromApplication.Cortex ? 'legacy' : 'config',
|
||||
} satisfies RulerDataSourceConfig)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
data: {
|
||||
name: dataSourceSettings.name,
|
||||
uid: dataSourceSettings.uid,
|
||||
application: features.application,
|
||||
rulerConfig,
|
||||
} satisfies RulesSourceFeatures,
|
||||
};
|
||||
return {
|
||||
data: {
|
||||
name: dataSourceSettings.name,
|
||||
uid: dataSourceSettings.uid,
|
||||
application: features.application,
|
||||
rulerConfig,
|
||||
} satisfies RulesSourceFeatures,
|
||||
};
|
||||
} catch (error) {
|
||||
return { error: error };
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -38,7 +38,7 @@ export function GrafanaDataSourceLoader() {
|
||||
}
|
||||
|
||||
function DataSourceLoader({ rulesSourceIdentifier }: DataSourceLoaderProps) {
|
||||
const { data: dataSourceInfo, isLoading } = useDiscoverDsFeaturesQuery({ uid: rulesSourceIdentifier.uid });
|
||||
const { data: dataSourceInfo, isLoading, error } = useDiscoverDsFeaturesQuery({ uid: rulesSourceIdentifier.uid });
|
||||
|
||||
const { uid, name } = rulesSourceIdentifier;
|
||||
|
||||
@@ -46,6 +46,10 @@ function DataSourceLoader({ rulesSourceIdentifier }: DataSourceLoaderProps) {
|
||||
return <DataSourceSection loader={<Skeleton width={250} height={16} />} uid={uid} name={name} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <DataSourceSection error={error} uid={uid} name={name} />;
|
||||
}
|
||||
|
||||
// 2. grab prometheus rule groups with max_groups if supported
|
||||
if (dataSourceInfo) {
|
||||
return (
|
||||
|
||||
@@ -41,7 +41,7 @@ export function PaginatedDataSourceLoader({ rulesSourceIdentifier, application }
|
||||
};
|
||||
}, [groupsGenerator]);
|
||||
|
||||
const { isLoading, groups, hasMoreGroups, fetchMoreGroups } = useLazyLoadPrometheusGroups(
|
||||
const { isLoading, groups, hasMoreGroups, fetchMoreGroups, error } = useLazyLoadPrometheusGroups(
|
||||
groupsGenerator.current,
|
||||
DATA_SOURCE_GROUP_PAGE_SIZE
|
||||
);
|
||||
@@ -50,7 +50,7 @@ export function PaginatedDataSourceLoader({ rulesSourceIdentifier, application }
|
||||
const groupsByNamespace = useMemo(() => groupBy(groups, 'file'), [groups]);
|
||||
|
||||
return (
|
||||
<DataSourceSection name={name} application={application} uid={uid} isLoading={isLoading}>
|
||||
<DataSourceSection name={name} application={application} uid={uid} isLoading={isLoading} error={error}>
|
||||
<Stack direction="column" gap={0}>
|
||||
{Object.entries(groupsByNamespace).map(([namespace, groups]) => (
|
||||
<ListSection
|
||||
|
||||
@@ -33,7 +33,7 @@ export function PaginatedGrafanaLoader() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { isLoading, groups, hasMoreGroups, fetchMoreGroups } = useLazyLoadPrometheusGroups(
|
||||
const { isLoading, groups, hasMoreGroups, fetchMoreGroups, error } = useLazyLoadPrometheusGroups(
|
||||
groupsGenerator.current,
|
||||
GRAFANA_GROUP_PAGE_SIZE
|
||||
);
|
||||
@@ -42,7 +42,13 @@ export function PaginatedGrafanaLoader() {
|
||||
const hasNoRules = isEmpty(groups) && !isLoading;
|
||||
|
||||
return (
|
||||
<DataSourceSection name="Grafana" application="grafana" uid={GrafanaRulesSourceSymbol} isLoading={isLoading}>
|
||||
<DataSourceSection
|
||||
name="Grafana"
|
||||
application="grafana"
|
||||
uid={GrafanaRulesSourceSymbol}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
>
|
||||
<Stack direction="column" gap={0}>
|
||||
{Object.entries(groupsByFolder).map(([folderUid, groups]) => {
|
||||
// Groups are grouped by folder, so we can use the first group to get the folder name
|
||||
|
||||
@@ -4,13 +4,13 @@ import { useToggle } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, useTranslate } from '@grafana/i18n';
|
||||
import { IconButton, LinkButton, Stack, Text, useStyles2 } from '@grafana/ui';
|
||||
import { Button, IconButton, LinkButton, Stack, Text, Toggletip, useStyles2 } from '@grafana/ui';
|
||||
import { GrafanaRulesSourceSymbol, RulesSourceIdentifier } from 'app/types/unified-alerting';
|
||||
import { RulesSourceApplication } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { Spacer } from '../../components/Spacer';
|
||||
import { WithReturnButton } from '../../components/WithReturnButton';
|
||||
import { isAdmin } from '../../utils/misc';
|
||||
import { isAdmin, stringifyErrorLike } from '../../utils/misc';
|
||||
|
||||
import { DataSourceIcon } from './Namespace';
|
||||
import { LoadingIndicator } from './RuleGroup';
|
||||
@@ -22,6 +22,7 @@ export interface DataSourceSectionProps extends PropsWithChildren {
|
||||
application?: RulesSourceApplication;
|
||||
isLoading?: boolean;
|
||||
description?: ReactNode;
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export const DataSourceSection = ({
|
||||
@@ -30,6 +31,7 @@ export const DataSourceSection = ({
|
||||
application,
|
||||
children,
|
||||
loader,
|
||||
error,
|
||||
isLoading = false,
|
||||
description = null,
|
||||
}: DataSourceSectionProps) => {
|
||||
@@ -60,6 +62,7 @@ export const DataSourceSection = ({
|
||||
name={isCollapsed ? 'angle-right' : 'angle-down'}
|
||||
onClick={toggleCollapsed}
|
||||
aria-label={t('common.collapse', 'Collapse')}
|
||||
disabled={Boolean(error)}
|
||||
/>
|
||||
{application && <DataSourceIcon application={application} />}
|
||||
|
||||
@@ -71,6 +74,18 @@ export const DataSourceSection = ({
|
||||
{'·'} {description}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{Boolean(error) && (
|
||||
<Toggletip
|
||||
title={t('alerting.rule-list.ds-error.title', 'Cannot load rules for this datasource')}
|
||||
content={<div>{stringifyErrorLike(error)}</div>}
|
||||
>
|
||||
<Button variant="destructive" fill="outline" size="sm" icon="exclamation-circle">
|
||||
<Trans i18nKey="alerting.rule-list.error-button">Error</Trans>
|
||||
</Button>
|
||||
</Toggletip>
|
||||
)}
|
||||
|
||||
<Spacer />
|
||||
{configureLink && (
|
||||
<WithReturnButton
|
||||
|
||||
@@ -51,6 +51,7 @@ export function useLazyLoadPrometheusGroups<TGroup extends PromRuleGroupDTO>(
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
error: groupsRequestState.error,
|
||||
groups,
|
||||
hasMoreGroups: !isLoading && hasMoreGroups,
|
||||
fetchMoreGroups,
|
||||
|
||||
@@ -2364,11 +2364,15 @@
|
||||
"cannot-load-rule-details-for": "Cannot load rule details for UID {{uid}}",
|
||||
"configure-datasource": "Configure",
|
||||
"draft-new-rule": "Draft a new rule",
|
||||
"ds-error": {
|
||||
"title": "Cannot load rules for this datasource"
|
||||
},
|
||||
"ds-error-boundary": {
|
||||
"description": "Check the data source configuration. Does the data source support Prometheus API?",
|
||||
"title": "Unable to load rules from this data source"
|
||||
},
|
||||
"empty-data-source": "No rules found",
|
||||
"error-button": "Error",
|
||||
"filter-view": {
|
||||
"cancel-search": "Cancel search",
|
||||
"no-more-results": "No more results – found {{numberOfRules}} rules",
|
||||
|
||||
Reference in New Issue
Block a user