mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: rewire expression references when queries are updated (#41478)
This commit is contained in:
parent
fb2436af18
commit
9199a8b800
@ -13,6 +13,7 @@ import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { QueryWrapper } from './QueryWrapper';
|
||||
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
||||
import { isExpressionQuery } from 'app/features/expressions/guards';
|
||||
import { queriesWithUpdatedReferences } from './util';
|
||||
|
||||
interface Props {
|
||||
// The query configuration
|
||||
@ -128,11 +129,16 @@ export class QueryRows extends PureComponent<Props, State> {
|
||||
onChangeQuery = (query: DataQuery, index: number) => {
|
||||
const { queries, onQueriesChange } = this.props;
|
||||
|
||||
// find what queries still have a reference to the old name
|
||||
const previousRefId = queries[index].refId;
|
||||
const newRefId = query.refId;
|
||||
|
||||
onQueriesChange(
|
||||
queries.map((item, itemIndex) => {
|
||||
queriesWithUpdatedReferences(queries, previousRefId, newRefId).map((item, itemIndex) => {
|
||||
if (itemIndex !== index) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
refId: query.refId,
|
||||
|
@ -0,0 +1,169 @@
|
||||
import { ClassicCondition, ExpressionQuery } from 'app/features/expressions/types';
|
||||
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
||||
import { queriesWithUpdatedReferences, updateMathExpressionRefs } from './util';
|
||||
|
||||
describe('rule-editor', () => {
|
||||
const dataSource: AlertQuery = {
|
||||
refId: 'A',
|
||||
datasourceUid: 'abc123',
|
||||
queryType: '',
|
||||
relativeTimeRange: {
|
||||
from: 600,
|
||||
to: 0,
|
||||
},
|
||||
model: {
|
||||
refId: 'A',
|
||||
},
|
||||
};
|
||||
|
||||
const classicCondition = {
|
||||
refId: 'B',
|
||||
datasourceUid: '-100',
|
||||
queryType: '',
|
||||
model: {
|
||||
refId: 'B',
|
||||
type: 'classic_conditions',
|
||||
datasource: {
|
||||
uid: '-100',
|
||||
type: 'grafana-expression',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
type: 'query',
|
||||
evaluator: {
|
||||
params: [3],
|
||||
type: 'gt',
|
||||
},
|
||||
operator: {
|
||||
type: 'and',
|
||||
},
|
||||
query: {
|
||||
params: ['A'],
|
||||
},
|
||||
reducer: {
|
||||
params: [],
|
||||
type: 'last',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const mathExpression = {
|
||||
refId: 'B',
|
||||
datasourceUid: '-100',
|
||||
queryType: '',
|
||||
model: {
|
||||
refId: 'B',
|
||||
type: 'math',
|
||||
datasource: {
|
||||
uid: '-100',
|
||||
type: 'grafana-expression',
|
||||
},
|
||||
conditions: [],
|
||||
expression: 'abs($A) + $A',
|
||||
},
|
||||
};
|
||||
|
||||
const reduceExpression = {
|
||||
refId: 'B',
|
||||
datasourceUid: '-100',
|
||||
queryType: '',
|
||||
model: {
|
||||
refId: 'B',
|
||||
type: 'reduce',
|
||||
datasource: {
|
||||
uid: '-100',
|
||||
type: 'grafana-expression',
|
||||
},
|
||||
conditions: [],
|
||||
reducer: 'mean',
|
||||
expression: 'A',
|
||||
},
|
||||
};
|
||||
|
||||
const resampleExpression = {
|
||||
refId: 'A',
|
||||
datasourceUid: '-100',
|
||||
model: {
|
||||
refId: 'A',
|
||||
type: 'resample',
|
||||
datasource: {
|
||||
type: '__expr__',
|
||||
uid: '__expr__',
|
||||
},
|
||||
conditions: [],
|
||||
downsampler: 'mean',
|
||||
upsampler: 'fillna',
|
||||
expression: 'A',
|
||||
window: '30m',
|
||||
},
|
||||
queryType: '',
|
||||
};
|
||||
|
||||
describe('rewires query names', () => {
|
||||
it('should rewire classic expressions', () => {
|
||||
const queries: AlertQuery[] = [dataSource, classicCondition];
|
||||
const rewiredQueries = queriesWithUpdatedReferences(queries, 'A', 'C');
|
||||
|
||||
const queryModel = rewiredQueries[1].model as ExpressionQuery;
|
||||
|
||||
const checkConditionParams = (condition: ClassicCondition) => {
|
||||
return expect(condition.query.params).toEqual(['C']);
|
||||
};
|
||||
|
||||
expect(queryModel.conditions?.every(checkConditionParams));
|
||||
});
|
||||
|
||||
it('should rewire math expressions', () => {
|
||||
const queries: AlertQuery[] = [dataSource, mathExpression];
|
||||
const rewiredQueries = queriesWithUpdatedReferences(queries, 'A', 'Query A');
|
||||
|
||||
const queryModel = rewiredQueries[1].model as ExpressionQuery;
|
||||
|
||||
expect(queryModel.expression).toBe('abs(${Query A}) + ${Query A}');
|
||||
});
|
||||
|
||||
it('should rewire reduce expressions', () => {
|
||||
const queries: AlertQuery[] = [dataSource, reduceExpression];
|
||||
const rewiredQueries = queriesWithUpdatedReferences(queries, 'A', 'C');
|
||||
|
||||
const queryModel = rewiredQueries[1].model as ExpressionQuery;
|
||||
expect(queryModel.expression).toBe('C');
|
||||
});
|
||||
|
||||
it('should rewire resample expressions', () => {
|
||||
const queries: AlertQuery[] = [dataSource, resampleExpression];
|
||||
const rewiredQueries = queriesWithUpdatedReferences(queries, 'A', 'C');
|
||||
|
||||
const queryModel = rewiredQueries[1].model as ExpressionQuery;
|
||||
expect(queryModel.expression).toBe('C');
|
||||
});
|
||||
|
||||
it('should rewire multiple expressions', () => {
|
||||
const queries: AlertQuery[] = [dataSource, mathExpression, resampleExpression];
|
||||
const rewiredQueries = queriesWithUpdatedReferences(queries, 'A', 'C');
|
||||
|
||||
expect(rewiredQueries[1].model as ExpressionQuery).toHaveProperty('expression', 'abs(${C}) + ${C}');
|
||||
expect(rewiredQueries[2].model as ExpressionQuery).toHaveProperty('expression', 'C');
|
||||
});
|
||||
|
||||
it('should skip if refs are identical', () => {
|
||||
const queries: AlertQuery[] = [dataSource, reduceExpression, mathExpression];
|
||||
const rewiredQueries = queriesWithUpdatedReferences(queries, 'A', 'A');
|
||||
|
||||
expect(rewiredQueries[0]).toEqual(queries[0]);
|
||||
expect(rewiredQueries[1]).toEqual(queries[1]);
|
||||
expect(rewiredQueries[2]).toEqual(queries[2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateMathExpressionRefs', () => {
|
||||
it('should rewire refs without brackets', () => {
|
||||
expect(updateMathExpressionRefs('abs($Foo) + $Foo', 'Foo', 'Bar')).toBe('abs(${Bar}) + ${Bar}');
|
||||
});
|
||||
it('should rewire refs with brackets', () => {
|
||||
expect(updateMathExpressionRefs('abs(${Foo}) + $Foo', 'Foo', 'Bar')).toBe('abs(${Bar}) + ${Bar}');
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,64 @@
|
||||
import { isExpressionQuery } from 'app/features/expressions/guards';
|
||||
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
||||
|
||||
export function queriesWithUpdatedReferences(
|
||||
queries: AlertQuery[],
|
||||
previousRefId: string,
|
||||
newRefId: string
|
||||
): AlertQuery[] {
|
||||
return queries.map((query) => {
|
||||
if (previousRefId === newRefId) {
|
||||
return query;
|
||||
}
|
||||
|
||||
if (!isExpressionQuery(query.model)) {
|
||||
return query;
|
||||
}
|
||||
|
||||
const isMathExpression = query.model.type === 'math';
|
||||
const isReduceExpression = query.model.type === 'reduce';
|
||||
const isResampleExpression = query.model.type === 'resample';
|
||||
const isClassicExpression = query.model.type === 'classic_conditions';
|
||||
|
||||
if (isMathExpression) {
|
||||
return {
|
||||
...query,
|
||||
model: {
|
||||
...query.model,
|
||||
expression: updateMathExpressionRefs(query.model.expression ?? '', previousRefId, newRefId),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (isResampleExpression || isReduceExpression) {
|
||||
return {
|
||||
...query,
|
||||
model: {
|
||||
...query.model,
|
||||
expression: newRefId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (isClassicExpression) {
|
||||
const conditions = query.model.conditions?.map((condition) => ({
|
||||
...condition,
|
||||
query: {
|
||||
...condition.query,
|
||||
params: condition.query.params.map((param: string) => (param === previousRefId ? newRefId : param)),
|
||||
},
|
||||
}));
|
||||
|
||||
return { ...query, model: { ...query.model, conditions } };
|
||||
}
|
||||
|
||||
return query;
|
||||
});
|
||||
}
|
||||
|
||||
export function updateMathExpressionRefs(expression: string, previousRefId: string, newRefId: string): string {
|
||||
const oldExpression = new RegExp('\\${?' + previousRefId + '}?', 'gm');
|
||||
const newExpression = '${' + newRefId + '}';
|
||||
|
||||
return expression.replace(oldExpression, newExpression);
|
||||
}
|
Loading…
Reference in New Issue
Block a user