From d7e5c4cc3a463c925a9f0343fc6b1419ff9d29ca Mon Sep 17 00:00:00 2001 From: Revathy Ramasundaram Date: Thu, 1 Jul 2021 14:09:26 -0700 Subject: [PATCH 1/3] Add support for Notifications --- README.md | 1 + alert_notifications.go | 390 ++++++++++++++++++++++++++ alert_notifications_test.go | 528 ++++++++++++++++++++++++++++++++++++ 3 files changed, 919 insertions(+) create mode 100644 alert_notifications.go create mode 100644 alert_notifications_test.go diff --git a/README.md b/README.md index c608116c5c8..2516d6b9df0 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ The current feature list includes: * [x] Zone Lockdown and User-Agent Block rules * [x] Zones * [x] Workers KV +* [x] Notifications Pull Requests are welcome, but please open an issue (or comment in an existing issue) to discuss any non-trivial changes before submitting code. diff --git a/alert_notifications.go b/alert_notifications.go new file mode 100644 index 00000000000..7e199e64adb --- /dev/null +++ b/alert_notifications.go @@ -0,0 +1,390 @@ +package cloudflare + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" +) + +// MechanismData holds a single public facing mechanism data integation +type MechanismData struct { + Name string `json:"id"` + ID string `json:"name"` +} + +// MechanismIntegrations is a list of all the integrations of a certain mechanism type +// e.g. all email integrations +type MechanismIntegrations []MechanismData + +// Policy represents the notification policy created along with the destinations +type Policy struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Enabled bool `json:"enabled"` + AlertType string `json:"alert_type"` + Mechanisms map[string]MechanismIntegrations `json:"mechanisms"` + Created time.Time `json:"created"` + Modified time.Time `json:"modified"` + Conditions map[string]interface{} `json:"conditions"` + Filters map[string][]string `json:"filters"` +} + +// PoliciesResponse holds the response for listing all notification policies for an account +type PoliciesResponse struct { + Response Response + ResultInfo ResultInfo + Result []Policy +} + +// PolicyResponse holds the response type when a single policy is retrieved +type PolicyResponse struct { + Response Response + Result Policy +} + +// WebhookIntegration describes the webhook information along with its status +type WebhookIntegration struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + URL string `json:"url"` + CreatedAt time.Time `json:"created_at"` + LastSuccess *time.Time `json:"last_success"` + LastFailure *time.Time `json:"last_failure"` +} + +// WebhookResponse describes a single webhook retrieved +type WebhookResponse struct { + Response Response + ResultInfo ResultInfo + Result WebhookIntegration +} + +// WebhooksResponse describes a list of webhooks retrieved +type WebhooksResponse struct { + Response Response + ResultInfo ResultInfo + Result []WebhookIntegration +} + +// UpsertWebhooks describes a valid webhook request +type UpsertWebhooks struct { + Name string `json:"name"` + URL string `json:"url"` + Secret string `json:"secret"` +} + +// PagerDutyResource describes a PagerDuty integration +type PagerDutyResource struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// PagerDutyResponse describes the PagerDuty integration retrieved +type PagerDutyResponse struct { + Response Response + ResultInfo ResultInfo + Result PagerDutyResource +} + +// Resource describes the id of an inserted/updated/deleted resource +type Resource struct { + ID string +} + +// SaveResponse is returned when a resource is inserted/updated/deleted +type SaveResponse struct { + Response Response + Result Resource +} + +// MechanismMetaData represents the state of the delivery mechanism +type MechanismMetaData struct { + Eligible bool `json:"eligible"` + Ready bool `json:"ready"` + Type string `json:"type"` +} + +// Mechanisms are the different possible delivery mechanisms +type Mechanisms struct { + Email MechanismMetaData `json:"email"` + PagerDuty MechanismMetaData `json:"pagerduty"` + Webhooks MechanismMetaData `json:"webhooks,omitempty"` +} + +// EligibilityResponse describes the eligible mechanisms that can be configured for a notification +type EligibilityResponse struct { + Response Response + Result Mechanisms +} + +// NotificationsGroupedByProduct are grouped by products +type NotificationsGroupedByProduct map[string][]AlertWithDescription + +// AlertWithDescription represents the alert/notification available +type AlertWithDescription struct { + DisplayName string `json:"display_name"` + Type string `json:"type"` + Description string `json:"description"` +} + +// AvailableAlertsResponse describes the available alerts/notifications grouped by products +type AvailableAlertsResponse struct { + Response Response + Result NotificationsGroupedByProduct +} + +// ListNotificationPolicies will return the notification policies +// created by a user for a specific account +// +// API Reference: https://api.cloudflare.com/#notification-policies-properties +func (api *API) ListNotificationPolicies(ctx context.Context, accountID string) (PoliciesResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/policies", accountID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) + if err != nil { + return PoliciesResponse{}, err + } + var r PoliciesResponse + err = json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil +} + +// GetNotificationPolicy returns a specific created by a user, given the account id and the policy id +// +// API Reference: https://api.cloudflare.com/#notification-policies-properties +func (api *API) GetNotificationPolicy(ctx context.Context, accountID, policyID string) (PolicyResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/policies/%s", accountID, policyID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) + if err != nil { + return PolicyResponse{}, err + } + var r PolicyResponse + err = json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil +} + +// CreateNotificationPolicy creates a notification policy for an account +// +// API Reference: https://api.cloudflare.com/#notification-policies-create-notification-policy +func (api *API) CreateNotificationPolicy(ctx context.Context, accountID string, policy Policy) (SaveResponse, error) { + + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/policies", accountID) + + res, err := api.makeRequestContext(ctx, http.MethodPost, baseURL, policy) + if err != nil { + return SaveResponse{}, err + } + return unmarshalSaveResponse(res) +} + +// UpdateNotificationPolicy updates a notification policy, given the account id and the policy id and returns the policy id +// +// API Reference: https://api.cloudflare.com/#notification-policies-update-notification-policy +func (api *API) UpdateNotificationPolicy(ctx context.Context, accountID string, policy *Policy) (SaveResponse, error) { + if policy == nil { + return SaveResponse{}, fmt.Errorf("policy cannot be nil") + } + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/policies/%s", accountID, policy.ID) + + res, err := api.makeRequestContext(ctx, http.MethodPut, baseURL, policy) + if err != nil { + return SaveResponse{}, err + } + return unmarshalSaveResponse(res) +} + +// DeleteNotificationPolicy deletes a notification policy for an account +// +// API Reference: https://api.cloudflare.com/#notification-policies-delete-notification-policy +func (api *API) DeleteNotificationPolicy(ctx context.Context, accountID, policyID string) (SaveResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/policies/%s", accountID, policyID) + + res, err := api.makeRequestContext(ctx, http.MethodDelete, baseURL, nil) + if err != nil { + return SaveResponse{}, err + } + return unmarshalSaveResponse(res) +} + +// ListNotificationWebhooks will return the webhook destinations configured for an account +// +// API Reference: https://api.cloudflare.com/#notification-webhooks-list-webhooks +func (api *API) ListNotificationWebhooks(ctx context.Context, accountID string) (WebhooksResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/webhooks", accountID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) + if err != nil { + return WebhooksResponse{}, err + } + var r WebhooksResponse + err = json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil + +} + +// CreateNotificationWebhooks will help connect a webhooks destination. +// A test message will be sent to the webhooks endpoint during creation. +// If added successfully, the webhooks can be setup as a destination mechanism while creating policies. +// Notifications will be posted to this URL. +// +// API Reference: https://api.cloudflare.com/#notification-webhooks-create-webhook +func (api *API) CreateNotificationWebhooks(ctx context.Context, accountID string, webhooks *UpsertWebhooks) (SaveResponse, error) { + if webhooks == nil { + return SaveResponse{}, fmt.Errorf("webhooks cannot be nil") + } + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/webhooks", accountID) + + res, err := api.makeRequestContext(ctx, http.MethodPost, baseURL, webhooks) + if err != nil { + return SaveResponse{}, err + } + + return unmarshalSaveResponse(res) +} + +// GetNotificationWebhooks will return a specific webhook destination, given the account and webhooks ids +// +// API Reference: https://api.cloudflare.com/#notification-webhooks-get-webhook +func (api *API) GetNotificationWebhooks(ctx context.Context, accountID, webhookID string) (WebhookResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/webhooks/%s", accountID, webhookID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) + if err != nil { + return WebhookResponse{}, err + } + var r WebhookResponse + err = json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil +} + +// UpdateNotificationWebhooks will update a particular webhook's name, given the account and webhooks ids. +// The webhook url and secret cannot be updated +// +// API Reference: https://api.cloudflare.com/#notification-webhooks-update-webhook +func (api *API) UpdateNotificationWebhooks(ctx context.Context, accountID, webhookID string, webhooks *UpsertWebhooks) (SaveResponse, error) { + if webhooks == nil { + return SaveResponse{}, fmt.Errorf("webhooks cannot be nil") + } + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/webhooks/%s", accountID, webhookID) + + res, err := api.makeRequestContext(ctx, http.MethodPut, baseURL, webhooks) + if err != nil { + return SaveResponse{}, err + } + + return unmarshalSaveResponse(res) +} + +// DeleteNotificationWebhooks will delete a webhook, given the account and webhooks ids. +// Deleting the webhooks will remove it from any connected notification policies. +// +// API Reference: https://api.cloudflare.com/#notification-webhooks-delete-webhook +func (api *API) DeleteNotificationWebhooks(ctx context.Context, accountID, webhookID string) (SaveResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/webhooks/%s", accountID, webhookID) + + res, err := api.makeRequestContext(ctx, http.MethodDelete, baseURL, nil) + if err != nil { + return SaveResponse{}, err + } + + return unmarshalSaveResponse(res) +} + +// ListPagerDutyDestinations will return the pagerduty destinations configured for an account +// +// API Reference: https://api.cloudflare.com/#notification-destinations-with-pagerduty-list-pagerduty-services +func (api *API) ListPagerDutyDestinations(ctx context.Context, accountID string) (PagerDutyResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/pagerduty", accountID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) + if err != nil { + return PagerDutyResponse{}, err + } + var r PagerDutyResponse + err = json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil +} + +// DeletePagerDutyDestinations will delete the pagerduty destination connected for an account +// +// API Reference: https://api.cloudflare.com/#notification-destinations-with-pagerduty-delete-pagerduty-destinations +func (api *API) DeletePagerDutyDestinations(ctx context.Context, accountID string) (Response, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/pagerduty", accountID) + + res, err := api.makeRequestContext(ctx, http.MethodDelete, baseURL, nil) + if err != nil { + return Response{}, err + } + var r Response + err = json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil +} + +// GetEligibleNotificationDestinations will return the types of destinations an account is eligible to configure +// +// API Reference: https://api.cloudflare.com/#notification-mechanism-eligibility-properties +func (api *API) GetEligibleNotificationDestinations(ctx context.Context, accountID string) (EligibilityResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/eligible", accountID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) + if err != nil { + return EligibilityResponse{}, err + } + var r EligibilityResponse + err = json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil +} + +// GetAvailableNotificationTypes will return the alert types available for a given account +// +// API Reference: https://api.cloudflare.com/#notification-mechanism-eligibility-properties +func (api *API) GetAvailableNotificationTypes(ctx context.Context, accountID string) (AvailableAlertsResponse, error) { + baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/available_alerts", accountID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) + if err != nil { + return AvailableAlertsResponse{}, err + } + var r AvailableAlertsResponse + err = json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil +} + +// unmarshal will unmarshal bytes and return a SaveResponse +func unmarshalSaveResponse(res []byte) (SaveResponse, error) { + var r SaveResponse + err := json.Unmarshal(res, &r) + if err != nil { + return r, err + } + return r, nil +} diff --git a/alert_notifications_test.go b/alert_notifications_test.go new file mode 100644 index 00000000000..ba494e9b343 --- /dev/null +++ b/alert_notifications_test.go @@ -0,0 +1,528 @@ +package cloudflare + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + testWebhookID = "fe49ee055d23404e9d58f9110b210c8d" + testPolicyID = "6ec8a5145d0d2263a36fad55c03cb43d" +) + +var ( + notificationTimestamp = time.Date(2021, 05, 01, 10, 47, 01, 01, time.UTC) +) + +func TestAPI_GetEligibleNotificationDestinations(t *testing.T) { + setup() + defer teardown() + + expected := Mechanisms{ + Email: MechanismMetaData{true, true, "email"}, + PagerDuty: MechanismMetaData{true, true, "pagerduty"}, + Webhooks: MechanismMetaData{true, true, "webhooks"}, + } + b, err := json.Marshal(expected) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s +}`, fmt.Sprintf(string(b))) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/eligible", handler) + + actual, err := client.GetEligibleNotificationDestinations(context.Background(), testAccountID) + require.Nil(t, err) + require.NotNil(t, actual) + assert.Equal(t, expected, actual.Result) +} +func TestAPI_GetAvailableNotificationTypes(t *testing.T) { + setup() + defer teardown() + + expected := make(NotificationsGroupedByProduct, 1) + alert1 := AlertWithDescription{Type: "secondary_dns_zone_successfully_updated", DisplayName: "Secondary DNS Successfully Updated", Description: "Secondary zone transfers are succeeding, the zone has been updated."} + alert2 := AlertWithDescription{Type: "secondary_dns_zone_validation_warning", DisplayName: "Secondary DNSSEC Validation Warning", Description: "The transferred DNSSEC zone is incorrectly configured."} + expected["DNS"] = []AlertWithDescription{alert1, alert2} + + b, err := json.Marshal(expected) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/available_alerts", handler) + + actual, err := client.GetAvailableNotificationTypes(context.Background(), testAccountID) + require.Nil(t, err) + require.NotNil(t, actual) + assert.Equal(t, expected, actual.Result) +} +func TestAPI_ListPagerDutyDestinations(t *testing.T) { + setup() + defer teardown() + + expected := PagerDutyResource{ID: "valid-uuid", Name: "my pagerduty connection"} + b, err := json.Marshal(expected) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/pagerduty", handler) + + actual, err := client.ListPagerDutyDestinations(context.Background(), testAccountID) + require.Nil(t, err) + require.NotNil(t, actual) + assert.Equal(t, expected, actual.Result) +} +func TestAPI_DeletePagerDutyDestinations(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err := fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [] + }`, + ) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/pagerduty", handler) + + actual, err := client.DeletePagerDutyDestinations(context.Background(), testAccountID) + require.Nil(t, err) + require.NotNil(t, actual) + assert.True(t, actual.Success) +} +func TestAPI_CreateNotificationPolicy(t *testing.T) { + setup() + defer teardown() + + mechanisms := make(map[string]MechanismIntegrations) + mechanisms["email"] = []MechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} + policy := Policy{ + Description: "Notifies when my zones are under attack", + Name: "CF DOS attack alert - L4", + Enabled: true, + AlertType: "dos_attack_l4", + Mechanisms: mechanisms, + Conditions: nil, + Filters: nil, + } + b, err := json.Marshal(policy) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/policies", handler) + res, err := client.CreateNotificationPolicy(context.Background(), testAccountID, policy) + require.NoError(t, err) + require.NotNil(t, res) +} + +func TestAPI_GetNotificationPolicy(t *testing.T) { + setup() + defer teardown() + + mechanisms := make(map[string]MechanismIntegrations) + mechanisms["email"] = []MechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} + policy := Policy{ + ID: testPolicyID, + Description: "Notifies when my zones are under attack", + Name: "CF DOS attack alert - L4", + Enabled: true, + AlertType: "dos_attack_l4", + Mechanisms: mechanisms, + Conditions: nil, + Filters: nil, + Created: notificationTimestamp, + Modified: notificationTimestamp, + } + b, err := json.Marshal(policy) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/policies/"+testPolicyID, handler) + + res, err := client.GetNotificationPolicy(context.Background(), testAccountID, testPolicyID) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, policy, res.Result) + +} + +func TestAPI_ListNotificationPolicies(t *testing.T) { + setup() + defer teardown() + + mechanisms := make(map[string]MechanismIntegrations) + mechanisms["email"] = []MechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} + policy := Policy{ + ID: testPolicyID, + Description: "Notifies when my zones are under attack", + Name: "CF DOS attack alert - L4", + Enabled: true, + AlertType: "dos_attack_l4", + Mechanisms: mechanisms, + Conditions: nil, + Filters: nil, + Created: time.Date(2021, 05, 01, 10, 47, 01, 01, time.UTC), + Modified: time.Date(2021, 05, 01, 10, 47, 01, 01, time.UTC), + } + policies := []Policy{ + policy, + } + b, err := json.Marshal(policies) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/policies", handler) + + res, err := client.ListNotificationPolicies(context.Background(), testAccountID) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, policies, res.Result) +} + +func TestAPI_UpdateNotificationPolicy(t *testing.T) { + setup() + defer teardown() + + mechanisms := make(map[string]MechanismIntegrations) + mechanisms["email"] = []MechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} + policy := Policy{ + ID: testPolicyID, + Description: "Notifies when my zones are under attack", + Name: "CF DOS attack alert - L4", + Enabled: true, + AlertType: "dos_attack_l4", + Mechanisms: mechanisms, + Conditions: nil, + Filters: nil, + Created: time.Date(2021, 05, 01, 10, 47, 01, 01, time.UTC), + Modified: time.Date(2021, 05, 01, 10, 47, 01, 01, time.UTC), + } + b, err := json.Marshal(policy) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/policies/"+testPolicyID, handler) + + res, err := client.UpdateNotificationPolicy(context.Background(), testAccountID, &policy) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, testPolicyID, res.Result.ID) + +} +func TestAPI_DeleteNotificationPolicy(t *testing.T) { + setup() + defer teardown() + + result := Resource{ID: testPolicyID} + b, err := json.Marshal(result) + require.NoError(t, err) + require.NotNil(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/policies/"+testPolicyID, handler) + + res, err := client.DeleteNotificationPolicy(context.Background(), testAccountID, testPolicyID) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, testPolicyID, res.Result.ID) + +} +func TestAPI_CreateNotificationWebhooks(t *testing.T) { + setup() + defer teardown() + + webhook := UpsertWebhooks{ + Name: "my test webhook", + URL: "https://example.com", + Secret: "mischief-managed", // optional + } + + result := Resource{ID: testWebhookID} + + b, err := json.Marshal(result) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/webhooks", handler) + + res, err := client.CreateNotificationWebhooks(context.Background(), testAccountID, &webhook) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, testWebhookID, res.Result.ID) + +} +func TestAPI_ListNotificationWebhooks(t *testing.T) { + setup() + defer teardown() + + webhook := WebhookIntegration{ + ID: testWebhookID, + Name: "my test webhook", + URL: "https://example.com", + Type: "generic", + CreatedAt: notificationTimestamp, + LastSuccess: ¬ificationTimestamp, + LastFailure: ¬ificationTimestamp, + } + webhooks := []WebhookIntegration{webhook} + b, err := json.Marshal(webhooks) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/webhooks", handler) + + res, err := client.ListNotificationWebhooks(context.Background(), testAccountID) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, webhooks, res.Result) +} + +func TestAPI_GetNotificationWebhooks(t *testing.T) { + setup() + defer teardown() + + webhook := WebhookIntegration{ + ID: testWebhookID, + Name: "my test webhook", + URL: "https://example.com", + Type: "generic", + CreatedAt: notificationTimestamp, + LastSuccess: ¬ificationTimestamp, + LastFailure: ¬ificationTimestamp, + } + b, err := json.Marshal(webhook) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/webhooks/"+testWebhookID, handler) + + res, err := client.GetNotificationWebhooks(context.Background(), testAccountID, testWebhookID) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, webhook, res.Result) +} + +func TestAPI_UpdateNotificationWebhooks(t *testing.T) { + setup() + defer teardown() + + result := Resource{ID: testWebhookID} + b, err := json.Marshal(result) + require.NoError(t, err) + require.NotEmpty(t, b) + + webhook := UpsertWebhooks{ + Name: "my test webhook with a new name", + URL: "https://example.com", + Secret: "mischief-managed", + } + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/webhooks/"+testWebhookID, handler) + + res, err := client.UpdateNotificationWebhooks(context.Background(), testAccountID, testWebhookID, &webhook) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, testWebhookID, res.Result.ID) + +} + +func TestAPI_DeleteNotificationWebhooks(t *testing.T) { + setup() + defer teardown() + + result := Resource{ID: testWebhookID} + b, err := json.Marshal(result) + require.NoError(t, err) + require.NotEmpty(t, b) + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) + w.Header().Set("content-type", "application/json") + _, err = fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result":%s + }`, + fmt.Sprintf(string(b))) + require.NoError(t, err) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/webhooks/"+testWebhookID, handler) + + res, err := client.DeleteNotificationWebhooks(context.Background(), testAccountID, testWebhookID) + require.NoError(t, err) + require.NotNil(t, res) + + assert.Equal(t, testWebhookID, res.Result.ID) +} From 8adf134fd28f44f28b608e856c9da0b6c7f53cef Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 2 Jul 2021 09:16:20 +1000 Subject: [PATCH 2/3] alert_notifications: namespace methods for Notifications --- alert_notifications.go | 264 ++++++++++++++++++++---------------- alert_notifications_test.go | 92 ++++++------- 2 files changed, 190 insertions(+), 166 deletions(-) diff --git a/alert_notifications.go b/alert_notifications.go index 7e199e64adb..fec7545cf3c 100644 --- a/alert_notifications.go +++ b/alert_notifications.go @@ -8,45 +8,50 @@ import ( "time" ) -// MechanismData holds a single public facing mechanism data integation -type MechanismData struct { +// NotificationMechanismData holds a single public facing mechanism data +// integation. +type NotificationMechanismData struct { Name string `json:"id"` ID string `json:"name"` } -// MechanismIntegrations is a list of all the integrations of a certain mechanism type -// e.g. all email integrations -type MechanismIntegrations []MechanismData +// NotificationMechanismIntegrations is a list of all the integrations of a +// certain mechanism type e.g. all email integrations +type NotificationMechanismIntegrations []NotificationMechanismData -// Policy represents the notification policy created along with the destinations -type Policy struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Enabled bool `json:"enabled"` - AlertType string `json:"alert_type"` - Mechanisms map[string]MechanismIntegrations `json:"mechanisms"` - Created time.Time `json:"created"` - Modified time.Time `json:"modified"` - Conditions map[string]interface{} `json:"conditions"` - Filters map[string][]string `json:"filters"` +// NotificationPolicy represents the notification policy created along with +// the destinations. +type NotificationPolicy struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Enabled bool `json:"enabled"` + AlertType string `json:"alert_type"` + Mechanisms map[string]NotificationMechanismIntegrations `json:"mechanisms"` + Created time.Time `json:"created"` + Modified time.Time `json:"modified"` + Conditions map[string]interface{} `json:"conditions"` + Filters map[string][]string `json:"filters"` } -// PoliciesResponse holds the response for listing all notification policies for an account -type PoliciesResponse struct { - Response Response - ResultInfo ResultInfo - Result []Policy +// NotificationPoliciesResponse holds the response for listing all +// notification policies for an account. +type NotificationPoliciesResponse struct { + Response + ResultInfo + Result []NotificationPolicy } -// PolicyResponse holds the response type when a single policy is retrieved -type PolicyResponse struct { - Response Response - Result Policy +// NotificationPolicyResponse holds the response type when a single policy +// is retrieved. +type NotificationPolicyResponse struct { + Response + Result NotificationPolicy } -// WebhookIntegration describes the webhook information along with its status -type WebhookIntegration struct { +// NotificationWebhookIntegration describes the webhook information along +// with its status. +type NotificationWebhookIntegration struct { ID string `json:"id"` Name string `json:"name"` Type string `json:"type"` @@ -56,99 +61,105 @@ type WebhookIntegration struct { LastFailure *time.Time `json:"last_failure"` } -// WebhookResponse describes a single webhook retrieved -type WebhookResponse struct { - Response Response - ResultInfo ResultInfo - Result WebhookIntegration +// NotificationWebhookResponse describes a single webhook retrieved. +type NotificationWebhookResponse struct { + Response + ResultInfo + Result NotificationWebhookIntegration } -// WebhooksResponse describes a list of webhooks retrieved -type WebhooksResponse struct { - Response Response - ResultInfo ResultInfo - Result []WebhookIntegration +// NotificationWebhooksResponse describes a list of webhooks retrieved. +type NotificationWebhooksResponse struct { + Response + ResultInfo + Result []NotificationWebhookIntegration } -// UpsertWebhooks describes a valid webhook request -type UpsertWebhooks struct { +// NotificationUpsertWebhooks describes a valid webhook request. +type NotificationUpsertWebhooks struct { Name string `json:"name"` URL string `json:"url"` Secret string `json:"secret"` } -// PagerDutyResource describes a PagerDuty integration -type PagerDutyResource struct { +// NotificationPagerDutyResource describes a PagerDuty integration. +type NotificationPagerDutyResource struct { ID string `json:"id"` Name string `json:"name"` } -// PagerDutyResponse describes the PagerDuty integration retrieved -type PagerDutyResponse struct { - Response Response - ResultInfo ResultInfo - Result PagerDutyResource +// NotificationPagerDutyResponse describes the PagerDuty integration +// retrieved. +type NotificationPagerDutyResponse struct { + Response + ResultInfo + Result NotificationPagerDutyResource } -// Resource describes the id of an inserted/updated/deleted resource -type Resource struct { +// NotificationResource describes the id of an inserted/updated/deleted +// resource. +type NotificationResource struct { ID string } -// SaveResponse is returned when a resource is inserted/updated/deleted +// SaveResponse is returned when a resource is inserted/updated/deleted. type SaveResponse struct { - Response Response - Result Resource + Response + Result NotificationResource } -// MechanismMetaData represents the state of the delivery mechanism -type MechanismMetaData struct { +// NotificationMechanismMetaData represents the state of the delivery +// mechanism. +type NotificationMechanismMetaData struct { Eligible bool `json:"eligible"` Ready bool `json:"ready"` Type string `json:"type"` } -// Mechanisms are the different possible delivery mechanisms -type Mechanisms struct { - Email MechanismMetaData `json:"email"` - PagerDuty MechanismMetaData `json:"pagerduty"` - Webhooks MechanismMetaData `json:"webhooks,omitempty"` +// NotificationMechanisms are the different possible delivery mechanisms. +type NotificationMechanisms struct { + Email NotificationMechanismMetaData `json:"email"` + PagerDuty NotificationMechanismMetaData `json:"pagerduty"` + Webhooks NotificationMechanismMetaData `json:"webhooks,omitempty"` } -// EligibilityResponse describes the eligible mechanisms that can be configured for a notification -type EligibilityResponse struct { - Response Response - Result Mechanisms +// NotificationEligibilityResponse describes the eligible mechanisms that +// can be configured for a notification. +type NotificationEligibilityResponse struct { + Response + Result NotificationMechanisms } -// NotificationsGroupedByProduct are grouped by products -type NotificationsGroupedByProduct map[string][]AlertWithDescription +// NotificationsGroupedByProduct are grouped by products. +type NotificationsGroupedByProduct map[string][]NotificationAlertWithDescription -// AlertWithDescription represents the alert/notification available -type AlertWithDescription struct { +// NotificationAlertWithDescription represents the alert/notification +// available. +type NotificationAlertWithDescription struct { DisplayName string `json:"display_name"` Type string `json:"type"` Description string `json:"description"` } -// AvailableAlertsResponse describes the available alerts/notifications grouped by products -type AvailableAlertsResponse struct { - Response Response - Result NotificationsGroupedByProduct +// NotificationAvailableAlertsResponse describes the available +// alerts/notifications grouped by products. +type NotificationAvailableAlertsResponse struct { + Response + Result NotificationsGroupedByProduct } // ListNotificationPolicies will return the notification policies -// created by a user for a specific account +// created by a user for a specific account. // // API Reference: https://api.cloudflare.com/#notification-policies-properties -func (api *API) ListNotificationPolicies(ctx context.Context, accountID string) (PoliciesResponse, error) { +func (api *API) ListNotificationPolicies(ctx context.Context, accountID string) (NotificationPoliciesResponse, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/policies", accountID) res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) if err != nil { - return PoliciesResponse{}, err + return NotificationPoliciesResponse{}, err } - var r PoliciesResponse + var r NotificationPoliciesResponse err = json.Unmarshal(res, &r) if err != nil { return r, err @@ -156,17 +167,18 @@ func (api *API) ListNotificationPolicies(ctx context.Context, accountID string) return r, nil } -// GetNotificationPolicy returns a specific created by a user, given the account id and the policy id +// GetNotificationPolicy returns a specific created by a user, given the account +// id and the policy id. // // API Reference: https://api.cloudflare.com/#notification-policies-properties -func (api *API) GetNotificationPolicy(ctx context.Context, accountID, policyID string) (PolicyResponse, error) { +func (api *API) GetNotificationPolicy(ctx context.Context, accountID, policyID string) (NotificationPolicyResponse, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/policies/%s", accountID, policyID) res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) if err != nil { - return PolicyResponse{}, err + return NotificationPolicyResponse{}, err } - var r PolicyResponse + var r NotificationPolicyResponse err = json.Unmarshal(res, &r) if err != nil { return r, err @@ -174,10 +186,10 @@ func (api *API) GetNotificationPolicy(ctx context.Context, accountID, policyID s return r, nil } -// CreateNotificationPolicy creates a notification policy for an account +// CreateNotificationPolicy creates a notification policy for an account. // // API Reference: https://api.cloudflare.com/#notification-policies-create-notification-policy -func (api *API) CreateNotificationPolicy(ctx context.Context, accountID string, policy Policy) (SaveResponse, error) { +func (api *API) CreateNotificationPolicy(ctx context.Context, accountID string, policy NotificationPolicy) (SaveResponse, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/policies", accountID) @@ -185,13 +197,14 @@ func (api *API) CreateNotificationPolicy(ctx context.Context, accountID string, if err != nil { return SaveResponse{}, err } - return unmarshalSaveResponse(res) + return unmarshalNotificationSaveResponse(res) } -// UpdateNotificationPolicy updates a notification policy, given the account id and the policy id and returns the policy id +// UpdateNotificationPolicy updates a notification policy, given the +// account id and the policy id and returns the policy id. // // API Reference: https://api.cloudflare.com/#notification-policies-update-notification-policy -func (api *API) UpdateNotificationPolicy(ctx context.Context, accountID string, policy *Policy) (SaveResponse, error) { +func (api *API) UpdateNotificationPolicy(ctx context.Context, accountID string, policy *NotificationPolicy) (SaveResponse, error) { if policy == nil { return SaveResponse{}, fmt.Errorf("policy cannot be nil") } @@ -201,10 +214,10 @@ func (api *API) UpdateNotificationPolicy(ctx context.Context, accountID string, if err != nil { return SaveResponse{}, err } - return unmarshalSaveResponse(res) + return unmarshalNotificationSaveResponse(res) } -// DeleteNotificationPolicy deletes a notification policy for an account +// DeleteNotificationPolicy deletes a notification policy for an account. // // API Reference: https://api.cloudflare.com/#notification-policies-delete-notification-policy func (api *API) DeleteNotificationPolicy(ctx context.Context, accountID, policyID string) (SaveResponse, error) { @@ -214,20 +227,21 @@ func (api *API) DeleteNotificationPolicy(ctx context.Context, accountID, policyI if err != nil { return SaveResponse{}, err } - return unmarshalSaveResponse(res) + return unmarshalNotificationSaveResponse(res) } -// ListNotificationWebhooks will return the webhook destinations configured for an account +// ListNotificationWebhooks will return the webhook destinations configured +// for an account. // // API Reference: https://api.cloudflare.com/#notification-webhooks-list-webhooks -func (api *API) ListNotificationWebhooks(ctx context.Context, accountID string) (WebhooksResponse, error) { +func (api *API) ListNotificationWebhooks(ctx context.Context, accountID string) (NotificationWebhooksResponse, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/webhooks", accountID) res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) if err != nil { - return WebhooksResponse{}, err + return NotificationWebhooksResponse{}, err } - var r WebhooksResponse + var r NotificationWebhooksResponse err = json.Unmarshal(res, &r) if err != nil { return r, err @@ -238,11 +252,13 @@ func (api *API) ListNotificationWebhooks(ctx context.Context, accountID string) // CreateNotificationWebhooks will help connect a webhooks destination. // A test message will be sent to the webhooks endpoint during creation. -// If added successfully, the webhooks can be setup as a destination mechanism while creating policies. +// If added successfully, the webhooks can be setup as a destination mechanism +// while creating policies. +// // Notifications will be posted to this URL. // // API Reference: https://api.cloudflare.com/#notification-webhooks-create-webhook -func (api *API) CreateNotificationWebhooks(ctx context.Context, accountID string, webhooks *UpsertWebhooks) (SaveResponse, error) { +func (api *API) CreateNotificationWebhooks(ctx context.Context, accountID string, webhooks *NotificationUpsertWebhooks) (SaveResponse, error) { if webhooks == nil { return SaveResponse{}, fmt.Errorf("webhooks cannot be nil") } @@ -253,20 +269,21 @@ func (api *API) CreateNotificationWebhooks(ctx context.Context, accountID string return SaveResponse{}, err } - return unmarshalSaveResponse(res) + return unmarshalNotificationSaveResponse(res) } -// GetNotificationWebhooks will return a specific webhook destination, given the account and webhooks ids +// GetNotificationWebhooks will return a specific webhook destination, +// given the account and webhooks ids. // // API Reference: https://api.cloudflare.com/#notification-webhooks-get-webhook -func (api *API) GetNotificationWebhooks(ctx context.Context, accountID, webhookID string) (WebhookResponse, error) { +func (api *API) GetNotificationWebhooks(ctx context.Context, accountID, webhookID string) (NotificationWebhookResponse, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/webhooks/%s", accountID, webhookID) res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) if err != nil { - return WebhookResponse{}, err + return NotificationWebhookResponse{}, err } - var r WebhookResponse + var r NotificationWebhookResponse err = json.Unmarshal(res, &r) if err != nil { return r, err @@ -274,11 +291,13 @@ func (api *API) GetNotificationWebhooks(ctx context.Context, accountID, webhookI return r, nil } -// UpdateNotificationWebhooks will update a particular webhook's name, given the account and webhooks ids. -// The webhook url and secret cannot be updated +// UpdateNotificationWebhooks will update a particular webhook's name, +// given the account and webhooks ids. +// +// The webhook url and secret cannot be updated. // // API Reference: https://api.cloudflare.com/#notification-webhooks-update-webhook -func (api *API) UpdateNotificationWebhooks(ctx context.Context, accountID, webhookID string, webhooks *UpsertWebhooks) (SaveResponse, error) { +func (api *API) UpdateNotificationWebhooks(ctx context.Context, accountID, webhookID string, webhooks *NotificationUpsertWebhooks) (SaveResponse, error) { if webhooks == nil { return SaveResponse{}, fmt.Errorf("webhooks cannot be nil") } @@ -289,11 +308,12 @@ func (api *API) UpdateNotificationWebhooks(ctx context.Context, accountID, webho return SaveResponse{}, err } - return unmarshalSaveResponse(res) + return unmarshalNotificationSaveResponse(res) } -// DeleteNotificationWebhooks will delete a webhook, given the account and webhooks ids. -// Deleting the webhooks will remove it from any connected notification policies. +// DeleteNotificationWebhooks will delete a webhook, given the account and +// webhooks ids. Deleting the webhooks will remove it from any connected +// notification policies. // // API Reference: https://api.cloudflare.com/#notification-webhooks-delete-webhook func (api *API) DeleteNotificationWebhooks(ctx context.Context, accountID, webhookID string) (SaveResponse, error) { @@ -304,20 +324,21 @@ func (api *API) DeleteNotificationWebhooks(ctx context.Context, accountID, webho return SaveResponse{}, err } - return unmarshalSaveResponse(res) + return unmarshalNotificationSaveResponse(res) } -// ListPagerDutyDestinations will return the pagerduty destinations configured for an account +// ListPagerDutyNotificationDestinations will return the pagerduty +// destinations configured for an account. // // API Reference: https://api.cloudflare.com/#notification-destinations-with-pagerduty-list-pagerduty-services -func (api *API) ListPagerDutyDestinations(ctx context.Context, accountID string) (PagerDutyResponse, error) { +func (api *API) ListPagerDutyNotificationDestinations(ctx context.Context, accountID string) (NotificationPagerDutyResponse, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/pagerduty", accountID) res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) if err != nil { - return PagerDutyResponse{}, err + return NotificationPagerDutyResponse{}, err } - var r PagerDutyResponse + var r NotificationPagerDutyResponse err = json.Unmarshal(res, &r) if err != nil { return r, err @@ -325,10 +346,11 @@ func (api *API) ListPagerDutyDestinations(ctx context.Context, accountID string) return r, nil } -// DeletePagerDutyDestinations will delete the pagerduty destination connected for an account +// DeletePagerDutyNotificationDestinations will delete the pagerduty +// destination connected for an account. // // API Reference: https://api.cloudflare.com/#notification-destinations-with-pagerduty-delete-pagerduty-destinations -func (api *API) DeletePagerDutyDestinations(ctx context.Context, accountID string) (Response, error) { +func (api *API) DeletePagerDutyNotificationDestinations(ctx context.Context, accountID string) (Response, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/pagerduty", accountID) res, err := api.makeRequestContext(ctx, http.MethodDelete, baseURL, nil) @@ -343,17 +365,18 @@ func (api *API) DeletePagerDutyDestinations(ctx context.Context, accountID strin return r, nil } -// GetEligibleNotificationDestinations will return the types of destinations an account is eligible to configure +// GetEligibleNotificationDestinations will return the types of +// destinations an account is eligible to configure. // // API Reference: https://api.cloudflare.com/#notification-mechanism-eligibility-properties -func (api *API) GetEligibleNotificationDestinations(ctx context.Context, accountID string) (EligibilityResponse, error) { +func (api *API) GetEligibleNotificationDestinations(ctx context.Context, accountID string) (NotificationEligibilityResponse, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/destinations/eligible", accountID) res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) if err != nil { - return EligibilityResponse{}, err + return NotificationEligibilityResponse{}, err } - var r EligibilityResponse + var r NotificationEligibilityResponse err = json.Unmarshal(res, &r) if err != nil { return r, err @@ -361,17 +384,18 @@ func (api *API) GetEligibleNotificationDestinations(ctx context.Context, account return r, nil } -// GetAvailableNotificationTypes will return the alert types available for a given account +// GetAvailableNotificationTypes will return the alert types available for +// a given account. // // API Reference: https://api.cloudflare.com/#notification-mechanism-eligibility-properties -func (api *API) GetAvailableNotificationTypes(ctx context.Context, accountID string) (AvailableAlertsResponse, error) { +func (api *API) GetAvailableNotificationTypes(ctx context.Context, accountID string) (NotificationAvailableAlertsResponse, error) { baseURL := fmt.Sprintf("/accounts/%s/alerting/v3/available_alerts", accountID) res, err := api.makeRequestContext(ctx, http.MethodGet, baseURL, nil) if err != nil { - return AvailableAlertsResponse{}, err + return NotificationAvailableAlertsResponse{}, err } - var r AvailableAlertsResponse + var r NotificationAvailableAlertsResponse err = json.Unmarshal(res, &r) if err != nil { return r, err @@ -380,7 +404,7 @@ func (api *API) GetAvailableNotificationTypes(ctx context.Context, accountID str } // unmarshal will unmarshal bytes and return a SaveResponse -func unmarshalSaveResponse(res []byte) (SaveResponse, error) { +func unmarshalNotificationSaveResponse(res []byte) (SaveResponse, error) { var r SaveResponse err := json.Unmarshal(res, &r) if err != nil { diff --git a/alert_notifications_test.go b/alert_notifications_test.go index ba494e9b343..6a0b4d3c5d8 100644 --- a/alert_notifications_test.go +++ b/alert_notifications_test.go @@ -21,14 +21,14 @@ var ( notificationTimestamp = time.Date(2021, 05, 01, 10, 47, 01, 01, time.UTC) ) -func TestAPI_GetEligibleNotificationDestinations(t *testing.T) { +func TestGetEligibleNotificationDestinations(t *testing.T) { setup() defer teardown() - expected := Mechanisms{ - Email: MechanismMetaData{true, true, "email"}, - PagerDuty: MechanismMetaData{true, true, "pagerduty"}, - Webhooks: MechanismMetaData{true, true, "webhooks"}, + expected := NotificationMechanisms{ + Email: NotificationMechanismMetaData{true, true, "email"}, + PagerDuty: NotificationMechanismMetaData{true, true, "pagerduty"}, + Webhooks: NotificationMechanismMetaData{true, true, "webhooks"}, } b, err := json.Marshal(expected) require.NoError(t, err) @@ -52,14 +52,14 @@ func TestAPI_GetEligibleNotificationDestinations(t *testing.T) { require.NotNil(t, actual) assert.Equal(t, expected, actual.Result) } -func TestAPI_GetAvailableNotificationTypes(t *testing.T) { +func TestGetAvailableNotificationTypes(t *testing.T) { setup() defer teardown() expected := make(NotificationsGroupedByProduct, 1) - alert1 := AlertWithDescription{Type: "secondary_dns_zone_successfully_updated", DisplayName: "Secondary DNS Successfully Updated", Description: "Secondary zone transfers are succeeding, the zone has been updated."} - alert2 := AlertWithDescription{Type: "secondary_dns_zone_validation_warning", DisplayName: "Secondary DNSSEC Validation Warning", Description: "The transferred DNSSEC zone is incorrectly configured."} - expected["DNS"] = []AlertWithDescription{alert1, alert2} + alert1 := NotificationAlertWithDescription{Type: "secondary_dns_zone_successfully_updated", DisplayName: "Secondary DNS Successfully Updated", Description: "Secondary zone transfers are succeeding, the zone has been updated."} + alert2 := NotificationAlertWithDescription{Type: "secondary_dns_zone_validation_warning", DisplayName: "Secondary DNSSEC Validation Warning", Description: "The transferred DNSSEC zone is incorrectly configured."} + expected["DNS"] = []NotificationAlertWithDescription{alert1, alert2} b, err := json.Marshal(expected) require.NoError(t, err) @@ -84,11 +84,11 @@ func TestAPI_GetAvailableNotificationTypes(t *testing.T) { require.NotNil(t, actual) assert.Equal(t, expected, actual.Result) } -func TestAPI_ListPagerDutyDestinations(t *testing.T) { +func TestListPagerDutyDestinations(t *testing.T) { setup() defer teardown() - expected := PagerDutyResource{ID: "valid-uuid", Name: "my pagerduty connection"} + expected := NotificationPagerDutyResource{ID: "valid-uuid", Name: "my pagerduty connection"} b, err := json.Marshal(expected) require.NoError(t, err) require.NotEmpty(t, b) @@ -108,12 +108,12 @@ func TestAPI_ListPagerDutyDestinations(t *testing.T) { mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/pagerduty", handler) - actual, err := client.ListPagerDutyDestinations(context.Background(), testAccountID) + actual, err := client.ListPagerDutyNotificationDestinations(context.Background(), testAccountID) require.Nil(t, err) require.NotNil(t, actual) assert.Equal(t, expected, actual.Result) } -func TestAPI_DeletePagerDutyDestinations(t *testing.T) { +func TestDeletePagerDutyDestinations(t *testing.T) { setup() defer teardown() @@ -131,18 +131,18 @@ func TestAPI_DeletePagerDutyDestinations(t *testing.T) { mux.HandleFunc("/accounts/"+testAccountID+"/alerting/v3/destinations/pagerduty", handler) - actual, err := client.DeletePagerDutyDestinations(context.Background(), testAccountID) + actual, err := client.DeletePagerDutyNotificationDestinations(context.Background(), testAccountID) require.Nil(t, err) require.NotNil(t, actual) assert.True(t, actual.Success) } -func TestAPI_CreateNotificationPolicy(t *testing.T) { +func TestCreateNotificationPolicy(t *testing.T) { setup() defer teardown() - mechanisms := make(map[string]MechanismIntegrations) - mechanisms["email"] = []MechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} - policy := Policy{ + mechanisms := make(map[string]NotificationMechanismIntegrations) + mechanisms["email"] = []NotificationMechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} + policy := NotificationPolicy{ Description: "Notifies when my zones are under attack", Name: "CF DOS attack alert - L4", Enabled: true, @@ -174,13 +174,13 @@ func TestAPI_CreateNotificationPolicy(t *testing.T) { require.NotNil(t, res) } -func TestAPI_GetNotificationPolicy(t *testing.T) { +func TestGetNotificationPolicy(t *testing.T) { setup() defer teardown() - mechanisms := make(map[string]MechanismIntegrations) - mechanisms["email"] = []MechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} - policy := Policy{ + mechanisms := make(map[string]NotificationMechanismIntegrations) + mechanisms["email"] = []NotificationMechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} + policy := NotificationPolicy{ ID: testPolicyID, Description: "Notifies when my zones are under attack", Name: "CF DOS attack alert - L4", @@ -219,13 +219,13 @@ func TestAPI_GetNotificationPolicy(t *testing.T) { } -func TestAPI_ListNotificationPolicies(t *testing.T) { +func TestListNotificationPolicies(t *testing.T) { setup() defer teardown() - mechanisms := make(map[string]MechanismIntegrations) - mechanisms["email"] = []MechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} - policy := Policy{ + mechanisms := make(map[string]NotificationMechanismIntegrations) + mechanisms["email"] = []NotificationMechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} + policy := NotificationPolicy{ ID: testPolicyID, Description: "Notifies when my zones are under attack", Name: "CF DOS attack alert - L4", @@ -237,7 +237,7 @@ func TestAPI_ListNotificationPolicies(t *testing.T) { Created: time.Date(2021, 05, 01, 10, 47, 01, 01, time.UTC), Modified: time.Date(2021, 05, 01, 10, 47, 01, 01, time.UTC), } - policies := []Policy{ + policies := []NotificationPolicy{ policy, } b, err := json.Marshal(policies) @@ -266,13 +266,13 @@ func TestAPI_ListNotificationPolicies(t *testing.T) { assert.Equal(t, policies, res.Result) } -func TestAPI_UpdateNotificationPolicy(t *testing.T) { +func TestUpdateNotificationPolicy(t *testing.T) { setup() defer teardown() - mechanisms := make(map[string]MechanismIntegrations) - mechanisms["email"] = []MechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} - policy := Policy{ + mechanisms := make(map[string]NotificationMechanismIntegrations) + mechanisms["email"] = []NotificationMechanismData{{Name: "email to send notification", ID: "test@gmail.com"}} + policy := NotificationPolicy{ ID: testPolicyID, Description: "Notifies when my zones are under attack", Name: "CF DOS attack alert - L4", @@ -310,11 +310,11 @@ func TestAPI_UpdateNotificationPolicy(t *testing.T) { assert.Equal(t, testPolicyID, res.Result.ID) } -func TestAPI_DeleteNotificationPolicy(t *testing.T) { +func TestDeleteNotificationPolicy(t *testing.T) { setup() defer teardown() - result := Resource{ID: testPolicyID} + result := NotificationResource{ID: testPolicyID} b, err := json.Marshal(result) require.NoError(t, err) require.NotNil(t, b) @@ -341,17 +341,17 @@ func TestAPI_DeleteNotificationPolicy(t *testing.T) { assert.Equal(t, testPolicyID, res.Result.ID) } -func TestAPI_CreateNotificationWebhooks(t *testing.T) { +func TestCreateNotificationWebhooks(t *testing.T) { setup() defer teardown() - webhook := UpsertWebhooks{ + webhook := NotificationUpsertWebhooks{ Name: "my test webhook", URL: "https://example.com", Secret: "mischief-managed", // optional } - result := Resource{ID: testWebhookID} + result := NotificationResource{ID: testWebhookID} b, err := json.Marshal(result) require.NoError(t, err) @@ -379,11 +379,11 @@ func TestAPI_CreateNotificationWebhooks(t *testing.T) { assert.Equal(t, testWebhookID, res.Result.ID) } -func TestAPI_ListNotificationWebhooks(t *testing.T) { +func TestListNotificationWebhooks(t *testing.T) { setup() defer teardown() - webhook := WebhookIntegration{ + webhook := NotificationWebhookIntegration{ ID: testWebhookID, Name: "my test webhook", URL: "https://example.com", @@ -392,7 +392,7 @@ func TestAPI_ListNotificationWebhooks(t *testing.T) { LastSuccess: ¬ificationTimestamp, LastFailure: ¬ificationTimestamp, } - webhooks := []WebhookIntegration{webhook} + webhooks := []NotificationWebhookIntegration{webhook} b, err := json.Marshal(webhooks) require.NoError(t, err) require.NotEmpty(t, b) @@ -419,11 +419,11 @@ func TestAPI_ListNotificationWebhooks(t *testing.T) { assert.Equal(t, webhooks, res.Result) } -func TestAPI_GetNotificationWebhooks(t *testing.T) { +func TestGetNotificationWebhooks(t *testing.T) { setup() defer teardown() - webhook := WebhookIntegration{ + webhook := NotificationWebhookIntegration{ ID: testWebhookID, Name: "my test webhook", URL: "https://example.com", @@ -458,16 +458,16 @@ func TestAPI_GetNotificationWebhooks(t *testing.T) { assert.Equal(t, webhook, res.Result) } -func TestAPI_UpdateNotificationWebhooks(t *testing.T) { +func TestUpdateNotificationWebhooks(t *testing.T) { setup() defer teardown() - result := Resource{ID: testWebhookID} + result := NotificationResource{ID: testWebhookID} b, err := json.Marshal(result) require.NoError(t, err) require.NotEmpty(t, b) - webhook := UpsertWebhooks{ + webhook := NotificationUpsertWebhooks{ Name: "my test webhook with a new name", URL: "https://example.com", Secret: "mischief-managed", @@ -496,11 +496,11 @@ func TestAPI_UpdateNotificationWebhooks(t *testing.T) { } -func TestAPI_DeleteNotificationWebhooks(t *testing.T) { +func TestDeleteNotificationWebhooks(t *testing.T) { setup() defer teardown() - result := Resource{ID: testWebhookID} + result := NotificationResource{ID: testWebhookID} b, err := json.Marshal(result) require.NoError(t, err) require.NotEmpty(t, b) From bacfdc97210b3f3a05f1c4784158307d010fd264 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 2 Jul 2021 09:21:03 +1000 Subject: [PATCH 3/3] s/alert_notifications/notifications --- alert_notifications.go => notifications.go | 0 alert_notifications_test.go => notifications_test.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename alert_notifications.go => notifications.go (100%) rename alert_notifications_test.go => notifications_test.go (100%) diff --git a/alert_notifications.go b/notifications.go similarity index 100% rename from alert_notifications.go rename to notifications.go diff --git a/alert_notifications_test.go b/notifications_test.go similarity index 100% rename from alert_notifications_test.go rename to notifications_test.go