| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | import { map as _map } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2021-08-19 19:43:41 +08:00
										 |  |  | import { lastValueFrom, of } from 'rxjs'; | 
					
						
							|  |  |  | import { map, catchError } from 'rxjs/operators'; | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | import { BackendDataSourceResponse, DataSourceWithBackend, FetchResponse, getBackendSrv } from '@grafana/runtime'; | 
					
						
							|  |  |  | import { AnnotationEvent, DataSourceInstanceSettings, MetricFindValue, ScopedVars } from '@grafana/data'; | 
					
						
							| 
									
										
										
										
											2020-09-15 14:06:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  | import ResponseParser from './response_parser'; | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | import PostgresQueryModel from 'app/plugins/datasource/postgres/postgres_query_model'; | 
					
						
							| 
									
										
										
										
											2020-10-02 01:51:23 +08:00
										 |  |  | import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; | 
					
						
							|  |  |  | import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; | 
					
						
							| 
									
										
										
										
											2019-10-08 23:01:20 +08:00
										 |  |  | //Types
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | import { PostgresOptions, PostgresQuery, PostgresQueryForInterpolation } from './types'; | 
					
						
							| 
									
										
										
										
											2020-06-04 19:44:48 +08:00
										 |  |  | import { getSearchFilterScopedVar } from '../../../features/variables/utils'; | 
					
						
							| 
									
										
										
										
											2021-07-08 20:32:27 +08:00
										 |  |  | import { toTestingStatus } from '@grafana/runtime/src/utils/queryResponse'; | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | export class PostgresDatasource extends DataSourceWithBackend<PostgresQuery, PostgresOptions> { | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |   id: any; | 
					
						
							|  |  |  |   name: any; | 
					
						
							| 
									
										
										
										
											2018-08-14 21:27:58 +08:00
										 |  |  |   jsonData: any; | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |   responseParser: ResponseParser; | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |   queryModel: PostgresQueryModel; | 
					
						
							| 
									
										
										
										
											2018-09-05 15:46:22 +08:00
										 |  |  |   interval: string; | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-01 17:11:57 +08:00
										 |  |  |   constructor( | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |     instanceSettings: DataSourceInstanceSettings<PostgresOptions>, | 
					
						
							| 
									
										
										
										
											2020-10-02 01:51:23 +08:00
										 |  |  |     private readonly templateSrv: TemplateSrv = getTemplateSrv(), | 
					
						
							|  |  |  |     private readonly timeSrv: TimeSrv = getTimeSrv() | 
					
						
							| 
									
										
										
										
											2019-07-01 17:11:57 +08:00
										 |  |  |   ) { | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |     super(instanceSettings); | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |     this.name = instanceSettings.name; | 
					
						
							|  |  |  |     this.id = instanceSettings.id; | 
					
						
							| 
									
										
										
										
											2018-08-14 21:27:58 +08:00
										 |  |  |     this.jsonData = instanceSettings.jsonData; | 
					
						
							| 
									
										
										
										
											2019-12-05 17:04:03 +08:00
										 |  |  |     this.responseParser = new ResponseParser(); | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |     this.queryModel = new PostgresQueryModel({}); | 
					
						
							|  |  |  |     const settingsData = instanceSettings.jsonData || ({} as PostgresOptions); | 
					
						
							|  |  |  |     this.interval = settingsData.timeInterval || '1m'; | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-15 14:06:04 +08:00
										 |  |  |   interpolateVariable = (value: string | string[], variable: { multi: any; includeAll: any }) => { | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  |     if (typeof value === 'string') { | 
					
						
							| 
									
										
										
										
											2017-10-31 19:23:21 +08:00
										 |  |  |       if (variable.multi || variable.includeAll) { | 
					
						
							| 
									
										
										
										
											2018-08-18 22:08:38 +08:00
										 |  |  |         return this.queryModel.quoteLiteral(value); | 
					
						
							| 
									
										
										
										
											2017-10-31 19:23:21 +08:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         return value; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  |     if (typeof value === 'number') { | 
					
						
							| 
									
										
										
										
											2017-10-24 20:05:41 +08:00
										 |  |  |       return value; | 
					
						
							| 
									
										
										
										
											2017-10-19 00:10:01 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-21 15:38:00 +08:00
										 |  |  |     const quotedValues = _map(value, (v) => { | 
					
						
							| 
									
										
										
										
											2018-08-18 22:08:38 +08:00
										 |  |  |       return this.queryModel.quoteLiteral(v); | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  |     return quotedValues.join(','); | 
					
						
							| 
									
										
										
										
											2018-10-17 19:30:07 +08:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 16:50:09 +08:00
										 |  |  |   interpolateVariablesInQueries( | 
					
						
							|  |  |  |     queries: PostgresQueryForInterpolation[], | 
					
						
							|  |  |  |     scopedVars: ScopedVars | 
					
						
							|  |  |  |   ): PostgresQueryForInterpolation[] { | 
					
						
							| 
									
										
										
										
											2019-10-08 23:01:20 +08:00
										 |  |  |     let expandedQueries = queries; | 
					
						
							|  |  |  |     if (queries && queries.length > 0) { | 
					
						
							| 
									
										
										
										
											2021-01-20 14:59:48 +08:00
										 |  |  |       expandedQueries = queries.map((query) => { | 
					
						
							| 
									
										
										
										
											2019-10-08 23:01:20 +08:00
										 |  |  |         const expandedQuery = { | 
					
						
							|  |  |  |           ...query, | 
					
						
							| 
									
										
										
										
											2021-10-30 01:57:24 +08:00
										 |  |  |           datasource: this.getRef(), | 
					
						
							| 
									
										
										
										
											2020-01-24 16:50:09 +08:00
										 |  |  |           rawSql: this.templateSrv.replace(query.rawSql, scopedVars, this.interpolateVariable), | 
					
						
							| 
									
										
										
										
											2020-05-28 20:28:56 +08:00
										 |  |  |           rawQuery: true, | 
					
						
							| 
									
										
										
										
											2019-10-08 23:01:20 +08:00
										 |  |  |         }; | 
					
						
							|  |  |  |         return expandedQuery; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return expandedQueries; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |   filterQuery(query: PostgresQuery): boolean { | 
					
						
							|  |  |  |     return !query.hide; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |   applyTemplateVariables(target: PostgresQuery, scopedVars: ScopedVars): Record<string, any> { | 
					
						
							|  |  |  |     const queryModel = new PostgresQueryModel(target, this.templateSrv, scopedVars); | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       refId: target.refId, | 
					
						
							|  |  |  |       datasourceId: this.id, | 
					
						
							|  |  |  |       rawSql: queryModel.render(this.interpolateVariable as any), | 
					
						
							|  |  |  |       format: target.format, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |   async annotationQuery(options: any): Promise<AnnotationEvent[]> { | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |     if (!options.annotation.rawQuery) { | 
					
						
							| 
									
										
										
										
											2019-12-05 17:04:03 +08:00
										 |  |  |       return Promise.reject({ | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  |         message: 'Query missing in annotation definition', | 
					
						
							| 
									
										
										
										
											2017-12-19 23:06:54 +08:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const query = { | 
					
						
							|  |  |  |       refId: options.annotation.name, | 
					
						
							|  |  |  |       datasourceId: this.id, | 
					
						
							| 
									
										
										
										
											2018-10-17 19:30:07 +08:00
										 |  |  |       rawSql: this.templateSrv.replace(options.annotation.rawQuery, options.scopedVars, this.interpolateVariable), | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  |       format: 'table', | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-19 12:38:31 +08:00
										 |  |  |     return lastValueFrom( | 
					
						
							|  |  |  |       getBackendSrv() | 
					
						
							|  |  |  |         .fetch<BackendDataSourceResponse>({ | 
					
						
							|  |  |  |           url: '/api/ds/query', | 
					
						
							|  |  |  |           method: 'POST', | 
					
						
							|  |  |  |           data: { | 
					
						
							|  |  |  |             from: options.range.from.valueOf().toString(), | 
					
						
							|  |  |  |             to: options.range.to.valueOf().toString(), | 
					
						
							|  |  |  |             queries: [query], | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           requestId: options.annotation.name, | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         .pipe( | 
					
						
							|  |  |  |           map( | 
					
						
							|  |  |  |             async (res: FetchResponse<BackendDataSourceResponse>) => | 
					
						
							|  |  |  |               await this.responseParser.transformAnnotationResponse(options, res.data) | 
					
						
							|  |  |  |           ) | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-08-19 12:38:31 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |   metricFindQuery(query: string, optionalOptions: any): Promise<MetricFindValue[]> { | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  |     let refId = 'tempvar'; | 
					
						
							| 
									
										
										
										
											2017-12-21 15:39:31 +08:00
										 |  |  |     if (optionalOptions && optionalOptions.variable && optionalOptions.variable.name) { | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |       refId = optionalOptions.variable.name; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-04 13:43:03 +08:00
										 |  |  |     const rawSql = this.templateSrv.replace( | 
					
						
							|  |  |  |       query, | 
					
						
							|  |  |  |       getSearchFilterScopedVar({ query, wildcardChar: '%', options: optionalOptions }), | 
					
						
							|  |  |  |       this.interpolateVariable | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-10-18 17:40:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |     const interpolatedQuery = { | 
					
						
							|  |  |  |       refId: refId, | 
					
						
							|  |  |  |       datasourceId: this.id, | 
					
						
							| 
									
										
										
										
											2019-10-18 17:40:08 +08:00
										 |  |  |       rawSql, | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  |       format: 'table', | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-01 01:02:46 +08:00
										 |  |  |     const range = this.timeSrv.timeRange(); | 
					
						
							| 
									
										
										
										
											2017-12-07 17:05:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-19 12:38:31 +08:00
										 |  |  |     return lastValueFrom( | 
					
						
							|  |  |  |       getBackendSrv() | 
					
						
							|  |  |  |         .fetch<BackendDataSourceResponse>({ | 
					
						
							|  |  |  |           url: '/api/ds/query', | 
					
						
							|  |  |  |           method: 'POST', | 
					
						
							|  |  |  |           data: { | 
					
						
							|  |  |  |             from: range.from.valueOf().toString(), | 
					
						
							|  |  |  |             to: range.to.valueOf().toString(), | 
					
						
							|  |  |  |             queries: [interpolatedQuery], | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           requestId: refId, | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |         }) | 
					
						
							| 
									
										
										
										
											2021-08-19 12:38:31 +08:00
										 |  |  |         .pipe( | 
					
						
							|  |  |  |           map((rsp) => { | 
					
						
							|  |  |  |             return this.responseParser.transformMetricFindResponse(rsp); | 
					
						
							| 
									
										
										
										
											2021-08-19 19:43:41 +08:00
										 |  |  |           }), | 
					
						
							|  |  |  |           catchError((err) => { | 
					
						
							|  |  |  |             return of([]); | 
					
						
							| 
									
										
										
										
											2021-08-19 12:38:31 +08:00
										 |  |  |           }) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |   getVersion(): Promise<any> { | 
					
						
							| 
									
										
										
										
											2018-08-15 15:48:06 +08:00
										 |  |  |     return this.metricFindQuery("SELECT current_setting('server_version_num')::int/100", {}); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |   getTimescaleDBVersion(): Promise<any> { | 
					
						
							| 
									
										
										
										
											2018-08-15 16:41:06 +08:00
										 |  |  |     return this.metricFindQuery("SELECT extversion FROM pg_extension WHERE extname = 'timescaledb'", {}); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |   testDatasource(): Promise<any> { | 
					
						
							| 
									
										
										
										
											2018-07-22 02:00:26 +08:00
										 |  |  |     return this.metricFindQuery('SELECT 1', {}) | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |       .then(() => { | 
					
						
							| 
									
										
										
										
											2017-12-20 19:33:33 +08:00
										 |  |  |         return { status: 'success', message: 'Database Connection OK' }; | 
					
						
							| 
									
										
										
										
											2017-12-19 23:06:54 +08:00
										 |  |  |       }) | 
					
						
							| 
									
										
										
										
											2019-07-01 17:11:57 +08:00
										 |  |  |       .catch((err: any) => { | 
					
						
							| 
									
										
										
										
											2021-07-08 20:32:27 +08:00
										 |  |  |         return toTestingStatus(err); | 
					
						
							| 
									
										
										
										
											2017-12-19 23:06:54 +08:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-09-25 02:46:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   targetContainsTemplate(target: any) { | 
					
						
							|  |  |  |     let rawSql = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (target.rawQuery) { | 
					
						
							|  |  |  |       rawSql = target.rawSql; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  |       const query = new PostgresQueryModel(target); | 
					
						
							| 
									
										
										
										
											2019-09-25 02:46:07 +08:00
										 |  |  |       rawSql = query.buildQuery(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rawSql = rawSql.replace('$__', ''); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return this.templateSrv.variableExists(rawSql); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-10-10 21:19:14 +08:00
										 |  |  | } |