| 
									
										
										
										
											2019-10-31 17:48:05 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   DataQuery, | 
					
						
							|  |  |  |   DataQueryRequest, | 
					
						
							|  |  |  |   DataQueryResponse, | 
					
						
							| 
									
										
										
										
											2020-08-19 14:52:40 +08:00
										 |  |  |   DataSourceApi, | 
					
						
							| 
									
										
										
										
											2019-10-31 17:48:05 +08:00
										 |  |  |   DataSourceInstanceSettings, | 
					
						
							| 
									
										
										
										
											2020-08-19 14:52:40 +08:00
										 |  |  |   LoadingState, | 
					
						
							| 
									
										
										
										
											2019-10-31 17:48:05 +08:00
										 |  |  | } from '@grafana/data'; | 
					
						
							| 
									
										
										
										
											2020-08-19 14:52:40 +08:00
										 |  |  | import { getDataSourceSrv, toDataQueryError } from '@grafana/runtime'; | 
					
						
							| 
									
										
										
										
											2021-10-26 18:54:22 +08:00
										 |  |  | import { cloneDeep, groupBy } from 'lodash'; | 
					
						
							|  |  |  | import { forkJoin, from, Observable, of, OperatorFunction } from 'rxjs'; | 
					
						
							|  |  |  | import { catchError, map, mergeAll, mergeMap, reduce, toArray } from 'rxjs/operators'; | 
					
						
							| 
									
										
										
										
											2019-09-09 17:29:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const MIXED_DATASOURCE_NAME = '-- Mixed --'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  | export interface BatchedQueries { | 
					
						
							|  |  |  |   datasource: Promise<DataSourceApi>; | 
					
						
							|  |  |  |   targets: DataQuery[]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-09 17:29:37 +08:00
										 |  |  | export class MixedDatasource extends DataSourceApi<DataQuery> { | 
					
						
							|  |  |  |   constructor(instanceSettings: DataSourceInstanceSettings) { | 
					
						
							|  |  |  |     super(instanceSettings); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |   query(request: DataQueryRequest<DataQuery>): Observable<DataQueryResponse> { | 
					
						
							| 
									
										
										
										
											2019-09-09 17:29:37 +08:00
										 |  |  |     // Remove any invalid queries
 | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |     const queries = request.targets.filter((t) => { | 
					
						
							| 
									
										
										
										
											2021-10-30 01:57:24 +08:00
										 |  |  |       return t.datasource?.type !== MIXED_DATASOURCE_NAME; | 
					
						
							| 
									
										
										
										
											2019-09-09 17:29:37 +08:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!queries.length) { | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |       return of({ data: [] } as DataQueryResponse); // nothing
 | 
					
						
							| 
									
										
										
										
											2019-09-09 17:29:37 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  |     // Build groups of queries to run in parallel
 | 
					
						
							| 
									
										
										
										
											2021-10-30 01:57:24 +08:00
										 |  |  |     const sets: { [key: string]: DataQuery[] } = groupBy(queries, 'datasource.uid'); | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  |     const mixed: BatchedQueries[] = []; | 
					
						
							| 
									
										
										
										
											2020-07-07 03:16:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |     for (const key in sets) { | 
					
						
							|  |  |  |       const targets = sets[key]; | 
					
						
							| 
									
										
										
										
											2020-07-07 03:16:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  |       mixed.push({ | 
					
						
							| 
									
										
										
										
											2021-10-30 01:57:24 +08:00
										 |  |  |         datasource: getDataSourceSrv().get(targets[0].datasource, request.scopedVars), | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  |         targets, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-07 03:16:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 01:57:24 +08:00
										 |  |  |     // Missing UIDs?
 | 
					
						
							|  |  |  |     if (!mixed.length) { | 
					
						
							|  |  |  |       return of({ data: [] } as DataQueryResponse); // nothing
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  |     return this.batchQueries(mixed, request); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  |   batchQueries(mixed: BatchedQueries[], request: DataQueryRequest<DataQuery>): Observable<DataQueryResponse> { | 
					
						
							| 
									
										
										
										
											2020-03-13 01:16:32 +08:00
										 |  |  |     const runningQueries = mixed.filter(this.isQueryable).map((query, i) => | 
					
						
							|  |  |  |       from(query.datasource).pipe( | 
					
						
							|  |  |  |         mergeMap((api: DataSourceApi) => { | 
					
						
							|  |  |  |           const dsRequest = cloneDeep(request); | 
					
						
							|  |  |  |           dsRequest.requestId = `mixed-${i}-${dsRequest.requestId || ''}`; | 
					
						
							|  |  |  |           dsRequest.targets = query.targets; | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-13 01:16:32 +08:00
										 |  |  |           return from(api.query(dsRequest)).pipe( | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |             map((response) => { | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |               return { | 
					
						
							|  |  |  |                 ...response, | 
					
						
							|  |  |  |                 data: response.data || [], | 
					
						
							| 
									
										
										
										
											2020-03-13 01:16:32 +08:00
										 |  |  |                 state: LoadingState.Loading, | 
					
						
							| 
									
										
										
										
											2019-11-07 14:18:27 +08:00
										 |  |  |                 key: `mixed-${i}-${response.key || ''}`, | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |               } as DataQueryResponse; | 
					
						
							| 
									
										
										
										
											2020-08-19 14:52:40 +08:00
										 |  |  |             }), | 
					
						
							| 
									
										
										
										
											2021-10-26 18:54:22 +08:00
										 |  |  |             toArray(), | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |             catchError((err) => { | 
					
						
							| 
									
										
										
										
											2020-08-19 14:52:40 +08:00
										 |  |  |               err = toDataQueryError(err); | 
					
						
							|  |  |  |               err.message = `${api.name}: ${err.message}`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 18:54:22 +08:00
										 |  |  |               return of<DataQueryResponse[]>([ | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   data: [], | 
					
						
							|  |  |  |                   state: LoadingState.Error, | 
					
						
							|  |  |  |                   error: err, | 
					
						
							|  |  |  |                   key: `mixed-${i}-${dsRequest.requestId || ''}`, | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |               ]); | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  |             }) | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2020-03-13 01:16:32 +08:00
										 |  |  |       ) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-09-12 23:28:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 18:54:22 +08:00
										 |  |  |     return forkJoin(runningQueries).pipe(flattenResponses(), map(this.finalizeResponses), mergeAll()); | 
					
						
							| 
									
										
										
										
											2019-09-09 17:29:37 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   testDatasource() { | 
					
						
							|  |  |  |     return Promise.resolve({}); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-13 01:16:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   private isQueryable(query: BatchedQueries): boolean { | 
					
						
							|  |  |  |     return query && Array.isArray(query.targets) && query.targets.length > 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 14:52:40 +08:00
										 |  |  |   private finalizeResponses(responses: DataQueryResponse[]): DataQueryResponse[] { | 
					
						
							| 
									
										
										
										
											2020-03-13 01:16:32 +08:00
										 |  |  |     const { length } = responses; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (length === 0) { | 
					
						
							|  |  |  |       return responses; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |     const error = responses.find((response) => response.state === LoadingState.Error); | 
					
						
							| 
									
										
										
										
											2020-08-19 14:52:40 +08:00
										 |  |  |     if (error) { | 
					
						
							|  |  |  |       responses.push(error); // adds the first found error entry so error shows up in the panel
 | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       responses[length - 1].state = LoadingState.Done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-13 01:16:32 +08:00
										 |  |  |     return responses; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-09-09 17:29:37 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 18:54:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | function flattenResponses(): OperatorFunction<DataQueryResponse[][], DataQueryResponse[]> { | 
					
						
							|  |  |  |   return reduce((all: DataQueryResponse[], current) => { | 
					
						
							|  |  |  |     return current.reduce((innerAll, innerCurrent) => { | 
					
						
							|  |  |  |       innerAll.push.apply(innerAll, innerCurrent); | 
					
						
							|  |  |  |       return innerAll; | 
					
						
							|  |  |  |     }, all); | 
					
						
							|  |  |  |   }, []); | 
					
						
							|  |  |  | } |