1import { Observable } from 'rxjs';
2import { ComponentType } from 'react';
3import { GrafanaPlugin, PluginMeta } from './plugin';
4import { PanelData } from './panel';
5import { LogRowModel } from './logs';
6import { AnnotationEvent, AnnotationQuery, AnnotationSupport } from './annotations';
7import { KeyValue, LoadingState, TableData, TimeSeries } from './data';
8import { DataFrame, DataFrameDTO } from './dataFrame';
9import { RawTimeRange, TimeRange } from './time';
10import { ScopedVars } from './ScopedVars';
11import { CoreApp } from './app';
12import { CustomVariableSupport, DataSourceVariableSupport, StandardVariableSupport } from './variables';
13import { makeClassES5Compatible } from '../utils/makeClassES5Compatible';
14import { DataQuery } from './query';
15import { DataSourceRef } from '.';
16
17export interface DataSourcePluginOptionsEditorProps<JSONData = DataSourceJsonData, SecureJSONData = {}> {
18  options: DataSourceSettings<JSONData, SecureJSONData>;
19  onOptionsChange: (options: DataSourceSettings<JSONData, SecureJSONData>) => void;
20}
21
22// Utility type to extract the query type TQuery from a class extending DataSourceApi<TQuery, TOptions>
23export type DataSourceQueryType<DSType> = DSType extends DataSourceApi<infer TQuery, any> ? TQuery : never;
24
25// Utility type to extract the options type TOptions from a class extending DataSourceApi<TQuery, TOptions>
26export type DataSourceOptionsType<DSType> = DSType extends DataSourceApi<any, infer TOptions> ? TOptions : never;
27
28export class DataSourcePlugin<
29  DSType extends DataSourceApi<TQuery, TOptions>,
30  TQuery extends DataQuery = DataSourceQueryType<DSType>,
31  TOptions extends DataSourceJsonData = DataSourceOptionsType<DSType>,
32  TSecureOptions = {}
33> extends GrafanaPlugin<DataSourcePluginMeta<TOptions>> {
34  components: DataSourcePluginComponents<DSType, TQuery, TOptions, TSecureOptions> = {};
35
36  constructor(public DataSourceClass: DataSourceConstructor<DSType, TQuery, TOptions>) {
37    super();
38  }
39
40  setConfigEditor(editor: ComponentType<DataSourcePluginOptionsEditorProps<TOptions, TSecureOptions>>) {
41    this.components.ConfigEditor = editor;
42    return this;
43  }
44
45  setConfigCtrl(ConfigCtrl: any) {
46    this.angularConfigCtrl = ConfigCtrl;
47    return this;
48  }
49
50  setQueryCtrl(QueryCtrl: any) {
51    this.components.QueryCtrl = QueryCtrl;
52    return this;
53  }
54
55  setAnnotationQueryCtrl(AnnotationsQueryCtrl: any) {
56    this.components.AnnotationsQueryCtrl = AnnotationsQueryCtrl;
57    return this;
58  }
59
60  setQueryEditor(QueryEditor: ComponentType<QueryEditorProps<DSType, TQuery, TOptions>>) {
61    this.components.QueryEditor = QueryEditor;
62    return this;
63  }
64
65  setExploreQueryField(ExploreQueryField: ComponentType<QueryEditorProps<DSType, TQuery, TOptions>>) {
66    this.components.ExploreQueryField = ExploreQueryField;
67    return this;
68  }
69
70  setExploreMetricsQueryField(ExploreQueryField: ComponentType<QueryEditorProps<DSType, TQuery, TOptions>>) {
71    this.components.ExploreMetricsQueryField = ExploreQueryField;
72    return this;
73  }
74
75  setExploreLogsQueryField(ExploreQueryField: ComponentType<QueryEditorProps<DSType, TQuery, TOptions>>) {
76    this.components.ExploreLogsQueryField = ExploreQueryField;
77    return this;
78  }
79
80  setQueryEditorHelp(QueryEditorHelp: ComponentType<QueryEditorHelpProps<TQuery>>) {
81    this.components.QueryEditorHelp = QueryEditorHelp;
82    return this;
83  }
84
85  /**
86   * @deprecated prefer using `setQueryEditorHelp`
87   */
88  setExploreStartPage(ExploreStartPage: ComponentType<QueryEditorHelpProps<TQuery>>) {
89    return this.setQueryEditorHelp(ExploreStartPage);
90  }
91
92  /*
93   * @deprecated -- prefer using {@link StandardVariableSupport} or {@link CustomVariableSupport} or {@link DataSourceVariableSupport} in data source instead
94   * */
95  setVariableQueryEditor(VariableQueryEditor: any) {
96    this.components.VariableQueryEditor = VariableQueryEditor;
97    return this;
98  }
99
100  setMetadataInspector(MetadataInspector: ComponentType<MetadataInspectorProps<DSType, TQuery, TOptions>>) {
101    this.components.MetadataInspector = MetadataInspector;
102    return this;
103  }
104
105  setComponentsFromLegacyExports(pluginExports: any) {
106    this.angularConfigCtrl = pluginExports.ConfigCtrl;
107
108    this.components.QueryCtrl = pluginExports.QueryCtrl;
109    this.components.AnnotationsQueryCtrl = pluginExports.AnnotationsQueryCtrl;
110    this.components.ExploreQueryField = pluginExports.ExploreQueryField;
111    this.components.QueryEditor = pluginExports.QueryEditor;
112    this.components.QueryEditorHelp = pluginExports.QueryEditorHelp;
113    this.components.VariableQueryEditor = pluginExports.VariableQueryEditor;
114  }
115}
116
117export interface DataSourcePluginMeta<T extends KeyValue = {}> extends PluginMeta<T> {
118  builtIn?: boolean; // Is this for all
119  metrics?: boolean;
120  logs?: boolean;
121  annotations?: boolean;
122  alerting?: boolean;
123  tracing?: boolean;
124  mixed?: boolean;
125  hasQueryHelp?: boolean;
126  category?: string;
127  queryOptions?: PluginMetaQueryOptions;
128  sort?: number;
129  streaming?: boolean;
130  unlicensed?: boolean;
131  backend?: boolean;
132  isBackend?: boolean;
133}
134
135interface PluginMetaQueryOptions {
136  cacheTimeout?: boolean;
137  maxDataPoints?: boolean;
138  minInterval?: boolean;
139}
140
141export interface DataSourcePluginComponents<
142  DSType extends DataSourceApi<TQuery, TOptions>,
143  TQuery extends DataQuery = DataQuery,
144  TOptions extends DataSourceJsonData = DataSourceJsonData,
145  TSecureOptions = {}
146> {
147  QueryCtrl?: any;
148  AnnotationsQueryCtrl?: any;
149  VariableQueryEditor?: any;
150  QueryEditor?: ComponentType<QueryEditorProps<DSType, TQuery, TOptions>>;
151  ExploreQueryField?: ComponentType<QueryEditorProps<DSType, TQuery, TOptions>>;
152  ExploreMetricsQueryField?: ComponentType<QueryEditorProps<DSType, TQuery, TOptions>>;
153  ExploreLogsQueryField?: ComponentType<QueryEditorProps<DSType, TQuery, TOptions>>;
154  QueryEditorHelp?: ComponentType<QueryEditorHelpProps<TQuery>>;
155  ConfigEditor?: ComponentType<DataSourcePluginOptionsEditorProps<TOptions, TSecureOptions>>;
156  MetadataInspector?: ComponentType<MetadataInspectorProps<DSType, TQuery, TOptions>>;
157}
158
159// Only exported for tests
160export interface DataSourceConstructor<
161  DSType extends DataSourceApi<TQuery, TOptions>,
162  TQuery extends DataQuery = DataQuery,
163  TOptions extends DataSourceJsonData = DataSourceJsonData
164> {
165  new (instanceSettings: DataSourceInstanceSettings<TOptions>, ...args: any[]): DSType;
166}
167
168/**
169 * The main data source abstraction interface, represents an instance of a data source
170 *
171 * Although this is a class, datasource implementations do not *yet* need to extend it.
172 * As such, we can not yet add functions with default implementations.
173 */
174abstract class DataSourceApi<
175  TQuery extends DataQuery = DataQuery,
176  TOptions extends DataSourceJsonData = DataSourceJsonData,
177  TQueryImportConfiguration extends Record<string, object> = {}
178> {
179  /**
180   *  Set in constructor
181   */
182  readonly name: string;
183
184  /**
185   *  Set in constructor
186   */
187  readonly id: number;
188
189  /**
190   *  Set in constructor
191   */
192  readonly type: string;
193
194  /**
195   *  Set in constructor
196   */
197  readonly uid: string;
198
199  /**
200   *  min interval range
201   */
202  interval?: string;
203
204  constructor(instanceSettings: DataSourceInstanceSettings<TOptions>) {
205    this.name = instanceSettings.name;
206    this.id = instanceSettings.id;
207    this.type = instanceSettings.type;
208    this.meta = instanceSettings.meta;
209    this.uid = instanceSettings.uid;
210  }
211
212  /**
213   * Imports queries from a different datasource
214   */
215  async importQueries?(queries: DataQuery[], originDataSource: DataSourceApi<DataQuery>): Promise<TQuery[]>;
216
217  /**
218   * Returns configuration for importing queries from other data sources
219   */
220  getImportQueryConfiguration?(): TQueryImportConfiguration;
221
222  /**
223   * Initializes a datasource after instantiation
224   */
225  init?: () => void;
226
227  /**
228   * Query for data, and optionally stream results
229   */
230  abstract query(request: DataQueryRequest<TQuery>): Promise<DataQueryResponse> | Observable<DataQueryResponse>;
231
232  /**
233   * Test & verify datasource settings & connection details (returning TestingStatus)
234   *
235   * When verification fails - errors specific to the data source should be handled here and converted to
236   * a TestingStatus object. Unknown errors and HTTP errors can be re-thrown and will be handled here:
237   * public/app/features/datasources/state/actions.ts
238   */
239  abstract testDatasource(): Promise<any>;
240
241  /**
242   * Override to skip executing a query
243   *
244   * @returns false if the query should be skipped
245   *
246   * @virtual
247   */
248  filterQuery?(query: TQuery): boolean;
249
250  /**
251   *  Get hints for query improvements
252   */
253  getQueryHints?(query: TQuery, results: any[], ...rest: any): QueryHint[];
254
255  /**
256   * Convert a query to a simple text string
257   */
258  getQueryDisplayText?(query: TQuery): string;
259
260  /**
261   * @deprecated getLogRowContext and showContextToggle in `DataSourceApi` is deprecated.
262   *
263   * DataSourceWithLogsContextSupport should be implemented instead (these methods have exactly
264   * the same signature in DataSourceWithLogsContextSupport).
265   * This method will be removed from DataSourceApi in the future. Some editors may still show
266   * a deprecation warning which can be ignored for time being.
267   */
268  getLogRowContext?: <TContextQueryOptions extends {}>(
269    row: LogRowModel,
270    options?: TContextQueryOptions
271  ) => Promise<DataQueryResponse>;
272
273  /**
274   * @deprecated getLogRowContext and showContextToggle in `DataSourceApi` is deprecated.
275   *
276   * DataSourceWithLogsContextSupport should be implemented instead (these methods have exactly
277   * the same signature in DataSourceWithLogsContextSupport).
278   * This method will be removed from DataSourceApi in the future. Some editors may still show
279   * a deprecation warning which can be ignored for time being.
280   */
281  showContextToggle?(row?: LogRowModel): boolean;
282
283  /**
284   * Variable query action.
285   */
286  metricFindQuery?(query: any, options?: any): Promise<MetricFindValue[]>;
287
288  /**
289   * Get tag keys for adhoc filters
290   */
291  getTagKeys?(options?: any): Promise<MetricFindValue[]>;
292
293  /**
294   * Get tag values for adhoc filters
295   */
296  getTagValues?(options: any): Promise<MetricFindValue[]>;
297
298  /**
299   * Set after constructor call, as the data source instance is the most common thing to pass around
300   * we attach the components to this instance for easy access
301   */
302  components?: DataSourcePluginComponents<DataSourceApi<TQuery, TOptions>, TQuery, TOptions>;
303
304  /**
305   * static information about the datasource
306   */
307  meta: DataSourcePluginMeta;
308
309  /**
310   * Used by alerting to check if query contains template variables
311   */
312  targetContainsTemplate?(query: TQuery): boolean;
313
314  /**
315   * Used in explore
316   */
317  modifyQuery?(query: TQuery, action: QueryFixAction): TQuery;
318
319  /**
320   * @deprecated since version 8.2.0
321   * Not used anymore.
322   */
323  getHighlighterExpression?(query: TQuery): string[];
324
325  /** Get an identifier object for this datasource instance */
326  getRef(): DataSourceRef {
327    return { type: this.type, uid: this.uid };
328  }
329
330  /**
331   * Used in explore
332   */
333  languageProvider?: any;
334
335  getVersion?(optionalOptions?: any): Promise<string>;
336
337  interpolateVariablesInQueries?(queries: TQuery[], scopedVars: ScopedVars | {}): TQuery[];
338
339  /**
340   * An annotation processor allows explicit control for how annotations are managed.
341   *
342   * It is only necessary to configure an annotation processor if the default behavior is not desirable
343   */
344  annotations?: AnnotationSupport<TQuery>;
345
346  /**
347   * Can be optionally implemented to allow datasource to be a source of annotations for dashboard.
348   * This function will only be called if an angular {@link AnnotationsQueryCtrl} is configured and
349   * the {@link annotations} is undefined
350   *
351   * @deprecated -- prefer using {@link AnnotationSupport}
352   */
353  annotationQuery?(options: AnnotationQueryRequest<TQuery>): Promise<AnnotationEvent[]>;
354
355  /**
356   * Defines new variable support
357   * @alpha -- experimental
358   */
359  variables?:
360    | StandardVariableSupport<DataSourceApi<TQuery, TOptions>>
361    | CustomVariableSupport<DataSourceApi<TQuery, TOptions>>
362    | DataSourceVariableSupport<DataSourceApi<TQuery, TOptions>>;
363}
364
365export interface MetadataInspectorProps<
366  DSType extends DataSourceApi<TQuery, TOptions>,
367  TQuery extends DataQuery = DataQuery,
368  TOptions extends DataSourceJsonData = DataSourceJsonData
369> {
370  datasource: DSType;
371
372  // All Data from this DataSource
373  data: DataFrame[];
374}
375
376export interface QueryEditorProps<
377  DSType extends DataSourceApi<TQuery, TOptions>,
378  TQuery extends DataQuery = DataQuery,
379  TOptions extends DataSourceJsonData = DataSourceJsonData,
380  TVQuery extends DataQuery = TQuery
381> {
382  datasource: DSType;
383  query: TVQuery;
384  onRunQuery: () => void;
385  onChange: (value: TVQuery) => void;
386  onBlur?: () => void;
387  /**
388   * Contains query response filtered by refId of QueryResultBase and possible query error
389   */
390  data?: PanelData;
391  range?: TimeRange;
392  exploreId?: any;
393  history?: Array<HistoryItem<TQuery>>;
394  queries?: DataQuery[];
395  app?: CoreApp;
396}
397
398// TODO: not really needed but used as type in some data sources and in DataQueryRequest
399export enum ExploreMode {
400  Logs = 'Logs',
401  Metrics = 'Metrics',
402  Tracing = 'Tracing',
403}
404
405/**
406 * @deprecated use QueryEditorProps instead
407 */
408export type ExploreQueryFieldProps<
409  DSType extends DataSourceApi<TQuery, TOptions>,
410  TQuery extends DataQuery = DataQuery,
411  TOptions extends DataSourceJsonData = DataSourceJsonData
412> = QueryEditorProps<DSType, TQuery, TOptions>;
413
414export interface QueryEditorHelpProps<TQuery extends DataQuery = DataQuery> {
415  datasource: DataSourceApi<TQuery>;
416  onClickExample: (query: TQuery) => void;
417  exploreId?: any;
418}
419
420/**
421 * Starting in v6.2 DataFrame can represent both TimeSeries and TableData
422 */
423export type LegacyResponseData = TimeSeries | TableData | any;
424
425export type DataQueryResponseData = DataFrame | DataFrameDTO | LegacyResponseData;
426
427export interface DataQueryResponse {
428  /**
429   * The response data.  When streaming, this may be empty
430   * or a partial result set
431   */
432  data: DataQueryResponseData[];
433
434  /**
435   * When returning multiple partial responses or streams
436   * Use this key to inform Grafana how to combine the partial responses
437   * Multiple responses with same key are replaced (latest used)
438   */
439  key?: string;
440
441  /**
442   * Optionally include error info along with the response data
443   */
444  error?: DataQueryError;
445
446  /**
447   * Use this to control which state the response should have
448   * Defaults to LoadingState.Done if state is not defined
449   */
450  state?: LoadingState;
451}
452
453export enum DataQueryErrorType {
454  Cancelled = 'cancelled',
455  Timeout = 'timeout',
456  Unknown = 'unknown',
457}
458
459export interface DataQueryError {
460  data?: {
461    /**
462     * Short information about the error
463     */
464    message?: string;
465    /**
466     * Detailed information about the error. Only returned when app_mode is development.
467     */
468    error?: string;
469  };
470  message?: string;
471  status?: string;
472  statusText?: string;
473  refId?: string;
474  type?: DataQueryErrorType;
475}
476
477export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
478  requestId: string; // Used to identify results and optionally cancel the request in backendSrv
479
480  interval: string;
481  intervalMs: number;
482  maxDataPoints?: number;
483  range: TimeRange;
484  reverse?: boolean;
485  scopedVars: ScopedVars;
486  targets: TQuery[];
487  timezone: string;
488  app: CoreApp | string;
489
490  cacheTimeout?: string | null;
491  rangeRaw?: RawTimeRange;
492  timeInfo?: string; // The query time description (blue text in the upper right)
493  panelId?: number;
494  dashboardId?: number;
495
496  // Request Timing
497  startTime: number;
498  endTime?: number;
499
500  // Explore state used by various datasources
501  liveStreaming?: boolean;
502}
503
504export interface DataQueryTimings {
505  dataProcessingTime: number;
506}
507
508export interface QueryFix {
509  label: string;
510  action?: QueryFixAction;
511}
512
513export interface QueryFixAction {
514  type: string;
515  query?: string;
516  preventSubmit?: boolean;
517}
518
519export interface QueryHint {
520  type: string;
521  label: string;
522  fix?: QueryFix;
523}
524
525export interface MetricFindValue {
526  text: string;
527  value?: string | number;
528  expandable?: boolean;
529}
530
531export interface DataSourceJsonData {
532  authType?: string;
533  defaultRegion?: string;
534  profile?: string;
535  manageAlerts?: boolean;
536  alertmanagerUid?: string;
537}
538
539/**
540 * Data Source instance edit model.  This is returned from:
541 *  /api/datasources
542 */
543export interface DataSourceSettings<T extends DataSourceJsonData = DataSourceJsonData, S = {}> {
544  id: number;
545  uid: string;
546  orgId: number;
547  name: string;
548  typeLogoUrl: string;
549  type: string;
550  typeName: string;
551  access: string;
552  url: string;
553  password: string;
554  user: string;
555  database: string;
556  basicAuth: boolean;
557  basicAuthPassword: string;
558  basicAuthUser: string;
559  isDefault: boolean;
560  jsonData: T;
561  secureJsonData?: S;
562  secureJsonFields: KeyValue<boolean>;
563  readOnly: boolean;
564  withCredentials: boolean;
565  version?: number;
566}
567
568/**
569 * Frontend settings model that is passed to Datasource constructor. This differs a bit from the model above
570 * as this data model is available to every user who has access to a data source (Viewers+).  This is loaded
571 * in bootData (on page load), or from: /api/frontend/settings
572 */
573export interface DataSourceInstanceSettings<T extends DataSourceJsonData = DataSourceJsonData> {
574  id: number;
575  uid: string;
576  type: string;
577  name: string;
578  meta: DataSourcePluginMeta;
579  url?: string;
580  jsonData: T;
581  username?: string;
582  password?: string; // when access is direct, for some legacy datasources
583  database?: string;
584  isDefault?: boolean;
585  access: 'direct' | 'proxy'; // Currently we support 2 options - direct (browser) and proxy (server)
586
587  /**
588   * This is the full Authorization header if basic auth is enabled.
589   * Only available here when access is Browser (direct), when access is Server (proxy)
590   * The basic auth header, username & password is never exposed to browser/Frontend
591   * so this will be empty then.
592   */
593  basicAuth?: string;
594  withCredentials?: boolean;
595
596  /** When the name+uid are based on template variables, maintain access to the real values */
597  rawRef?: DataSourceRef;
598}
599
600/**
601 * @deprecated -- use {@link DataSourceInstanceSettings} instead
602 */
603export interface DataSourceSelectItem {
604  name: string;
605  value: string | null;
606  meta: DataSourcePluginMeta;
607}
608
609/**
610 * Options passed to the datasource.annotationQuery method. See docs/plugins/developing/datasource.md
611 *
612 * @deprecated -- use {@link AnnotationSupport}
613 */
614export interface AnnotationQueryRequest<MoreOptions = {}> {
615  range: TimeRange;
616  rangeRaw: RawTimeRange;
617  // Should be DataModel but cannot import that here from the main app. Needs to be moved to package first.
618  dashboard: any;
619  annotation: AnnotationQuery;
620}
621
622export interface HistoryItem<TQuery extends DataQuery = DataQuery> {
623  ts: number;
624  query: TQuery;
625}
626
627abstract class LanguageProvider {
628  abstract datasource: DataSourceApi<any, any>;
629  abstract request: (url: string, params?: any) => Promise<any>;
630
631  /**
632   * Returns startTask that resolves with a task list when main syntax is loaded.
633   * Task list consists of secondary promises that load more detailed language features.
634   */
635  abstract start: () => Promise<Array<Promise<any>>>;
636  startTask?: Promise<any[]>;
637}
638
639//@ts-ignore
640LanguageProvider = makeClassES5Compatible(LanguageProvider);
641export { LanguageProvider };
642
643//@ts-ignore
644DataSourceApi = makeClassES5Compatible(DataSourceApi);
645
646export { DataSourceApi };
647