diff --git a/public/app/features/alerting/unified/components/rules/RulesTable.tsx b/public/app/features/alerting/unified/components/rules/RulesTable.tsx index 44d30d72a15..98412a1aaba 100644 --- a/public/app/features/alerting/unified/components/rules/RulesTable.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesTable.tsx @@ -1,4 +1,5 @@ import { css, cx } from '@emotion/css'; +import { isBefore, formatDuration } from 'date-fns'; import React, { FC, useCallback, useMemo } from 'react'; import { @@ -16,8 +17,7 @@ import { CombinedRule } from 'app/types/unified-alerting'; import { DEFAULT_PER_PAGE_PAGINATION } from '../../../../../core/constants'; import { useHasRuler } from '../../hooks/useHasRuler'; import { Annotation } from '../../utils/constants'; -import { isGrafanaRulerRule } from '../../utils/rules'; -import { isNullDate } from '../../utils/time'; +import { isGrafanaRulerRule, isGrafanaRulerRulePaused } from '../../utils/rules'; import { DynamicTable, DynamicTableColumnProps, DynamicTableItemProps } from '../DynamicTable'; import { DynamicTableWithGuidelines } from '../DynamicTableWithGuidelines'; import { ProvisioningBadge } from '../Provisioning'; @@ -116,21 +116,30 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean, showNe const { hasRuler, rulerRulesLoaded } = useHasRuler(); const calculateNextEvaluationDate = useCallback((rule: CombinedRule) => { - const isValidLastEvaluation = - rule.promRule?.lastEvaluation && - !isNullDate(rule.promRule.lastEvaluation) && - isValidDate(rule.promRule.lastEvaluation); + const isValidLastEvaluation = rule.promRule?.lastEvaluation && isValidDate(rule.promRule.lastEvaluation); const isValidIntervalDuration = rule.group.interval && isValidDuration(rule.group.interval); - if (!isValidLastEvaluation || !isValidIntervalDuration) { + if (!isValidLastEvaluation || !isValidIntervalDuration || isGrafanaRulerRulePaused(rule)) { return; } - const lastEvaluationDate = Date.parse(rule.promRule?.lastEvaluation || ''); const intervalDuration = parseDuration(rule.group.interval!); + const lastEvaluationDate = Date.parse(rule.promRule?.lastEvaluation || ''); const nextEvaluationDate = addDurationToDate(lastEvaluationDate, intervalDuration); + + //when `nextEvaluationDate` is a past date it means lastEvaluation was more than one evaluation interval ago. + //in this case we use the interval value to show a more generic estimate. + //See https://github.com/grafana/grafana/issues/65125 + const isPastDate = isBefore(nextEvaluationDate, new Date()); + if (isPastDate) { + return { + humanized: `within ${formatDuration(intervalDuration)}`, + fullDate: `within ${formatDuration(intervalDuration)}`, + }; + } + return { - humanized: dateTime(nextEvaluationDate).locale('en').fromNow(true), + humanized: `in ${dateTime(nextEvaluationDate).locale('en').fromNow(true)}`, fullDate: dateTimeFormat(nextEvaluationDate, { format: 'YYYY-MM-DD HH:mm:ss' }), }; }, []); @@ -212,9 +221,9 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean, showNe renderCell: ({ data: rule }) => { const nextEvalInfo = calculateNextEvaluationDate(rule); return ( - nextEvalInfo?.fullDate && ( + nextEvalInfo && ( - in {nextEvalInfo?.humanized} + {nextEvalInfo?.humanized} ) ); diff --git a/public/app/features/alerting/unified/utils/rules.ts b/public/app/features/alerting/unified/utils/rules.ts index c44ba0869d4..7863697607e 100644 --- a/public/app/features/alerting/unified/utils/rules.ts +++ b/public/app/features/alerting/unified/utils/rules.ts @@ -5,6 +5,7 @@ import { Alert, AlertingRule, CloudRuleIdentifier, + CombinedRule, CombinedRuleGroup, GrafanaRuleIdentifier, PrometheusRuleIdentifier, @@ -52,6 +53,10 @@ export function isGrafanaRulerRule(rule?: RulerRuleDTO): rule is RulerGrafanaRul return typeof rule === 'object' && 'grafana_alert' in rule; } +export function isGrafanaRulerRulePaused(rule: CombinedRule) { + return rule.rulerRule && isGrafanaRulerRule(rule.rulerRule) && Boolean(rule.rulerRule.grafana_alert.is_paused); +} + export function alertInstanceKey(alert: Alert): string { return JSON.stringify(alert.labels); }