Skip to content

Commit 4e98739

Browse files
committed
Introduce notifications Severity type
Moved from Icinga Notifications with some enhancements made.
1 parent b5ef61f commit 4e98739

File tree

4 files changed

+269
-0
lines changed

4 files changed

+269
-0
lines changed

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ require (
3333
golang.org/x/sys v0.26.0 // indirect
3434
gopkg.in/yaml.v3 v3.0.1 // indirect
3535
)
36+
37+
tool (
38+
golang.org/x/tools/cmd/stringer
39+
)

notifications/event/severity.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//go:generate go tool stringer -linecomment -type Severity -output severity_string.go
2+
3+
package event
4+
5+
import (
6+
"database/sql/driver"
7+
"encoding/json"
8+
"fmt"
9+
)
10+
11+
// Severity represents the severity level of an event in Icinga notifications.
12+
// It is an integer type with predefined constants for different severity levels.
13+
type Severity uint8
14+
15+
const (
16+
SeverityNone Severity = iota // none
17+
18+
SeverityOK // ok
19+
SeverityDebug // debug
20+
SeverityInfo // info
21+
SeverityNotice // notice
22+
SeverityWarning // warning
23+
SeverityErr // err
24+
SeverityCrit // crit
25+
SeverityAlert // alert
26+
SeverityEmerg // emerg
27+
28+
severityMax // internal
29+
)
30+
31+
// MarshalJSON implements the [json.Marshaler] interface for Severity.
32+
func (s Severity) MarshalJSON() ([]byte, error) {
33+
if s != SeverityNone {
34+
return json.Marshal(s.String())
35+
} else {
36+
return json.Marshal(nil)
37+
}
38+
}
39+
40+
// UnmarshalJSON implements the [json.Unmarshaler] interface for Severity.
41+
func (s *Severity) UnmarshalJSON(data []byte) error {
42+
if string(data) == "null" {
43+
*s = SeverityNone
44+
return nil
45+
}
46+
47+
var severityStr string
48+
if err := json.Unmarshal(data, &severityStr); err != nil {
49+
return err
50+
}
51+
52+
severity, err := ParseSeverity(severityStr)
53+
if err != nil {
54+
return err
55+
}
56+
57+
*s = severity
58+
return nil
59+
}
60+
61+
// Scan implements the [sql.Scanner] interface for Severity.
62+
// Supports SQL NULL values.
63+
func (s *Severity) Scan(src any) error {
64+
if src == nil {
65+
*s = SeverityNone
66+
return nil
67+
}
68+
69+
var severityStr string
70+
switch val := src.(type) {
71+
case string:
72+
severityStr = val
73+
case []byte:
74+
severityStr = string(val)
75+
default:
76+
return fmt.Errorf("cannot scan severity from type %T", src)
77+
}
78+
79+
severity, err := ParseSeverity(severityStr)
80+
if err != nil {
81+
return err
82+
}
83+
84+
*s = severity
85+
return nil
86+
}
87+
88+
// Value implements the [driver.Valuer] interface for Severity.
89+
func (s Severity) Value() (driver.Value, error) {
90+
if s != SeverityNone {
91+
return s.String(), nil
92+
}
93+
return nil, nil // Return nil for SeverityNone or invalid values
94+
}
95+
96+
// ParseSeverity parses a string representation of a severity level and returns the corresponding Severity value.
97+
// If the string does not match any known severity, it returns an error indicating the unknown severity.
98+
func ParseSeverity(name string) (Severity, error) {
99+
for s := SeverityNone + 1; s < severityMax; s++ {
100+
if s.String() == name {
101+
return s, nil
102+
}
103+
}
104+
105+
return SeverityNone, fmt.Errorf("unknown severity %q", name)
106+
}

notifications/event/severity_string.go

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package event
2+
3+
import (
4+
"encoding/json"
5+
"github.com/stretchr/testify/assert"
6+
"github.com/stretchr/testify/require"
7+
"testing"
8+
)
9+
10+
func TestSeverity(t *testing.T) {
11+
t.Parallel()
12+
13+
t.Run("MarshalJson", func(t *testing.T) {
14+
t.Parallel()
15+
16+
tests := []struct {
17+
severity Severity
18+
expected string
19+
}{
20+
{SeverityNone, "null"},
21+
{SeverityOK, `"ok"`},
22+
{SeverityDebug, `"debug"`},
23+
{SeverityInfo, `"info"`},
24+
{SeverityNotice, `"notice"`},
25+
{SeverityWarning, `"warning"`},
26+
{SeverityErr, `"err"`},
27+
{SeverityCrit, `"crit"`},
28+
{SeverityAlert, `"alert"`},
29+
{SeverityEmerg, `"emerg"`},
30+
}
31+
32+
for _, test := range tests {
33+
data, err := json.Marshal(test.severity)
34+
require.NoError(t, err)
35+
assert.Equal(t, test.expected, string(data))
36+
}
37+
})
38+
39+
t.Run("UnmarshalJson", func(t *testing.T) {
40+
t.Parallel()
41+
42+
tests := []struct {
43+
input string
44+
expected Severity
45+
wantErr bool
46+
}{
47+
{`null`, SeverityNone, false},
48+
{`"ok"`, SeverityOK, false},
49+
{`"debug"`, SeverityDebug, false},
50+
{`"info"`, SeverityInfo, false},
51+
{`"notice"`, SeverityNotice, false},
52+
{`"warning"`, SeverityWarning, false},
53+
{`"err"`, SeverityErr, false},
54+
{`"crit"`, SeverityCrit, false},
55+
{`"alert"`, SeverityAlert, false},
56+
{`"emerg"`, SeverityEmerg, false},
57+
{`"invalid"`, SeverityNone, true}, // Invalid severity
58+
}
59+
60+
for _, test := range tests {
61+
var severity Severity
62+
err := json.Unmarshal([]byte(test.input), &severity)
63+
assert.Equalf(t, test.wantErr, err != nil, "expected error: %v, got: %v", test.wantErr, err)
64+
assert.Equal(t, test.expected, severity)
65+
}
66+
})
67+
68+
t.Run("Scan", func(t *testing.T) {
69+
t.Parallel()
70+
71+
tests := []struct {
72+
input any
73+
expected Severity
74+
wantErr bool
75+
}{
76+
{nil, SeverityNone, false},
77+
{"ok", SeverityOK, false},
78+
{"debug", SeverityDebug, false},
79+
{"info", SeverityInfo, false},
80+
{"notice", SeverityNotice, false},
81+
{"warning", SeverityWarning, false},
82+
{"err", SeverityErr, false},
83+
{"crit", SeverityCrit, false},
84+
{"alert", SeverityAlert, false},
85+
{"emerg", SeverityEmerg, false},
86+
{123, SeverityNone, true}, // Invalid type
87+
{[]byte("invalid"), SeverityNone, true}, // Invalid severity
88+
}
89+
90+
for _, test := range tests {
91+
var severity Severity
92+
if err := severity.Scan(test.input); test.wantErr {
93+
assert.Error(t, err)
94+
} else {
95+
assert.NoError(t, err)
96+
assert.Equal(t, test.expected, severity)
97+
}
98+
}
99+
})
100+
101+
t.Run("Value", func(t *testing.T) {
102+
t.Parallel()
103+
104+
tests := []struct {
105+
severity Severity
106+
expected any
107+
}{
108+
{SeverityNone, nil},
109+
{SeverityOK, "ok"},
110+
{SeverityDebug, "debug"},
111+
{SeverityInfo, "info"},
112+
{SeverityNotice, "notice"},
113+
{SeverityWarning, "warning"},
114+
{SeverityErr, "err"},
115+
{SeverityCrit, "crit"},
116+
{SeverityAlert, "alert"},
117+
{SeverityEmerg, "emerg"},
118+
}
119+
120+
for _, test := range tests {
121+
value, err := test.severity.Value()
122+
require.NoError(t, err)
123+
assert.Equal(t, test.expected, value)
124+
}
125+
})
126+
}

0 commit comments

Comments
 (0)