diff --git a/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx b/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx index 77b1feb17cf..ccecd195812 100644 --- a/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx @@ -5,6 +5,7 @@ import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; import { DataQuery, DataSourceInstanceSettings, + getDefaultRelativeTimeRange, LoadingState, PanelData, RelativeTimeRange, @@ -14,6 +15,8 @@ import { import { config, getDataSourceSrv } from '@grafana/runtime'; import { Button, Card, Icon } from '@grafana/ui'; import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow'; +import { findDataSourceFromExpressionRecursive } from 'app/features/alerting/utils/dataSourceFromExpression'; +import { ExpressionDatasourceUID } from 'app/features/expressions/ExpressionDatasource'; import { isExpressionQuery } from 'app/features/expressions/guards'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { AlertDataQuery, AlertQuery } from 'app/types/unified-alerting-dto'; @@ -56,7 +59,17 @@ export class QueryRows extends PureComponent { onQueriesChange( queries.map((item, itemIndex) => { if (itemIndex !== index) { - return item; + // It's an expression , let's update the relativeTimeRange with this new relativeTimeRange + if (item.datasourceUid === ExpressionDatasourceUID) { + const dataSource = this.findDataSourceFromExpression(queries, item.model.expression); + const timeRangeToUpdate: RelativeTimeRange = + dataSource?.datasourceUid === queries[index].datasourceUid + ? timeRange + : dataSource?.relativeTimeRange ?? getDefaultRelativeTimeRange(); + return { ...item, relativeTimeRange: timeRangeToUpdate }; + } else { + return item; + } } return { ...item, @@ -116,6 +129,12 @@ export class QueryRows extends PureComponent { onQueriesChange(updatedQueries); }; + findDataSourceFromExpression(queries: AlertQuery[], expression: string | undefined): AlertQuery | null | undefined { + const firstReference = queries.find((alertQuery) => alertQuery.refId === expression); + const dataSource = firstReference && findDataSourceFromExpressionRecursive(queries, firstReference); + return dataSource; + } + onChangeQuery = (query: DataQuery, index: number) => { const { queries, onQueriesChange } = this.props; @@ -128,6 +147,11 @@ export class QueryRows extends PureComponent { if (itemIndex !== index) { return item; } + const dataSourceAlertQuery = this.findDataSourceFromExpression(queries, item.refId); + + const relativeTimeRange = dataSourceAlertQuery + ? dataSourceAlertQuery.relativeTimeRange + : getDefaultRelativeTimeRange(); return { ...item, @@ -138,6 +162,7 @@ export class QueryRows extends PureComponent { ...query, datasource: query.datasource!, }, + relativeTimeRange: relativeTimeRange, }; }) ); diff --git a/public/app/features/alerting/utils/dataSourceFromExpression.ts b/public/app/features/alerting/utils/dataSourceFromExpression.ts new file mode 100644 index 00000000000..9cb150ac2dd --- /dev/null +++ b/public/app/features/alerting/utils/dataSourceFromExpression.ts @@ -0,0 +1,34 @@ +import { ExpressionDatasourceUID } from 'app/features/expressions/ExpressionDatasource'; +import { AlertQuery } from 'app/types/unified-alerting-dto'; + +export const hasCyclicalReferences = (queries: AlertQuery[]) => { + try { + JSON.stringify(queries); + return false; + } catch (e) { + return true; + } +}; + +export const findDataSourceFromExpressionRecursive = ( + queries: AlertQuery[], + alertQuery: AlertQuery +): AlertQuery | null | undefined => { + //Check if this is not cyclical structre + if (hasCyclicalReferences(queries)) { + return null; + } + // We have the data source in this dataQuery + if (alertQuery.datasourceUid !== ExpressionDatasourceUID) { + return alertQuery; + } + // alertQuery it's an expression, we have to traverse all the tree up to the data source + else { + const alertQueryReferenced = queries.find((alertQuery_) => alertQuery_.refId === alertQuery.model.expression); + if (alertQueryReferenced) { + return findDataSourceFromExpressionRecursive(queries, alertQueryReferenced); + } else { + return null; + } + } +};