From aec31bc428f3fc7dfa17ac209d4836aadc36d2bc Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Sun, 7 Jun 2020 18:38:01 -0400 Subject: [PATCH] add support for alerting API This addresses issue #54. It should be noted that it builds on top of PR #51, which should probably be reviewed and, ideally, merged first, assuming PR #51 is approved. --- admin.go | 22 +++++++++ admin_test.go | 35 ++++++++++++++ alert.go | 77 +++++++++++++++++++++++++++++ alert_test.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 265 insertions(+) create mode 100644 alert.go create mode 100644 alert_test.go diff --git a/admin.go b/admin.go index 28d3c24..9e3a4dd 100644 --- a/admin.go +++ b/admin.go @@ -6,6 +6,13 @@ import ( "fmt" ) +// PauseAllAlertsResponse represents the response body for a PauseAllAlerts request. +type PauseAllAlertsResponse struct { + AlertsAffected int64 `json:"alertsAffected,omitempty"` + State string `json:"state,omitempty"` + Message string `json:"message,omitempty"` +} + // CreateUser creates a Grafana user. func (c *Client) CreateUser(user User) (int64, error) { id := int64(0) @@ -30,3 +37,18 @@ func (c *Client) CreateUser(user User) (int64, error) { func (c *Client) DeleteUser(id int64) error { return c.request("DELETE", fmt.Sprintf("/api/admin/users/%d", id), nil, nil, nil) } + +// PauseAllAlerts pauses all Grafana alerts. +func (c *Client) PauseAllAlerts() (PauseAllAlertsResponse, error) { + result := PauseAllAlertsResponse{} + data, err := json.Marshal(PauseAlertRequest{ + Paused: true, + }) + if err != nil { + return result, err + } + + err = c.request("POST", "/api/admin/pause-all-alerts", nil, bytes.NewBuffer(data), &result) + + return result, err +} diff --git a/admin_test.go b/admin_test.go index 5a836b8..97ce5a6 100644 --- a/admin_test.go +++ b/admin_test.go @@ -1,12 +1,21 @@ package gapi import ( + "strings" "testing" + + "github.com/gobs/pretty" ) const ( createUserJSON = `{"id":1,"message":"User created"}` deleteUserJSON = `{"message":"User deleted"}` + + pauseAllAlertsJSON = `{ + "alertsAffected": 1, + "state": "Paused", + "message": "alert paused" + }` ) func TestCreateUser(t *testing.T) { @@ -37,3 +46,29 @@ func TestDeleteUser(t *testing.T) { t.Error(err) } } + +func TestPauseAllAlerts(t *testing.T) { + server, client := gapiTestTools(200, pauseAllAlertsJSON) + defer server.Close() + + res, err := client.PauseAllAlerts() + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(res)) + + if res.State != "Paused" { + t.Error("pause all alerts response should contain the correct response message") + } +} + +func TestPauseAllAlerts_500(t *testing.T) { + server, client := gapiTestTools(500, pauseAllAlertsJSON) + defer server.Close() + + _, err := client.PauseAllAlerts() + if !strings.Contains(err.Error(), "status: 500") { + t.Errorf("expected error to contain 'status: 500'; got: %s", err.Error()) + } +} diff --git a/alert.go b/alert.go new file mode 100644 index 0000000..c3eb602 --- /dev/null +++ b/alert.go @@ -0,0 +1,77 @@ +package gapi + +import ( + "bytes" + "encoding/json" + "fmt" + "net/url" +) + +// Alert represents a Grafana API Alert +type Alert struct { + ID int64 `json:"id,omitempty"` + DashboardID int64 `json:"dashboardId,omitempty"` + DashboardUID string `json:"dashboardUid,omitempty"` + DashboardSlug string `json:"dashboardSlug,omitempty"` + PanelID int64 `json:"panelId,omitempty"` + Name string `json:"name,omitempty"` + State string `json:"state,omitempty"` + NewStateDate string `json:"newStateDate,omitempty"` + EvalDate string `json:"evalDate,omitempty"` + ExecutionError string `json:"executionError,omitempty"` + URL string `json:"url,omitempty"` +} + +// PauseAlertRequest represents the request payload for a PauseAlert request. +type PauseAlertRequest struct { + Paused bool `json:"paused"` +} + +// PauseAlertResponse represents the response body for a PauseAlert request. +type PauseAlertResponse struct { + AlertID int64 `json:"alertId,omitempty"` + State string `json:"state,omitempty"` + Message string `json:"message,omitempty"` +} + +// Alerts fetches the annotations queried with the params it's passed. +func (c *Client) Alerts(params url.Values) ([]Alert, error) { + result := []Alert{} + err := c.request("GET", "/api/alerts", params, nil, &result) + if err != nil { + return nil, err + } + + return result, err +} + +// Alert fetches and returns an individual Grafana alert. +func (c *Client) Alert(id int64) (Alert, error) { + path := fmt.Sprintf("/api/alerts/%d", id) + result := Alert{} + err := c.request("GET", path, nil, nil, &result) + if err != nil { + return result, err + } + + return result, err +} + +// PauseAlert pauses the Grafana alert whose ID it's passed. +func (c *Client) PauseAlert(id int64) (PauseAlertResponse, error) { + path := fmt.Sprintf("/api/alerts/%d", id) + result := PauseAlertResponse{} + data, err := json.Marshal(PauseAlertRequest{ + Paused: true, + }) + if err != nil { + return result, err + } + + err = c.request("POST", path, nil, bytes.NewBuffer(data), &result) + if err != nil { + return result, err + } + + return result, err +} diff --git a/alert_test.go b/alert_test.go new file mode 100644 index 0000000..7187200 --- /dev/null +++ b/alert_test.go @@ -0,0 +1,131 @@ +package gapi + +import ( + "net/url" + "strings" + "testing" + + "github.com/gobs/pretty" +) + +const ( + alertsJSON = `[{ + "id": 1, + "dashboardId": 1, + "dashboardUId": "ABcdEFghij", + "dashboardSlug": "sensors", + "panelId": 1, + "name": "fire place sensor", + "state": "alerting", + "newStateDate": "2018-05-14T05:55:20+02:00", + "evalDate": "0001-01-01T00:00:00Z", + "evalData": null, + "executionError": "", + "url": "http://grafana.com/dashboard/db/sensors" + }]` + + alertJSON = `{ + "id": 1, + "dashboardId": 1, + "dashboardUId": "ABcdEFghij", + "dashboardSlug": "sensors", + "panelId": 1, + "name": "fire place sensor", + "state": "alerting", + "message": "Someone is trying to break in through the fire place", + "newStateDate": "2018-05-14T05:55:20+02:00", + "evalDate": "0001-01-01T00:00:00Z", + "executionError": "", + "url": "http://grafana.com/dashboard/db/sensors" + }` + + pauseAlertJSON = `{ + "alertId": 1, + "state": "Paused", + "message": "alert paused" + }` +) + +func TestAlerts(t *testing.T) { + server, client := gapiTestTools(200, alertsJSON) + defer server.Close() + + params := url.Values{} + params.Add("dashboardId", "123") + + as, err := client.Alerts(params) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(as)) + + if as[0].ID != 1 { + t.Error("alerts response should contain alerts with an ID") + } +} + +func TestAlerts_500(t *testing.T) { + server, client := gapiTestTools(500, alertsJSON) + defer server.Close() + + params := url.Values{} + params.Add("dashboardId", "123") + + _, err := client.Alerts(params) + if !strings.Contains(err.Error(), "status: 500") { + t.Errorf("expected error to contain 'status: 500'; got: %s", err.Error()) + } +} + +func TestAlert(t *testing.T) { + server, client := gapiTestTools(200, alertJSON) + defer server.Close() + + res, err := client.Alert(1) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(res)) + + if res.ID != 1 { + t.Error("alert response should contain the ID of the queried alert") + } +} + +func TestAlert_500(t *testing.T) { + server, client := gapiTestTools(500, alertJSON) + defer server.Close() + + _, err := client.Alert(1) + if !strings.Contains(err.Error(), "status: 500") { + t.Errorf("expected error to contain 'status: 500'; got: %s", err.Error()) + } +} + +func TestPauseAlert(t *testing.T) { + server, client := gapiTestTools(200, pauseAlertJSON) + defer server.Close() + + res, err := client.PauseAlert(1) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(res)) + + if res.State != "Paused" { + t.Error("pause alert response should contain the correct response message") + } +} + +func TestPauseAlert_500(t *testing.T) { + server, client := gapiTestTools(500, pauseAlertJSON) + defer server.Close() + + _, err := client.PauseAlert(1) + if !strings.Contains(err.Error(), "status: 500") { + t.Errorf("expected error to contain 'status: 500'; got: %s", err.Error()) + } +}