| 
									
										
										
										
											2024-01-03 13:33:31 +08:00
										 |  |  | import { cloneDeep, merge } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  | import { Observable, of, ReplaySubject, Unsubscribable } from 'rxjs'; | 
					
						
							| 
									
										
										
										
											2023-08-19 04:55:42 +08:00
										 |  |  | import { map, mergeMap, catchError } from 'rxjs/operators'; | 
					
						
							| 
									
										
										
										
											2019-04-19 05:10:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 17:48:05 +08:00
										 |  |  | import { | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  |   applyFieldOverrides, | 
					
						
							| 
									
										
										
										
											2021-04-20 17:05:50 +08:00
										 |  |  |   compareArrayValues, | 
					
						
							|  |  |  |   compareDataFrameStructures, | 
					
						
							| 
									
										
										
										
											2019-12-27 16:11:16 +08:00
										 |  |  |   CoreApp, | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  |   DataConfigSource, | 
					
						
							| 
									
										
										
										
											2021-04-20 17:05:50 +08:00
										 |  |  |   DataFrame, | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  |   DataQuery, | 
					
						
							| 
									
										
										
										
											2019-10-31 17:48:05 +08:00
										 |  |  |   DataQueryRequest, | 
					
						
							|  |  |  |   DataSourceApi, | 
					
						
							|  |  |  |   DataSourceJsonData, | 
					
						
							| 
									
										
										
										
											2021-10-30 01:57:24 +08:00
										 |  |  |   DataSourceRef, | 
					
						
							| 
									
										
										
										
											2023-01-06 00:34:09 +08:00
										 |  |  |   DataTransformContext, | 
					
						
							| 
									
										
										
										
											2019-10-31 17:48:05 +08:00
										 |  |  |   DataTransformerConfig, | 
					
						
							| 
									
										
										
										
											2021-11-10 19:40:04 +08:00
										 |  |  |   getDefaultTimeRange, | 
					
						
							| 
									
										
										
										
											2020-07-01 15:32:27 +08:00
										 |  |  |   LoadingState, | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  |   PanelData, | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:06 +08:00
										 |  |  |   rangeUtil, | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  |   ScopedVars, | 
					
						
							|  |  |  |   TimeRange, | 
					
						
							|  |  |  |   TimeZone, | 
					
						
							| 
									
										
										
										
											2021-11-10 19:40:04 +08:00
										 |  |  |   toDataFrame, | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  |   transformDataFrame, | 
					
						
							| 
									
										
										
										
											2023-09-08 16:57:31 +08:00
										 |  |  |   preProcessPanelData, | 
					
						
							|  |  |  |   ApplyFieldOverrideOptions, | 
					
						
							|  |  |  |   StreamingDataFrame, | 
					
						
							| 
									
										
										
										
											2024-01-03 13:33:31 +08:00
										 |  |  |   DataTopic, | 
					
						
							| 
									
										
										
										
											2019-10-31 17:48:05 +08:00
										 |  |  | } from '@grafana/data'; | 
					
						
							| 
									
										
										
										
											2023-09-28 22:28:58 +08:00
										 |  |  | import { toDataQueryError } from '@grafana/runtime'; | 
					
						
							| 
									
										
										
										
											2022-06-09 19:50:16 +08:00
										 |  |  | import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | import { isStreamingDataFrame } from 'app/features/live/data/utils'; | 
					
						
							|  |  |  | import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; | 
					
						
							| 
									
										
										
										
											2023-09-28 22:28:58 +08:00
										 |  |  | import { getTemplateSrv } from 'app/features/templating/template_srv'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import { isSharedDashboardQuery, runSharedRequest } from '../../../plugins/datasource/dashboard'; | 
					
						
							|  |  |  | import { PanelModel } from '../../dashboard/state'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  | import { getDashboardQueryRunner } from './DashboardQueryRunner/DashboardQueryRunner'; | 
					
						
							|  |  |  | import { mergePanelAndDashData } from './mergePanelAndDashData'; | 
					
						
							| 
									
										
										
										
											2023-03-06 17:59:47 +08:00
										 |  |  | import { runRequest } from './runRequest'; | 
					
						
							| 
									
										
										
										
											2019-04-26 02:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 15:37:50 +08:00
										 |  |  | export interface QueryRunnerOptions< | 
					
						
							|  |  |  |   TQuery extends DataQuery = DataQuery, | 
					
						
							| 
									
										
										
										
											2023-07-17 22:58:22 +08:00
										 |  |  |   TOptions extends DataSourceJsonData = DataSourceJsonData, | 
					
						
							| 
									
										
										
										
											2019-05-08 15:37:50 +08:00
										 |  |  | > { | 
					
						
							| 
									
										
										
										
											2021-10-30 01:57:24 +08:00
										 |  |  |   datasource: DataSourceRef | DataSourceApi<TQuery, TOptions> | null; | 
					
						
							| 
									
										
										
										
											2019-04-18 03:05:58 +08:00
										 |  |  |   queries: TQuery[]; | 
					
						
							| 
									
										
										
										
											2020-12-02 22:42:54 +08:00
										 |  |  |   panelId?: number; | 
					
						
							| 
									
										
										
										
											2024-02-21 16:38:42 +08:00
										 |  |  |   panelPluginId?: string; | 
					
						
							| 
									
										
										
										
											2022-08-10 16:36:19 +08:00
										 |  |  |   dashboardUID?: string; | 
					
						
							| 
									
										
										
										
											2020-07-09 21:16:35 +08:00
										 |  |  |   timezone: TimeZone; | 
					
						
							| 
									
										
										
										
											2019-04-19 05:10:18 +08:00
										 |  |  |   timeRange: TimeRange; | 
					
						
							| 
									
										
										
										
											2019-04-18 15:00:46 +08:00
										 |  |  |   timeInfo?: string; // String description of time range for display
 | 
					
						
							| 
									
										
										
										
											2020-04-28 00:29:41 +08:00
										 |  |  |   maxDataPoints: number; | 
					
						
							| 
									
										
										
										
											2019-04-19 05:10:18 +08:00
										 |  |  |   minInterval: string | undefined | null; | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |   scopedVars?: ScopedVars; | 
					
						
							| 
									
										
										
										
											2021-11-24 21:24:33 +08:00
										 |  |  |   cacheTimeout?: string | null; | 
					
						
							| 
									
										
										
										
											2023-02-03 12:39:54 +08:00
										 |  |  |   queryCachingTTL?: number | null; | 
					
						
							| 
									
										
										
										
											2019-09-09 14:58:57 +08:00
										 |  |  |   transformations?: DataTransformerConfig[]; | 
					
						
							| 
									
										
										
										
											2022-12-08 18:52:28 +08:00
										 |  |  |   app?: CoreApp; | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-19 12:56:27 +08:00
										 |  |  | let counter = 100; | 
					
						
							| 
									
										
										
										
											2021-12-16 03:01:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-19 04:40:27 +08:00
										 |  |  | export function getNextRequestId() { | 
					
						
							| 
									
										
										
										
											2019-04-19 12:56:27 +08:00
										 |  |  |   return 'Q' + counter++; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-13 19:03:34 +08:00
										 |  |  | export interface GetDataOptions { | 
					
						
							| 
									
										
										
										
											2020-05-25 20:05:43 +08:00
										 |  |  |   withTransforms: boolean; | 
					
						
							|  |  |  |   withFieldConfig: boolean; | 
					
						
							| 
									
										
										
										
											2020-05-13 19:03:34 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  | export class PanelQueryRunner { | 
					
						
							| 
									
										
										
										
											2020-07-07 03:16:27 +08:00
										 |  |  |   private subject: ReplaySubject<PanelData>; | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |   private subscription?: Unsubscribable; | 
					
						
							| 
									
										
										
										
											2019-09-13 03:42:50 +08:00
										 |  |  |   private lastResult?: PanelData; | 
					
						
							| 
									
										
										
										
											2020-03-16 21:26:03 +08:00
										 |  |  |   private dataConfigSource: DataConfigSource; | 
					
						
							| 
									
										
										
										
											2021-12-16 19:05:00 +08:00
										 |  |  |   private lastRequest?: DataQueryRequest; | 
					
						
							| 
									
										
										
										
											2023-09-28 22:28:58 +08:00
										 |  |  |   private templateSrv = getTemplateSrv(); | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-16 21:26:03 +08:00
										 |  |  |   constructor(dataConfigSource: DataConfigSource) { | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |     this.subject = new ReplaySubject(1); | 
					
						
							| 
									
										
										
										
											2020-03-16 21:26:03 +08:00
										 |  |  |     this.dataConfigSource = dataConfigSource; | 
					
						
							| 
									
										
										
										
											2019-09-09 14:58:57 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |   /** | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |    * Returns an observable that subscribes to the shared multi-cast subject (that reply last result). | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2020-05-25 20:05:43 +08:00
										 |  |  |   getData(options: GetDataOptions): Observable<PanelData> { | 
					
						
							| 
									
										
										
										
											2020-05-13 19:03:34 +08:00
										 |  |  |     const { withFieldConfig, withTransforms } = options; | 
					
						
							| 
									
										
										
										
											2021-04-20 17:05:50 +08:00
										 |  |  |     let structureRev = 1; | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |     let lastFieldConfig: ApplyFieldOverrideOptions | undefined = undefined; | 
					
						
							|  |  |  |     let lastProcessedFrames: DataFrame[] = []; | 
					
						
							|  |  |  |     let lastRawFrames: DataFrame[] = []; | 
					
						
							| 
									
										
										
										
											2023-05-16 04:48:03 +08:00
										 |  |  |     let lastTransformations: DataTransformerConfig[] | undefined; | 
					
						
							| 
									
										
										
										
											2021-12-16 03:01:55 +08:00
										 |  |  |     let isFirstPacket = true; | 
					
						
							| 
									
										
										
										
											2021-04-20 17:05:50 +08:00
										 |  |  |     let lastConfigRev = -1; | 
					
						
							| 
									
										
										
										
											2020-05-13 19:03:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-10 19:40:04 +08:00
										 |  |  |     if (this.dataConfigSource.snapshotData) { | 
					
						
							|  |  |  |       const snapshotPanelData: PanelData = { | 
					
						
							|  |  |  |         state: LoadingState.Done, | 
					
						
							|  |  |  |         series: this.dataConfigSource.snapshotData.map((v) => toDataFrame(v)), | 
					
						
							|  |  |  |         timeRange: getDefaultTimeRange(), // Don't need real time range for snapshots
 | 
					
						
							| 
									
										
										
										
											2023-10-14 07:56:51 +08:00
										 |  |  |         structureRev, | 
					
						
							| 
									
										
										
										
											2021-11-10 19:40:04 +08:00
										 |  |  |       }; | 
					
						
							|  |  |  |       return of(snapshotPanelData); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-16 21:26:03 +08:00
										 |  |  |     return this.subject.pipe( | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |       mergeMap((data: PanelData) => { | 
					
						
							|  |  |  |         let fieldConfig = this.dataConfigSource.getFieldOverrideOptions(); | 
					
						
							| 
									
										
										
										
											2023-05-16 04:48:03 +08:00
										 |  |  |         let transformations = this.dataConfigSource.getTransformations(); | 
					
						
							| 
									
										
										
										
											2020-04-07 12:54:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-16 04:48:03 +08:00
										 |  |  |         if ( | 
					
						
							|  |  |  |           data.series === lastRawFrames && | 
					
						
							|  |  |  |           lastFieldConfig?.fieldConfig === fieldConfig?.fieldConfig && | 
					
						
							|  |  |  |           lastTransformations === transformations | 
					
						
							|  |  |  |         ) { | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |           return of({ ...data, structureRev, series: lastProcessedFrames }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lastFieldConfig = fieldConfig; | 
					
						
							| 
									
										
										
										
											2023-05-16 04:48:03 +08:00
										 |  |  |         lastTransformations = transformations; | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |         lastRawFrames = data.series; | 
					
						
							|  |  |  |         let dataWithTransforms = of(data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (withTransforms) { | 
					
						
							|  |  |  |           dataWithTransforms = this.applyTransformations(data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return dataWithTransforms.pipe( | 
					
						
							|  |  |  |           map((data: PanelData) => { | 
					
						
							|  |  |  |             let processedData = data; | 
					
						
							|  |  |  |             let streamingPacketWithSameSchema = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (withFieldConfig && data.series?.length) { | 
					
						
							|  |  |  |               if (lastConfigRev === this.dataConfigSource.configRev) { | 
					
						
							| 
									
										
										
										
											2024-01-15 22:29:39 +08:00
										 |  |  |                 let streamingDataFrame: StreamingDataFrame | undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for (const frame of data.series) { | 
					
						
							|  |  |  |                   if (isStreamingDataFrame(frame)) { | 
					
						
							|  |  |  |                     streamingDataFrame = frame; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if ( | 
					
						
							|  |  |  |                   streamingDataFrame && | 
					
						
							|  |  |  |                   !streamingDataFrame.packetInfo.schemaChanged && | 
					
						
							|  |  |  |                   // TODO: remove the condition below after fixing
 | 
					
						
							|  |  |  |                   // https://github.com/grafana/grafana/pull/41492#issuecomment-970281430
 | 
					
						
							|  |  |  |                   lastProcessedFrames[0].fields.length === streamingDataFrame.fields.length | 
					
						
							|  |  |  |                 ) { | 
					
						
							|  |  |  |                   processedData = { | 
					
						
							|  |  |  |                     ...processedData, | 
					
						
							|  |  |  |                     series: lastProcessedFrames.map((frame, frameIndex) => ({ | 
					
						
							|  |  |  |                       ...frame, | 
					
						
							|  |  |  |                       length: data.series[frameIndex].length, | 
					
						
							|  |  |  |                       fields: frame.fields.map((field, fieldIndex) => ({ | 
					
						
							|  |  |  |                         ...field, | 
					
						
							|  |  |  |                         values: data.series[frameIndex].fields[fieldIndex].values, | 
					
						
							|  |  |  |                         state: { | 
					
						
							|  |  |  |                           ...field.state, | 
					
						
							|  |  |  |                           calcs: undefined, | 
					
						
							|  |  |  |                           range: undefined, | 
					
						
							|  |  |  |                         }, | 
					
						
							|  |  |  |                       })), | 
					
						
							|  |  |  |                     })), | 
					
						
							|  |  |  |                   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   streamingPacketWithSameSchema = true; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               if (fieldConfig != null && (isFirstPacket || !streamingPacketWithSameSchema)) { | 
					
						
							|  |  |  |                 lastConfigRev = this.dataConfigSource.configRev!; | 
					
						
							|  |  |  |                 processedData = { | 
					
						
							|  |  |  |                   ...processedData, | 
					
						
							|  |  |  |                   series: applyFieldOverrides({ | 
					
						
							|  |  |  |                     timeZone: data.request?.timezone ?? 'browser', | 
					
						
							|  |  |  |                     data: processedData.series, | 
					
						
							|  |  |  |                     ...fieldConfig!, | 
					
						
							|  |  |  |                   }), | 
					
						
							|  |  |  |                 }; | 
					
						
							| 
									
										
										
										
											2023-07-31 20:10:03 +08:00
										 |  |  |                 if (processedData.annotations) { | 
					
						
							|  |  |  |                   processedData.annotations = applyFieldOverrides({ | 
					
						
							|  |  |  |                     data: processedData.annotations, | 
					
						
							|  |  |  |                     ...fieldConfig!, | 
					
						
							|  |  |  |                     fieldConfig: { | 
					
						
							|  |  |  |                       defaults: {}, | 
					
						
							|  |  |  |                       overrides: [], | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                   }); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |                 isFirstPacket = false; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-12-16 03:01:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if ( | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |               !streamingPacketWithSameSchema && | 
					
						
							|  |  |  |               !compareArrayValues(lastProcessedFrames, processedData.series, compareDataFrameStructures) | 
					
						
							| 
									
										
										
										
											2021-12-16 03:01:55 +08:00
										 |  |  |             ) { | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |               structureRev++; | 
					
						
							| 
									
										
										
										
											2021-04-20 17:05:50 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-04-07 12:54:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |             lastProcessedFrames = processedData.series; | 
					
						
							| 
									
										
										
										
											2021-04-20 17:05:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |             return { ...processedData, structureRev }; | 
					
						
							|  |  |  |           }) | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2020-03-16 21:26:03 +08:00
										 |  |  |       }) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |   private applyTransformations(data: PanelData): Observable<PanelData> { | 
					
						
							|  |  |  |     const transformations = this.dataConfigSource.getTransformations(); | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-27 01:14:59 +08:00
										 |  |  |     const allTransformationsDisabled = transformations && transformations.every((t) => t.disabled); | 
					
						
							|  |  |  |     if (allTransformationsDisabled || !transformations || transformations.length === 0) { | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |       return of(data); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |     const ctx: DataTransformContext = { | 
					
						
							| 
									
										
										
										
											2023-09-28 22:28:58 +08:00
										 |  |  |       interpolate: (v: string) => this.templateSrv.replace(v, data?.request?.scopedVars), | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2022-03-18 18:25:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-03 13:33:31 +08:00
										 |  |  |     let seriesTransformations = transformations.filter((t) => t.topic == null || t.topic === DataTopic.Series); | 
					
						
							|  |  |  |     let annotationsTransformations = transformations.filter((t) => t.topic === DataTopic.Annotations); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let seriesStream = transformDataFrame(seriesTransformations, data.series, ctx); | 
					
						
							|  |  |  |     let annotationsStream = transformDataFrame(annotationsTransformations, data.annotations ?? [], ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return merge(seriesStream, annotationsStream).pipe( | 
					
						
							|  |  |  |       map((frames) => { | 
					
						
							|  |  |  |         let isAnnotations = frames.some((f) => f.meta?.dataTopic === DataTopic.Annotations); | 
					
						
							|  |  |  |         let transformed = isAnnotations ? { annotations: frames } : { series: frames }; | 
					
						
							|  |  |  |         return { ...data, ...transformed }; | 
					
						
							|  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2023-08-19 04:55:42 +08:00
										 |  |  |       catchError((err) => { | 
					
						
							|  |  |  |         console.warn('Error running transformation:', err); | 
					
						
							|  |  |  |         return of({ | 
					
						
							|  |  |  |           ...data, | 
					
						
							|  |  |  |           state: LoadingState.Error, | 
					
						
							| 
									
										
										
										
											2023-09-21 00:09:51 +08:00
										 |  |  |           errors: [toDataQueryError(err)], | 
					
						
							| 
									
										
										
										
											2023-08-19 04:55:42 +08:00
										 |  |  |         }); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-10-06 13:55:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |   async run(options: QueryRunnerOptions) { | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |     const { | 
					
						
							|  |  |  |       queries, | 
					
						
							|  |  |  |       timezone, | 
					
						
							|  |  |  |       datasource, | 
					
						
							|  |  |  |       panelId, | 
					
						
							| 
									
										
										
										
											2024-02-21 16:38:42 +08:00
										 |  |  |       panelPluginId, | 
					
						
							| 
									
										
										
										
											2022-08-10 16:36:19 +08:00
										 |  |  |       dashboardUID, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       timeRange, | 
					
						
							| 
									
										
										
										
											2019-04-18 15:00:46 +08:00
										 |  |  |       timeInfo, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       cacheTimeout, | 
					
						
							| 
									
										
										
										
											2023-02-03 12:39:54 +08:00
										 |  |  |       queryCachingTTL, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       maxDataPoints, | 
					
						
							|  |  |  |       scopedVars, | 
					
						
							| 
									
										
										
										
											2019-04-19 05:10:18 +08:00
										 |  |  |       minInterval, | 
					
						
							| 
									
										
										
										
											2022-12-08 18:52:28 +08:00
										 |  |  |       app, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |     } = options; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-27 17:06:49 +08:00
										 |  |  |     if (isSharedDashboardQuery(datasource)) { | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |       this.pipeToSubject(runSharedRequest(options, queries[0]), panelId, true); | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |       return; | 
					
						
							| 
									
										
										
										
											2019-08-27 17:06:49 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-19 12:56:27 +08:00
										 |  |  |     const request: DataQueryRequest = { | 
					
						
							| 
									
										
										
										
											2022-12-08 18:52:28 +08:00
										 |  |  |       app: app ?? CoreApp.Dashboard, | 
					
						
							| 
									
										
										
										
											2019-04-19 12:56:27 +08:00
										 |  |  |       requestId: getNextRequestId(), | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       timezone, | 
					
						
							|  |  |  |       panelId, | 
					
						
							| 
									
										
										
										
											2024-02-21 16:38:42 +08:00
										 |  |  |       panelPluginId, | 
					
						
							| 
									
										
										
										
											2022-08-10 16:36:19 +08:00
										 |  |  |       dashboardUID, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       range: timeRange, | 
					
						
							| 
									
										
										
										
											2019-04-18 15:00:46 +08:00
										 |  |  |       timeInfo, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       interval: '', | 
					
						
							|  |  |  |       intervalMs: 0, | 
					
						
							| 
									
										
										
										
											2019-05-21 19:46:19 +08:00
										 |  |  |       targets: cloneDeep(queries), | 
					
						
							| 
									
										
										
										
											2020-04-28 00:29:41 +08:00
										 |  |  |       maxDataPoints: maxDataPoints, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       scopedVars: scopedVars || {}, | 
					
						
							|  |  |  |       cacheTimeout, | 
					
						
							| 
									
										
										
										
											2023-02-03 12:39:54 +08:00
										 |  |  |       queryCachingTTL, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       startTime: Date.now(), | 
					
						
							| 
									
										
										
										
											2022-12-14 22:07:22 +08:00
										 |  |  |       rangeRaw: timeRange.raw, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2019-04-26 02:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  |       const ds = await getDataSource(datasource, request.scopedVars); | 
					
						
							| 
									
										
										
										
											2023-09-28 22:28:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 19:50:16 +08:00
										 |  |  |       const isMixedDS = ds.meta?.mixed; | 
					
						
							| 
									
										
										
										
											2022-06-14 08:03:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 18:39:47 +08:00
										 |  |  |       // Attach the data source to each query
 | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |       request.targets = request.targets.map((query) => { | 
					
						
							| 
									
										
										
										
											2022-06-09 19:50:16 +08:00
										 |  |  |         const isExpressionQuery = query.datasource?.type === ExpressionDatasourceRef.type; | 
					
						
							| 
									
										
										
										
											2022-05-26 18:39:47 +08:00
										 |  |  |         // When using a data source variable, the panel might have the incorrect datasource
 | 
					
						
							|  |  |  |         // stored, so when running the query make sure it is done with the correct one
 | 
					
						
							| 
									
										
										
										
											2022-06-09 19:50:16 +08:00
										 |  |  |         if (!query.datasource || (query.datasource.uid !== ds.uid && !isMixedDS && !isExpressionQuery)) { | 
					
						
							| 
									
										
										
										
											2021-11-17 14:55:12 +08:00
										 |  |  |           query.datasource = ds.getRef(); | 
					
						
							| 
									
										
										
										
											2019-04-23 10:14:42 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |         return query; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 22:28:58 +08:00
										 |  |  |       const lowerIntervalLimit = minInterval ? this.templateSrv.replace(minInterval, request.scopedVars) : ds.interval; | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:06 +08:00
										 |  |  |       const norm = rangeUtil.calculateInterval(timeRange, maxDataPoints, lowerIntervalLimit); | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // make shallow copy of scoped vars,
 | 
					
						
							|  |  |  |       // and add built in variables interval and interval_ms
 | 
					
						
							|  |  |  |       request.scopedVars = Object.assign({}, request.scopedVars, { | 
					
						
							|  |  |  |         __interval: { text: norm.interval, value: norm.interval }, | 
					
						
							| 
									
										
										
										
											2019-04-26 02:01:02 +08:00
										 |  |  |         __interval_ms: { text: norm.intervalMs.toString(), value: norm.intervalMs }, | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-04-19 05:10:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |       request.interval = norm.interval; | 
					
						
							|  |  |  |       request.intervalMs = norm.intervalMs; | 
					
						
							| 
									
										
										
										
											2023-11-21 03:22:01 +08:00
										 |  |  |       request.filters = this.templateSrv.getAdhocFilters(ds.name, true); | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-16 19:05:00 +08:00
										 |  |  |       this.lastRequest = request; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  |       this.pipeToSubject(runRequest(ds, request), panelId); | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |     } catch (err) { | 
					
						
							| 
									
										
										
										
											2022-12-14 20:37:31 +08:00
										 |  |  |       this.pipeToSubject( | 
					
						
							|  |  |  |         of({ | 
					
						
							|  |  |  |           state: LoadingState.Error, | 
					
						
							|  |  |  |           error: toDataQueryError(err), | 
					
						
							|  |  |  |           series: [], | 
					
						
							|  |  |  |           timeRange: request.range, | 
					
						
							|  |  |  |         }), | 
					
						
							|  |  |  |         panelId | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-18 14:22:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 21:21:40 +08:00
										 |  |  |   private pipeToSubject(observable: Observable<PanelData>, panelId?: number, skipPreProcess = false) { | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |     if (this.subscription) { | 
					
						
							|  |  |  |       this.subscription.unsubscribe(); | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-26 20:50:08 +08:00
										 |  |  |     let panelData = observable; | 
					
						
							| 
									
										
										
										
											2021-05-03 14:52:05 +08:00
										 |  |  |     const dataSupport = this.dataConfigSource.getDataSupport(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (dataSupport.alertStates || dataSupport.annotations) { | 
					
						
							| 
									
										
										
										
											2022-02-02 20:02:32 +08:00
										 |  |  |       const panel = this.dataConfigSource as unknown as PanelModel; | 
					
						
							| 
									
										
										
										
											2021-10-13 14:53:36 +08:00
										 |  |  |       panelData = mergePanelAndDashData(observable, getDashboardQueryRunner().getResult(panel.id)); | 
					
						
							| 
									
										
										
										
											2021-04-26 20:50:08 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.subscription = panelData.subscribe({ | 
					
						
							| 
									
										
										
										
											2023-09-08 16:57:31 +08:00
										 |  |  |       next: (data) => { | 
					
						
							| 
									
										
										
										
											2023-12-13 06:11:17 +08:00
										 |  |  |         const last = this.lastResult; | 
					
						
							|  |  |  |         const next = skipPreProcess ? data : preProcessPanelData(data, last); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-27 05:23:41 +08:00
										 |  |  |         if (last != null && next.state !== LoadingState.Streaming) { | 
					
						
							| 
									
										
										
										
											2023-12-13 06:11:17 +08:00
										 |  |  |           let sameSeries = compareArrayValues(last.series ?? [], next.series ?? [], (a, b) => a === b); | 
					
						
							|  |  |  |           let sameAnnotations = compareArrayValues(last.annotations ?? [], next.annotations ?? [], (a, b) => a === b); | 
					
						
							| 
									
										
										
										
											2023-12-19 22:13:11 +08:00
										 |  |  |           let sameState = last.state === next.state; | 
					
						
							| 
									
										
										
										
											2023-12-13 06:11:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |           if (sameSeries) { | 
					
						
							|  |  |  |             next.series = last.series; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (sameAnnotations) { | 
					
						
							|  |  |  |             next.annotations = last.annotations; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-19 22:13:11 +08:00
										 |  |  |           if (sameSeries && sameAnnotations && sameState) { | 
					
						
							| 
									
										
										
										
											2023-12-13 06:11:17 +08:00
										 |  |  |             return; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.lastResult = next; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-16 21:26:03 +08:00
										 |  |  |         // Store preprocessed query results for applying overrides later on in the pipeline
 | 
					
						
							| 
									
										
										
										
											2023-12-13 06:11:17 +08:00
										 |  |  |         this.subject.next(next); | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-18 14:22:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 15:32:27 +08:00
										 |  |  |   cancelQuery() { | 
					
						
							|  |  |  |     if (!this.subscription) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.subscription.unsubscribe(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-16 20:56:58 +08:00
										 |  |  |     // If we have an old result with loading or streaming state, send it with done state
 | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |       this.lastResult && | 
					
						
							|  |  |  |       (this.lastResult.state === LoadingState.Loading || this.lastResult.state === LoadingState.Streaming) | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2020-07-01 15:32:27 +08:00
										 |  |  |       this.subject.next({ | 
					
						
							|  |  |  |         ...this.lastResult, | 
					
						
							|  |  |  |         state: LoadingState.Done, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-16 21:26:03 +08:00
										 |  |  |   resendLastResult = () => { | 
					
						
							|  |  |  |     if (this.lastResult) { | 
					
						
							|  |  |  |       this.subject.next(this.lastResult); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2019-04-26 02:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-13 14:53:36 +08:00
										 |  |  |   clearLastResult() { | 
					
						
							|  |  |  |     this.lastResult = undefined; | 
					
						
							|  |  |  |     // A new subject is also needed since it's a replay subject that remembers/sends last value
 | 
					
						
							|  |  |  |     this.subject = new ReplaySubject(1); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-19 12:56:27 +08:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Called when the panel is closed | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   destroy() { | 
					
						
							|  |  |  |     // Tell anyone listening that we are done
 | 
					
						
							|  |  |  |     if (this.subject) { | 
					
						
							|  |  |  |       this.subject.complete(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |     if (this.subscription) { | 
					
						
							|  |  |  |       this.subscription.unsubscribe(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-19 12:56:27 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-01-29 22:41:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-06 22:06:21 +08:00
										 |  |  |   useLastResultFrom(runner: PanelQueryRunner) { | 
					
						
							|  |  |  |     this.lastResult = runner.getLastResult(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this.lastResult) { | 
					
						
							|  |  |  |       // The subject is a replay subject so anyone subscribing will get this last result
 | 
					
						
							|  |  |  |       this.subject.next(this.lastResult); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-15 03:54:09 +08:00
										 |  |  |   /** Useful from tests */ | 
					
						
							|  |  |  |   setLastResult(data: PanelData) { | 
					
						
							|  |  |  |     this.lastResult = data; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-07 03:16:27 +08:00
										 |  |  |   getLastResult(): PanelData | undefined { | 
					
						
							| 
									
										
										
										
											2020-01-29 22:41:25 +08:00
										 |  |  |     return this.lastResult; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-12-16 19:05:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   getLastRequest(): DataQueryRequest | undefined { | 
					
						
							|  |  |  |     return this.lastRequest; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-26 02:01:02 +08:00
										 |  |  | async function getDataSource( | 
					
						
							| 
									
										
										
										
											2021-10-30 01:57:24 +08:00
										 |  |  |   datasource: DataSourceRef | string | DataSourceApi | null, | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  |   scopedVars: ScopedVars | 
					
						
							| 
									
										
										
										
											2019-04-26 02:01:02 +08:00
										 |  |  | ): Promise<DataSourceApi> { | 
					
						
							| 
									
										
										
										
											2023-08-30 20:38:13 +08:00
										 |  |  |   if (datasource && typeof datasource === 'object' && 'query' in datasource) { | 
					
						
							| 
									
										
										
										
											2022-09-22 00:29:27 +08:00
										 |  |  |     return datasource; | 
					
						
							| 
									
										
										
										
											2022-06-14 08:03:43 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-30 20:38:13 +08:00
										 |  |  |   return await getDatasourceSrv().get(datasource, scopedVars); | 
					
						
							| 
									
										
										
										
											2019-04-18 01:51:50 +08:00
										 |  |  | } |