alert_tab_ctrl.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. ///<reference path="../../headers/common.d.ts" />
  2. import _ from 'lodash';
  3. import {
  4. QueryPartDef,
  5. QueryPart,
  6. } from 'app/core/components/query_part/query_part';
  7. var alertQueryDef = new QueryPartDef({
  8. type: 'query',
  9. params: [
  10. {name: "queryRefId", type: 'string', options: ['A', 'B', 'C', 'D', 'E', 'F']},
  11. {name: "from", type: "string", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h']},
  12. {name: "to", type: "string", options: ['now']},
  13. ],
  14. defaultParams: ['#A', '5m', 'now', 'avg']
  15. });
  16. var reducerAvgDef = new QueryPartDef({
  17. type: 'avg',
  18. params: [],
  19. defaultParams: []
  20. });
  21. export class AlertTabCtrl {
  22. panel: any;
  23. panelCtrl: any;
  24. testing: boolean;
  25. testResult: any;
  26. subTabIndex: number;
  27. handlers = [{text: 'Grafana', value: 1}, {text: 'External', value: 0}];
  28. conditionTypes = [
  29. {text: 'Query', value: 'query'},
  30. ];
  31. alert: any;
  32. conditionModels: any;
  33. evalFunctions = [
  34. {text: '>', value: '>'},
  35. {text: '<', value: '<'},
  36. ];
  37. severityLevels = [
  38. {text: 'Critical', value: 'critical'},
  39. {text: 'Warning', value: 'warning'},
  40. ];
  41. addNotificationSegment;
  42. notifications;
  43. alertNotifications;
  44. /** @ngInject */
  45. constructor(private $scope, private $timeout, private backendSrv, private dashboardSrv, private uiSegmentSrv) {
  46. this.panelCtrl = $scope.ctrl;
  47. this.panel = this.panelCtrl.panel;
  48. this.$scope.ctrl = this;
  49. this.subTabIndex = 0;
  50. }
  51. $onInit() {
  52. this.addNotificationSegment = this.uiSegmentSrv.newPlusButton();
  53. this.initModel();
  54. // set panel alert edit mode
  55. this.$scope.$on("$destroy", () => {
  56. this.panelCtrl.editingThresholds = false;
  57. this.panelCtrl.render();
  58. });
  59. // subscribe to graph threshold handle changes
  60. this.panelCtrl.events.on('threshold-changed', this.graphThresholdChanged.bind(this));
  61. // build notification model
  62. this.notifications = [];
  63. this.alertNotifications = [];
  64. return this.backendSrv.get('/api/alert-notifications').then(res => {
  65. this.notifications = res;
  66. _.each(this.alert.notifications, item => {
  67. var model = _.findWhere(this.notifications, {id: item.id});
  68. if (model) {
  69. model.iconClass = this.getNotificationIcon(model.type);
  70. this.alertNotifications.push(model);
  71. }
  72. });
  73. });
  74. }
  75. getNotificationIcon(type) {
  76. switch (type) {
  77. case "email": return "fa fa-envelope";
  78. case "slack": return "fa fa-slack";
  79. case "webhook": return "fa fa-cubes";
  80. }
  81. }
  82. getNotifications() {
  83. return Promise.resolve(this.notifications.map(item => {
  84. return this.uiSegmentSrv.newSegment(item.name);
  85. }));
  86. }
  87. notificationAdded() {
  88. var model = _.findWhere(this.notifications, {name: this.addNotificationSegment.value});
  89. if (!model) {
  90. return;
  91. }
  92. this.alertNotifications.push({name: model.name, iconClass: this.getNotificationIcon(model.type)});
  93. this.alert.notifications.push({id: model.id});
  94. // reset plus button
  95. this.addNotificationSegment.value = this.uiSegmentSrv.newPlusButton().value;
  96. this.addNotificationSegment.html = this.uiSegmentSrv.newPlusButton().html;
  97. }
  98. removeNotification(index) {
  99. this.alert.notifications.splice(index, 1);
  100. this.alertNotifications.splice(index, 1);
  101. }
  102. initModel() {
  103. var alert = this.alert = this.panel.alert = this.panel.alert || {};
  104. alert.conditions = alert.conditions || [];
  105. if (alert.conditions.length === 0) {
  106. alert.conditions.push(this.buildDefaultCondition());
  107. }
  108. alert.severity = alert.severity || 'critical';
  109. alert.frequency = alert.frequency || '60s';
  110. alert.handler = alert.handler || 1;
  111. alert.notifications = alert.notifications || [];
  112. var defaultName = this.panel.title + ' alert';
  113. alert.name = alert.name || defaultName;
  114. alert.description = alert.description || defaultName;
  115. this.conditionModels = _.reduce(alert.conditions, (memo, value) => {
  116. memo.push(this.buildConditionModel(value));
  117. return memo;
  118. }, []);
  119. if (this.alert.enabled) {
  120. this.panelCtrl.editingThresholds = true;
  121. }
  122. this.syncThresholds();
  123. this.panelCtrl.render();
  124. }
  125. syncThresholds() {
  126. if (this.panel.type !== 'graph') {
  127. return;
  128. }
  129. var threshold: any = {};
  130. if (this.panel.thresholds && this.panel.thresholds.length > 0) {
  131. threshold = this.panel.thresholds[0];
  132. } else {
  133. this.panel.thresholds = [threshold];
  134. }
  135. var updated = false;
  136. for (var condition of this.conditionModels) {
  137. if (condition.type === 'query') {
  138. var value = condition.evaluator.params[0];
  139. if (!_.isNumber(value)) {
  140. continue;
  141. }
  142. if (value !== threshold.value) {
  143. threshold.value = value;
  144. updated = true;
  145. }
  146. if (condition.evaluator.type !== threshold.op) {
  147. threshold.op = condition.evaluator.type;
  148. updated = true;
  149. }
  150. }
  151. }
  152. return updated;
  153. }
  154. graphThresholdChanged(evt) {
  155. for (var condition of this.alert.conditions) {
  156. if (condition.type === 'query') {
  157. condition.evaluator.params[0] = evt.threshold.value;
  158. break;
  159. }
  160. }
  161. }
  162. buildDefaultCondition() {
  163. return {
  164. type: 'query',
  165. query: {params: ['A', '5m', 'now']},
  166. reducer: {type: 'avg', params: []},
  167. evaluator: {type: '>', params: [null]},
  168. };
  169. }
  170. buildConditionModel(source) {
  171. var cm: any = {source: source, type: source.type};
  172. cm.queryPart = new QueryPart(source.query, alertQueryDef);
  173. cm.reducerPart = new QueryPart({params: []}, reducerAvgDef);
  174. cm.evaluator = source.evaluator;
  175. return cm;
  176. }
  177. queryPartUpdated(conditionModel) {
  178. }
  179. addCondition(type) {
  180. var condition = this.buildDefaultCondition();
  181. // add to persited model
  182. this.alert.conditions.push(condition);
  183. // add to view model
  184. this.conditionModels.push(this.buildConditionModel(condition));
  185. }
  186. removeCondition(index) {
  187. this.alert.conditions.splice(index, 1);
  188. this.conditionModels.splice(index, 1);
  189. }
  190. delete() {
  191. this.alert.enabled = false;
  192. this.initModel();
  193. }
  194. enable() {
  195. this.alert.enabled = true;
  196. this.initModel();
  197. }
  198. thresholdUpdated() {
  199. if (this.syncThresholds()) {
  200. this.panelCtrl.render();
  201. }
  202. }
  203. test() {
  204. this.testing = true;
  205. var payload = {
  206. dashboard: this.dashboardSrv.getCurrent().getSaveModelClone(),
  207. panelId: this.panelCtrl.panel.id,
  208. };
  209. return this.backendSrv.post('/api/alerts/test', payload).then(res => {
  210. this.testResult = res;
  211. this.testing = false;
  212. });
  213. }
  214. }
  215. /** @ngInject */
  216. export function alertTab() {
  217. 'use strict';
  218. return {
  219. restrict: 'E',
  220. scope: true,
  221. templateUrl: 'public/app/features/alerting/partials/alert_tab.html',
  222. controller: AlertTabCtrl,
  223. };
  224. }