diff --git a/api/alert_nrql_conditions_test.go b/api/alert_nrql_conditions_test.go index 3ef2082a..731026ee 100644 --- a/api/alert_nrql_conditions_test.go +++ b/api/alert_nrql_conditions_test.go @@ -1,7 +1,9 @@ package api import ( + "encoding/json" "fmt" + "io/ioutil" "net/http" "testing" ) @@ -212,6 +214,274 @@ func TestCreateAlertNrqlCondition(t *testing.T) { } } +func TestCreateAlertNrqlStaticCondition(t *testing.T) { + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + condition, err := extractAlertNrqlConditionFromRequest(r) + if err != nil { + t.Fatalf("Failed to parse request: %s", err) + } + + if condition.Type != "static" { + t.Fatalf("Type different from expected value: expected 'static', got '%s'", condition.Type) + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "nrql_condition": + { + "id": 12345, + "type": "static", + "name": "NRQL Condition", + "runbook_url": "https://example.com/runbook.md", + "enabled": true, + "terms": [ + { + "duration": "10", + "operator": "below", + "priority": "critical", + "threshold": "2", + "time_function": "all" + } + ], + "value_function": "single_value", + "nrql": { + "query": "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'", + "since_value": "5" + } + } + } + `)) + })) + + nrqlAlertConditionTerms := []AlertConditionTerm{ + { + Duration: 10, + Operator: "below", + Priority: "critical", + Threshold: 2.0, + TimeFunction: "all", + }, + } + + nrqlAlertQuery := AlertNrqlQuery{ + Query: "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'", + SinceValue: "5", + } + + nrqlAlertCondition := AlertNrqlCondition{ + PolicyID: 123, + Name: "Test Condition", + Type: "static", + Enabled: true, + RunbookURL: "https://example.com/runbook.md", + Terms: nrqlAlertConditionTerms, + ValueFunction: "all", + Nrql: nrqlAlertQuery, + } + + nrqlAlertConditionResp, err := c.CreateAlertNrqlCondition(nrqlAlertCondition) + if err != nil { + t.Log(err) + t.Fatal("CreateAlertNrqlCondition error") + } + if nrqlAlertConditionResp == nil { + t.Log(err) + t.Fatal("CreateAlertNrqlCondition error") + } + if nrqlAlertConditionResp.ID != 12345 { + t.Fatal("Condition ID was not parsed correctly") + } + if nrqlAlertConditionResp.Type != "static" { + t.Fatal("Type was not parsed correctly") + } +} + +func TestCreateAlertNrqlBaselineCondition(t *testing.T) { + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + condition, err := extractAlertNrqlConditionFromRequest(r) + if err != nil { + t.Fatalf("Failed to parse request: %s", err) + } + + if condition.Type != "baseline" { + t.Fatalf("Type different from expected value: expected 'baseline', got '%s'", condition.Type) + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "nrql_condition": + { + "id": 12345, + "type": "baseline", + "name": "NRQL Condition", + "runbook_url": "https://example.com/runbook.md", + "enabled": true, + "terms": [ + { + "duration": "10", + "operator": "below", + "priority": "critical", + "threshold": "2", + "time_function": "all" + } + ], + "value_function": "single_value", + "nrql": { + "query": "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'", + "since_value": "5" + } + } + } + `)) + })) + + nrqlAlertConditionTerms := []AlertConditionTerm{ + { + Duration: 10, + Operator: "below", + Priority: "critical", + Threshold: 2.0, + TimeFunction: "all", + }, + } + + nrqlAlertQuery := AlertNrqlQuery{ + Query: "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'", + SinceValue: "5", + } + + nrqlAlertCondition := AlertNrqlCondition{ + PolicyID: 123, + Type: "baseline", + Name: "Test Condition", + Enabled: true, + RunbookURL: "https://example.com/runbook.md", + Terms: nrqlAlertConditionTerms, + ValueFunction: "all", + Nrql: nrqlAlertQuery, + } + + nrqlAlertConditionResp, err := c.CreateAlertNrqlCondition(nrqlAlertCondition) + if err != nil { + t.Log(err) + t.Fatal("CreateAlertNrqlCondition error") + } + if nrqlAlertConditionResp == nil { + t.Log(err) + t.Fatal("CreateAlertNrqlCondition error") + } + if nrqlAlertConditionResp.ID != 12345 { + t.Fatal("Condition ID was not parsed correctly") + } + if nrqlAlertConditionResp.Type != "baseline" { + t.Fatal("Type was not parsed correctly") + } +} + +func TestCreateAlertNrqlOutlierCondition(t *testing.T) { + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + condition, err := extractAlertNrqlConditionFromRequest(r) + if err != nil { + t.Fatalf("Failed to parse request: %s", err) + } + + if condition.Type != "outlier" { + t.Fatalf("Type different from expected value: expected 'outlier', got '%s'", condition.Type) + } + if condition.ExpectedGroups != 2 { + t.Fatalf("ExpectedGroups different from expected value: expected '2', got '%d'", condition.ExpectedGroups) + } + if condition.IgnoreOverlap != false { + t.Fatalf("IgnoreOverlap different from expected value: expected 'false', got '%t'", condition.IgnoreOverlap) + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "nrql_condition": + { + "id": 12345, + "type": "outlier", + "name": "NRQL Condition", + "runbook_url": "https://example.com/runbook.md", + "enabled": true, + "terms": [ + { + "duration": "10", + "operator": "below", + "priority": "critical", + "threshold": "2", + "time_function": "all" + } + ], + "value_function": "single_value", + "expected_groups": 2, + "ignore_overlap": false, + "nrql": { + "query": "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'", + "since_value": "5" + } + } + } + `)) + })) + + nrqlAlertConditionTerms := []AlertConditionTerm{ + { + Duration: 10, + Operator: "below", + Priority: "critical", + Threshold: 2.0, + TimeFunction: "all", + }, + } + + nrqlAlertQuery := AlertNrqlQuery{ + Query: "SELECT uniqueCount(fieldname) FROM indexname WHERE fieldname2 = 'somevaluetofilterby'", + SinceValue: "5", + } + + nrqlAlertCondition := AlertNrqlCondition{ + PolicyID: 123, + Type: "outlier", + Name: "Test Condition", + Enabled: true, + RunbookURL: "https://example.com/runbook.md", + Terms: nrqlAlertConditionTerms, + ValueFunction: "all", + ExpectedGroups: 2, + IgnoreOverlap: false, + Nrql: nrqlAlertQuery, + } + + nrqlAlertConditionResp, err := c.CreateAlertNrqlCondition(nrqlAlertCondition) + if err != nil { + t.Log(err) + t.Fatal("CreateAlertNrqlCondition error") + } + if nrqlAlertConditionResp == nil { + t.Log(err) + t.Fatal("CreateAlertNrqlCondition error") + } + if nrqlAlertConditionResp.ID != 12345 { + t.Fatal("Condition ID was not parsed correctly") + } + if nrqlAlertConditionResp.Type != "outlier" { + t.Fatal("Type was not parsed correctly") + } + if nrqlAlertConditionResp.ExpectedGroups != 2 { + t.Fatal("Expected Groups was not parsed correctly") + } + if nrqlAlertConditionResp.IgnoreOverlap != false { + t.Fatal("Ignore Overlap was not parsed correctly") + } +} + func TestUpdateAlertNrqlCondition(t *testing.T) { c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -301,3 +571,21 @@ func TestDeleteAlertNrqlCondition(t *testing.T) { t.Fatal("DeleteAlertNrqlCondition error") } } + +func extractAlertNrqlConditionFromRequest(r *http.Request) (*AlertNrqlCondition, error) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + defer r.Body.Close() + + var query *struct { + Condition AlertNrqlCondition `json:"nrql_condition"` + } + err = json.Unmarshal(body, &query) + if err != nil { + return nil, err + } + + return &query.Condition, nil +} diff --git a/api/types.go b/api/types.go index b178e1a2..f56c886c 100755 --- a/api/types.go +++ b/api/types.go @@ -72,14 +72,17 @@ type AlertNrqlQuery struct { // AlertNrqlCondition represents a New Relic NRQL Alert condition. type AlertNrqlCondition struct { - PolicyID int `json:"-"` - ID int `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Enabled bool `json:"enabled"` - RunbookURL string `json:"runbook_url,omitempty"` - Terms []AlertConditionTerm `json:"terms,omitempty"` - ValueFunction string `json:"value_function,omitempty"` - Nrql AlertNrqlQuery `json:"nrql,omitempty"` + PolicyID int `json:"-"` + ID int `json:"id,omitempty"` + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + Enabled bool `json:"enabled"` + RunbookURL string `json:"runbook_url,omitempty"` + Terms []AlertConditionTerm `json:"terms,omitempty"` + ValueFunction string `json:"value_function,omitempty"` + ExpectedGroups int `json:"expected_groups,omitempty"` + IgnoreOverlap bool `json:"ignore_overlap,omitempty"` + Nrql AlertNrqlQuery `json:"nrql,omitempty"` } // AlertPlugin represents a plugin to use with a Plugin alert condition.