| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  | import { cloneDeep } from 'lodash'; | 
					
						
							|  |  |  | import { from, merge, Observable, of } from 'rxjs'; | 
					
						
							| 
									
										
										
										
											2021-09-09 19:43:05 +08:00
										 |  |  | import { catchError, filter, finalize, map, mergeAll, mergeMap, reduce, takeUntil } from 'rxjs/operators'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  | import { AnnotationQuery, DataSourceApi } from '@grafana/data'; | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  | import { config, getDataSourceSrv } from '@grafana/runtime'; | 
					
						
							| 
									
										
										
										
											2023-08-30 20:38:13 +08:00
										 |  |  | import { PublicAnnotationsDataSource } from 'app/features/query/state/DashboardQueryRunner/PublicAnnotationsDataSource'; | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | import { AnnotationQueryFinished, AnnotationQueryStarted } from '../../../../types/events'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { AnnotationsQueryRunner } from './AnnotationsQueryRunner'; | 
					
						
							|  |  |  | import { getDashboardQueryRunner } from './DashboardQueryRunner'; | 
					
						
							|  |  |  | import { LegacyAnnotationQueryRunner } from './LegacyAnnotationQueryRunner'; | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   AnnotationQueryRunner, | 
					
						
							|  |  |  |   DashboardQueryRunnerOptions, | 
					
						
							|  |  |  |   DashboardQueryRunnerWorker, | 
					
						
							|  |  |  |   DashboardQueryRunnerWorkerResult, | 
					
						
							|  |  |  | } from './types'; | 
					
						
							| 
									
										
										
										
											2021-09-09 19:43:05 +08:00
										 |  |  | import { emptyResult, handleDatasourceSrvError, translateQueryResult } from './utils'; | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export class AnnotationsWorker implements DashboardQueryRunnerWorker { | 
					
						
							|  |  |  |   constructor( | 
					
						
							|  |  |  |     private readonly runners: AnnotationQueryRunner[] = [ | 
					
						
							|  |  |  |       new LegacyAnnotationQueryRunner(), | 
					
						
							|  |  |  |       new AnnotationsQueryRunner(), | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  |   ) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   canWork({ dashboard }: DashboardQueryRunnerOptions): boolean { | 
					
						
							|  |  |  |     const annotations = dashboard.annotations.list.find(AnnotationsWorker.getAnnotationsToProcessFilter); | 
					
						
							| 
									
										
										
										
											2022-10-19 09:48:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return Boolean(annotations); | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   work(options: DashboardQueryRunnerOptions): Observable<DashboardQueryRunnerWorkerResult> { | 
					
						
							|  |  |  |     if (!this.canWork(options)) { | 
					
						
							|  |  |  |       return emptyResult(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const { dashboard, range } = options; | 
					
						
							| 
									
										
										
										
											2022-10-19 09:48:20 +08:00
										 |  |  |     let annotations = dashboard.annotations.list.filter(AnnotationsWorker.getAnnotationsToProcessFilter); | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-19 09:48:20 +08:00
										 |  |  |     // We only want to create a single PublicDashboardDatasource. This will get all annotations in one request.
 | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  |     if (config.publicDashboardAccessToken && annotations.length > 0) { | 
					
						
							| 
									
										
										
										
											2022-10-19 09:48:20 +08:00
										 |  |  |       annotations = [annotations[0]]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  |     const observables = annotations.map((annotation) => { | 
					
						
							| 
									
										
										
										
											2022-10-19 09:48:20 +08:00
										 |  |  |       let datasourceObservable; | 
					
						
							| 
									
										
										
										
											2022-10-21 23:06:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  |       if (config.publicDashboardAccessToken) { | 
					
						
							| 
									
										
										
										
											2023-08-30 20:38:13 +08:00
										 |  |  |         const pubdashDatasource = new PublicAnnotationsDataSource(); | 
					
						
							| 
									
										
										
										
											2022-10-19 09:48:20 +08:00
										 |  |  |         datasourceObservable = of(pubdashDatasource).pipe(catchError(handleDatasourceSrvError)); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         datasourceObservable = from(getDataSourceSrv().get(annotation.datasource)).pipe( | 
					
						
							|  |  |  |           catchError(handleDatasourceSrvError) // because of the reduce all observables need to be completed, so an erroneous observable wont do
 | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-09 19:43:05 +08:00
										 |  |  |       return datasourceObservable.pipe( | 
					
						
							| 
									
										
										
										
											2023-11-07 21:20:20 +08:00
										 |  |  |         mergeMap((datasource: DataSourceApi | undefined) => { | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  |           const runner = this.runners.find((r) => r.canRun(datasource)); | 
					
						
							|  |  |  |           if (!runner) { | 
					
						
							|  |  |  |             return of([]); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-27 16:03:52 +08:00
										 |  |  |           dashboard.events.publish(new AnnotationQueryStarted(annotation)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  |           return runner.run({ annotation, datasource, dashboard, range }).pipe( | 
					
						
							| 
									
										
										
										
											2021-04-27 16:03:52 +08:00
										 |  |  |             takeUntil( | 
					
						
							|  |  |  |               getDashboardQueryRunner() | 
					
						
							|  |  |  |                 .cancellations() | 
					
						
							|  |  |  |                 .pipe(filter((a) => a === annotation)) | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  |             map((results) => { | 
					
						
							|  |  |  |               // store response in annotation object if this is a snapshot call
 | 
					
						
							|  |  |  |               if (dashboard.snapshot) { | 
					
						
							|  |  |  |                 annotation.snapshotData = cloneDeep(results); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               // translate result
 | 
					
						
							| 
									
										
										
										
											2023-08-26 02:56:02 +08:00
										 |  |  |               if (config.publicDashboardAccessToken) { | 
					
						
							| 
									
										
										
										
											2022-10-19 09:48:20 +08:00
										 |  |  |                 return results; | 
					
						
							|  |  |  |               } else { | 
					
						
							|  |  |  |                 return translateQueryResult(annotation, results); | 
					
						
							|  |  |  |               } | 
					
						
							| 
									
										
										
										
											2021-04-27 16:03:52 +08:00
										 |  |  |             }), | 
					
						
							|  |  |  |             finalize(() => { | 
					
						
							|  |  |  |               dashboard.events.publish(new AnnotationQueryFinished(annotation)); | 
					
						
							| 
									
										
										
										
											2021-04-26 12:13:03 +08:00
										 |  |  |             }) | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return merge(observables).pipe( | 
					
						
							|  |  |  |       mergeAll(), | 
					
						
							|  |  |  |       reduce((acc, value) => { | 
					
						
							|  |  |  |         // should we use scan or reduce here
 | 
					
						
							|  |  |  |         // reduce will only emit when all observables are completed
 | 
					
						
							|  |  |  |         // scan will emit when any observable is completed
 | 
					
						
							|  |  |  |         // choosing reduce to minimize re-renders
 | 
					
						
							|  |  |  |         return acc.concat(value); | 
					
						
							|  |  |  |       }), | 
					
						
							|  |  |  |       map((annotations) => { | 
					
						
							|  |  |  |         return { annotations, alertStates: [] }; | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private static getAnnotationsToProcessFilter(annotation: AnnotationQuery): boolean { | 
					
						
							|  |  |  |     return annotation.enable && !Boolean(annotation.snapshotData); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |