Browse Source

tech: alert list react migration progress

Torkel Ödegaard 8 years ago
parent
commit
029317ed18

+ 2 - 2
pkg/api/alerting.go

@@ -278,7 +278,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
 	}
 
 	var response models.AlertStateType = models.AlertStatePending
-	pausedState := "un paused"
+	pausedState := "un-paused"
 	if cmd.Paused {
 		response = models.AlertStatePaused
 		pausedState = "paused"
@@ -287,7 +287,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
 	result := map[string]interface{}{
 		"alertId": alertId,
 		"state":   response,
-		"message": "alert " + pausedState,
+		"message": "Alert " + pausedState,
 	}
 
 	return Json(200, result)

+ 1 - 1
pkg/services/sqlstore/alert.go

@@ -88,7 +88,7 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error {
 		params = append(params, query.PanelId)
 	}
 
-	if len(query.State) > 0 && query.State[0] != "ALL" {
+	if len(query.State) > 0 && query.State[0] != "all" {
 		sql.WriteString(` AND (`)
 		for i, v := range query.State {
 			if i > 0 {

+ 95 - 46
public/app/containers/AlertRuleList/AlertRuleList.tsx

@@ -2,49 +2,73 @@ import React from 'react';
 import classNames from 'classnames';
 import { inject, observer } from 'mobx-react';
 import PageHeader from 'app/core/components/PageHeader/PageHeader';
+import { IRootStore } from 'app/stores/RootStore';
+import { IAlertRule } from 'app/stores/AlertListStore';
+import appEvents from 'app/core/app_events';
 
-export interface IProps {
-  store: any;
+export interface AlertRuleListProps {
+  store: IRootStore;
 }
 
 @inject('store')
 @observer
-export class AlertRuleList extends React.Component<IProps, any> {
+export class AlertRuleList extends React.Component<AlertRuleListProps, any> {
+  stateFilters = [
+    { text: 'All', value: 'all' },
+    { text: 'OK', value: 'ok' },
+    { text: 'Not OK', value: 'not_ok' },
+    { text: 'Alerting', value: 'alerting' },
+    { text: 'No Data', value: 'no_data' },
+    { text: 'Paused', value: 'paused' },
+  ];
+
   constructor(props) {
     super(props);
 
     this.props.store.nav.load('alerting', 'alert-list');
-    this.props.store.alerting.loadRules();
+    this.props.store.alertList.loadRules();
   }
 
+  onStateFilterChanged = evt => {
+    this.props.store.alertList.setStateFilter(evt.target.value);
+    this.props.store.alertList.loadRules();
+  };
+
+  onOpenHowTo = () => {
+    appEvents.emit('show-modal', {
+      src: 'public/app/features/alerting/partials/alert_howto.html',
+      modalClass: 'confirm-modal',
+      model: {},
+    });
+  };
+
   render() {
+    const { nav, alertList } = this.props.store;
+
     return (
       <div>
-        <PageHeader model={this.props.store.nav} />
+        <PageHeader model={nav as any} />
         <div className="page-container page-body">
           <div className="page-action-bar">
             <div className="gf-form">
               <label className="gf-form-label">Filter by state</label>
 
               <div className="gf-form-select-wrapper width-13">
-                <select
-                  className="gf-form-input"
-                  ng-model="ctrl.filters.state"
-                  ng-options="f.value as f.text for f in ctrl.stateFilters"
-                  ng-change="ctrl.filtersChanged()"
-                />
+                <select className="gf-form-input" onChange={this.onStateFilterChanged}>
+                  {this.stateFilters.map(AlertStateFilterOption)}
+                </select>
               </div>
             </div>
 
             <div className="page-action-bar__spacer" />
 
-            <a className="btn btn-secondary" ng-click="ctrl.openHowTo()">
+            <a className="btn btn-secondary" onClick={this.onOpenHowTo}>
               <i className="fa fa-info-circle" /> How to add an alert
             </a>
           </div>
 
           <section className="card-section card-list-layout-list">
-            <ol className="card-list">{this.props.store.alerting.rules.map(AlertRuleItem)}</ol>
+            <ol className="card-list">{alertList.rules.map(rule => <AlertRuleItem rule={rule} key={rule.id} />)}</ol>
           </section>
         </div>
       </div>
@@ -52,43 +76,68 @@ export class AlertRuleList extends React.Component<IProps, any> {
   }
 }
 
-function AlertRuleItem(rule) {
-  let stateClass = classNames({
-    fa: true,
-    'fa-play': rule.state === 'paused',
-    'fa-pause': rule.state !== 'paused',
-  });
+function AlertStateFilterOption({ text, value }) {
+  return (
+    <option key={value} value={value}>
+      {text}
+    </option>
+  );
+}
 
-  let ruleUrl = `dashboard/${rule.dashboardUri}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`;
+export interface AlertRuleItemProps {
+  rule: IAlertRule;
+}
 
-  return (
-    <li className="card-item-wrapper" key={rule.id}>
-      <div className="card-item card-item--alert">
-        <div className="card-item-header">
-          <div className="card-item-type">
-            <a className="card-item-cog" title="Pausing an alert rule prevents it from executing">
-              <i className={stateClass} />
-            </a>
-            <a className="card-item-cog" href={ruleUrl} title="Edit alert rule">
-              <i className="icon-gf icon-gf-settings" />
-            </a>
-          </div>
-        </div>
-        <div className="card-item-body">
-          <div className="card-item-details">
-            <div className="card-item-name">
-              <a href={ruleUrl}>{rule.name}</a>
+@observer
+export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
+  toggleState = () => {
+    this.props.rule.togglePaused();
+  };
+
+  render() {
+    const { rule } = this.props;
+
+    let stateClass = classNames({
+      fa: true,
+      'fa-play': rule.isPaused,
+      'fa-pause': !rule.isPaused,
+    });
+
+    let ruleUrl = `dashboard/${rule.dashboardUri}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`;
+
+    return (
+      <li className="card-item-wrapper">
+        <div className="card-item card-item--alert">
+          <div className="card-item-header">
+            <div className="card-item-type">
+              <a
+                className="card-item-cog"
+                title="Pausing an alert rule prevents it from executing"
+                onClick={this.toggleState}
+              >
+                <i className={stateClass} />
+              </a>
+              <a className="card-item-cog" href={ruleUrl} title="Edit alert rule">
+                <i className="icon-gf icon-gf-settings" />
+              </a>
             </div>
-            <div className="card-item-sub-name">
-              <span className={`alert-list-item-state ${rule.stateClass}`}>
-                <i className={rule.stateIcon} /> {rule.stateText}
-              </span>
-              <span> for {rule.stateAge}</span>
+          </div>
+          <div className="card-item-body">
+            <div className="card-item-details">
+              <div className="card-item-name">
+                <a href={ruleUrl}>{rule.name}</a>
+              </div>
+              <div className="card-item-sub-name">
+                <span className={`alert-list-item-state ${rule.stateClass}`}>
+                  <i className={rule.stateIcon} /> {rule.stateText}
+                </span>
+                <span> for {rule.stateAge}</span>
+              </div>
+              {rule.info && <div className="small muted">{rule.info}</div>}
             </div>
-            {rule.info && <div className="small muted">{rule.info}</div>}
           </div>
         </div>
-      </div>
-    </li>
-  );
+      </li>
+    );
+  }
 }

+ 85 - 0
public/app/stores/AlertListStore.ts

@@ -0,0 +1,85 @@
+import { types, getEnv, flow } from 'mobx-state-tree';
+import moment from 'moment';
+import alertDef from 'app/features/alerting/alert_def';
+
+function setStateFields(rule, state) {
+  let stateModel = alertDef.getStateDisplayModel(state);
+  rule.state = state;
+  rule.stateText = stateModel.text;
+  rule.stateIcon = stateModel.iconClass;
+  rule.stateClass = stateModel.stateClass;
+  rule.stateAge = moment(rule.newStateDate)
+    .fromNow()
+    .replace(' ago', '');
+}
+
+export const AlertRule = types
+  .model('AlertRule', {
+    id: types.identifier(types.number),
+    dashboardId: types.number,
+    panelId: types.number,
+    name: types.string,
+    state: types.string,
+    stateText: types.string,
+    stateIcon: types.string,
+    stateClass: types.string,
+    stateAge: types.string,
+    info: types.optional(types.string, ''),
+    dashboardUri: types.string,
+  })
+  .views(self => ({
+    get isPaused() {
+      return self.state === 'paused';
+    },
+  }))
+  .actions(self => ({
+    /**
+     * will toggle alert rule paused state
+     */
+    togglePaused: flow(function* togglePaused() {
+      let backendSrv = getEnv(self).backendSrv;
+
+      var payload = { paused: self.isPaused };
+      let res = yield backendSrv.post(`/api/alerts/${self.id}/pause`, payload);
+      setStateFields(self, res.state);
+      self.info = '';
+    }),
+  }));
+
+type IAlertRuleType = typeof AlertRule.Type;
+export interface IAlertRule extends IAlertRuleType {}
+
+export const AlertListStore = types
+  .model('AlertListStore', {
+    rules: types.array(AlertRule),
+    stateFilter: types.optional(types.string, 'all'),
+  })
+  .actions(self => ({
+    setStateFilter: function(state) {
+      self.stateFilter = state;
+    },
+
+    loadRules: flow(function* load() {
+      let backendSrv = getEnv(self).backendSrv;
+
+      let filters = { state: self.stateFilter };
+
+      let rules = yield backendSrv.get('/api/alerts', filters);
+
+      self.rules.clear();
+
+      for (let rule of rules) {
+        setStateFields(rule, rule.state);
+
+        if (rule.executionError) {
+          rule.info = 'Execution Error: ' + rule.executionError;
+        }
+
+        if (rule.evalData && rule.evalData.noData) {
+          rule.info = 'Query returned no data';
+        }
+
+        self.rules.push(AlertRule.create(rule));
+      }
+    }),
+  }));

+ 0 - 51
public/app/stores/AlertingStore.ts

@@ -1,51 +0,0 @@
-import { types, getEnv, flow } from 'mobx-state-tree';
-import moment from 'moment';
-import alertDef from 'app/features/alerting/alert_def';
-
-export const AlertRule = types.model('AlertRule', {
-  id: types.identifier(types.number),
-  dashboardId: types.number,
-  panelId: types.number,
-  name: types.string,
-  state: types.string,
-  stateText: types.string,
-  stateIcon: types.string,
-  stateClass: types.string,
-  stateAge: types.string,
-  info: types.optional(types.string, ''),
-  dashboardUri: types.string,
-});
-
-export const AlertingStore = types
-  .model('AlertingStore', {
-    rules: types.array(AlertRule),
-  })
-  .actions(self => ({
-    loadRules: flow(function* load() {
-      let backendSrv = getEnv(self).backendSrv;
-
-      let rules = yield backendSrv.get('/api/alerts');
-
-      self.rules.clear();
-
-      for (let rule of rules) {
-        let stateModel = alertDef.getStateDisplayModel(rule.state);
-        rule.stateText = stateModel.text;
-        rule.stateIcon = stateModel.iconClass;
-        rule.stateClass = stateModel.stateClass;
-        rule.stateAge = moment(rule.newStateDate)
-          .fromNow()
-          .replace(' ago', '');
-
-        if (rule.executionError) {
-          rule.info = 'Execution Error: ' + rule.executionError;
-        }
-
-        if (rule.evalData && rule.evalData.noData) {
-          rule.info = 'Query returned no data';
-        }
-
-        self.rules.push(AlertRule.create(rule));
-      }
-    }),
-  }));

+ 2 - 2
public/app/stores/RootStore.ts

@@ -2,7 +2,7 @@ import { types } from 'mobx-state-tree';
 import { SearchStore } from './SearchStore';
 import { ServerStatsStore } from './ServerStatsStore';
 import { NavStore } from './NavStore';
-import { AlertingStore } from './AlertingStore';
+import { AlertListStore } from './AlertListStore';
 
 export const RootStore = types.model({
   search: types.optional(SearchStore, {
@@ -12,7 +12,7 @@ export const RootStore = types.model({
     stats: [],
   }),
   nav: types.optional(NavStore, {}),
-  alerting: types.optional(AlertingStore, {
+  alertList: types.optional(AlertListStore, {
     rules: [],
   }),
 });