1import { cloneDeep } from 'lodash';
2import { Observable, of } from 'rxjs';
3import { AnnotationEvent, AnnotationQuery, DataFrame, DataFrameView, DataSourceApi } from '@grafana/data';
4import { config, toDataQueryError } from '@grafana/runtime';
5
6import { dispatch } from 'app/store/store';
7import { createErrorNotification } from '../../../../core/copy/appNotification';
8import { notifyApp } from '../../../../core/reducers/appNotification';
9import { DashboardQueryRunnerWorkerResult } from './types';
10
11export function handleAnnotationQueryRunnerError(err: any): Observable<AnnotationEvent[]> {
12  if (err.cancelled) {
13    return of([]);
14  }
15
16  notifyWithError('AnnotationQueryRunner failed', err);
17  return of([]);
18}
19
20export function handleDatasourceSrvError(err: any): Observable<DataSourceApi | undefined> {
21  notifyWithError('Failed to retrieve datasource', err);
22  return of(undefined);
23}
24
25export const emptyResult: () => Observable<DashboardQueryRunnerWorkerResult> = () =>
26  of({ annotations: [], alertStates: [] });
27
28export function handleDashboardQueryRunnerWorkerError(err: any): Observable<DashboardQueryRunnerWorkerResult> {
29  if (err.cancelled) {
30    return emptyResult();
31  }
32
33  notifyWithError('DashboardQueryRunner failed', err);
34  return emptyResult();
35}
36
37function notifyWithError(title: string, err: any) {
38  const error = toDataQueryError(err);
39  console.error('handleAnnotationQueryRunnerError', error);
40  const notification = createErrorNotification(title, error.message);
41  dispatch(notifyApp(notification));
42}
43
44export function getAnnotationsByPanelId(annotations: AnnotationEvent[], panelId?: number) {
45  return annotations.filter((item) => {
46    if (panelId !== undefined && item.panelId && item.source?.type === 'dashboard') {
47      return item.panelId === panelId;
48    }
49    return true;
50  });
51}
52
53export function translateQueryResult(annotation: AnnotationQuery, results: AnnotationEvent[]): AnnotationEvent[] {
54  // if annotation has snapshotData
55  // make clone and remove it
56  if (annotation.snapshotData) {
57    annotation = cloneDeep(annotation);
58    delete annotation.snapshotData;
59  }
60
61  for (const item of results) {
62    item.source = annotation;
63    item.color = config.theme2.visualization.getColorByName(annotation.iconColor);
64    item.type = annotation.name;
65    item.isRegion = Boolean(item.timeEnd && item.time !== item.timeEnd);
66
67    switch (item.newState?.toLowerCase()) {
68      case 'pending':
69        item.color = 'yellow';
70        break;
71      case 'alerting':
72        item.color = 'red';
73        break;
74      case 'ok':
75        item.color = 'green';
76        break;
77      case 'normal': // ngalert ("normal" instead of "ok")
78        item.color = 'green';
79        break;
80      case 'no_data':
81        item.color = 'gray';
82        break;
83      case 'nodata': // ngalert
84        item.color = 'gray';
85        break;
86    }
87  }
88
89  return results;
90}
91
92export function annotationsFromDataFrames(data?: DataFrame[]): AnnotationEvent[] {
93  if (!data || !data.length) {
94    return [];
95  }
96
97  const annotations: AnnotationEvent[] = [];
98  for (const frame of data) {
99    const view = new DataFrameView<AnnotationEvent>(frame);
100    for (let index = 0; index < frame.length; index++) {
101      const annotation = cloneDeep(view.get(index));
102      annotations.push(annotation);
103    }
104  }
105
106  return annotations;
107}
108