diff --git a/docs/sources/http_api/alerting.md b/docs/sources/http_api/alerting.md
index 4d52105cf3c..e4fe0dad3ff 100644
--- a/docs/sources/http_api/alerting.md
+++ b/docs/sources/http_api/alerting.md
@@ -35,10 +35,15 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
`/api/alerts?dashboardId=1`
- - **dashboardId** – Return alerts for a specified dashboard.
- - **panelId** – Return alerts for a specified panel on a dashboard.
- - **limit** - Limit response to x number of alerts.
+ - **dashboardId** – Limit response to alerts in specified dashboard(s). You can specify multiple dashboards, e.g. dashboardId=23&dashboardId=35.
+ - **panelId** – Limit response to alert for a specified panel on a dashboard.
+ - **query** - Limit response to alerts having a name like this value.
- **state** - Return alerts with one or more of the following alert states: `ALL`,`no_data`, `paused`, `alerting`, `ok`, `pending`. To specify multiple states use the following format: `?state=paused&state=alerting`
+ - **limit** - Limit response to *X* number of alerts.
+ - **folderId** – Limit response to alerts of dashboards in specified folder(s). You can specify multiple folders, e.g. folderId=23&folderId=35.
+ - **dashboardQuery** - Limit response to alerts having a dashboard name like this value.
+ - **dashboardTag** - Limit response to alerts of dashboards with specified tags. To do an "AND" filtering with multiple tags, specify the tags parameter multiple times e.g. dashboardTag=tag1&dashboardTag=tag2.
+
**Example Response**:
diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go
index a9a3773ceb1..961fc11b2dc 100644
--- a/pkg/api/alerting.go
+++ b/pkg/api/alerting.go
@@ -2,12 +2,14 @@ package api
import (
"fmt"
+ "strconv"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/guardian"
+ "github.com/grafana/grafana/pkg/services/search"
)
func ValidateOrgAlert(c *m.ReqContext) {
@@ -46,12 +48,64 @@ func GetAlertStatesForDashboard(c *m.ReqContext) Response {
// GET /api/alerts
func GetAlerts(c *m.ReqContext) Response {
+ dashboardQuery := c.Query("dashboardQuery")
+ dashboardTags := c.QueryStrings("dashboardTag")
+ stringDashboardIDs := c.QueryStrings("dashboardId")
+ stringFolderIDs := c.QueryStrings("folderId")
+
+ dashboardIDs := make([]int64, 0)
+ for _, id := range stringDashboardIDs {
+ dashboardID, err := strconv.ParseInt(id, 10, 64)
+ if err == nil {
+ dashboardIDs = append(dashboardIDs, dashboardID)
+ }
+ }
+
+ if dashboardQuery != "" || len(dashboardTags) > 0 || len(stringFolderIDs) > 0 {
+ folderIDs := make([]int64, 0)
+ for _, id := range stringFolderIDs {
+ folderID, err := strconv.ParseInt(id, 10, 64)
+ if err == nil {
+ folderIDs = append(folderIDs, folderID)
+ }
+ }
+
+ searchQuery := search.Query{
+ Title: dashboardQuery,
+ Tags: dashboardTags,
+ SignedInUser: c.SignedInUser,
+ Limit: 1000,
+ OrgId: c.OrgId,
+ DashboardIds: dashboardIDs,
+ Type: string(search.DashHitDB),
+ FolderIds: folderIDs,
+ Permission: m.PERMISSION_EDIT,
+ }
+
+ err := bus.Dispatch(&searchQuery)
+ if err != nil {
+ return Error(500, "List alerts failed", err)
+ }
+
+ for _, d := range searchQuery.Result {
+ if d.Type == search.DashHitDB && d.Id > 0 {
+ dashboardIDs = append(dashboardIDs, d.Id)
+ }
+ }
+
+ // if we didn't find any dashboards, return empty result
+ if len(dashboardIDs) == 0 {
+ return JSON(200, []*m.AlertListItemDTO{})
+ }
+ }
+
query := m.GetAlertsQuery{
- OrgId: c.OrgId,
- DashboardId: c.QueryInt64("dashboardId"),
- PanelId: c.QueryInt64("panelId"),
- Limit: c.QueryInt64("limit"),
- User: c.SignedInUser,
+ OrgId: c.OrgId,
+ DashboardIDs: dashboardIDs,
+ PanelId: c.QueryInt64("panelId"),
+ Limit: c.QueryInt64("limit"),
+ User: c.SignedInUser,
+ Query: c.Query("query"),
}
states := c.QueryStrings("state")
diff --git a/pkg/api/alerting_test.go b/pkg/api/alerting_test.go
index 9302ef7beca..abfdfb66322 100644
--- a/pkg/api/alerting_test.go
+++ b/pkg/api/alerting_test.go
@@ -6,6 +6,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/services/search"
. "github.com/smartystreets/goconvey/convey"
)
@@ -64,6 +65,60 @@ func TestAlertingApiEndpoint(t *testing.T) {
})
})
})
+
+ loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1", "/api/alerts", m.ROLE_EDITOR, func(sc *scenarioContext) {
+ var searchQuery *search.Query
+ bus.AddHandler("test", func(query *search.Query) error {
+ searchQuery = query
+ return nil
+ })
+
+ var getAlertsQuery *m.GetAlertsQuery
+ bus.AddHandler("test", func(query *m.GetAlertsQuery) error {
+ getAlertsQuery = query
+ return nil
+ })
+
+ sc.handlerFunc = GetAlerts
+ sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
+
+ So(searchQuery, ShouldBeNil)
+ So(getAlertsQuery, ShouldNotBeNil)
+ })
+
+ loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1&dashboardId=2&folderId=3&dashboardTag=abc&dashboardQuery=dbQuery&limit=5&query=alertQuery", "/api/alerts", m.ROLE_EDITOR, func(sc *scenarioContext) {
+ var searchQuery *search.Query
+ bus.AddHandler("test", func(query *search.Query) error {
+ searchQuery = query
+ query.Result = search.HitList{
+ &search.Hit{Id: 1},
+ &search.Hit{Id: 2},
+ }
+ return nil
+ })
+
+ var getAlertsQuery *m.GetAlertsQuery
+ bus.AddHandler("test", func(query *m.GetAlertsQuery) error {
+ getAlertsQuery = query
+ return nil
+ })
+
+ sc.handlerFunc = GetAlerts
+ sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
+
+ So(searchQuery, ShouldNotBeNil)
+ So(searchQuery.DashboardIds[0], ShouldEqual, 1)
+ So(searchQuery.DashboardIds[1], ShouldEqual, 2)
+ So(searchQuery.FolderIds[0], ShouldEqual, 3)
+ So(searchQuery.Tags[0], ShouldEqual, "abc")
+ So(searchQuery.Title, ShouldEqual, "dbQuery")
+
+ So(getAlertsQuery, ShouldNotBeNil)
+ So(getAlertsQuery.DashboardIDs[0], ShouldEqual, 1)
+ So(getAlertsQuery.DashboardIDs[1], ShouldEqual, 2)
+ So(getAlertsQuery.Limit, ShouldEqual, 5)
+ So(getAlertsQuery.Query, ShouldEqual, "alertQuery")
+ })
})
}
diff --git a/pkg/models/alert.go b/pkg/models/alert.go
index b72d87e94b2..fba2aa63df9 100644
--- a/pkg/models/alert.go
+++ b/pkg/models/alert.go
@@ -161,12 +161,13 @@ type SetAlertStateCommand struct {
//Queries
type GetAlertsQuery struct {
- OrgId int64
- State []string
- DashboardId int64
- PanelId int64
- Limit int64
- User *SignedInUser
+ OrgId int64
+ State []string
+ DashboardIDs []int64
+ PanelId int64
+ Limit int64
+ Query string
+ User *SignedInUser
Result []*AlertListItemDTO
}
diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go
index b0ca50eb67d..58ec7e2857a 100644
--- a/pkg/services/sqlstore/alert.go
+++ b/pkg/services/sqlstore/alert.go
@@ -82,8 +82,16 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error {
builder.Write(`WHERE alert.org_id = ?`, query.OrgId)
- if query.DashboardId != 0 {
- builder.Write(` AND alert.dashboard_id = ?`, query.DashboardId)
+ if len(strings.TrimSpace(query.Query)) > 0 {
+ builder.Write(" AND alert.name "+dialect.LikeStr()+" ?", "%"+query.Query+"%")
+ }
+
+ if len(query.DashboardIDs) > 0 {
+ builder.sql.WriteString(` AND alert.dashboard_id IN (?` + strings.Repeat(",?", len(query.DashboardIDs)-1) + `) `)
+
+ for _, dbID := range query.DashboardIDs {
+ builder.AddParams(dbID)
+ }
}
if query.PanelId != 0 {
diff --git a/pkg/services/sqlstore/alert_test.go b/pkg/services/sqlstore/alert_test.go
index 296d16c2f45..be48c7b2f52 100644
--- a/pkg/services/sqlstore/alert_test.go
+++ b/pkg/services/sqlstore/alert_test.go
@@ -3,10 +3,11 @@ package sqlstore
import (
"testing"
+ "time"
+
"github.com/grafana/grafana/pkg/components/simplejson"
m "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
- "time"
)
func mockTimeNow() {
@@ -99,7 +100,7 @@ func TestAlertingDataAccess(t *testing.T) {
})
Convey("Can read properties", func() {
- alertQuery := m.GetAlertsQuery{DashboardId: testDash.Id, PanelId: 1, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
+ alertQuery := m.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
err2 := HandleAlertsQuery(&alertQuery)
alert := alertQuery.Result[0]
@@ -109,7 +110,7 @@ func TestAlertingDataAccess(t *testing.T) {
})
Convey("Viewer cannot read alerts", func() {
- alertQuery := m.GetAlertsQuery{DashboardId: testDash.Id, PanelId: 1, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_VIEWER}}
+ alertQuery := m.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_VIEWER}}
err2 := HandleAlertsQuery(&alertQuery)
So(err2, ShouldBeNil)
@@ -134,7 +135,7 @@ func TestAlertingDataAccess(t *testing.T) {
})
Convey("Alerts should be updated", func() {
- query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
+ query := m.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
err2 := HandleAlertsQuery(&query)
So(err2, ShouldBeNil)
@@ -183,7 +184,7 @@ func TestAlertingDataAccess(t *testing.T) {
Convey("Should save 3 dashboards", func() {
So(err, ShouldBeNil)
- queryForDashboard := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
+ queryForDashboard := m.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
err2 := HandleAlertsQuery(&queryForDashboard)
So(err2, ShouldBeNil)
@@ -197,7 +198,7 @@ func TestAlertingDataAccess(t *testing.T) {
err = SaveAlerts(&cmd)
Convey("should delete the missing alert", func() {
- query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
+ query := m.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
err2 := HandleAlertsQuery(&query)
So(err2, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
@@ -232,7 +233,7 @@ func TestAlertingDataAccess(t *testing.T) {
So(err, ShouldBeNil)
Convey("Alerts should be removed", func() {
- query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
+ query := m.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &m.SignedInUser{OrgRole: m.ROLE_ADMIN}}
err2 := HandleAlertsQuery(&query)
So(testDash.Id, ShouldEqual, 1)
diff --git a/public/app/features/dashboard/folder_picker/folder_picker.ts b/public/app/features/dashboard/folder_picker/folder_picker.ts
index b8ae18b14d3..69a09455c4d 100644
--- a/public/app/features/dashboard/folder_picker/folder_picker.ts
+++ b/public/app/features/dashboard/folder_picker/folder_picker.ts
@@ -12,6 +12,7 @@ export class FolderPickerCtrl {
enterFolderCreation: any;
exitFolderCreation: any;
enableCreateNew: boolean;
+ enableReset: boolean;
rootName = 'General';
folder: any;
createNewFolder: boolean;
@@ -58,6 +59,10 @@ export class FolderPickerCtrl {
result.unshift({ title: '-- New Folder --', id: -1 });
}
+ if (this.enableReset && query === '' && this.initialTitle !== '') {
+ result.unshift({ title: this.initialTitle, id: null });
+ }
+
return _.map(result, item => {
return { text: item.title, value: item.id };
});
@@ -65,7 +70,9 @@ export class FolderPickerCtrl {
}
onFolderChange(option) {
- if (option.value === -1) {
+ if (!option) {
+ option = { value: 0, text: this.rootName };
+ } else if (option.value === -1) {
this.createNewFolder = true;
this.enterFolderCreation();
return;
@@ -134,7 +141,7 @@ export class FolderPickerCtrl {
this.onFolderLoad();
});
} else {
- if (this.initialTitle) {
+ if (this.initialTitle && this.initialFolderId === null) {
this.folder = { text: this.initialTitle, value: null };
} else {
this.folder = { text: this.rootName, value: 0 };
@@ -171,6 +178,7 @@ export function folderPicker() {
enterFolderCreation: '&',
exitFolderCreation: '&',
enableCreateNew: '@',
+ enableReset: '@',
},
};
}
diff --git a/public/app/plugins/panel/alertlist/editor.html b/public/app/plugins/panel/alertlist/editor.html
index 36c989dd72c..c48b70e02c0 100644
--- a/public/app/plugins/panel/alertlist/editor.html
+++ b/public/app/plugins/panel/alertlist/editor.html
@@ -19,6 +19,30 @@