diff --git a/.gitignore b/.gitignore index daf913b..6e79e69 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ _testmain.go *.exe *.test *.prof + +.idea/ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..660ae72 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,35 @@ +include: + - project: "infrastructure/templates" + ref: master + file: "/.golang.yml" + - project: "infrastructure/templates" + ref: master + file: "/.runner-tags.yml" + +image: ${INFRA_GOLANG_IMAGE} + +stages: + - test + +golangci-lint: + stage: test + before_script: + - export GO111MODULE=on + - go mod download + script: + - go version + - golangci-lint run ./... + extends: .INFRA_RUNNER_TAGS_K8S + +testing: + stage: test + coverage: '/^total:\t+\(statements\)\t+(\d+\.\d+)%/' + before_script: + - export GO111MODULE=on + - go mod download + script: + - go version + - go test ./... -coverprofile cover.out + - go tool cover -func cover.out + extends: .INFRA_RUNNER_TAGS_K8S + diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..f2a197a --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,16 @@ +run: + deadline: 5m + tests: false + +linters-settings: + errcheck: + # ignore stupid FlagCheck that is already checked, rest is default + ignore: fmt:.* + +linters: + enable-all: true + fast: false + +issues: + max-issues-per-linter: 50 + max-same-issues: 50 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2e4acfd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: go + +env: + - GO111MODULE=on + +git: + depth: 1 + +go: + - 1.12.x + - 1.13.x + +before_script: + - go install github.com/golangci/golangci-lint/cmd/golangci-lint + +script: + - golangci-lint run ./... + - go test ./... -coverprofile cover.out + - go tool cover -func cover.out \ No newline at end of file diff --git a/LICENSE b/LICENSE index 91de61f..cc3cfd1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016, Paessler AG +Copyright (c) 2019, Paessler AG All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index 7f039ef..35a1ae4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ -# go-prtg-api +# go-prtg-sensor-api API for writing PRTG custom sensors in Go. -## Example -This simple exampe sensor sends an HTTP request to http://paessler.com +[![Build Status](https://travis-ci.org/PRTG/go-prtg-sensor-api.svg "Travis CI status")](https://travis-ci.org/PRTG/go-prtg-sensor-api) +[![GoDoc](https://godoc.org/github.com/PRTG/go-prtg-sensor-api?status.svg)](https://godoc.org/github.com/PRTG/go-prtg-sensor-api) +[![Go Report Card](https://goreportcard.com/badge/github.com/PRTG/go-prtg-sensor-api)](https://goreportcard.com/report/PRTG/go-prtg-sensor-api/) + +## Example EXE Sensor +This simple example sensor sends an HTTP request to http://paessler.com and returns two channels: - `Response time` - time it takes to perform the request and read the body - `Bytes read` - number of bytes the body contained @@ -13,54 +17,57 @@ package main import ( "fmt" "io/ioutil" + "log" "net/http" "time" - "github.com/PaesslerAG/go-prtg-sensor-api" + "github.com/PRTG/go-prtg-sensor-api" ) func main() { - // Create empty response and log start time - r := &prtg.SensorResponse{} + // Initiate Sensor instance + sensor := prtg.New() + + // Log start time start := time.Now() // Perform HTTP request resp, err := http.Get("http://paessler.com") if err != nil { - r.PRTG.Error = "1" - r.PRTG.Text = err.Error() - fmt.Println(r.String()) + sensor.SetError(true) + sensor.SetSensorText(err.Error()) + json, err := sensor.MarshalToString() + if err != nil { + log.Fatal(err) + } + fmt.Println(json) return } // Read the response buffer, err := ioutil.ReadAll(resp.Body) if err != nil { - r.PRTG.Error = "1" - r.PRTG.Text = err.Error() - fmt.Println(r.String()) + sensor.SetError(true) + sensor.SetSensorText(err.Error()) + json, err := sensor.MarshalToString() + if err != nil { + log.Fatal(err) + } + fmt.Println(json) return } // Evaluate results responseTime := time.Since(start) responseBytes := len(buffer) + // Response time channel - r.AddChannel(prtg.SensorChannel{ - Channel: "Response time", - Value: fmt.Sprintf("%f", responseTime.Seconds()*1000), - Float: 1, - ShowChart: 1, - ShowTable: 1, - Unit: prtg.UnitTimeResponse, - }) + sensor.AddChannel("Response time").SetValue(responseTime.Seconds() * 1000).SetUnit(prtg.TimeResponse) // Bytes read channel - r.AddChannel(prtg.SensorChannel{ - Channel: "Bytes read", - Value: fmt.Sprintf("%d", responseBytes), - ShowChart: 1, - ShowTable: 1, - Unit: prtg.UnitBytesFile, - }) - - fmt.Println(r.String()) + sensor.AddChannel("Bytes read").SetValue(responseBytes).SetUnit(prtg.BytesFile) + + json, err := sensor.MarshalToString() + if err != nil { + log.Fatal(err) + } + fmt.Println(json) } ``` @@ -74,6 +81,87 @@ To test this example in your PRTG installation follow these steps: 6. Under `EXE/Script` choose the `.exe` you copied in step 2 7. Done + +## Example HTTP Data Advanced Sensor + +This example sensor uses a http server to serve the sensor data, that can be pulled +by a http data advanced sensor. +- `Some value` - shows sample percentage value +- `Something` - shows if the Something service is up and running + +The Sensor returns an error if "Something" is not ok. + +```golang +package main + +import ( + "fmt" + "log" + "math/rand" + "net/http" + + "github.com/PRTG/go-prtg-sensor-api" +) + +func main() { + // Create a webserver listening on "/" + http.HandleFunc("/", reportStatus) + http.ListenAndServe(":8080", nil) +} + +func reportStatus(w http.ResponseWriter, r *http.Request) { + // Initiate PRTG instance + sensor := prtg.New() + + // Set sensor text + sensor.SetSensorText("This is a test sensor") + + // Add a channel with a random float value in Percent + sensor.AddChannel("Some value").SetValue(rand.Float64() * 100). + SetUnit(prtg.Percent).SetMaxWarnLimit(80).SetMaxErrLimit(90) + + // Take a look if Something is working + isUp, err := isSomethingUp() + // Create a Sensor that shows the uptime of Something + sensor.AddChannel("Something").SetValue(isUp). + SetValueLookup("prtg.standardlookups.connectionstate.stateonlineok") + // Create error message on sensor if the Something is down + if err != nil { + sensor.SetError(true) + sensor.SetSensorText("Test sensor error: " + err.Error()) + } + + // Create json output + json, err := sensor.MarshalToString() + if err != nil { + log.Fatal(err) + } + + // Deliver to website + _, err = fmt.Fprint(w, json) + if err != nil { + log.Fatal(err) + } +} + +func isSomethingUp() (bool, error) { + // Generate a "Thing" to watch, that is working in 90% of the time + if rand.Intn(10) > 1 { + return true, nil + } + return false, fmt.Errorf("the Something is struggling") +} +``` + +To test this example in your PRTG installation follow these steps: + +1. Build the example +2. Run the binary +3. Go to PRTG web interface +4. Add Sensor to a device of your choice +5. Choose `HTTP Data Advanced` as sensor type +6. Under `URL` choose the ip of your device with the port `8080` +7. Done + ## Documentation -- [godoc.org/github.com/PaesslerAG/go-prtg-sensor-api](https://godoc.org/github.com/PaesslerAG/go-prtg-sensor-api) - [PRTG Manual](https://www.paessler.com/manuals/prtg/exe_script_advanced_sensor) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2f043db --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/PRTG/go-prtg-sensor-api + +go 1.13 diff --git a/prtg.go b/prtg.go new file mode 100644 index 0000000..bb8f328 --- /dev/null +++ b/prtg.go @@ -0,0 +1,68 @@ +// Copyright (c) 2019, Paessler AG. +// All Rights Reserved + +// Package PRTG implements the API for PRTG custom sensors. +// It provides all structs and constants needed to implement your own advanced exe sensor in Go. +package prtg + +import ( + "encoding/json" +) + +// SensorStatus has the whole JSON object +type SensorResponse struct { + SensorResults SensorResults `json:"prtg"` +} + +// SensorResults has all the channels +type SensorResults struct { + SensorChannels []SensorChannel `json:"result"` + Text string `json:"text,omitempty"` + Error string `json:"error,omitempty"` +} + +type Sensor interface { + AddChannel(string) *SensorChannel + MarshalToString() (string, error) + SetSensorText(string) *SensorResults + SetError(bool) *SensorResults +} + +// Creates the Sensor instance and returns the interface. +func New() Sensor { + return &SensorResults{} +} + +// Name of the channel as displayed in user interfaces. +func (sr *SensorResults) AddChannel(channelName string) *SensorChannel { + newChan := SensorChannel{Channel: channelName} + sr.SensorChannels = append(sr.SensorChannels, newChan) + + return &sr.SensorChannels[len(sr.SensorChannels)-1] +} + +// Create a JSON string from the PRTG object +func (sr *SensorResults) MarshalToString() (string, error) { + bytes, err := json.Marshal(&SensorResponse{*sr}) + return string(bytes), err +} + +// Text the sensor returns in the Message field with every scanning interval. +// There can be one message per sensor, regardless of the number of channels. +// Default is OK. +func (sr *SensorResults) SetSensorText(text string) *SensorResults { + sr.Text = text + return sr +} + +// If enabled, the sensor will return an error status. +// This element can be combined with the SensorText element in order to show an error message. +// Default is 0. +func (sr *SensorResults) SetError(err bool) *SensorResults { + sr.Error = "0" + if err { + sr.Error = "1" + } + + return sr +} diff --git a/prtg_test.go b/prtg_test.go new file mode 100644 index 0000000..d7d2ee4 --- /dev/null +++ b/prtg_test.go @@ -0,0 +1,131 @@ +// Copyright (c) 2019, Paessler AG. +// All Rights Reserved +package prtg + +import ( + "math/rand" + "os" + "reflect" + "testing" + "time" +) + +func TestMain(m *testing.M) { + rand.Seed(time.Now().Unix()) + os.Exit(m.Run()) +} + +func TestAddChannel(t *testing.T) { + // Initiate PRTG instance + sensor := New() + // Create channel with random data + channel1 := sensor.AddChannel("channel1").SetValue(1000.25).SetCustomUnit("someUnit") + channel2 := sensor.AddChannel("channel2").SetValue(50).SetUnit(Percent) + // Create empty SensorResults + sc1 := &SensorChannel{Channel: "channel1", Value: "1000.25", Unit: "Custom", CustomUnit: "someUnit", Float: "1"} + sc2 := &SensorChannel{Channel: "channel2", Value: "50", Unit: "Percent", Float: "0"} + + if !reflect.DeepEqual(channel1, sc1) { + t.Errorf("sensor.AddChannel() => failed to add channel") + } + + if !reflect.DeepEqual(channel2, sc2) { + t.Errorf("sensor.AddChannel() => failed to add channel") + } + + // Create output + json, _ := sensor.MarshalToString() + wantedJson := "{\"prtg\":{\"result\":[{\"channel\":\"channel1\",\"value\":\"1000.25\",\"unit\":\"Custom\",\"customunit\":\"someUnit\",\"float\":\"1\"},{\"channel\":\"channel2\",\"value\":\"50\",\"unit\":\"Percent\",\"float\":\"0\"}]}}" + // Check if it safely arrived + if json != wantedJson { + t.Error("AddChannel() => failed to add channel") + } +} + +func TestMarshalToString(t *testing.T) { + // Initiate PRTG instance + sensor := New() + // Create dummy sensor + sensor.AddChannel("").SetValue("") + // Create output + _, err := sensor.MarshalToString() + if err != nil { + t.Error("MarshalToString() => failed to create json string") + } +} + +func TestSetError(t *testing.T) { + var tests = []struct { + name string + results SensorResults + arg bool + want *SensorResults + }{ + { + name: "set error true with not set before", + results: SensorResults{}, + arg: true, + want: &SensorResults{Error: "1"}, + }, + { + name: "set error true with set false before", + results: SensorResults{Error: "0"}, + arg: true, + want: &SensorResults{Error: "1"}, + }, + { + name: "set error false with not set before", + results: SensorResults{}, + arg: false, + want: &SensorResults{Error: "0"}, + }, + { + name: "set error false with set true before", + results: SensorResults{Error: "1"}, + arg: false, + want: &SensorResults{Error: "0"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorResults{ + Error: tt.results.Error, + } + if got := r.SetError(tt.arg); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetError() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSetSensorText(t *testing.T) { + var tests = []struct { + name string + results SensorResults + arg string + want *SensorResults + }{ + { + name: "set text with no text set before", + results: SensorResults{}, + arg: "Some Text!", + want: &SensorResults{Text: "Some Text!"}, + }, + { + name: "overwrite text", + results: SensorResults{Text: "previous text"}, + arg: "Some other Text!", + want: &SensorResults{Text: "Some other Text!"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorResults{ + Error: tt.results.Error, + } + if got := r.SetSensorText(tt.arg); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetSensorText() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/result.go b/result.go new file mode 100644 index 0000000..fe2fe6c --- /dev/null +++ b/result.go @@ -0,0 +1,192 @@ +// Copyright (c) 2019, Paessler AG. +// All Rights Reserved +package prtg + +import ( + "fmt" + "strconv" +) + +// SensorChannel has the channel name and value and options +type SensorChannel struct { + Channel string `json:"channel"` + Value string `json:"value"` + + // Options + ValueMode string `json:"mode,omitempty"` + Unit string `json:"unit,omitempty"` + CustomUnit string `json:"customunit,omitempty"` + ValueLookup string `json:"valuelookup,omitempty"` + VolumeSize string `json:"volumesize,omitempty"` + SpeedSize string `json:"speedsize,omitempty"` + SpeedTime string `json:"speedtime,omitempty"` + Float string `json:"float,omitempty"` + DecimalMode string `json:"decimalmode,omitempty"` + ShowChart string `json:"showchart,omitempty"` + ShowTable string `json:"showtable,omitempty"` + LimitMinWarning string `json:"limitminwarning,omitempty"` + LimitMaxWarning string `json:"limitmaxwarning,omitempty"` + LimitWarningMsg string `json:"limitwarningmsg,omitempty"` + LimitMinError string `json:"limitminerror,omitempty"` + LimitMaxError string `json:"limitmaxerror,omitempty"` + LimitErrorMsg string `json:"limiterrormsg,omitempty"` + LimitMode string `json:"limitmode,omitempty"` + Warning string `json:"warning,omitempty"` +} + +// The unit of the value. Default is Custom. This is useful for PRTG to be able to convert volumes and times. +func (r *SensorChannel) SetUnit(unit UnitType) *SensorChannel { + r.Unit = string(unit) + return r +} + +// Sets a custom unit text, that is displayed behind the value. +func (r *SensorChannel) SetCustomUnit(unit string) *SensorChannel { + r.Unit = "Custom" + r.CustomUnit = unit + + return r +} + +// Define if you want to use a lookup file (for example, to view integer values as status texts). +// Enter the ID of the lookup file you want to use, or omit this element to not use lookups. +func (r *SensorChannel) SetValueLookup(lookup string) *SensorChannel { + r.ValueLookup = lookup + return r +} + +// Volume size used for the display value. +// For example, if you have a value of 50000 and use Kilo as size, the display is 50 kilo. +// Default is One (value used as returned). +func (r *SensorChannel) SetVolumeSize(size SizeType) *SensorChannel { + r.VolumeSize = string(size) + return r +} + +// Speed size used for the display value. +// For example, if you have a value of 50000 and use KiloBit as size, the display is 50 kiloBits. +// Default is One (value used as returned). +func (r *SensorChannel) SetSpeedSize(size SpeedType) *SensorChannel { + r.SpeedSize = string(size) + return r +} + +// Time size used for the display value. +// For example, if you have a value of 780 and use Minute as size, the display is 13 Minutes. +// Default is Second (value used as returned). +func (r *SensorChannel) SetSpeedTime(time TimeType) *SensorChannel { + r.SpeedTime = string(time) + return r +} + +// Sets a value. +// All types allowed except complex and uintptr. +func (r *SensorChannel) SetValue(val interface{}) *SensorChannel { + switch t := val.(type) { + case float32, float64: + r.Float = "1" + r.Value = fmt.Sprintf("%v", t) + case int8, uint8, int16, uint16, int32, uint32, uint64, int64, uint, int, string: + r.Float = "0" + r.Value = fmt.Sprintf("%v", t) + case bool: + r.Float = "0" + r.Value = "0" + + if t { + r.Value = "1" + } + default: + fmt.Printf("type of %v is not supported\n", t) + } + + return r +} + +// Select if the value is an absolute value or counter. Default is "Absolute" set. +func (r *SensorChannel) ValueIsDifference() *SensorChannel { + r.ValueMode = "Difference" + return r +} + +// If value is a float a custom number of decimal places can be set. +func (r *SensorChannel) SetDecimalMode(mode DecimalModeType) *SensorChannel { + r.DecimalMode = string(mode) + return r +} + +// Define an upper warning limit for the channel. +// If enabled, the sensor will be set to a Warning status if this value is overrun. +func (r *SensorChannel) SetMaxWarnLimit(limit int) *SensorChannel { + r.LimitMode = "1" + r.LimitMaxWarning = strconv.Itoa(limit) + + return r +} + +// Define an upper error limit for the channel. +// If enabled, the sensor will be set to a Down status if this value is overrun. +func (r *SensorChannel) SetMinWarnLimit(limit int) *SensorChannel { + r.LimitMode = "1" + r.LimitMinWarning = strconv.Itoa(limit) + + return r +} + +// Define an additional message. +// It will be added to the sensor's message when entering a Warning status that is triggered by a limit. +func (r *SensorChannel) SetWarnLimitMsg(msg string) *SensorChannel { + r.LimitWarningMsg = msg + return r +} + +// Define an upper error limit for the channel. +// If enabled, the sensor will be set to a Down status if this value is overrun. +func (r *SensorChannel) SetMaxErrLimit(limit int) *SensorChannel { + r.LimitMode = "1" + r.LimitMaxError = strconv.Itoa(limit) + + return r +} + +// Define a lower error limit for the channel. +// If enabled, the sensor will be set to a Down status if this value is undercut. +func (r *SensorChannel) SetMinErrLimit(limit int) *SensorChannel { + r.LimitMode = "1" + r.LimitMinError = strconv.Itoa(limit) + + return r +} + +// Define an additional message. +// It will be added to the sensor's message when entering a Down status that is triggered by a limit. +func (r *SensorChannel) SetErrLimitMsg(msg string) *SensorChannel { + r.LimitErrorMsg = msg + return r +} + +// If enabled for at least one channel, the entire sensor is set to "Warning" status. Default is "not set". +func (r *SensorChannel) SetToWarning() *SensorChannel { + r.Warning = "1" + return r +} + +// Init value for the Show in graphs option. Default is yes. +func (r *SensorChannel) SetShowChart(show bool) *SensorChannel { + r.ShowChart = "0" + if show { + r.ShowChart = "1" + } + + return r +} + +// Init value for the Show in tables option. Default is yes. +func (r *SensorChannel) SetShowTable(show bool) *SensorChannel { + r.ShowTable = "0" + if show { + r.ShowTable = "1" + } + + return r +} diff --git a/result_test.go b/result_test.go new file mode 100644 index 0000000..3b1a20b --- /dev/null +++ b/result_test.go @@ -0,0 +1,788 @@ +// Copyright (c) 2019, Paessler AG. +// All Rights Reserved +package prtg + +import ( + "reflect" + "testing" +) + +func TestSensorChannel_SetErrLimitMsg(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetErrLimitMsg("TestMsg") + if channel.LimitErrorMsg != "TestMsg" { + t.Error("Message was not set") + } + channel = &SensorChannel{} + if channel.LimitErrorMsg != "" { + t.Error("Message should be empty if not set") + } +} +func TestSensorChannel_SetWarnLimitMsg(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetWarnLimitMsg("TestMsg") + if channel.LimitWarningMsg != "TestMsg" { + t.Error("Message was not set") + } + channel = &SensorChannel{} + if channel.LimitWarningMsg != "" { + t.Error("Message should be empty if not set") + } +} + +func TestSensorChannel_SetToWarning(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetToWarning() + if channel.Warning != "1" { + t.Error("Warning was not set to '1'") + } + channel = &SensorChannel{} + if channel.Warning != "" { + t.Error("Warning should be empty if not set") + } +} + +func TestSensorChannel_SetShowChart(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetShowChart(true) + if channel.ShowChart != "1" { + t.Error("Warning was not set to '1'") + } + channel = &SensorChannel{} + channel = channel.SetShowChart(false) + if channel.ShowChart != "0" { + t.Error("Warning was not set to '0'") + } + channel = &SensorChannel{} + if channel.ShowChart != "" { + t.Error("Warning should be empty if not set") + } +} + +func TestSensorChannel_SetShowTable(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetShowTable(true) + if channel.ShowTable != "1" { + t.Error("ShowTable was not set to '1'") + } + channel = &SensorChannel{} + channel = channel.SetShowTable(false) + if channel.ShowTable != "0" { + t.Error("ShowTable was not set to '0'") + } + channel = &SensorChannel{} + if channel.ShowTable != "" { + t.Error("ShowTable should be empty if not set") + } +} + +func TestSensorChannel_ValueIsDifference(t *testing.T) { + channel := &SensorChannel{} + channel = channel.ValueIsDifference() + if channel.ValueMode != "Difference" { + t.Error("ValueMode was not set to 'Difference'") + } + channel = &SensorChannel{} + if channel.ValueMode != "" { + t.Error("ValueMode should be empty if not set") + } +} + +func TestSensorChannel_SetMaxWarnLimit(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetMaxWarnLimit(100) + if channel.LimitMode != "1" && channel.LimitMaxWarning != "100" { + t.Error("Limit not set correctly to 100") + } + channel = &SensorChannel{} + channel = channel.SetMaxWarnLimit(-2) + if channel.LimitMode != "1" && channel.LimitMaxWarning != "-2" { + t.Error("Limit not set correctly to -2") + } + channel = &SensorChannel{} + channel = channel.SetMaxWarnLimit(0) + if channel.LimitMode != "1" && channel.LimitMaxWarning != "0" { + t.Error("Limit not set correctly to 0") + } + channel = New().AddChannel("test") + if channel.LimitMode != "" && channel.LimitMaxWarning != "" { + t.Error("Limits should be empty if not set") + } +} + +func TestSensorChannel_SetMinWarnLimit(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetMinWarnLimit(100) + if channel.LimitMode != "1" && channel.LimitMinWarning != "100" { + t.Error("Limit not set correctly to 100") + } + channel = &SensorChannel{} + channel = channel.SetMinWarnLimit(-2) + if channel.LimitMode != "1" && channel.LimitMinWarning != "-2" { + t.Error("Limit not set correctly to -2") + } + channel = &SensorChannel{} + channel = channel.SetMinWarnLimit(0) + if channel.LimitMode != "1" && channel.LimitMinWarning != "0" { + t.Error("Limit not set correctly to 0") + } + channel = New().AddChannel("test") + if channel.LimitMode != "" && channel.LimitMinWarning != "" { + t.Error("Limits should be empty if not set") + } +} + +func TestSensorChannel_SetMaxErrLimit(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetMaxErrLimit(100) + if channel.LimitMode != "1" && channel.LimitMaxError != "100" { + t.Error("Limit not set correctly to 100") + } + channel = &SensorChannel{} + channel = channel.SetMaxErrLimit(-2) + if channel.LimitMode != "1" && channel.LimitMaxError != "-2" { + t.Error("Limit not set correctly to -2") + } + channel = &SensorChannel{} + channel = channel.SetMaxErrLimit(0) + if channel.LimitMode != "1" && channel.LimitMaxError != "0" { + t.Error("Limit not set correctly to 0") + } + channel = New().AddChannel("test") + if channel.LimitMode != "" && channel.LimitMaxError != "" { + t.Error("Limits should be empty if not set") + } +} + +func TestSensorChannel_SetMinErrLimit(t *testing.T) { + channel := &SensorChannel{} + channel = channel.SetMinErrLimit(100) + if channel.LimitMode != "1" && channel.LimitMinError != "100" { + t.Error("Limit not set correctly to 100") + } + channel = &SensorChannel{} + channel = channel.SetMinErrLimit(-2) + if channel.LimitMode != "1" && channel.LimitMinError != "-2" { + t.Error("Limit not set correctly to -2") + } + channel = &SensorChannel{} + channel = channel.SetMinErrLimit(0) + if channel.LimitMode != "1" && channel.LimitMinError != "0" { + t.Error("Limit not set correctly to 0") + } + channel = New().AddChannel("test") + if channel.LimitMode != "" && channel.LimitMinError != "" { + t.Error("Limits should be empty if not set") + } +} + +func TestSensorChannel_SetValue(t *testing.T) { + type args struct { + val interface{} + } + var tests = []struct { + name string + channel SensorChannel + args args + want *SensorChannel + }{ + { + name: "set float (1 decimal) value to Float value", + channel: SensorChannel{Value: "empty_test", Float: "0"}, + args: args{val: 1.1}, + want: &SensorChannel{Value: "1.1", Float: "1"}, + }, + { + name: "set float32 (0 decimal)", + channel: SensorChannel{}, + args: args{val: float32(2)}, + want: &SensorChannel{Value: "2", Float: "1"}, + }, + { + name: "set float64 (0 decimal) value to Float value", + channel: SensorChannel{}, + args: args{val: float64(2)}, + want: &SensorChannel{Value: "2", Float: "1"}, + }, + { + name: "set float (4 decimal) value to Float value", + channel: SensorChannel{Value: "empty_test", Float: "0"}, + args: args{val: 0.1999}, + want: &SensorChannel{Value: "0.1999", Float: "1"}, + }, + { + name: "set uint64 value", + channel: SensorChannel{}, + args: args{val: uint64(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set uint32 value", + channel: SensorChannel{}, + args: args{val: uint32(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set uint16 value", + channel: SensorChannel{}, + args: args{val: uint16(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set uint8 value", + channel: SensorChannel{}, + args: args{val: uint8(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set uint value", + channel: SensorChannel{}, + args: args{val: uint(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set int64 value", + channel: SensorChannel{}, + args: args{val: int64(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set int32 value", + channel: SensorChannel{}, + args: args{val: int32(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set int16 value", + channel: SensorChannel{}, + args: args{val: int16(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set int8 value", + channel: SensorChannel{}, + args: args{val: int8(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set int value", + channel: SensorChannel{}, + args: args{val: int(2)}, + want: &SensorChannel{Value: "2", Float: "0"}, + }, + { + name: "set bool value, true", + channel: SensorChannel{}, + args: args{val: true}, + want: &SensorChannel{Value: "1", Float: "0"}, + }, + { + name: "set bool value, false", + channel: SensorChannel{}, + args: args{val: false}, + want: &SensorChannel{Value: "0", Float: "0"}, + }, + { + name: "set string value", + channel: SensorChannel{}, + args: args{val: "testString123"}, + want: &SensorChannel{Value: "testString123", Float: "0"}, + }, + { + name: "set complex value, shall fail", + channel: SensorChannel{}, + args: args{val: 6 + 7i}, + want: &SensorChannel{}, + }, + { + name: "set uintptr value, shall fail", + channel: SensorChannel{}, + args: args{val: new(uintptr)}, + want: &SensorChannel{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorChannel{ + Channel: tt.channel.Channel, + Value: tt.channel.Value, + ValueMode: tt.channel.ValueMode, + Unit: tt.channel.Unit, + CustomUnit: tt.channel.CustomUnit, + ValueLookup: tt.channel.ValueLookup, + VolumeSize: tt.channel.VolumeSize, + SpeedSize: tt.channel.SpeedSize, + SpeedTime: tt.channel.SpeedTime, + Float: tt.channel.Float, + DecimalMode: tt.channel.DecimalMode, + ShowChart: tt.channel.ShowChart, + ShowTable: tt.channel.ShowTable, + LimitMinWarning: tt.channel.LimitMinWarning, + LimitMaxWarning: tt.channel.LimitMaxWarning, + LimitWarningMsg: tt.channel.LimitWarningMsg, + LimitMinError: tt.channel.LimitMinError, + LimitMaxError: tt.channel.LimitMaxError, + LimitErrorMsg: tt.channel.LimitErrorMsg, + LimitMode: tt.channel.LimitMode, + Warning: tt.channel.Warning, + } + if got := r.SetValue(tt.args.val); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetValue() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSensorChannel_SetUnit(t *testing.T) { + tests := []struct { + name string + channel SensorChannel + unitType UnitType + want *SensorChannel + }{ + { + name: "returns custom string in Unit field", + channel: SensorChannel{}, + unitType: "freeText", + want: &SensorChannel{Unit: "freeText"}, + }, + { + name: "returns exact constant string as called with, BytesBandwidth", + channel: SensorChannel{}, + unitType: BytesBandwidth, + want: &SensorChannel{Unit: string(BytesBandwidth)}, + }, + { + name: "returns exact constant string as called with, BytesDisk", + channel: SensorChannel{}, + unitType: BytesDisk, + want: &SensorChannel{Unit: string(BytesDisk)}, + }, + { + name: "returns exact constant string as called with, Temperature", + channel: SensorChannel{}, + unitType: Temperature, + want: &SensorChannel{Unit: string(Temperature)}, + }, + { + name: "returns exact constant string as called with, Percent", + channel: SensorChannel{}, + unitType: Percent, + want: &SensorChannel{Unit: string(Percent)}, + }, + { + name: "returns exact constant string as called with, TimeResponse", + channel: SensorChannel{}, + unitType: TimeResponse, + want: &SensorChannel{Unit: string(TimeResponse)}, + }, + { + name: "returns exact constant string as called with, TimeSeconds", + channel: SensorChannel{}, + unitType: TimeSeconds, + want: &SensorChannel{Unit: string(TimeSeconds)}, + }, + { + name: "returns exact constant string as called with, Custom", + channel: SensorChannel{}, + unitType: Custom, + want: &SensorChannel{Unit: string(Custom)}, + }, + { + name: "returns exact constant string as called with, Count", + channel: SensorChannel{}, + unitType: Count, + want: &SensorChannel{Unit: string(Count)}, + }, + { + name: "returns exact constant string as called with, CPU", + channel: SensorChannel{}, + unitType: CPU, + want: &SensorChannel{Unit: string(CPU)}, + }, + { + name: "returns exact constant string as called with, BytesFile", + channel: SensorChannel{}, + unitType: BytesFile, + want: &SensorChannel{Unit: string(BytesFile)}, + }, + { + name: "returns exact constant string as called with, SpeedDisk", + channel: SensorChannel{}, + unitType: SpeedDisk, + want: &SensorChannel{Unit: string(SpeedDisk)}, + }, + { + name: "returns exact constant string as called with, SpeedNet", + channel: SensorChannel{}, + unitType: SpeedNet, + want: &SensorChannel{Unit: string(SpeedNet)}, + }, + { + name: "returns exact constant string as called with, TimeHours", + channel: SensorChannel{}, + unitType: TimeHours, + want: &SensorChannel{Unit: string(TimeHours)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorChannel{ + Channel: tt.channel.Channel, + Value: tt.channel.Value, + ValueMode: tt.channel.ValueMode, + Unit: tt.channel.Unit, + CustomUnit: tt.channel.CustomUnit, + ValueLookup: tt.channel.ValueLookup, + VolumeSize: tt.channel.VolumeSize, + SpeedSize: tt.channel.SpeedSize, + SpeedTime: tt.channel.SpeedTime, + Float: tt.channel.Float, + DecimalMode: tt.channel.DecimalMode, + ShowChart: tt.channel.ShowChart, + ShowTable: tt.channel.ShowTable, + LimitMinWarning: tt.channel.LimitMinWarning, + LimitMaxWarning: tt.channel.LimitMaxWarning, + LimitWarningMsg: tt.channel.LimitWarningMsg, + LimitMinError: tt.channel.LimitMinError, + LimitMaxError: tt.channel.LimitMaxError, + LimitErrorMsg: tt.channel.LimitErrorMsg, + LimitMode: tt.channel.LimitMode, + Warning: tt.channel.Warning, + } + if got := r.SetUnit(tt.unitType); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetUnit() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSensorChannel_SetCustomUnit(t *testing.T) { + var tests = []struct { + name string + channel SensorChannel + unitTypeStr string + want *SensorChannel + }{ + { + name: "returns custom string to CustomUnit and 'Custom' to Unit", + channel: SensorChannel{}, + unitTypeStr: "mySpecialUnitType asdf 123 🍌", + want: &SensorChannel{Unit: "Custom", CustomUnit: "mySpecialUnitType asdf 123 🍌"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorChannel{ + Channel: tt.channel.Channel, + Value: tt.channel.Value, + ValueMode: tt.channel.ValueMode, + Unit: tt.channel.Unit, + CustomUnit: tt.channel.CustomUnit, + ValueLookup: tt.channel.ValueLookup, + VolumeSize: tt.channel.VolumeSize, + SpeedSize: tt.channel.SpeedSize, + SpeedTime: tt.channel.SpeedTime, + Float: tt.channel.Float, + DecimalMode: tt.channel.DecimalMode, + ShowChart: tt.channel.ShowChart, + ShowTable: tt.channel.ShowTable, + LimitMinWarning: tt.channel.LimitMinWarning, + LimitMaxWarning: tt.channel.LimitMaxWarning, + LimitWarningMsg: tt.channel.LimitWarningMsg, + LimitMinError: tt.channel.LimitMinError, + LimitMaxError: tt.channel.LimitMaxError, + LimitErrorMsg: tt.channel.LimitErrorMsg, + LimitMode: tt.channel.LimitMode, + Warning: tt.channel.Warning, + } + if got := r.SetCustomUnit(tt.unitTypeStr); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetCustomUnit() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSensorChannel_SetValueLookup(t *testing.T) { + var tests = []struct { + name string + channel SensorChannel + lookup string + want *SensorChannel + }{ + { + name: "returns ValueLookup as given", + channel: SensorChannel{}, + lookup: "mySpecialValueLookup 123 🍌", + want: &SensorChannel{ValueLookup: "mySpecialValueLookup 123 🍌"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorChannel{ + Channel: tt.channel.Channel, + Value: tt.channel.Value, + ValueMode: tt.channel.ValueMode, + Unit: tt.channel.Unit, + CustomUnit: tt.channel.CustomUnit, + ValueLookup: tt.channel.ValueLookup, + VolumeSize: tt.channel.VolumeSize, + SpeedSize: tt.channel.SpeedSize, + SpeedTime: tt.channel.SpeedTime, + Float: tt.channel.Float, + DecimalMode: tt.channel.DecimalMode, + ShowChart: tt.channel.ShowChart, + ShowTable: tt.channel.ShowTable, + LimitMinWarning: tt.channel.LimitMinWarning, + LimitMaxWarning: tt.channel.LimitMaxWarning, + LimitWarningMsg: tt.channel.LimitWarningMsg, + LimitMinError: tt.channel.LimitMinError, + LimitMaxError: tt.channel.LimitMaxError, + LimitErrorMsg: tt.channel.LimitErrorMsg, + LimitMode: tt.channel.LimitMode, + Warning: tt.channel.Warning, + } + if got := r.SetValueLookup(tt.lookup); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetValueLookup() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSensorChannel_SetVolumeSize(t *testing.T) { + var tests = []struct { + name string + channel SensorChannel + volumeSize SizeType + want *SensorChannel + }{ + { + name: "returns VolumeSize as string from given SizeType, One", + channel: SensorChannel{}, + volumeSize: One, + want: &SensorChannel{VolumeSize: string(One)}, + }, + { + name: "returns VolumeSize as string from given SizeType, Kilo", + channel: SensorChannel{}, + volumeSize: Kilo, + want: &SensorChannel{VolumeSize: string(Kilo)}, + }, + { + name: "returns VolumeSize as string from given SizeType, KiloByte", + channel: SensorChannel{}, + volumeSize: KiloByte, + want: &SensorChannel{VolumeSize: string(KiloByte)}, + }, + { + name: "returns VolumeSize as string from given SizeType, Tera", + channel: SensorChannel{}, + volumeSize: Tera, + want: &SensorChannel{VolumeSize: string(Tera)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorChannel{ + Channel: tt.channel.Channel, + Value: tt.channel.Value, + ValueMode: tt.channel.ValueMode, + Unit: tt.channel.Unit, + CustomUnit: tt.channel.CustomUnit, + ValueLookup: tt.channel.ValueLookup, + VolumeSize: tt.channel.VolumeSize, + SpeedSize: tt.channel.SpeedSize, + SpeedTime: tt.channel.SpeedTime, + Float: tt.channel.Float, + DecimalMode: tt.channel.DecimalMode, + ShowChart: tt.channel.ShowChart, + ShowTable: tt.channel.ShowTable, + LimitMinWarning: tt.channel.LimitMinWarning, + LimitMaxWarning: tt.channel.LimitMaxWarning, + LimitWarningMsg: tt.channel.LimitWarningMsg, + LimitMinError: tt.channel.LimitMinError, + LimitMaxError: tt.channel.LimitMaxError, + LimitErrorMsg: tt.channel.LimitErrorMsg, + LimitMode: tt.channel.LimitMode, + Warning: tt.channel.Warning, + } + if got := r.SetVolumeSize(tt.volumeSize); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetVolumeSize() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSensorChannel_SetSpeedSize(t *testing.T) { + var tests = []struct { + name string + channel SensorChannel + speedType SpeedType + want *SensorChannel + }{ + { + name: "returns given speed as string, Bit", + channel: SensorChannel{}, + speedType: Bit, + want: &SensorChannel{SpeedSize: string(Bit)}, + }, + { + name: "returns given speed as string, KiloBit", + channel: SensorChannel{}, + speedType: KiloBit, + want: &SensorChannel{SpeedSize: string(KiloBit)}, + }, + { + name: "returns given speed as string, TeraBit", + channel: SensorChannel{}, + speedType: TeraBit, + want: &SensorChannel{SpeedSize: string(TeraBit)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorChannel{ + Channel: tt.channel.Channel, + Value: tt.channel.Value, + ValueMode: tt.channel.ValueMode, + Unit: tt.channel.Unit, + CustomUnit: tt.channel.CustomUnit, + ValueLookup: tt.channel.ValueLookup, + VolumeSize: tt.channel.VolumeSize, + SpeedSize: tt.channel.SpeedSize, + SpeedTime: tt.channel.SpeedTime, + Float: tt.channel.Float, + DecimalMode: tt.channel.DecimalMode, + ShowChart: tt.channel.ShowChart, + ShowTable: tt.channel.ShowTable, + LimitMinWarning: tt.channel.LimitMinWarning, + LimitMaxWarning: tt.channel.LimitMaxWarning, + LimitWarningMsg: tt.channel.LimitWarningMsg, + LimitMinError: tt.channel.LimitMinError, + LimitMaxError: tt.channel.LimitMaxError, + LimitErrorMsg: tt.channel.LimitErrorMsg, + LimitMode: tt.channel.LimitMode, + Warning: tt.channel.Warning, + } + if got := r.SetSpeedSize(tt.speedType); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetSpeedSize() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSensorChannel_SetSpeedTime(t *testing.T) { + var tests = []struct { + name string + channel SensorChannel + speedTime TimeType + want *SensorChannel + }{ + { + name: "returns speedTime as string, Second", + channel: SensorChannel{}, + speedTime: Second, + want: &SensorChannel{SpeedTime: string(Second)}, + }, + { + name: "returns speedTime as string, Minute", + channel: SensorChannel{}, + speedTime: Minute, + want: &SensorChannel{SpeedTime: string(Minute)}, + }, + { + name: "returns speedTime as string, Hour", + channel: SensorChannel{}, + speedTime: Hour, + want: &SensorChannel{SpeedTime: string(Hour)}, + }, + { + name: "returns speedTime as string, Day", + channel: SensorChannel{}, + speedTime: Day, + want: &SensorChannel{SpeedTime: string(Day)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorChannel{ + Channel: tt.channel.Channel, + Value: tt.channel.Value, + ValueMode: tt.channel.ValueMode, + Unit: tt.channel.Unit, + CustomUnit: tt.channel.CustomUnit, + ValueLookup: tt.channel.ValueLookup, + VolumeSize: tt.channel.VolumeSize, + SpeedSize: tt.channel.SpeedSize, + SpeedTime: tt.channel.SpeedTime, + Float: tt.channel.Float, + DecimalMode: tt.channel.DecimalMode, + ShowChart: tt.channel.ShowChart, + ShowTable: tt.channel.ShowTable, + LimitMinWarning: tt.channel.LimitMinWarning, + LimitMaxWarning: tt.channel.LimitMaxWarning, + LimitWarningMsg: tt.channel.LimitWarningMsg, + LimitMinError: tt.channel.LimitMinError, + LimitMaxError: tt.channel.LimitMaxError, + LimitErrorMsg: tt.channel.LimitErrorMsg, + LimitMode: tt.channel.LimitMode, + Warning: tt.channel.Warning, + } + if got := r.SetSpeedTime(tt.speedTime); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetSpeedTime() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSensorChannel_SetDecimalMode(t *testing.T) { + var tests = []struct { + name string + channel SensorChannel + modeType DecimalModeType + want *SensorChannel + }{ + { + name: "returns modeType as string, OptionAuto", + channel: SensorChannel{}, + modeType: OptionAuto, + want: &SensorChannel{DecimalMode: string(OptionAuto)}, + }, + { + name: "returns modeType as string, OptionAll", + channel: SensorChannel{}, + modeType: OptionAll, + want: &SensorChannel{DecimalMode: string(OptionAll)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &SensorChannel{ + Channel: tt.channel.Channel, + Value: tt.channel.Value, + ValueMode: tt.channel.ValueMode, + Unit: tt.channel.Unit, + CustomUnit: tt.channel.CustomUnit, + ValueLookup: tt.channel.ValueLookup, + VolumeSize: tt.channel.VolumeSize, + SpeedSize: tt.channel.SpeedSize, + SpeedTime: tt.channel.SpeedTime, + Float: tt.channel.Float, + DecimalMode: tt.channel.DecimalMode, + ShowChart: tt.channel.ShowChart, + ShowTable: tt.channel.ShowTable, + LimitMinWarning: tt.channel.LimitMinWarning, + LimitMaxWarning: tt.channel.LimitMaxWarning, + LimitWarningMsg: tt.channel.LimitWarningMsg, + LimitMinError: tt.channel.LimitMinError, + LimitMaxError: tt.channel.LimitMaxError, + LimitErrorMsg: tt.channel.LimitErrorMsg, + LimitMode: tt.channel.LimitMode, + Warning: tt.channel.Warning, + } + if got := r.SetDecimalMode(tt.modeType); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetDecimalMode() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/sensor.go b/sensor.go deleted file mode 100644 index b85b931..0000000 --- a/sensor.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Paessler AG. -// All Rights Reserved - -// Package prtg implements the API for PRTG custom sensors. It provides all structs and constants needed to implement your own advanced exe sensor in go. -// -// API reference can be found here: https://prtg.paessler.com/api.htm?username=demo&password=demodemo&tabid=7 -package prtg - -import "encoding/json" - -// Units of channel values -const ( - UnitBytesBandwidth = "BytesBandwidth" - UnitBytesMemory = "BytesMemory" - UnitBytesDisk = "BytesDisk" - UnitBytesFile = "BytesFile" - UnitTemperature = "Temperature" - UnitPercent = "Percent" - UnitTimeResponse = "TimeResponse" - UnitTimeSeconds = "TimeSeconds" - UnitCustom = "Custom" - UnitCount = "Count" - UnitCPU = "CPU" - UnitSpeedDisk = "SpeedDisk" - UnitSpeedNet = "SpeedNet" - UnitTimeHours = "TimeHours" -) - -// Unit of VolumeSize or SpeedSize -const ( - UnitOne = "One" - UnitKilo = "Kilo" - UnitMega = "Mega" - UnitGiga = "Giga" - UnitTera = "Tera" - UnitByte = "Byte" - UnitKiloByte = "KiloByte" - UnitMegaByte = "MegaByte" - UnitGigaByte = "Gigabyte" - UnitTeraByte = "TeraByte" - UnitBit = "Bit" - UnitKiloBit = "KiloBit" - UnitMegaBit = "MegaBit" - UnitGigaBit = "GigaBit" - UnitTeraBit = "TeraBit" -) - -// Unit of speedTime -const ( - UnitSecond = "Second" - UnitMinute = "Minute" - UnitHour = "Hour" - UnitDay = "Day" -) - -// Options for DecimalMode and Mode -const ( - OptionAbsolute = "Absolute" - OptionDifference = "Difference" - OptionAuto = "Auto" - OptionAll = "All" -) - -// SensorResponse is the struct returned by the sensor -type SensorResponse struct { - SensorResult `json:"prtg"` -} - -// SensorResult contains the sensors channels and optional error/text -type SensorResult struct { - Channels []SensorChannel `json:"result"` - Error int `json:"error,omitempty"` - Text string `json:"text,omitempty"` -} - -// SensorChannel represents a PRTG sensor channel -type SensorChannel struct { - Name string `json:"channel,omitempty"` - ChannelID *int `json:"channelid,omitempty"` - Value float64 `` - - // Options - Unit string `json:",omitempty"` - CustomUnit string `json:",omitempty"` - SpeedSize string `json:",omitempty"` - VolumeSize string `json:",omitempty"` - SpeedTime string `json:",omitempty"` - Mode string `json:",omitempty"` - Float int `json:",omitempty"` - DecimalMode string `json:",omitempty"` - Warning int `json:",omitempty"` - ShowChart *int `json:",omitempty"` - ShowTable *int `json:",omitempty"` - LimitMaxError *float64 `json:",omitempty"` - LimitMaxWarning *float64 `json:",omitempty"` - LimitMinWarning *float64 `json:",omitempty"` - LimitMinError *float64 `json:",omitempty"` - LimitErrorMsg string `json:",omitempty"` - LimitWarningMsg string `json:",omitempty"` - LimitMode int `json:",omitempty"` - ValueLookup string `json:",omitempty"` - NotifyChanged bool `json:",omitempty"` -} - -// AddChannel adds a new channel to a SensorResponse -func (s *SensorResponse) AddChannel(channel SensorChannel) { - s.Channels = append(s.Channels, channel) -} - -// String converts the SensorResponse to a JSON formatted string -func (s SensorResponse) String() string { - b, _ := json.Marshal(s) - return string(b) -} diff --git a/sensor_test.go b/sensor_test.go deleted file mode 100644 index 17d54ab..0000000 --- a/sensor_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright Paessler AG. -// All Rights Reserved - -package prtg - -import ( - "encoding/json" - "math/rand" - "os" - "reflect" - "strconv" - "testing" - "time" -) - -func TestMain(m *testing.M) { - rand.Seed(time.Now().Unix()) - os.Exit(m.Run()) -} - -func TestAddChannel(t *testing.T) { - // Create an empty SensorResponse - response := &SensorResponse{} - // Create channel with random data - channel := createRandomChannel() - // Add channel to response - response.AddChannel(channel) - // Check if it safely arrived - if channel != response.Channels[0] { - t.Error("response.AddChannel(channel) => failed to add channel") - } -} - -func TestString(t *testing.T) { - // Create response with some random channels - response := &SensorResponse{} - for i := 0; i < 3; i++ { - response.AddChannel(createRandomChannel()) - } - s := response.String() - // Unmarshal string and check if we get back what we started with - var checkResponse *SensorResponse - if json.Unmarshal([]byte(s), &checkResponse) != nil { - t.Errorf("Failed to unmarshal stringified response") - t.FailNow() - } - if !reflect.DeepEqual(response, checkResponse) { - // Errors little helper function - toString := func(v interface{}) string { - b, _ := json.MarshalIndent(v, "", " ") - return string(b) - } - t.Errorf("Response marshaled from stringified response does not match original response.\nExpected:\n%s\nGot:\n%s", toString(response), toString(checkResponse)) - } -} - -func TestStringError(t *testing.T) { - response := &SensorResponse{} - _ = response.String() -} - -// createRandomChannel generates SensorChannels with random settings for testing -func createRandomChannel() SensorChannel { - // Helper to create semi-random string - randomString := func() string { - return strconv.FormatInt(rand.Int63n(245285), 10) - } - // Create channel and fill it with random data - channel := SensorChannel{} - channel.Name = randomString() - channel.Value = rand.Float64() * 1000 - channel.CustomUnit = randomString() - channel.DecimalMode = randomString() - channel.Float = rand.Intn(2) - channel.LimitErrorMsg = randomString() - channel.LimitMaxError = new(float64) - *channel.LimitMaxError = rand.Float64() * 1000 - channel.LimitMaxWarning = new(float64) - *channel.LimitMaxWarning = rand.Float64() * 1000 - channel.LimitMinError = new(float64) - *channel.LimitMinError = rand.Float64() * 1000 - channel.LimitMode = rand.Intn(2) - channel.LimitWarningMsg = randomString() - channel.Mode = randomString() - channel.NotifyChanged = rand.Intn(2) != 0 - channel.ShowChart = new(int) - *channel.ShowChart = rand.Intn(2) - channel.ShowTable = new(int) - *channel.ShowTable = rand.Intn(2) - channel.SpeedSize = randomString() - channel.SpeedTime = randomString() - channel.Unit = randomString() - channel.ValueLookup = randomString() - channel.Warning = rand.Int() - return channel -} diff --git a/types.go b/types.go new file mode 100644 index 0000000..4439eec --- /dev/null +++ b/types.go @@ -0,0 +1,63 @@ +// Copyright (c) 2019, Paessler AG. +// All Rights Reserved +package prtg + +type UnitType string + +const ( + BytesBandwidth UnitType = "BytesBandwidth" + BytesDisk UnitType = "BytesDisk" + Temperature UnitType = "Temperature" + Percent UnitType = "Percent" + TimeResponse UnitType = "TimeResponse" + TimeSeconds UnitType = "TimeSeconds" + Custom UnitType = "Custom" + Count UnitType = "Count" + CPU UnitType = "CPU" + BytesFile UnitType = "BytesFile" + SpeedDisk UnitType = "SpeedDisk" + SpeedNet UnitType = "SpeedNet" + TimeHours UnitType = "TimeHours" +) + +type SizeType string + +const ( + One SizeType = "One" + Kilo SizeType = "Kilo" + Mega SizeType = "Mega" + Giga SizeType = "Giga" + Tera SizeType = "Tera" + Byte SizeType = "Byte" + KiloByte SizeType = "KiloByte" + MegaByte SizeType = "MegaByte" + GigaByte SizeType = "GigaByte" + TeraByte SizeType = "TeraByte" +) + +type SpeedType string + +const ( + Bit SpeedType = "Bit" + KiloBit SpeedType = "KiloBit" + MegaBit SpeedType = "MegaBit" + GigaBit SpeedType = "GigaBit" + TeraBit SpeedType = "TeraBit" +) + +type TimeType string + +const ( + Second TimeType = "Second" + Minute TimeType = "Minute" + Hour TimeType = "Hour" + Day TimeType = "Day" +) + +type DecimalModeType string + +// Options for Decimalmode +const ( + OptionAuto DecimalModeType = "Auto" + OptionAll DecimalModeType = "All" +)