mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Fix loading states (#100641)
This commit is contained in:
parent
9d68c4f665
commit
ba3a90d8fd
@ -36,7 +36,7 @@ const RuleViewer = (): JSX.Element => {
|
||||
}, [id]);
|
||||
|
||||
// we then fetch the rule from the correct API endpoint(s)
|
||||
const { loading, error, result: rule } = useCombinedRule({ ruleIdentifier: identifier, limitAlerts });
|
||||
const { loading, error, result: rule, uninitialized } = useCombinedRule({ ruleIdentifier: identifier, limitAlerts });
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
@ -46,7 +46,7 @@ const RuleViewer = (): JSX.Element => {
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
if (loading || uninitialized) {
|
||||
return (
|
||||
<AlertingPageWrapper pageNav={defaultPageNav} navId="alert-list" isLoading={true}>
|
||||
<></>
|
||||
|
@ -80,6 +80,7 @@ interface RequestState<T> {
|
||||
result?: T;
|
||||
loading: boolean;
|
||||
error?: unknown;
|
||||
uninitialized: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
@ -99,6 +100,7 @@ export function useCombinedRule({ ruleIdentifier, limitAlerts }: Props): Request
|
||||
loading: isLoadingRuleLocation,
|
||||
error: ruleLocationError,
|
||||
result: ruleLocation,
|
||||
uninitialized,
|
||||
} = useRuleLocation(ruleIdentifier);
|
||||
|
||||
const {
|
||||
@ -125,7 +127,12 @@ export function useCombinedRule({ ruleIdentifier, limitAlerts }: Props): Request
|
||||
|
||||
const [
|
||||
fetchRulerRuleGroup,
|
||||
{ currentData: rulerRuleGroup, isLoading: isLoadingRulerGroup, error: rulerRuleGroupError },
|
||||
{
|
||||
currentData: rulerRuleGroup,
|
||||
isLoading: isLoadingRulerGroup,
|
||||
error: rulerRuleGroupError,
|
||||
isUninitialized: ruleGroupUninitialized,
|
||||
},
|
||||
] = alertRuleApi.endpoints.getRuleGroupForNamespace.useLazyQuery();
|
||||
|
||||
useEffect(() => {
|
||||
@ -158,9 +165,10 @@ export function useCombinedRule({ ruleIdentifier, limitAlerts }: Props): Request
|
||||
}, [ruleIdentifier, ruleSourceName, promRuleNs, rulerRuleGroup, ruleSource, ruleLocation, namespaceName]);
|
||||
|
||||
return {
|
||||
loading: isLoadingDsFeatures || isLoadingPromRules || isLoadingRulerGroup,
|
||||
loading: isLoadingDsFeatures || isLoadingPromRules || isLoadingRulerGroup || ruleGroupUninitialized,
|
||||
error: ruleLocationError ?? promRuleNsError ?? rulerRuleGroupError,
|
||||
result: rule,
|
||||
uninitialized,
|
||||
};
|
||||
}
|
||||
|
||||
@ -187,17 +195,19 @@ export function useRuleLocation(ruleIdentifier: RuleIdentifier): RequestState<Ru
|
||||
ruleName: ruleIdentifier.ruleName,
|
||||
},
|
||||
loading: false,
|
||||
uninitialized: isUninitialized,
|
||||
};
|
||||
}
|
||||
|
||||
if (isGrafanaRuleIdentifier(ruleIdentifier)) {
|
||||
if (isLoading || isUninitialized) {
|
||||
return { loading: true };
|
||||
if (isLoading) {
|
||||
return { loading: isLoading, uninitialized: isUninitialized };
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return { loading: false, error };
|
||||
return { loading: false, error, uninitialized: isUninitialized };
|
||||
}
|
||||
|
||||
if (currentData) {
|
||||
return {
|
||||
result: {
|
||||
@ -207,6 +217,7 @@ export function useRuleLocation(ruleIdentifier: RuleIdentifier): RequestState<Ru
|
||||
ruleName: currentData.grafana_alert.title,
|
||||
},
|
||||
loading: false,
|
||||
uninitialized: isUninitialized,
|
||||
};
|
||||
}
|
||||
|
||||
@ -214,14 +225,16 @@ export function useRuleLocation(ruleIdentifier: RuleIdentifier): RequestState<Ru
|
||||
return {
|
||||
loading: false,
|
||||
error: new Error(`Unable to obtain rule location for rule ${ruleIdentifier.uid}`),
|
||||
uninitialized: isUninitialized,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
loading: false,
|
||||
uninitialized: isUninitialized,
|
||||
error: new Error('Unsupported rule identifier'),
|
||||
};
|
||||
}, [ruleIdentifier, isLoading, isUninitialized, error, currentData]);
|
||||
}, [ruleIdentifier, isUninitialized, isLoading, error, currentData]);
|
||||
}
|
||||
|
||||
function getRulesSourceFromIdentifier(ruleIdentifier: RuleIdentifier): RulesSource | undefined {
|
||||
@ -249,16 +262,12 @@ export function useRuleWithLocation({
|
||||
loading: isLoadingRuleLocation,
|
||||
error: ruleLocationError,
|
||||
result: ruleLocation,
|
||||
uninitialized,
|
||||
} = useRuleLocation(ruleIdentifier);
|
||||
|
||||
const [
|
||||
fetchRulerRuleGroup,
|
||||
{
|
||||
currentData: rulerRuleGroup,
|
||||
isLoading: isLoadingRulerGroup,
|
||||
isUninitialized: isUninitializedRulerGroup,
|
||||
error: rulerRuleGroupError,
|
||||
},
|
||||
{ currentData: rulerRuleGroup, isLoading: isLoadingRulerGroup, error: rulerRuleGroupError },
|
||||
] = alertRuleApi.endpoints.getRuleGroupForNamespace.useLazyQuery();
|
||||
|
||||
useEffect(() => {
|
||||
@ -297,9 +306,10 @@ export function useRuleWithLocation({
|
||||
}, [ruleIdentifier, rulerRuleGroup, ruleSource, ruleLocation]);
|
||||
|
||||
return {
|
||||
loading: isLoadingRuleLocation || isLoadingDsFeatures || isLoadingRulerGroup || isUninitializedRulerGroup,
|
||||
loading: isLoadingRuleLocation || isLoadingDsFeatures || isLoadingRulerGroup,
|
||||
error: ruleLocationError ?? rulerRuleGroupError,
|
||||
result: ruleWithLocation,
|
||||
uninitialized,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -16,19 +16,22 @@ interface ResultBag {
|
||||
}
|
||||
|
||||
export function useIsRuleEditable(rulesSourceName: string, rule?: RulerRuleDTO): ResultBag {
|
||||
const { currentData: dsFeatures, isLoading } = featureDiscoveryApi.endpoints.discoverDsFeatures.useQuery({
|
||||
uid: getDatasourceAPIUid(rulesSourceName),
|
||||
});
|
||||
const { currentData: dsFeatures, isLoading: loadingDataSourceFeatures } =
|
||||
featureDiscoveryApi.endpoints.discoverDsFeatures.useQuery({
|
||||
uid: getDatasourceAPIUid(rulesSourceName),
|
||||
});
|
||||
|
||||
const folderUID = rule && isGrafanaRulerRule(rule) ? rule.grafana_alert.namespace_uid : undefined;
|
||||
|
||||
const rulePermission = getRulesPermissions(rulesSourceName);
|
||||
const { folder, loading } = useFolder(folderUID);
|
||||
|
||||
const { folder, loading: loadingFolder } = useFolder(folderUID);
|
||||
|
||||
if (!rule) {
|
||||
return { isEditable: false, isRemovable: false, loading: false };
|
||||
}
|
||||
|
||||
const loading = loadingFolder || loadingDataSourceFeatures;
|
||||
|
||||
// Grafana rules can be edited if user can edit the folder they're in
|
||||
// When RBAC is disabled access to a folder is the only requirement for managing rules
|
||||
// When RBAC is enabled the appropriate alerting permissions need to be met
|
||||
@ -39,13 +42,23 @@ export function useIsRuleEditable(rulesSourceName: string, rule?: RulerRuleDTO):
|
||||
);
|
||||
}
|
||||
|
||||
if (!folder) {
|
||||
// Loading or invalid folder UID
|
||||
// loading folder information
|
||||
if (loadingFolder) {
|
||||
return {
|
||||
isRulerAvailable: true,
|
||||
isEditable: false,
|
||||
isRemovable: false,
|
||||
loading,
|
||||
loading: true,
|
||||
};
|
||||
}
|
||||
|
||||
// invalid folder UID
|
||||
if (!folder) {
|
||||
return {
|
||||
isRulerAvailable: true,
|
||||
isEditable: false,
|
||||
isRemovable: false,
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -56,7 +69,7 @@ export function useIsRuleEditable(rulesSourceName: string, rule?: RulerRuleDTO):
|
||||
isRulerAvailable: true,
|
||||
isEditable: canEditGrafanaRules,
|
||||
isRemovable: canRemoveGrafanaRules,
|
||||
loading: loading || isLoading,
|
||||
loading: loading,
|
||||
};
|
||||
}
|
||||
|
||||
@ -69,6 +82,6 @@ export function useIsRuleEditable(rulesSourceName: string, rule?: RulerRuleDTO):
|
||||
isRulerAvailable,
|
||||
isEditable: canEditCloudRules && isRulerAvailable,
|
||||
isRemovable: canRemoveCloudRules && isRulerAvailable,
|
||||
loading: isLoading,
|
||||
loading: loading,
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Alert, LoadingPlaceholder } from '@grafana/ui';
|
||||
import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound';
|
||||
import { RuleIdentifier } from 'app/types/unified-alerting';
|
||||
|
||||
import { AlertWarning } from '../AlertWarning';
|
||||
@ -13,17 +14,21 @@ interface ExistingRuleEditorProps {
|
||||
}
|
||||
|
||||
export function ExistingRuleEditor({ identifier }: ExistingRuleEditorProps) {
|
||||
const ruleSourceName = ruleId.ruleIdentifierToRuleSourceName(identifier);
|
||||
|
||||
const {
|
||||
loading: loadingAlertRule,
|
||||
result: ruleWithLocation,
|
||||
error,
|
||||
uninitialized,
|
||||
} = useRuleWithLocation({ ruleIdentifier: identifier });
|
||||
|
||||
const ruleSourceName = ruleId.ruleIdentifierToRuleSourceName(identifier);
|
||||
|
||||
const { isEditable, loading: loadingEditable } = useIsRuleEditable(ruleSourceName, ruleWithLocation?.rule);
|
||||
|
||||
const loading = loadingAlertRule || loadingEditable;
|
||||
// the loading of the editable state only happens once we've got a rule with location loaded, so we set it to true by default here
|
||||
const loadingEditableState = Boolean(ruleWithLocation) ? loadingEditable : true;
|
||||
const loading = loadingAlertRule || loadingEditableState || uninitialized;
|
||||
const ruleNotFound = !Boolean(ruleWithLocation);
|
||||
|
||||
if (loading) {
|
||||
return <LoadingPlaceholder text="Loading rule..." />;
|
||||
@ -37,11 +42,11 @@ export function ExistingRuleEditor({ identifier }: ExistingRuleEditorProps) {
|
||||
);
|
||||
}
|
||||
|
||||
if (!ruleWithLocation) {
|
||||
return <AlertWarning title="Rule not found">Sorry! This rule does not exist.</AlertWarning>;
|
||||
if (ruleNotFound) {
|
||||
return <EntityNotFound entity="Rule" />;
|
||||
}
|
||||
|
||||
if (isEditable === false) {
|
||||
if (isEditable === false && !loadingEditable) {
|
||||
return <AlertWarning title="Cannot edit rule">Sorry! You do not have permission to edit this rule.</AlertWarning>;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user