feat(influxdb): add check struct
parent
b2fe0d1d63
commit
ec9ecf23a1
5
authz.go
5
authz.go
|
@ -127,6 +127,8 @@ const (
|
|||
NotificationRuleResourceType = ResourceType("notificationRules") // 14
|
||||
// NotificationEndpointResourceType gives permission to one or more notificationEndpoints.
|
||||
NotificationEndpointResourceType = ResourceType("notificationEndpoints") // 15
|
||||
// ChecksResourceType gives permission to one or more Checks.
|
||||
ChecksResourceType = ResourceType("checks") // 16
|
||||
)
|
||||
|
||||
// AllResourceTypes is the list of all known resource types.
|
||||
|
@ -147,6 +149,7 @@ var AllResourceTypes = []ResourceType{
|
|||
DocumentsResourceType, // 13
|
||||
NotificationRuleResourceType, // 14
|
||||
NotificationEndpointResourceType, // 15
|
||||
ChecksResourceType, // 16
|
||||
// NOTE: when modifying this list, please update the swagger for components.schemas.Permission resource enum.
|
||||
}
|
||||
|
||||
|
@ -163,6 +166,7 @@ var OrgResourceTypes = []ResourceType{
|
|||
DocumentsResourceType, // 13
|
||||
NotificationRuleResourceType, // 14
|
||||
NotificationEndpointResourceType, // 15
|
||||
ChecksResourceType, // 16
|
||||
}
|
||||
|
||||
// Valid checks if the resource type is a member of the ResourceType enum.
|
||||
|
@ -189,6 +193,7 @@ func (t ResourceType) Valid() (err error) {
|
|||
case DocumentsResourceType: // 13
|
||||
case NotificationRuleResourceType: // 14
|
||||
case NotificationEndpointResourceType: // 15
|
||||
case ChecksResourceType: // 16
|
||||
default:
|
||||
err = ErrInvalidResourceType
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
package influxdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// consts for checks config.
|
||||
const (
|
||||
CheckDefaultPageSize = 100
|
||||
CheckMaxPageSize = 500
|
||||
)
|
||||
|
||||
// Check represents the information required to generate a periodic check task.
|
||||
type Check interface {
|
||||
Valid() error
|
||||
Type() string
|
||||
json.Marshaler
|
||||
Updator
|
||||
Getter
|
||||
}
|
||||
|
||||
// ops for checks error
|
||||
var (
|
||||
OpFindCheckByID = "FindCheckByID"
|
||||
OpFindCheck = "FindCheck"
|
||||
OpFindChecks = "FindChecks"
|
||||
OpCreateCheck = "CreateCheck"
|
||||
OpUpdateCheck = "UpdateCheck"
|
||||
OpDeleteCheck = "DeleteCheck"
|
||||
)
|
||||
|
||||
// CheckService represents a service for managing checks.
|
||||
type CheckService interface {
|
||||
// UserResourceMappingService must be part of all CheckService service,
|
||||
// for create, delete.
|
||||
UserResourceMappingService
|
||||
// OrganizationService is needed for search filter
|
||||
OrganizationService
|
||||
|
||||
// FindCheckByID returns a single check by ID.
|
||||
FindCheckByID(ctx context.Context, id ID) (Check, error)
|
||||
|
||||
// FindCheck returns the first check that matches filter.
|
||||
FindCheck(ctx context.Context, filter CheckFilter) (Check, error)
|
||||
|
||||
// FindChecks returns a list of checks that match filter and the total count of matching checkns.
|
||||
// Additional options provide pagination & sorting.
|
||||
FindChecks(ctx context.Context, filter CheckFilter, opt ...FindOptions) ([]Check, int, error)
|
||||
|
||||
// CreateCheck creates a new check and sets b.ID with the new identifier.
|
||||
CreateCheck(ctx context.Context, c Check) error
|
||||
|
||||
// UpdateCheck updates the whole check.
|
||||
// Returns the new check state after update.
|
||||
UpdateCheck(ctx context.Context, id ID, c Check) (Check, error)
|
||||
|
||||
// PatchCheck updates a single bucket with changeset.
|
||||
// Returns the new check state after update.
|
||||
PatchCheck(ctx context.Context, id ID, upd CheckUpdate) (Check, error)
|
||||
|
||||
// DeleteCheck will delete the check by id.
|
||||
DeleteCheck(ctx context.Context, id ID) error
|
||||
}
|
||||
|
||||
// CheckUpdate are properties than can be updated on a check
|
||||
type CheckUpdate struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Status *Status `json:"status,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// Valid returns err is the update is invalid.
|
||||
func (n *CheckUpdate) Valid() error {
|
||||
if n.Name != nil && *n.Name == "" {
|
||||
return &Error{
|
||||
Code: EInvalid,
|
||||
Msg: "Check Name can't be empty",
|
||||
}
|
||||
}
|
||||
|
||||
if n.Description != nil && *n.Description == "" {
|
||||
return &Error{
|
||||
Code: EInvalid,
|
||||
Msg: "Check Description can't be empty",
|
||||
}
|
||||
}
|
||||
|
||||
if n.Status != nil {
|
||||
if err := n.Status.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckFilter represents a set of filters that restrict the returned results.
|
||||
type CheckFilter struct {
|
||||
ID *ID
|
||||
Name *string
|
||||
OrgID *ID
|
||||
Org *string
|
||||
}
|
||||
|
||||
// QueryParams Converts CheckFilter fields to url query params.
|
||||
func (f CheckFilter) QueryParams() map[string][]string {
|
||||
qp := map[string][]string{}
|
||||
|
||||
if f.ID != nil {
|
||||
qp["id"] = []string{f.ID.String()}
|
||||
}
|
||||
|
||||
if f.Name != nil {
|
||||
qp["name"] = []string{*f.Name}
|
||||
}
|
||||
|
||||
if f.OrgID != nil {
|
||||
qp["orgID"] = []string{f.OrgID.String()}
|
||||
}
|
||||
|
||||
if f.Org != nil {
|
||||
qp["org"] = []string{*f.Org}
|
||||
}
|
||||
|
||||
return qp
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdata/influxdb"
|
||||
"github.com/influxdata/influxdb/notification"
|
||||
)
|
||||
|
||||
// Base will embed inside a check.
|
||||
type Base struct {
|
||||
ID influxdb.ID `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
AuthorizationID influxdb.ID `json:"authorizationID,omitempty"`
|
||||
OrgID influxdb.ID `json:"orgID,omitempty"`
|
||||
Status influxdb.Status `json:"status"`
|
||||
Query influxdb.DashboardQuery `json:"query"`
|
||||
StatusMessageTemplate string `json:"statusMessageTemplate"`
|
||||
|
||||
Cron string `json:"cron,omitempty"`
|
||||
Every influxdb.Duration `json:"every,omitempty"`
|
||||
// Offset represents a delay before execution.
|
||||
// It gets marshalled from a string duration, i.e.: "10s" is 10 seconds
|
||||
Offset influxdb.Duration `json:"offset,omitempty"`
|
||||
|
||||
Tags []notification.Tag `json:"tags"`
|
||||
influxdb.CRUDLog
|
||||
}
|
||||
|
||||
// Valid returns err if the check is invalid.
|
||||
func (b Base) Valid() error {
|
||||
if !b.ID.Valid() {
|
||||
return &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Check ID is invalid",
|
||||
}
|
||||
}
|
||||
if b.Name == "" {
|
||||
return &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Check Name can't be empty",
|
||||
}
|
||||
}
|
||||
if !b.AuthorizationID.Valid() {
|
||||
return &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Check AuthorizationID is invalid",
|
||||
}
|
||||
}
|
||||
if !b.OrgID.Valid() {
|
||||
return &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Check OrgID is invalid",
|
||||
}
|
||||
}
|
||||
if b.Status != influxdb.Active && b.Status != influxdb.Inactive {
|
||||
return &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "invalid status",
|
||||
}
|
||||
}
|
||||
for _, tag := range b.Tags {
|
||||
if err := tag.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetID implements influxdb.Getter interface.
|
||||
func (b Base) GetID() influxdb.ID {
|
||||
return b.ID
|
||||
}
|
||||
|
||||
// GetOrgID implements influxdb.Getter interface.
|
||||
func (b Base) GetOrgID() influxdb.ID {
|
||||
return b.OrgID
|
||||
}
|
||||
|
||||
// GetCRUDLog implements influxdb.Getter interface.
|
||||
func (b Base) GetCRUDLog() influxdb.CRUDLog {
|
||||
return b.CRUDLog
|
||||
}
|
||||
|
||||
// GetName implements influxdb.Getter interface.
|
||||
func (b *Base) GetName() string {
|
||||
return b.Name
|
||||
}
|
||||
|
||||
// GetDescription implements influxdb.Getter interface.
|
||||
func (b *Base) GetDescription() string {
|
||||
return b.Description
|
||||
}
|
||||
|
||||
// GetStatus implements influxdb.Getter interface.
|
||||
func (b *Base) GetStatus() influxdb.Status {
|
||||
return b.Status
|
||||
}
|
||||
|
||||
// SetID will set the primary key.
|
||||
func (b *Base) SetID(id influxdb.ID) {
|
||||
b.ID = id
|
||||
}
|
||||
|
||||
// SetOrgID will set the org key.
|
||||
func (b *Base) SetOrgID(id influxdb.ID) {
|
||||
b.OrgID = id
|
||||
}
|
||||
|
||||
// SetName implements influxdb.Updator interface.
|
||||
func (b *Base) SetName(name string) {
|
||||
b.Name = name
|
||||
}
|
||||
|
||||
// SetDescription implements influxdb.Updator interface.
|
||||
func (b *Base) SetDescription(description string) {
|
||||
b.Description = description
|
||||
}
|
||||
|
||||
// SetStatus implements influxdb.Updator interface.
|
||||
func (b *Base) SetStatus(status influxdb.Status) {
|
||||
b.Status = status
|
||||
}
|
||||
|
||||
var typeToCheck = map[string](func() influxdb.Check){
|
||||
"deadman": func() influxdb.Check { return &Deadman{} },
|
||||
"threshold": func() influxdb.Check { return &Threshold{} },
|
||||
}
|
||||
|
||||
type rawRuleJSON struct {
|
||||
Typ string `json:"type"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON will convert
|
||||
func UnmarshalJSON(b []byte) (influxdb.Check, error) {
|
||||
var raw rawRuleJSON
|
||||
if err := json.Unmarshal(b, &raw); err != nil {
|
||||
return nil, &influxdb.Error{
|
||||
Msg: "unable to detect the check type from json",
|
||||
}
|
||||
}
|
||||
convertedFunc, ok := typeToCheck[raw.Typ]
|
||||
if !ok {
|
||||
return nil, &influxdb.Error{
|
||||
Msg: fmt.Sprintf("invalid check type %s", raw.Typ),
|
||||
}
|
||||
}
|
||||
converted := convertedFunc()
|
||||
err := json.Unmarshal(b, converted)
|
||||
return converted, err
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
package check_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/influxdata/influxdb/notification"
|
||||
|
||||
"github.com/influxdata/influxdb/mock"
|
||||
|
||||
"github.com/influxdata/influxdb"
|
||||
"github.com/influxdata/influxdb/notification/check"
|
||||
influxTesting "github.com/influxdata/influxdb/testing"
|
||||
)
|
||||
|
||||
const (
|
||||
id1 = "020f755c3c082000"
|
||||
id2 = "020f755c3c082001"
|
||||
id3 = "020f755c3c082002"
|
||||
)
|
||||
|
||||
func numPtr(f float64) *float64 {
|
||||
p := new(float64)
|
||||
*p = f
|
||||
return p
|
||||
}
|
||||
|
||||
var goodBase = check.Base{
|
||||
ID: influxTesting.MustIDBase16(id1),
|
||||
Name: "name1",
|
||||
AuthorizationID: influxTesting.MustIDBase16(id2),
|
||||
OrgID: influxTesting.MustIDBase16(id3),
|
||||
Status: influxdb.Active,
|
||||
StatusMessageTemplate: "temp1",
|
||||
Tags: []notification.Tag{
|
||||
{Key: "k1", Value: "v1"},
|
||||
{Key: "k2", Value: "v2"},
|
||||
},
|
||||
}
|
||||
|
||||
func TestValidCheck(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
src influxdb.Check
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "invalid check id",
|
||||
src: &check.Deadman{},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Check ID is invalid",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty name",
|
||||
src: &check.Threshold{
|
||||
Base: check.Base{
|
||||
ID: influxTesting.MustIDBase16(id1),
|
||||
},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Check Name can't be empty",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid auth id",
|
||||
src: &check.Threshold{
|
||||
Base: check.Base{
|
||||
ID: influxTesting.MustIDBase16(id1),
|
||||
Name: "name1",
|
||||
},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Check AuthorizationID is invalid",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid org id",
|
||||
src: &check.Threshold{
|
||||
Base: check.Base{
|
||||
ID: influxTesting.MustIDBase16(id1),
|
||||
Name: "name1",
|
||||
AuthorizationID: influxTesting.MustIDBase16(id2),
|
||||
},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Check OrgID is invalid",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid status",
|
||||
src: &check.Deadman{
|
||||
Base: check.Base{
|
||||
ID: influxTesting.MustIDBase16(id1),
|
||||
Name: "name1",
|
||||
AuthorizationID: influxTesting.MustIDBase16(id2),
|
||||
OrgID: influxTesting.MustIDBase16(id3),
|
||||
},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "invalid status",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid tag",
|
||||
src: &check.Deadman{
|
||||
Base: check.Base{
|
||||
ID: influxTesting.MustIDBase16(id1),
|
||||
Name: "name1",
|
||||
AuthorizationID: influxTesting.MustIDBase16(id2),
|
||||
OrgID: influxTesting.MustIDBase16(id3),
|
||||
Status: influxdb.Active,
|
||||
StatusMessageTemplate: "temp1",
|
||||
Tags: []notification.Tag{{Key: "key1"}},
|
||||
},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "tag must contain a key and a value",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad thredshold",
|
||||
src: &check.Threshold{
|
||||
Base: goodBase,
|
||||
Thresholds: []check.ThresholdConfig{{}},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "threshold must have at least one lowerBound or upperBound value",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := c.src.Valid()
|
||||
influxTesting.ErrorsEqual(t, got, c.err)
|
||||
}
|
||||
}
|
||||
|
||||
var timeGen1 = mock.TimeGenerator{FakeValue: time.Date(2006, time.July, 13, 4, 19, 10, 0, time.UTC)}
|
||||
var timeGen2 = mock.TimeGenerator{FakeValue: time.Date(2006, time.July, 14, 5, 23, 53, 10, time.UTC)}
|
||||
|
||||
func TestJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
src influxdb.Check
|
||||
}{
|
||||
{
|
||||
name: "simple Deadman",
|
||||
src: &check.Deadman{
|
||||
Base: check.Base{
|
||||
ID: influxTesting.MustIDBase16(id1),
|
||||
AuthorizationID: influxTesting.MustIDBase16(id2),
|
||||
Name: "name1",
|
||||
OrgID: influxTesting.MustIDBase16(id3),
|
||||
Status: influxdb.Active,
|
||||
Every: influxdb.Duration{Duration: time.Hour},
|
||||
Tags: []notification.Tag{
|
||||
{
|
||||
Key: "k1",
|
||||
Value: "v1",
|
||||
},
|
||||
{
|
||||
Key: "k2",
|
||||
Value: "v2",
|
||||
},
|
||||
},
|
||||
CRUDLog: influxdb.CRUDLog{
|
||||
CreatedAt: timeGen1.Now(),
|
||||
UpdatedAt: timeGen2.Now(),
|
||||
},
|
||||
},
|
||||
TimeSince: 33,
|
||||
ReportZero: true,
|
||||
Level: notification.Warn,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple threshold",
|
||||
src: &check.Threshold{
|
||||
Base: check.Base{
|
||||
ID: influxTesting.MustIDBase16(id1),
|
||||
Name: "name1",
|
||||
AuthorizationID: influxTesting.MustIDBase16(id2),
|
||||
OrgID: influxTesting.MustIDBase16(id3),
|
||||
Status: influxdb.Active,
|
||||
Every: influxdb.Duration{Duration: time.Hour},
|
||||
Tags: []notification.Tag{
|
||||
{
|
||||
Key: "k1",
|
||||
Value: "v1",
|
||||
},
|
||||
{
|
||||
Key: "k2",
|
||||
Value: "v2",
|
||||
},
|
||||
},
|
||||
CRUDLog: influxdb.CRUDLog{
|
||||
CreatedAt: timeGen1.Now(),
|
||||
UpdatedAt: timeGen2.Now(),
|
||||
},
|
||||
},
|
||||
Thresholds: []check.ThresholdConfig{
|
||||
{AllValues: true, LowerBound: numPtr(-1.36)},
|
||||
{LowerBound: numPtr(10000), UpperBound: numPtr(500)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
b, err := json.Marshal(c.src)
|
||||
if err != nil {
|
||||
t.Fatalf("%s marshal failed, err: %s", c.name, err.Error())
|
||||
}
|
||||
got, err := check.UnmarshalJSON(b)
|
||||
if err != nil {
|
||||
t.Fatalf("%s unmarshal failed, err: %s", c.name, err.Error())
|
||||
}
|
||||
if diff := cmp.Diff(got, c.src); diff != "" {
|
||||
t.Errorf("failed %s, Check are different -got/+want\ndiff %s", c.name, diff)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/influxdata/influxdb"
|
||||
"github.com/influxdata/influxdb/notification"
|
||||
)
|
||||
|
||||
var _ influxdb.Check = &Deadman{}
|
||||
|
||||
// Deadman is the deadman check.
|
||||
type Deadman struct {
|
||||
Base
|
||||
// seconds before deadman triggers
|
||||
TimeSince uint `json:"timeSince"`
|
||||
// If only zero values reported since time, trigger alert.
|
||||
ReportZero bool `json:"reportZero"`
|
||||
Level notification.CheckLevel `json:"level"`
|
||||
}
|
||||
|
||||
// Type returns the type of the check.
|
||||
func (c Deadman) Type() string {
|
||||
return "deadman"
|
||||
}
|
||||
|
||||
type deadmanAlias Deadman
|
||||
|
||||
// MarshalJSON implement json.Marshaler interface.
|
||||
func (c Deadman) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(
|
||||
struct {
|
||||
deadmanAlias
|
||||
Type string `json:"type"`
|
||||
}{
|
||||
deadmanAlias: deadmanAlias(c),
|
||||
Type: c.Type(),
|
||||
})
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/influxdata/influxdb"
|
||||
"github.com/influxdata/influxdb/notification"
|
||||
)
|
||||
|
||||
var _ influxdb.Check = &Threshold{}
|
||||
|
||||
// Threshold is the threshold check.
|
||||
type Threshold struct {
|
||||
Base
|
||||
Thresholds []ThresholdConfig `json:"thresholds"`
|
||||
}
|
||||
|
||||
// Type returns the type of the check.
|
||||
func (c Threshold) Type() string {
|
||||
return "threshold"
|
||||
}
|
||||
|
||||
// Valid returns error if something is invalid.
|
||||
func (c Threshold) Valid() error {
|
||||
if err := c.Base.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, cc := range c.Thresholds {
|
||||
if err := cc.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type thresholdAlias Threshold
|
||||
|
||||
// MarshalJSON implement json.Marshaler interface.
|
||||
func (c Threshold) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(
|
||||
struct {
|
||||
thresholdAlias
|
||||
Type string `json:"type"`
|
||||
}{
|
||||
thresholdAlias: thresholdAlias(c),
|
||||
Type: c.Type(),
|
||||
})
|
||||
}
|
||||
|
||||
// ThresholdConfig is the base of all threshold config.
|
||||
type ThresholdConfig struct {
|
||||
// If true, only alert if all values meet threshold.
|
||||
AllValues bool `json:"allValues"`
|
||||
Level notification.CheckLevel `json:"level"`
|
||||
LowerBound *float64 `json:"lowerBound,omitempty"`
|
||||
UpperBound *float64 `json:"upperBound,omitempty"`
|
||||
}
|
||||
|
||||
// Valid returns error if something is invalid.
|
||||
func (c ThresholdConfig) Valid() error {
|
||||
if c.LowerBound == nil && c.UpperBound == nil {
|
||||
return &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "threshold must have at least one lowerBound or upperBound value",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -12,6 +12,17 @@ type Tag struct {
|
|||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Valid returns an error if the tag is missing fields
|
||||
func (t Tag) Valid() error {
|
||||
if t.Key == "" || t.Value == "" {
|
||||
return &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "tag must contain a key and a value",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TagRule is the struct of tag rule.
|
||||
type TagRule struct {
|
||||
Tag
|
||||
|
@ -38,6 +49,9 @@ var availableOperator = map[Operator]bool{
|
|||
|
||||
// Valid returns error for invalid operators.
|
||||
func (tr TagRule) Valid() error {
|
||||
if err := tr.Tag.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := availableOperator[tr.Operator]; !ok {
|
||||
return &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package notification_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/influxdb"
|
||||
"github.com/influxdata/influxdb/notification"
|
||||
influxTesting "github.com/influxdata/influxdb/testing"
|
||||
)
|
||||
|
||||
func TestTagValid(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
src notification.TagRule
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "regular status rule",
|
||||
src: notification.TagRule{
|
||||
Tag: notification.Tag{Key: "k1", Value: "v1"},
|
||||
Operator: notification.Equal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
src: notification.TagRule{},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "tag must contain a key and a value",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty key",
|
||||
src: notification.TagRule{
|
||||
Tag: notification.Tag{Value: "v1"},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "tag must contain a key and a value",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty value",
|
||||
src: notification.TagRule{
|
||||
Tag: notification.Tag{Key: "k1"},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "tag must contain a key and a value",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid operator",
|
||||
src: notification.TagRule{
|
||||
Tag: notification.Tag{Key: "k1", Value: "v1"},
|
||||
},
|
||||
err: &influxdb.Error{
|
||||
Code: influxdb.EInvalid,
|
||||
Msg: "Operator \"\" is invalid",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
err := c.src.Valid()
|
||||
influxTesting.ErrorsEqual(t, err, c.err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue