Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for the v1beta1 alerts api. #244

Open
wants to merge 1 commit into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this called in the example somewhere?

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("test@test.com")
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
Loading