diff --git a/public/app/features/alerting/unified/components/expressions/Expression.tsx b/public/app/features/alerting/unified/components/expressions/Expression.tsx index ec6f97af90a..cbf10a60dbc 100644 --- a/public/app/features/alerting/unified/components/expressions/Expression.tsx +++ b/public/app/features/alerting/unified/components/expressions/Expression.tsx @@ -63,7 +63,6 @@ export const Expression: FC = ({ const seriesCount = series.length; const alertCondition = isAlertCondition ?? false; - //const showSummary = isAlertCondition && hasResults; const groupedByState = { [PromAlertingRuleState.Firing]: series.filter((serie) => getSeriesValue(serie) !== 0), @@ -225,9 +224,15 @@ export const PreviewSummary: FC<{ firing: number; normal: number; isCondition: b seriesCount, }) => { const { mutedText } = useStyles2(getStyles); + + if (seriesCount === 0) { + return No series; + } + if (isCondition) { return {`${seriesCount} series: ${firing} firing, ${normal} normal`}; } + return {`${seriesCount} series`}; }; diff --git a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx index 3fb8af2ae56..dbb5206afab 100644 --- a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx @@ -25,7 +25,7 @@ import { NeedHelpInfo } from '../NeedHelpInfo'; import { QueryEditor } from '../QueryEditor'; import { RecordingRuleEditor } from '../RecordingRuleEditor'; import { RuleEditorSection } from '../RuleEditorSection'; -import { errorFromSeries, refIdExists } from '../util'; +import { errorFromSeries, refIdExists, findRenamedDataQueryReferences } from '../util'; import { CloudDataSourceSelector } from './CloudDataSourceSelector'; import { SmartAlertTypeDetector } from './SmartAlertTypeDetector'; @@ -161,15 +161,12 @@ export const QueryAndExpressionsStep = ({ editingExistingRule, onDataChange }: P dispatch(setDataQueries(updatedQueries)); dispatch(updateExpressionTimeRange()); - // check if we need to rewire expressions - updatedQueries.forEach((query, index) => { - const oldRefId = queries[index].refId; - const newRefId = query.refId; - if (oldRefId !== newRefId) { - dispatch(rewireExpressions({ oldRefId, newRefId })); - } - }); + // check if we need to rewire expressions (and which ones) + const [oldRefId, newRefId] = findRenamedDataQueryReferences(queries, updatedQueries); + if (oldRefId && newRefId) { + dispatch(rewireExpressions({ oldRefId, newRefId })); + } }, [queries, setValue, updateExpressionAndDatasource] ); @@ -539,9 +536,14 @@ const getStyles = (theme: GrafanaTheme2) => ({ const useSetExpressionAndDataSource = () => { const { setValue } = useFormContext(); + return (updatedQueries: AlertQuery[]) => { // update data source name and expression if it's been changed in the queries from the reducer when prom or loki query const query = updatedQueries[0]; + if (!query) { + return; + } + const dataSourceSettings = getDataSourceSrv().getInstanceSettings(query.datasourceUid); if (!dataSourceSettings) { throw new Error('The Data source has not been defined.'); diff --git a/public/app/features/alerting/unified/components/rule-editor/util.test.ts b/public/app/features/alerting/unified/components/rule-editor/util.test.ts index edb7407b3b9..fda4601ead3 100644 --- a/public/app/features/alerting/unified/components/rule-editor/util.test.ts +++ b/public/app/features/alerting/unified/components/rule-editor/util.test.ts @@ -4,6 +4,7 @@ import { AlertQuery } from 'app/types/unified-alerting-dto'; import { checkForPathSeparator, + findRenamedDataQueryReferences, getThresholdsForQueries, queriesWithUpdatedReferences, updateMathExpressionRefs, @@ -404,3 +405,33 @@ function createThresholdExample(thresholdType: string): AlertQuery[] { return [dataQuery, reduceExpression, thresholdExpression]; } + +describe('findRenamedReferences', () => { + it('should find the renamed ids', () => { + const previous = [{ refId: 'A' }, { refId: 'B' }, { refId: 'C' }] as AlertQuery[]; + const updated = [{ refId: 'FOO' }, { refId: 'B' }, { refId: 'C' }] as AlertQuery[]; + + expect(findRenamedDataQueryReferences(previous, updated)).toEqual(['A', 'FOO']); + }); + + it('should ignore expression queries', () => { + // @ts-expect-error + const previous = [ + { refId: 'A' }, + { refId: 'REDUCE', model: { datasource: '-100' } }, + { refId: 'MATH', model: { datasource: '-100' } }, + { refId: 'B' }, + { refId: 'C' }, + ] as AlertQuery[]; + + // @ts-expect-error + const updated = [ + { refId: 'FOO' }, + { refId: 'REDUCE', model: { datasource: '-100' } }, + { refId: 'B' }, + { refId: 'C' }, + ] as AlertQuery[]; + + expect(findRenamedDataQueryReferences(previous, updated)).toEqual(['A', 'FOO']); + }); +}); diff --git a/public/app/features/alerting/unified/components/rule-editor/util.ts b/public/app/features/alerting/unified/components/rule-editor/util.ts index 902f8d7276e..ab798d83996 100644 --- a/public/app/features/alerting/unified/components/rule-editor/util.ts +++ b/public/app/features/alerting/unified/components/rule-editor/util.ts @@ -1,3 +1,4 @@ +import { xor } from 'lodash'; import { ValidateResult } from 'react-hook-form'; import { DataFrame, ThresholdsConfig, ThresholdsMode, isTimeSeriesFrames, PanelData } from '@grafana/data'; @@ -303,3 +304,28 @@ export function translateRouteParamToRuleType(param = ''): RuleFormType { return RuleFormType.grafana; } + +/** + * This function finds what refIds have been updated given the previous Array of queries and an Array of updated data queries. + * All expression queries are discarded from the arrays, since we have separate handlers for those (see "onUpdateRefId") of the ExpressionEditor + * + * This code assumes not more than 1 query refId has changed per "onChangeQueries", + */ +export function findRenamedDataQueryReferences( + previousQueries: AlertQuery[], + updatedQueries: AlertQuery[] +): [string, string] { + const updatedDataQueries = updatedQueries + .filter((query) => !isExpressionQuery(query.model)) + .map((query) => query.refId); + const previousDataQueries = previousQueries + .filter((query) => !isExpressionQuery(query.model)) + .map((query) => query.refId); + + // given the following two arrays + // ['A', 'B', 'C'] and ['FOO', 'B' 'C'] + // the "xor" function will return ['A', 'FOO'] because those are not in both arrays + const [oldRefId, newRefId] = xor(previousDataQueries, updatedDataQueries); + + return [oldRefId, newRefId]; +}