1import { isArray, reduce } from 'lodash'; 2import { QueryPartDef, QueryPart } from 'app/angular/components/query_part'; 3 4const alertQueryDef = new QueryPartDef({ 5 type: 'query', 6 params: [ 7 { name: 'queryRefId', type: 'string', dynamicLookup: true }, 8 { 9 name: 'from', 10 type: 'string', 11 options: ['10s', '1m', '5m', '10m', '15m', '1h', '2h', '6h', '12h', '24h', '48h'], 12 }, 13 { name: 'to', type: 'string', options: ['now', 'now-1m', 'now-5m', 'now-10m', 'now-1h'] }, 14 ], 15 defaultParams: ['#A', '15m', 'now', 'avg'], 16}); 17 18const conditionTypes = [{ text: 'Query', value: 'query' }]; 19 20const alertStateSortScore = { 21 alerting: 1, 22 firing: 1, 23 no_data: 2, 24 pending: 3, 25 ok: 4, 26 paused: 5, 27 inactive: 5, 28}; 29 30export enum EvalFunction { 31 'IsAbove' = 'gt', 32 'IsBelow' = 'lt', 33 'IsOutsideRange' = 'outside_range', 34 'IsWithinRange' = 'within_range', 35 'HasNoValue' = 'no_value', 36} 37 38const evalFunctions = [ 39 { value: EvalFunction.IsAbove, text: 'IS ABOVE' }, 40 { value: EvalFunction.IsBelow, text: 'IS BELOW' }, 41 { value: EvalFunction.IsOutsideRange, text: 'IS OUTSIDE RANGE' }, 42 { value: EvalFunction.IsWithinRange, text: 'IS WITHIN RANGE' }, 43 { value: EvalFunction.HasNoValue, text: 'HAS NO VALUE' }, 44]; 45 46const evalOperators = [ 47 { text: 'OR', value: 'or' }, 48 { text: 'AND', value: 'and' }, 49]; 50 51const reducerTypes = [ 52 { text: 'avg()', value: 'avg' }, 53 { text: 'min()', value: 'min' }, 54 { text: 'max()', value: 'max' }, 55 { text: 'sum()', value: 'sum' }, 56 { text: 'count()', value: 'count' }, 57 { text: 'last()', value: 'last' }, 58 { text: 'median()', value: 'median' }, 59 { text: 'diff()', value: 'diff' }, 60 { text: 'diff_abs()', value: 'diff_abs' }, 61 { text: 'percent_diff()', value: 'percent_diff' }, 62 { text: 'percent_diff_abs()', value: 'percent_diff_abs' }, 63 { text: 'count_non_null()', value: 'count_non_null' }, 64]; 65 66const noDataModes = [ 67 { text: 'Alerting', value: 'alerting' }, 68 { text: 'No Data', value: 'no_data' }, 69 { text: 'Keep Last State', value: 'keep_state' }, 70 { text: 'Ok', value: 'ok' }, 71]; 72 73const executionErrorModes = [ 74 { text: 'Alerting', value: 'alerting' }, 75 { text: 'Keep Last State', value: 'keep_state' }, 76]; 77 78function createReducerPart(model: any) { 79 const def = new QueryPartDef({ type: model.type, defaultParams: [] }); 80 return new QueryPart(model, def); 81} 82 83function getStateDisplayModel(state: string) { 84 const normalizedState = state.toLowerCase().replace(/_/g, ''); 85 86 switch (normalizedState) { 87 case 'normal': 88 case 'ok': { 89 return { 90 text: 'OK', 91 iconClass: 'heart', 92 stateClass: 'alert-state-ok', 93 }; 94 } 95 case 'alerting': { 96 return { 97 text: 'ALERTING', 98 iconClass: 'heart-break', 99 stateClass: 'alert-state-critical', 100 }; 101 } 102 case 'nodata': { 103 return { 104 text: 'NO DATA', 105 iconClass: 'question-circle', 106 stateClass: 'alert-state-warning', 107 }; 108 } 109 case 'paused': { 110 return { 111 text: 'PAUSED', 112 iconClass: 'pause', 113 stateClass: 'alert-state-paused', 114 }; 115 } 116 case 'pending': { 117 return { 118 text: 'PENDING', 119 iconClass: 'hourglass', 120 stateClass: 'alert-state-warning', 121 }; 122 } 123 case 'unknown': { 124 return { 125 text: 'UNKNOWN', 126 iconClass: 'question-circle', 127 stateClass: '.alert-state-paused', 128 }; 129 } 130 131 case 'firing': { 132 return { 133 text: 'FIRING', 134 iconClass: 'fire', 135 stateClass: '', 136 }; 137 } 138 139 case 'inactive': { 140 return { 141 text: 'INACTIVE', 142 iconClass: 'check', 143 stateClass: '', 144 }; 145 } 146 147 case 'error': { 148 return { 149 text: 'ERROR', 150 iconClass: 'heart-break', 151 stateClass: 'alert-state-critical', 152 }; 153 } 154 } 155 156 throw { message: 'Unknown alert state' }; 157} 158 159function joinEvalMatches(matches: any, separator: string) { 160 return reduce( 161 matches, 162 (res, ev) => { 163 if (ev.metric !== undefined && ev.value !== undefined) { 164 res.push(ev.metric + '=' + ev.value); 165 } 166 167 // For backwards compatibility . Should be be able to remove this after ~2017-06-01 168 if (ev.Metric !== undefined && ev.Value !== undefined) { 169 res.push(ev.Metric + '=' + ev.Value); 170 } 171 172 return res; 173 }, 174 [] as string[] 175 ).join(separator); 176} 177 178function getAlertAnnotationInfo(ah: any) { 179 // backward compatibility, can be removed in grafana 5.x 180 // old way stored evalMatches in data property directly, 181 // new way stores it in evalMatches property on new data object 182 183 if (isArray(ah.data)) { 184 return joinEvalMatches(ah.data, ', '); 185 } else if (isArray(ah.data.evalMatches)) { 186 return joinEvalMatches(ah.data.evalMatches, ', '); 187 } 188 189 if (ah.data.error) { 190 return 'Error: ' + ah.data.error; 191 } 192 193 return ''; 194} 195 196export default { 197 alertQueryDef: alertQueryDef, 198 getStateDisplayModel: getStateDisplayModel, 199 conditionTypes: conditionTypes, 200 evalFunctions: evalFunctions, 201 evalOperators: evalOperators, 202 noDataModes: noDataModes, 203 executionErrorModes: executionErrorModes, 204 reducerTypes: reducerTypes, 205 createReducerPart: createReducerPart, 206 getAlertAnnotationInfo: getAlertAnnotationInfo, 207 alertStateSortScore: alertStateSortScore, 208}; 209