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