1
- import { createAction , createReducer } from '@reduxjs/toolkit' ;
1
+ import { createAction , createReducer , original } from '@reduxjs/toolkit' ;
2
2
3
3
import {
4
4
DataQuery ,
@@ -8,27 +8,34 @@ import {
8
8
rangeUtil ,
9
9
RelativeTimeRange ,
10
10
} from '@grafana/data' ;
11
- import { findDataSourceFromExpressionRecursive } from 'app/features/alerting/unified/utils/dataSourceFromExpression' ;
12
11
import { dataSource as expressionDatasource } from 'app/features/expressions/ExpressionDatasource' ;
13
12
import { isExpressionQuery } from 'app/features/expressions/guards' ;
14
13
import { ExpressionDatasourceUID , ExpressionQuery , ExpressionQueryType } from 'app/features/expressions/types' ;
15
14
import { defaultCondition } from 'app/features/expressions/utils/expressionTypes' ;
16
15
import { AlertQuery } from 'app/types/unified-alerting-dto' ;
17
16
17
+ import { logError } from '../../../Analytics' ;
18
18
import { getDefaultOrFirstCompatibleDataSource } from '../../../utils/datasource' ;
19
+ import { createDagFromQueries , getOriginOfRefId } from '../dag' ;
19
20
import { queriesWithUpdatedReferences , refIdExists } from '../util' ;
20
21
21
22
export interface QueriesAndExpressionsState {
22
23
queries : AlertQuery [ ] ;
23
24
}
24
25
25
- const findDataSourceFromExpression = (
26
- queries : AlertQuery [ ] ,
27
- expression : string | undefined
28
- ) : AlertQuery | null | undefined => {
29
- const firstReference = queries . find ( ( alertQuery ) => alertQuery . refId === expression ) ;
30
- const dataSource = firstReference && findDataSourceFromExpressionRecursive ( queries , firstReference ) ;
31
- return dataSource ;
26
+ const findDataSourceFromExpression = ( queries : AlertQuery [ ] , refId : string ) : AlertQuery | undefined => {
27
+ const dag = createDagFromQueries ( queries ) ;
28
+ const dataSource = getOriginOfRefId ( refId , dag ) [ 0 ] ;
29
+ if ( ! dataSource ) {
30
+ return ;
31
+ }
32
+
33
+ const originQuery = queries . find ( ( query ) => query . refId === dataSource ) ;
34
+ if ( originQuery && 'relativeTimeRange' in originQuery ) {
35
+ return originQuery ;
36
+ }
37
+
38
+ return ;
32
39
} ;
33
40
34
41
const initialState : QueriesAndExpressionsState = {
@@ -137,33 +144,51 @@ export const queriesAndExpressionsReducer = createReducer(initialState, (builder
137
144
state . queries = [ ...state . queries , ...payload ] ;
138
145
} )
139
146
. addCase ( updateExpression , ( state , { payload } ) => {
140
- state . queries = state . queries . map ( ( query ) => {
141
- const dataSourceAlertQuery = findDataSourceFromExpression ( state . queries , payload . expression ) ;
147
+ const queryToUpdate = state . queries . find ( ( query ) => query . refId === payload . refId ) ;
148
+ if ( ! queryToUpdate ) {
149
+ return ;
150
+ }
142
151
143
- const relativeTimeRange = dataSourceAlertQuery
144
- ? dataSourceAlertQuery . relativeTimeRange
145
- : getDefaultRelativeTimeRange ( ) ;
152
+ queryToUpdate . model = payload ;
146
153
147
- if ( query . refId === payload . refId ) {
148
- query . model = payload ;
149
- if ( payload . type === ExpressionQueryType . resample ) {
150
- query . relativeTimeRange = relativeTimeRange ;
154
+ // the resample expression needs to also know what the relative time range is to work with, this means we have to copy it from the source node (data source query)
155
+ if ( payload . type === ExpressionQueryType . resample && payload . expression ) {
156
+ // findDataSourceFromExpression uses memoization and it doesn't always work with proxies when the proxy has been revoked
157
+ const originalQueries = original ( state ) ?. queries ?? [ ] ;
158
+
159
+ let relativeTimeRange = getDefaultRelativeTimeRange ( ) ;
160
+ try {
161
+ const dataSourceAlertQuery = findDataSourceFromExpression ( originalQueries , payload . expression ) ;
162
+ if ( dataSourceAlertQuery ?. relativeTimeRange ) {
163
+ relativeTimeRange = dataSourceAlertQuery . relativeTimeRange ;
164
+ }
165
+ } catch ( error ) {
166
+ if ( error instanceof Error ) {
167
+ logError ( error ) ;
168
+ } else {
169
+ logError ( new Error ( 'Error while trying to find data source from expression' ) ) ;
151
170
}
152
171
}
153
- return query ;
154
- } ) ;
172
+
173
+ queryToUpdate . relativeTimeRange = relativeTimeRange ;
174
+ }
155
175
} )
156
176
. addCase ( updateExpressionTimeRange , ( state ) => {
157
- const newState = state . queries . map ( ( query ) => {
158
- // It's an expression , let's update the relativeTimeRange with its dataSource relativeTimeRange
159
- if ( query . datasourceUid === ExpressionDatasourceUID ) {
160
- const dataSource = findDataSourceFromExpression ( state . queries , query . model . expression ) ;
177
+ state . queries . forEach ( ( query ) => {
178
+ // Resample expression needs to get the relativeTimeRange with its dataSource relativeTimeRange
179
+ if (
180
+ isExpressionQuery ( query . model ) &&
181
+ query . model . type === ExpressionQueryType . resample &&
182
+ query . model . expression
183
+ ) {
184
+ // findDataSourceFromExpression uses memoization and doesn't work with proxies
185
+ const originalQueries = original ( state ) ?. queries ?? [ ] ;
186
+
187
+ const dataSource = findDataSourceFromExpression ( originalQueries , query . model . expression ) ;
161
188
const relativeTimeRange = dataSource ? dataSource . relativeTimeRange : getDefaultRelativeTimeRange ( ) ;
162
189
query . relativeTimeRange = relativeTimeRange ;
163
190
}
164
- return query ;
165
191
} ) ;
166
- state . queries = newState ;
167
192
} )
168
193
. addCase ( updateExpressionRefId , ( state , { payload } ) => {
169
194
const { newRefId, oldRefId } = payload ;
0 commit comments