feat(alerting): add support for alert page filter

This commit is contained in:
bergquist 2016-05-09 14:30:28 +02:00
parent 3e462f2914
commit 1be513fabd
9 changed files with 85 additions and 35 deletions

View File

@ -47,6 +47,7 @@ func GetAlertChanges(c *middleware.Context) Response {
func GetAlerts(c *middleware.Context) Response {
query := models.GetAlertsQuery{
OrgId: c.OrgId,
State: c.QueryStrings("state"),
}
if err := bus.Dispatch(&query); err != nil {
@ -78,8 +79,10 @@ func GetAlerts(c *middleware.Context) Response {
DashboardIds: dashboardIds,
}
if err := bus.Dispatch(&dashboardsQuery); err != nil {
return ApiError(500, "List alerts failed", err)
if len(alertDTOs) > 0 {
if err := bus.Dispatch(&dashboardsQuery); err != nil {
return ApiError(500, "List alerts failed", err)
}
}
//TODO: should be possible to speed this up with lookup table
@ -128,7 +131,7 @@ func DelAlert(c *middleware.Context) Response {
func GetAlertStates(c *middleware.Context) Response {
alertId := c.ParamsInt64(":alertId")
query := models.GetAlertsStateCommand{
query := models.GetAlertsStateQuery{
AlertId: alertId,
}

View File

@ -96,6 +96,7 @@ type DeleteAlertCommand struct {
//Queries
type GetAlertsQuery struct {
OrgId int64
State []string
Result []AlertRule
}

View File

@ -38,7 +38,7 @@ type UpdateAlertStateCommand struct {
// Queries
type GetAlertsStateCommand struct {
type GetAlertsStateQuery struct {
OrgId int64 `json:"orgId" binding:"Required"`
AlertId int64 `json:"alertId" binding:"Required"`

View File

@ -1,15 +1,17 @@
package sqlstore
import (
"bytes"
"fmt"
"github.com/go-xorm/xorm"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
"strings"
)
func init() {
bus.AddHandler("sql", SaveAlerts)
bus.AddHandler("sql", GetAllAlertsForOrg)
bus.AddHandler("sql", HandleAlertsQuery)
bus.AddHandler("sql", GetAlertById)
bus.AddHandler("sql", GetAlertsByDashboardId)
bus.AddHandler("sql", GetAlertsByDashboardAndPanelId)
@ -41,9 +43,33 @@ func DeleteAlertById(cmd *m.DeleteAlertCommand) error {
})
}
func GetAllAlertsForOrg(query *m.GetAlertsQuery) error {
func HandleAlertsQuery(query *m.GetAlertsQuery) error {
var sql bytes.Buffer
params := make([]interface{}, 0)
sql.WriteString(`SELECT *
from alert_rule
`)
sql.WriteString(`WHERE org_id = ?`)
params = append(params, query.OrgId)
if len(query.State) > 0 {
sql.WriteString(` AND (`)
for i, v := range query.State {
if i > 0 {
sql.WriteString(" OR ")
}
sql.WriteString("state = ? ")
params = append(params, strings.ToUpper(v))
}
sql.WriteString(")")
}
alerts := make([]m.AlertRule, 0)
if err := x.Where("org_id = ?", query.OrgId).Find(&alerts); err != nil {
if err := x.Sql(sql.String(), params...).Find(&alerts); err != nil {
return err
}
@ -127,6 +153,7 @@ func upsertAlerts(alerts []m.AlertRule, posted *[]m.AlertRule, sess *xorm.Sessio
}
} else {
alert.State = "OK"
_, err := sess.Insert(&alert)
if err != nil {
return err

View File

@ -30,7 +30,6 @@ func TestAlertingDataAccess(t *testing.T) {
Description: "Alerting description",
QueryRange: "5m",
Aggregator: "avg",
State: "OK",
},
}

View File

@ -47,7 +47,7 @@ func SetNewAlertState(cmd *m.UpdateAlertStateCommand) error {
})
}
func GetAlertStateLogByAlertId(cmd *m.GetAlertsStateCommand) error {
func GetAlertStateLogByAlertId(cmd *m.GetAlertsStateQuery) error {
alertLogs := make([]m.AlertState, 0)
if err := x.Where("alert_id = ?", cmd.AlertId).Desc("created").Find(&alertLogs); err != nil {

View File

@ -82,7 +82,7 @@ func TestAlertingStateAccess(t *testing.T) {
})
Convey("should have two event state logs", func() {
query := &m.GetAlertsStateCommand{
query := &m.GetAlertsStateQuery{
AlertId: 1,
OrgId: 1,
}
@ -92,6 +92,17 @@ func TestAlertingStateAccess(t *testing.T) {
So(len(*query.Result), ShouldEqual, 2)
})
Convey("should not get any alerts with critical state", func() {
query := &m.GetAlertsQuery{
OrgId: 1,
State: []string{"Critical"},
}
err := HandleAlertsQuery(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 0)
})
})
})
})

View File

@ -9,6 +9,8 @@ import alertDef from './alert_def';
export class AlertPageCtrl {
alerts: any;
stateFilters = [ 'Ok', 'Warn', 'Critical', 'Acknowledged' ];
stateFilter = 'Warn';
/** @ngInject */
constructor(private backendSrv) {
@ -16,7 +18,11 @@ export class AlertPageCtrl {
}
loadAlerts() {
this.backendSrv.get('/api/alerts/rules').then(result => {
var params = {
state: this.stateFilter
};
this.backendSrv.get('/api/alerts/rules', params).then(result => {
this.alerts = _.map(result, alert => {
alert.iconCss = alertDef.getCssForState(alert.state);
return alert;

View File

@ -3,34 +3,37 @@
<div class="page-container" >
<div class="page-header">
<h1>Alerts</h1>
</div>
<h1>Alerts</h1>
<div class="gf-form-select-wrapper max-width-10">
<select class="gf-form-input" ng-model="ctrl.stateFilter" ng-change="ctrl.loadAlerts()" ng-options="filter as filter for filter in ctrl.stateFilters"></select>
</div>
</div>
<table class="grafana-options-table">
<thead>
<th style="min-width: 200px"><strong>Name</strong></th>
<th style="width: 1%">State</th>
<th style="width: 1%"></th>
</thead>
<tr ng-repeat="alert in ctrl.alerts">
<td>
<table class="grafana-options-table">
<thead>
<th style="min-width: 200px"><strong>Name</strong></th>
<th style="width: 1%">State</th>
<th style="width: 1%"></th>
</thead>
<tr ng-repeat="alert in ctrl.alerts">
<td>
<a href="/alerts/events/{{alert.id}}">
{{alert.title}}
</a>
</td>
<td class="text-center">
{{alert.title}}
</a>
</td>
<td class="text-center">
<a href="/alerts/events/{{alert.id}}">
<i class="icon-gf {{alert.iconCss}}"></i>
</a>
</td>
<td class="text-center">
<i class="icon-gf {{alert.iconCss}}"></i>
</a>
</td>
<td class="text-center">
<a href="/dashboard/{{alert.dashboardUri}}?panelId={{alert.panelId}}&fullscreen&edit" class="btn btn-inverse btn-small">
<i class="fa fa-edit"></i>
edit
</a>
</td>
</tr>
</table>
<i class="fa fa-edit"></i>
edit
</a>
</td>
</tr>
</table>
</div>