From 73ae4a51b2620fdd604c3792b6688ea0a288a726 Mon Sep 17 00:00:00 2001 From: Gilles De Mey Date: Tue, 19 Nov 2024 12:32:45 +0100 Subject: [PATCH] Alerting: Silence drawer being forcefully closed (#96579) Co-authored-by: Tom Ratcliffe --- .../components/rules/RuleListStateView.tsx | 51 ++++++++++++------- .../unified/components/rules/RulesTable.tsx | 39 +++++++------- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/public/app/features/alerting/unified/components/rules/RuleListStateView.tsx b/public/app/features/alerting/unified/components/rules/RuleListStateView.tsx index 69faaec1d5c..69e4bc3f604 100644 --- a/public/app/features/alerting/unified/components/rules/RuleListStateView.tsx +++ b/public/app/features/alerting/unified/components/rules/RuleListStateView.tsx @@ -1,16 +1,18 @@ -import { css } from '@emotion/css'; import { useMemo } from 'react'; +import { useMeasure } from 'react-use'; -import { GrafanaTheme2 } from '@grafana/data'; -import { Counter, Pagination, Stack, useStyles2 } from '@grafana/ui'; +import { Counter, LoadingBar, Pagination, Stack } from '@grafana/ui'; import { DEFAULT_PER_PAGE_PAGINATION } from 'app/core/constants'; import { CombinedRule, CombinedRuleNamespace } from 'app/types/unified-alerting'; import { PromAlertingRuleState } from 'app/types/unified-alerting-dto'; import { usePagination } from '../../hooks/usePagination'; +import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; import { AlertRuleListItem } from '../../rule-list/components/AlertRuleListItem'; import { ListSection } from '../../rule-list/components/ListSection'; +import { getRulesDataSources, GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; import { createViewLink } from '../../utils/misc'; +import { isAsyncRequestStatePending } from '../../utils/redux'; import { hashRule } from '../../utils/rule-id'; import { getRulePluginOrigin, isAlertingRule, isProvisionedRule } from '../../utils/rules'; import { calculateTotalInstances } from '../rule-viewer/RuleViewer'; @@ -24,7 +26,9 @@ interface Props { type GroupedRules = Map; export const RuleListStateView = ({ namespaces }: Props) => { - const styles = useStyles2(getStyles); + const [ref, { width }] = useMeasure(); + + const isLoading = useDataSourcesLoadingState(); const groupedRules = useMemo(() => { const result: GroupedRules = new Map([ @@ -54,10 +58,13 @@ export const RuleListStateView = ({ namespaces }: Props) => { const entries = groupedRules.entries(); return ( -
    - {Array.from(entries).map(([state, rules]) => ( - - ))} +
      + {isLoading && } + + {Array.from(entries).map(([state, rules]) => ( + + ))} +
    ); }; @@ -71,7 +78,7 @@ const STATE_TITLES: Record = { const RulesByState = ({ state, rules }: { state: PromAlertingRuleState; rules: CombinedRule[] }) => { const { page, pageItems, numberOfPages, onPageChange } = usePagination(rules, 1, DEFAULT_PER_PAGE_PAGINATION); - const isNotFiringState = state !== PromAlertingRuleState.Firing; + const isFiringState = state !== PromAlertingRuleState.Firing; const hasRulesMatchingState = rules.length > 0; return ( @@ -82,7 +89,7 @@ const RulesByState = ({ state, rules }: { state: PromAlertingRuleState; rules: C } - collapsed={isNotFiringState || hasRulesMatchingState} + collapsed={isFiringState || hasRulesMatchingState} pagination={ ({ - columnStack: css({ - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(1), - }), -}); +function useDataSourcesLoadingState() { + const promRules = useUnifiedAlertingSelector((state) => state.promRules); + const rulesDataSources = useMemo(getRulesDataSources, []); + + const grafanaLoading = useUnifiedAlertingSelector((state) => { + const promLoading = isAsyncRequestStatePending(state.promRules[GRAFANA_RULES_SOURCE_NAME]); + const rulerLoading = isAsyncRequestStatePending(state.rulerRules[GRAFANA_RULES_SOURCE_NAME]); + + return promLoading || rulerLoading; + }); + + const externalDataSourcesLoading = rulesDataSources.some((ds) => isAsyncRequestStatePending(promRules[ds.name])); + + const loading = grafanaLoading || externalDataSourcesLoading; + + return loading; +} diff --git a/public/app/features/alerting/unified/components/rules/RulesTable.tsx b/public/app/features/alerting/unified/components/rules/RulesTable.tsx index 15f5bffb04b..bfcc8982e70 100644 --- a/public/app/features/alerting/unified/components/rules/RulesTable.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesTable.tsx @@ -3,7 +3,7 @@ import { useEffect, useMemo } from 'react'; import Skeleton from 'react-loading-skeleton'; import { GrafanaTheme2 } from '@grafana/data'; -import { LoadingPlaceholder, Pagination, Tooltip, useStyles2 } from '@grafana/ui'; +import { Pagination, Tooltip, useStyles2 } from '@grafana/ui'; import { CombinedRule } from 'app/types/unified-alerting'; import { DEFAULT_PER_PAGE_PAGINATION } from '../../../../../core/constants'; @@ -63,18 +63,9 @@ export const RulesTable = ({ const { pageItems, page, numberOfPages, onPageChange } = usePagination(rules, 1, DEFAULT_PER_PAGE_PAGINATION); - const [lazyLoadRules, { result: rulesWithRulerDefinitions, status: rulerRulesLoadingStatus }] = - useLazyLoadRulerRules(pageItems); - const isLoadingRulerGroup = useMemo( - () => !rulerRulesLoadingStatus || rulerRulesLoadingStatus === 'loading', - [rulerRulesLoadingStatus] - ); + const { result: rulesWithRulerDefinitions, status: rulerRulesLoadingStatus } = useLazyLoadRulerRules(pageItems); - useEffect(() => { - if (pageItems.length > 0) { - lazyLoadRules.execute(); - } - }, [lazyLoadRules, pageItems, rulerRulesLoadingStatus]); + const isLoadingRulerGroup = rulerRulesLoadingStatus === 'loading'; const items = useMemo((): RuleTableItemProps[] => { return rulesWithRulerDefinitions.map((rule, ruleIdx) => { @@ -91,10 +82,6 @@ export const RulesTable = ({ return
    {emptyMessage}
    ; } - if (isLoadingRulerGroup) { - return ; - } - const TableComponent = showGuidelines ? DynamicTableWithGuidelines : DynamicTable; return ( @@ -127,11 +114,8 @@ function useLazyLoadRulerRules(rules: CombinedRule[]) { const [fetchRulerRuleGroup] = useLazyGetRuleGroupForNamespaceQuery(); const [fetchDsFeatures] = useLazyDiscoverDsFeaturesQuery(); - return useAsync(async () => { - if (!prometheusRulesPrimary) { - return rules; - } - return Promise.all( + const [actions, state] = useAsync(async () => { + const result = Promise.all( rules.map(async (rule) => { const dsFeatures = await fetchDsFeatures( { rulesSourceName: getRulesSourceName(rule.namespace.rulesSource) }, @@ -156,7 +140,20 @@ function useLazyLoadRulerRules(rules: CombinedRule[]) { return rule; }) ); + return result; }, rules); + + useEffect(() => { + if (prometheusRulesPrimary) { + actions.execute(); + } else { + // We need to reset the actions to update the rules if they changed + // Otherwise useAsync acts like a cache and always return the first rules passed to it + actions.reset(); + } + }, [rules, actions]); + + return state; } export const getStyles = (theme: GrafanaTheme2) => ({