Skip to content

Commit

Permalink
refactor biggly
Browse files Browse the repository at this point in the history
  • Loading branch information
cvilsmeier committed Nov 13, 2023
1 parent 58f4a45 commit 7b706fd
Show file tree
Hide file tree
Showing 20 changed files with 454 additions and 458 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func main() {
// create new api
api := monibot.NewApi(apiKey)
// send a watchdog heartbeat
err := api.PostWatchdogHeartbeat("5f6d343f517715a471d8768730c3f2f4")
err := api.PostWatchdogHeartbeat("5f6d343a471d87687f51771530c3f2f4")
if err != nil {
log.Fatal(err)
}
Expand Down
97 changes: 78 additions & 19 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,83 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"

"github.com/cvilsmeier/monibot-go/internal/logging"
"github.com/cvilsmeier/monibot-go/internal/sending"
"github.com/cvilsmeier/monibot-go/internal/version"
)

// A Logger prints debug messages.
type Logger = logging.Logger

// TimeAfterFunc is the function type of time.After.
type TimeAfterFunc = sending.TimeAfterFunc

// Version is monibot-go sdk version.
const Version = version.Version

// ApiOptions holds optional parameters for a Api.
type ApiOptions struct {

// Default is no logging.
Logger Logger

// Default is "https://monibot.io".
MonibotUrl string

// Default is 12 trials.
Trials int

// Default is 5s delay.
Delay time.Duration

// Default time.After
TimeAfter TimeAfterFunc
}

// An apiSender provides a Send method and can be overridden in unit tests.
type apiSender interface {
Send(ctx context.Context, method, path string, body []byte) ([]byte, error)
}

// Api provides access to the Monibot REST API.
type Api struct {
sender Sender
sender apiSender
}

// NewApi creates an Api that sends data to
// https://monibot.io and retries 12 times every 5s if
// an error occurs.
// NewApi creates an Api that sends data to https://monibot.io
// and retries 12 times every 5s if an error occurs,
// and logs nothing.
func NewApi(apiKey string) *Api {
return NewApiWithSender(NewRetrySender(NewSender(apiKey)))
return NewApiWithOptions(apiKey, ApiOptions{})
}

// NewApiWithSender creates an Api that uses a custom Sender for sending data.
func NewApiWithSender(sender Sender) *Api {
// NewApiWithOptions creates an Api with custom options.
func NewApiWithOptions(apiKey string, opt ApiOptions) *Api {
logger := opt.Logger
if logger == nil {
logger = logging.NewDiscardLogger()
}
monibotUrl := opt.MonibotUrl
if monibotUrl == "" {
monibotUrl = "http://monibot.io"
}
trials := opt.Trials
if trials == 0 {
trials = 12
}
delay := opt.Delay
if delay == 0 {
delay = 5 * time.Second
}
timeAfter := opt.TimeAfter
if timeAfter == nil {
timeAfter = time.After
}
transport := sending.NewTransport(logger, Version, monibotUrl, apiKey)
sender := sending.NewSender(transport, logger, trials, delay, timeAfter)
return &Api{sender}
}

Expand All @@ -35,7 +94,7 @@ func (a *Api) GetPing() error {
// reachable. It returns nil on success or a non-nil error if
// something goes wrong.
func (a *Api) GetPingWithContext(ctx context.Context) error {
_, err := a.sender.Send(ctx, http.MethodGet, "ping", nil)
_, err := a.sender.Send(ctx, "GET", "ping", nil)
return err
}

Expand All @@ -46,7 +105,7 @@ func (a *Api) GetWatchdogs() ([]Watchdog, error) {

// GetWatchdogsWithContext fetches the list of watchdogs.
func (a *Api) GetWatchdogsWithContext(ctx context.Context) ([]Watchdog, error) {
data, err := a.sender.Send(ctx, http.MethodGet, "watchdogs", nil)
data, err := a.sender.Send(ctx, "GET", "watchdogs", nil)
if err != nil {
return nil, err
}
Expand All @@ -62,7 +121,7 @@ func (a *Api) GetWatchdog(watchdogId string) (Watchdog, error) {

// GetWatchdogWithContext fetches a watchdog by id.
func (a *Api) GetWatchdogWithContext(ctx context.Context, watchdogId string) (Watchdog, error) {
data, err := a.sender.Send(ctx, http.MethodGet, "watchdog/"+watchdogId, nil)
data, err := a.sender.Send(ctx, "GET", "watchdog/"+watchdogId, nil)
if err != nil {
return Watchdog{}, err
}
Expand All @@ -78,7 +137,7 @@ func (a *Api) PostWatchdogHeartbeat(watchdogId string) error {

// PostWatchdogHeartbeatWithContext sends a watchdog heartbeat.
func (a *Api) PostWatchdogHeartbeatWithContext(ctx context.Context, watchdogId string) error {
_, err := a.sender.Send(ctx, http.MethodPost, "watchdog/"+watchdogId+"/heartbeat", nil)
_, err := a.sender.Send(ctx, "POST", "watchdog/"+watchdogId+"/heartbeat", nil)
return err
}

Expand All @@ -89,7 +148,7 @@ func (a *Api) GetMachines() ([]Machine, error) {

// GetMachinesWithContext fetches the list of machines.
func (a *Api) GetMachinesWithContext(ctx context.Context) ([]Machine, error) {
data, err := a.sender.Send(ctx, http.MethodGet, "machines", nil)
data, err := a.sender.Send(ctx, "GET", "machines", nil)
if err != nil {
return nil, err
}
Expand All @@ -105,7 +164,7 @@ func (a *Api) GetMachine(machineId string) (Machine, error) {

// GetMachineWithContext fetches a machine by id.
func (a *Api) GetMachineWithContext(ctx context.Context, machineId string) (Machine, error) {
data, err := a.sender.Send(ctx, http.MethodGet, "machine/"+machineId, nil)
data, err := a.sender.Send(ctx, "GET", "machine/"+machineId, nil)
if err != nil {
return Machine{}, err
}
Expand Down Expand Up @@ -135,7 +194,7 @@ func (a *Api) PostMachineSampleWithContext(ctx context.Context, machineId string
fmt.Sprintf("netSend=%d", sample.NetSend),
}
body := strings.Join(toks, "&")
_, err := a.sender.Send(ctx, http.MethodPost, "machine/"+machineId+"/sample", []byte(body))
_, err := a.sender.Send(ctx, "POST", "machine/"+machineId+"/sample", []byte(body))
return err
}

Expand All @@ -146,7 +205,7 @@ func (a *Api) GetMetrics() ([]Metric, error) {

// GetMetricsWithContext fetches the list of metrics.
func (a *Api) GetMetricsWithContext(ctx context.Context) ([]Metric, error) {
data, err := a.sender.Send(ctx, http.MethodGet, "metrics", nil)
data, err := a.sender.Send(ctx, "GET", "metrics", nil)
if err != nil {
return nil, err
}
Expand All @@ -162,7 +221,7 @@ func (a *Api) GetMetric(metricId string) (Metric, error) {

// GetMetricWithContext fetches a metric by id.
func (a *Api) GetMetricWithContext(ctx context.Context, metricId string) (Metric, error) {
data, err := a.sender.Send(ctx, http.MethodGet, "metric/"+metricId, nil)
data, err := a.sender.Send(ctx, "GET", "metric/"+metricId, nil)
if err != nil {
return Metric{}, err
}
Expand All @@ -182,7 +241,7 @@ func (a *Api) PostMetricInc(metricId string, value int64) error {
// The value is a non-negative int64 number.
func (a *Api) PostMetricIncWithContext(ctx context.Context, metricId string, value int64) error {
body := fmt.Sprintf("value=%d", value)
_, err := a.sender.Send(ctx, http.MethodPost, "metric/"+metricId+"/inc", []byte(body))
_, err := a.sender.Send(ctx, "POST", "metric/"+metricId+"/inc", []byte(body))
return err
}

Expand All @@ -197,6 +256,6 @@ func (a *Api) PostMetricSet(metricId string, value int64) error {
// The value is a non-negative int64 number.
func (a *Api) PostMetricSetWithContext(ctx context.Context, metricId string, value int64) error {
body := fmt.Sprintf("value=%d", value)
_, err := a.sender.Send(ctx, http.MethodPost, "metric/"+metricId+"/set", []byte(body))
_, err := a.sender.Send(ctx, "POST", "metric/"+metricId+"/set", []byte(body))
return err
}
85 changes: 54 additions & 31 deletions api_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package monibot

import (
"context"
"fmt"
"strings"
"testing"
"time"

Expand All @@ -24,20 +26,20 @@ func TestApi(t *testing.T) {
// this test uses a fake HTTP sender
sender := &fakeSender{}
// create Api
api := NewApiWithSender(sender)
api := &Api{sender}
// GET ping
{
sender.requests = nil
sender.calls = nil
sender.responses = append(sender.responses, fakeResponse{})
err := api.GetPing()
ass.Nil(err)
ass.Eq(1, len(sender.requests))
ass.Eq("GET ping", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("GET ping", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// GET watchdogs
{
sender.requests = nil
sender.calls = nil
resp := `[
{"id":"0001", "name":"Cronjob 1", "intervalMillis": 72000000},
{"id":"0002", "name":"Cronjob 2", "intervalMillis": 36000000}
Expand All @@ -48,35 +50,35 @@ func TestApi(t *testing.T) {
ass.Eq(2, len(watchdogs))
ass.Eq("Id=0001, Name=Cronjob 1, IntervalMillis=72000000", str(watchdogs[0]))
ass.Eq("Id=0002, Name=Cronjob 2, IntervalMillis=36000000", str(watchdogs[1]))
ass.Eq(1, len(sender.requests))
ass.Eq("GET watchdogs", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("GET watchdogs", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// GET watchdog/00000001
{
sender.requests = nil
sender.calls = nil
resp := `{"id":"0001", "name":"Cronjob 1", "intervalMillis": 72000000}`
sender.responses = append(sender.responses, fakeResponse{data: []byte(resp)})
watchdog, err := api.GetWatchdog("00000001")
ass.Nil(err)
ass.Eq("Id=0001, Name=Cronjob 1, IntervalMillis=72000000", str(watchdog))
ass.Eq(1, len(sender.requests))
ass.Eq("GET watchdog/00000001", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("GET watchdog/00000001", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// POST watchdog/00000001/heartbeat
{
sender.requests = nil
sender.calls = nil
sender.responses = append(sender.responses, fakeResponse{})
err := api.PostWatchdogHeartbeat("00000001")
ass.Nil(err)
ass.Eq(1, len(sender.requests))
ass.Eq("POST watchdog/00000001/heartbeat", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("POST watchdog/00000001/heartbeat", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// GET machines
{
sender.requests = nil
sender.calls = nil
resp := `[
{"id":"01", "name":"Server 1"},
{"id":"02", "name":"Server 2"}
Expand All @@ -87,25 +89,25 @@ func TestApi(t *testing.T) {
ass.Eq(2, len(machines))
ass.Eq("Id=01, Name=Server 1", str(machines[0]))
ass.Eq("Id=02, Name=Server 2", str(machines[1]))
ass.Eq(1, len(sender.requests))
ass.Eq("GET machines", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("GET machines", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// GET machine/01
{
sender.requests = nil
sender.calls = nil
resp := `{"id":"01", "name":"Server 1"}`
sender.responses = append(sender.responses, fakeResponse{data: []byte(resp)})
machine, err := api.GetMachine("01")
ass.Nil(err)
ass.Eq("Id=01, Name=Server 1", str(machine))
ass.Eq(1, len(sender.requests))
ass.Eq("GET machine/01", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("GET machine/01", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// POST machine/00000001/sample
{
sender.requests = nil
sender.calls = nil
sender.responses = append(sender.responses, fakeResponse{})
tstamp := time.Date(2023, 10, 27, 10, 0, 0, 0, time.UTC)
sample := MachineSample{
Expand All @@ -123,13 +125,13 @@ func TestApi(t *testing.T) {
}
err := api.PostMachineSample("00000001", sample)
ass.Nil(err)
ass.Eq(1, len(sender.requests))
ass.Eq("POST machine/00000001/sample tstamp=1698400800000&load1=1.010&load5=0.780&load15=0.120&cpu=12&mem=34&disk=12&diskReads=678&diskWrites=567&netRecv=13&netSend=14", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("POST machine/00000001/sample tstamp=1698400800000&load1=1.010&load5=0.780&load15=0.120&cpu=12&mem=34&disk=12&diskReads=678&diskWrites=567&netRecv=13&netSend=14", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// GET metrics
{
sender.requests = nil
sender.calls = nil
resp := `[
{"id":"01", "name":"Metric 1", "type": 0},
{"id":"02", "name":"Metric 2", "type": 1}
Expand All @@ -140,30 +142,51 @@ func TestApi(t *testing.T) {
ass.Eq(2, len(metrics))
ass.Eq("Id=01, Name=Metric 1, Type=0", str(metrics[0]))
ass.Eq("Id=02, Name=Metric 2, Type=1", str(metrics[1]))
ass.Eq(1, len(sender.requests))
ass.Eq("GET metrics", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("GET metrics", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// GET metric/01
{
sender.requests = nil
sender.calls = nil
resp := `{"id":"01", "name":"Metric 1", "type": 0}`
sender.responses = append(sender.responses, fakeResponse{data: []byte(resp)})
metric, err := api.GetMetric("01")
ass.Nil(err)
ass.Eq("Id=01, Name=Metric 1, Type=0", str(metric))
ass.Eq(1, len(sender.requests))
ass.Eq("GET metric/01", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("GET metric/01", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
// POST metric/00000001/inc
{
sender.requests = nil
sender.calls = nil
sender.responses = append(sender.responses, fakeResponse{nil, fmt.Errorf("connect timeout")})
err := api.PostMetricInc("00000001", 42)
ass.Eq("connect timeout", err.Error())
ass.Eq(1, len(sender.requests))
ass.Eq("POST metric/00000001/inc value=42", sender.requests[0])
ass.Eq(1, len(sender.calls))
ass.Eq("POST metric/00000001/inc value=42", sender.calls[0])
ass.Eq(0, len(sender.responses))
}
}

type fakeSender struct {
calls []string
responses []fakeResponse
}

func (f *fakeSender) Send(ctx context.Context, method, path string, body []byte) ([]byte, error) {
call := strings.TrimSpace(method + " " + path + " " + string(body))
f.calls = append(f.calls, call)
if len(f.responses) == 0 {
return nil, fmt.Errorf("no response for %s %s", method, path)
}
re := f.responses[0]
f.responses = f.responses[1:]
return re.data, re.err
}

type fakeResponse struct {
data []byte
err error
}
2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ see https://monibot.io for details.
// create new api
api := monibot.NewApi(apiKey)
// send a watchdog heartbeat
err := api.PostWatchdogHeartbeat("5f6d343f517715a471d8768730c3f2f4")
err := api.PostWatchdogHeartbeat("5f6d343a471d87687f51771530c3f2f4")
if err != nil {
log.Fatal(err)
}
Expand Down
Loading

0 comments on commit 7b706fd

Please sign in to comment.