Skip to content

Commit

Permalink
Add support for the v1beta1 alerts api.
Browse files Browse the repository at this point in the history
- Add code to call api.
- Add testcases.
- Add example.
  • Loading branch information
hhellyer committed Oct 28, 2024
1 parent 12abb22 commit c522965
Show file tree
Hide file tree
Showing 9 changed files with 1,125 additions and 7 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 2.13.0 (Oct 28th, 2024)

FEATURES:

* Adds support for alerts

## 2.12.2 (October 17th, 2024)

BUG FIXES:
Expand Down
153 changes: 153 additions & 0 deletions mockns1/alert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package mockns1

import (
"fmt"
"net/http"

"gopkg.in/ns1/ns1-go.v2/rest/model/alerting"
)

// Should be identical to rest.alertListResponse
type mockAlertListResponse struct {
Limit *int64 `json:"limit,omitempty"`
Next *string `json:"next,omitempty"`
Results []*alerting.Alert `json:"results"`
TotalResults *int64 `json:"total_results,omitempty"`
}

const alertPath = "../alerting/v1beta1/alerts"

// AddAlertListTestCase sets up a test case for the api.Client.Alert.List()
// function
func (s *Service) AddAlertListTestCase(
params string, requestHeaders, responseHeaders http.Header,
response []*alerting.Alert,
) error {
length := int64(len(response))
next := ""
if length > 0 {
next = *response[length-1].Name
}
listResponse := &mockAlertListResponse{

Next: &next,
Results: response,
Limit: &length,
TotalResults: &length,
}
uri := alertPath
if params != "" {
uri = fmt.Sprintf("%s?%s", uri, params)
}
return s.AddTestCase(
http.MethodGet, uri, http.StatusOK, requestHeaders,
responseHeaders, "", listResponse,
)
}

// AddAlertGetTestCase sets up a test case for the api.Client.Alerts.Get()
// function
func (s *Service) AddAlertGetTestCase(
id string,
requestHeaders, responseHeaders http.Header,
response *alerting.Alert,
) error {
return s.AddTestCase(
http.MethodGet, fmt.Sprintf("%s/%s", alertPath, id), http.StatusOK, requestHeaders,
responseHeaders, "", response,
)
}

// AddAlertCreateTestCase sets up a test case for the api.Client.Alerts.Update()
// function
func (s *Service) AddAlertCreateTestCase(
requestHeaders, responseHeaders http.Header,
request alerting.Alert,
response alerting.Alert,
) error {
return s.AddTestCase(
http.MethodPost, alertPath, http.StatusOK, requestHeaders,
responseHeaders, request, response,
)
}

// AddAlertUpdateTestCase sets up a test case for the api.Client.Alerts.Update()
// function
func (s *Service) AddAlertUpdateTestCase(
requestHeaders, responseHeaders http.Header,
request alerting.Alert,
response alerting.Alert,
) error {
return s.AddTestCase(
http.MethodPatch, fmt.Sprintf("%s/%s", alertPath, *request.ID), http.StatusOK, requestHeaders,
responseHeaders, request, response,
)
}

// AddAlertReplaceTestCase sets up a test case for the api.Client.Alerts.Update()
// function
func (s *Service) AddAlertReplaceTestCase(
requestHeaders, responseHeaders http.Header,
request alerting.Alert,
response alerting.Alert,
) error {
return s.AddTestCase(
http.MethodPut, fmt.Sprintf("%s/%s", alertPath, *request.ID), http.StatusOK, requestHeaders,
responseHeaders, request, response,
)
}

// AddAlertDeleteTestCase sets up a test case for the api.Client.Alerts.Delete()
// function
func (s *Service) AddAlertDeleteTestCase(
id string,
requestHeaders, responseHeaders http.Header,
) error {
return s.AddTestCase(
http.MethodDelete, fmt.Sprintf("%s/%s", alertPath, id), http.StatusNoContent, requestHeaders,
responseHeaders, "", nil,
)
}

// AddAlertTestPostTestCase sets up a test case for the api.Client.Alerts.Test()
// function
func (s *Service) AddAlertTestPostTestCase(
id string,
requestHeaders, responseHeaders http.Header,
) error {
return s.AddTestCase(
http.MethodPost, fmt.Sprintf("%s/%s/test", alertPath, id), http.StatusNoContent, requestHeaders,
responseHeaders, "", nil,
)
}

// AddAlertFailTestCase sets up a test case for the api.Client.Alerts.*()
// functions that fails.
func (s *Service) AddAlertFailTestCase(
method string, id string, returnStatus int,
requestHeaders, responseHeaders http.Header,
responseBody string,
) error {
path := alertPath
if id != "" {
path = fmt.Sprintf("%s/%s", alertPath, id)
}
return s.AddTestCase(
method, path, returnStatus,
nil, nil, "", responseBody)
}

func (s *Service) AddAlertFailTestCaseWithReqBody(
method string, id string, returnStatus int,
requestHeaders, responseHeaders http.Header,
requestBody interface{},
responseBody string,
) error {
path := alertPath
if id != "" {
path = fmt.Sprintf("%s/%s", alertPath, id)
}
return s.AddTestCase(
method, path, returnStatus,
nil, nil, requestBody, responseBody)
}
9 changes: 9 additions & 0 deletions mockns1/testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -39,6 +40,14 @@ func (s *Service) AddTestCase(
uri = "/v1/" + uri
}

baseUri, _ := url.Parse("/")
rel, uriErr := url.Parse(uri)
if uriErr != nil {
return fmt.Errorf("could not parse testcase uri")
}

uri = baseUri.ResolveReference(rel).RequestURI()

if len(params) > 0 {
uri = fmt.Sprintf("%s?%s=%s", uri, params[0].Key, params[0].Value)

Expand Down
132 changes: 132 additions & 0 deletions rest/_examples/alerts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package main

import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"time"

api "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/alerting"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
"gopkg.in/ns1/ns1-go.v2/rest/model/monitor"
)

var client *api.Client

// Helper that initializes rest api client from environment variable.
func init() {
k := os.Getenv("NS1_APIKEY")
if k == "" {
fmt.Println("NS1_APIKEY environment variable is not set, giving up")
}

httpClient := &http.Client{Timeout: time.Second * 10}
// Adds logging to each http request.
doer := api.Decorate(httpClient, api.Logging(log.New(os.Stdout, "", log.LstdFlags)))
client = api.NewClient(doer, api.SetAPIKey(k))
}

func prettyPrint(header string, v interface{}) {
fmt.Println(header)
fmt.Printf("%#v \n", v)
b, _ := json.MarshalIndent(v, "", " ")
fmt.Println(string(b))
}

func main() {
alerts, _, err := client.Alerts.List()
if err != nil {
log.Fatal(err)
}
for _, a := range alerts {
prettyPrint("alert:", a)
}

webhook := monitor.NewWebNotification("test.com/test", map[string]string{})
webhookList := monitor.NewNotifyList("my webhook list", webhook)
_, err = client.Notifications.Create(webhookList)
if err != nil {
log.Fatal(err)
}
prettyPrint("Webhook NotifyList:", webhookList)

email := monitor.NewEmailNotification("[email protected]")
emailList := monitor.NewNotifyList("my email list", email)
_, err = client.Notifications.Create(emailList)
if err != nil {
log.Fatal(err)
}
prettyPrint("Email NotifyList:", emailList)

// Construct/Create a zone.
domain := "myalerttest.com"

z := dns.NewZone(domain)
z.NxTTL = 3600
_, err = client.Zones.Create(z)
if err != nil {
// Ignore if zone already exists
if err != api.ErrZoneExists {
log.Fatal(err)
} else {
log.Println("Zone already exists, continuing...")
}
}

prettyPrint("Zone:", z)
fmt.Printf("Creating alert...\n")
alert := alerting.NewZoneAlert("myalerttest.com - transfer failed", "transfer_failed", []string{webhookList.ID}, []string{domain})
_, err = client.Alerts.Create(alert)
if err != nil {
if err == api.ErrAlertExists {
// This is fatal as we need the id returned on create.
log.Println("Alert already exists.")
}
log.Fatal(err)

}
alertID := *alert.ID

// Pass the id and the field(s) to change on Update.
updatedName := "myalerttest.com - updated"
alertUpdate := &alerting.Alert{
ID: &alertID,
Name: &updatedName,
NotifierListIds: []string{webhookList.ID, emailList.ID},
}
_, err = client.Alerts.Update(alertUpdate)
if err != nil {
log.Fatal(err)
}

prettyPrint("Updated Alert:", alertUpdate)

// To pass the whole alert object on Replace, retrieve it by ID it first.
alertToReplace, _, err := client.Alerts.Get(alertID)
if err != nil {
log.Fatal(err)
}

// Replace values in retrieved struct with new values.
// e.g. Change name and clear list.
replacedName := "myalerttest.com - replaced"
alertToReplace.Name = &replacedName
alertToReplace.NotifierListIds = []string{}

// Pass the whole alert object
_, err = client.Alerts.Replace(alertToReplace)
if err != nil {
log.Fatal(err)
}

prettyPrint("Replaced Alert:", alertToReplace)

// Delete the alert.
_, err = client.Alerts.Delete(*alertToReplace.ID)
if err != nil {
log.Fatal(err)
}
}
12 changes: 6 additions & 6 deletions rest/_examples/zones.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func main() {
}

// Add an A record with a single static answer.
orchidRec := dns.NewRecord(domain, "orchid", "A")
orchidRec := dns.NewRecord(domain, "orchid", "A", nil, nil)
orchidRec.AddAnswer(dns.NewAv4Answer("2.2.2.2"))
_, err = client.Records.Create(orchidRec)
if err != nil {
Expand Down Expand Up @@ -98,7 +98,7 @@ func main() {
fmt.Println(string(bRec))

// Add an A record with two static answers.
honeyRec := dns.NewRecord(domain, "honey", "A")
honeyRec := dns.NewRecord(domain, "honey", "A", nil, nil)
honeyRec.Answers = []*dns.Answer{
dns.NewAv4Answer("1.2.3.4"),
dns.NewAv4Answer("5.6.7.8"),
Expand All @@ -114,7 +114,7 @@ func main() {
}

// Add a cname
potRec := dns.NewRecord(domain, "pot", "CNAME")
potRec := dns.NewRecord(domain, "pot", "CNAME", nil, nil)
potRec.AddAnswer(dns.NewCNAMEAnswer("honey.test.com"))
_, err = client.Records.Create(potRec)
if err != nil {
Expand All @@ -127,7 +127,7 @@ func main() {
}

// Add a MX with two answers, priority 5 and 10
mailRec := dns.NewRecord(domain, "mail", "MX")
mailRec := dns.NewRecord(domain, "mail", "MX", nil, nil)
mailRec.Answers = []*dns.Answer{
dns.NewMXAnswer(5, "mail1.test.com"),
dns.NewMXAnswer(10, "mail2.test.com"),
Expand All @@ -143,7 +143,7 @@ func main() {
}

// Add a AAAA, specify ttl of 300 seconds
aaaaRec := dns.NewRecord(domain, "honey6", "AAAA")
aaaaRec := dns.NewRecord(domain, "honey6", "AAAA", nil, nil)
aaaaRec.TTL = 300
aaaaRec.AddAnswer(dns.NewAv6Answer("2607:f8b0:4006:806::1010"))
_, err = client.Records.Create(aaaaRec)
Expand All @@ -159,7 +159,7 @@ func main() {
// Add an A record using full answer format to specify 2 answers with meta data.
// ensure edns-client-subnet is in use, and add two filters: geotarget_country,
// and select_first_n, which has a filter config option N set to 1.
bumbleRec := dns.NewRecord(domain, "bumble", "A")
bumbleRec := dns.NewRecord(domain, "bumble", "A", nil, nil)

usAns := dns.NewAv4Answer("1.1.1.1")
usAns.Meta.Up = false
Expand Down
Loading

0 comments on commit c522965

Please sign in to comment.