refactor(notification): modularize the package

This commit is contained in:
Nicolas Carlier 2023-01-02 22:45:15 +00:00
parent dba7604a43
commit 13194eb0ca
13 changed files with 108 additions and 66 deletions

View File

@ -14,6 +14,7 @@ import (
configflag "github.com/ncarlier/webhookd/pkg/config/flag"
"github.com/ncarlier/webhookd/pkg/logger"
"github.com/ncarlier/webhookd/pkg/notification"
_ "github.com/ncarlier/webhookd/pkg/notification/all"
"github.com/ncarlier/webhookd/pkg/server"
"github.com/ncarlier/webhookd/pkg/version"
"github.com/ncarlier/webhookd/pkg/worker"

View File

@ -8,7 +8,7 @@ import (
"strings"
"time"
"github.com/ncarlier/webhookd/pkg/strcase"
"github.com/ncarlier/webhookd/pkg/helper"
)
// HTTPParamsToShellVars convert URL values to shell vars.
@ -20,7 +20,7 @@ func HTTPParamsToShellVars[T url.Values | http.Header](params T) []string {
if err != nil {
continue
}
buf.WriteString(strcase.ToSnake(k))
buf.WriteString(helper.ToSnake(k))
buf.WriteString("=")
buf.WriteString(value)
result = append(result, buf.String())

View File

@ -9,7 +9,7 @@ import (
"strings"
"time"
"github.com/ncarlier/webhookd/pkg/strcase"
"github.com/ncarlier/webhookd/pkg/helper"
)
// Bind conf struct tags with flags
@ -103,7 +103,7 @@ func Bind(conf interface{}, prefix string) error {
}
func getEnvKey(prefix, key string) string {
return strcase.ToScreamingSnake(prefix + "_" + key)
return helper.ToScreamingSnake(prefix + "_" + key)
}
func getEnvValue(prefix, key, fallback string) string {

View File

@ -23,8 +23,7 @@
* SOFTWARE.
*/
// Package strcase converts strings to snake_case or CamelCase
package strcase
package helper
import (
"strings"

View File

@ -4,7 +4,7 @@ import (
"testing"
"github.com/ncarlier/webhookd/pkg/assert"
"github.com/ncarlier/webhookd/pkg/strcase"
"github.com/ncarlier/webhookd/pkg/helper"
)
func TestToSnakeCase(t *testing.T) {
@ -19,7 +19,7 @@ func TestToSnakeCase(t *testing.T) {
{"Hello/world", "hello_world"},
}
for _, tc := range testCases {
value := strcase.ToSnake(tc.value)
value := helper.ToSnake(tc.value)
assert.Equal(t, tc.expected, value, "")
}
}

14
pkg/helper/values.go Normal file
View File

@ -0,0 +1,14 @@
package helper
import (
"net/url"
"strings"
)
// GetValueOrAlt get value or alt
func GetValueOrAlt(values url.Values, key, alt string) string {
if val, ok := values[key]; ok {
return strings.Join(val[:], ",")
}
return alt
}

View File

@ -15,8 +15,8 @@ import (
"syscall"
"time"
"github.com/ncarlier/webhookd/pkg/helper"
"github.com/ncarlier/webhookd/pkg/logger"
"github.com/ncarlier/webhookd/pkg/strcase"
)
var hookID uint64
@ -54,7 +54,7 @@ func NewHookJob(request *Request) (*Job, error) {
MessageChan: make(chan []byte),
status: Idle,
}
job.logFilename = path.Join(request.OutputDir, fmt.Sprintf("%s_%d_%s.txt", strcase.ToSnake(job.name), job.id, time.Now().Format("20060102_1504")))
job.logFilename = path.Join(request.OutputDir, fmt.Sprintf("%s_%d_%s.txt", helper.ToSnake(job.name), job.id, time.Now().Format("20060102_1504")))
return job, nil
}

View File

@ -6,12 +6,12 @@ import (
"path"
"path/filepath"
"github.com/ncarlier/webhookd/pkg/strcase"
"github.com/ncarlier/webhookd/pkg/helper"
)
// Logs get hook log with its name and id
func Logs(id, name, base string) (*os.File, error) {
logPattern := path.Join(base, fmt.Sprintf("%s_%s_*.txt", strcase.ToSnake(name), id))
logPattern := path.Join(base, fmt.Sprintf("%s_%s_*.txt", helper.ToSnake(name), id))
files, err := filepath.Glob(logPattern)
if err != nil {
return nil, err

View File

@ -0,0 +1,8 @@
package all
import (
// activate HTTP notifier
_ "github.com/ncarlier/webhookd/pkg/notification/http"
// activate SMTP notifier
_ "github.com/ncarlier/webhookd/pkg/notification/smtp"
)

View File

@ -1,4 +1,4 @@
package notification
package http
import (
"bytes"
@ -8,7 +8,9 @@ import (
"strconv"
"strings"
"github.com/ncarlier/webhookd/pkg/helper"
"github.com/ncarlier/webhookd/pkg/logger"
"github.com/ncarlier/webhookd/pkg/notification"
)
type notifPayload struct {
@ -18,22 +20,22 @@ type notifPayload struct {
Error error `json:"error,omitempty"`
}
// HTTPNotifier is able to send a notification to a HTTP endpoint.
type HTTPNotifier struct {
// httpNotifier is able to send a notification to a HTTP endpoint.
type httpNotifier struct {
URL *url.URL
PrefixFilter string
}
func newHTTPNotifier(uri *url.URL) *HTTPNotifier {
func newHTTPNotifier(uri *url.URL) (notification.Notifier, error) {
logger.Info.Println("using HTTP notification system: ", uri.String())
return &HTTPNotifier{
return &httpNotifier{
URL: uri,
PrefixFilter: getValueOrAlt(uri.Query(), "prefix", "notify:"),
}
PrefixFilter: helper.GetValueOrAlt(uri.Query(), "prefix", "notify:"),
}, nil
}
// Notify send a notification to a HTTP endpoint.
func (n *HTTPNotifier) Notify(result HookResult) error {
func (n *httpNotifier) Notify(result notification.HookResult) error {
payload := result.Logs(n.PrefixFilter)
if strings.TrimSpace(payload) == "" {
// Nothing to notify, abort
@ -66,3 +68,8 @@ func (n *HTTPNotifier) Notify(result HookResult) error {
logger.Info.Printf("job %s#%d notification sent to %s\n", result.Name(), result.ID(), n.URL.String())
return nil
}
func init() {
notification.Register("http", newHTTPNotifier)
notification.Register("https", newHTTPNotifier)
}

View File

@ -1,10 +1,6 @@
package notification
import (
"fmt"
"net/url"
"strings"
"github.com/ncarlier/webhookd/pkg/logger"
)
@ -25,30 +21,8 @@ func Notify(result HookResult) {
}
}
// Init creates a notifier regarding the URI.
func Init(uri string) error {
if uri == "" {
return nil
}
u, err := url.Parse(uri)
if err != nil {
return fmt.Errorf("invalid notification URL: %s", uri)
}
switch u.Scheme {
case "mailto":
notifier = newSMTPNotifier(u)
case "http", "https":
notifier = newHTTPNotifier(u)
default:
return fmt.Errorf("unable to create notifier: %v", err)
}
return nil
}
func getValueOrAlt(values url.Values, key, alt string) string {
if val, ok := values[key]; ok {
return strings.Join(val[:], ",")
}
return alt
// Init creates the notifier singleton regarding the URI.
func Init(uri string) (err error) {
notifier, err = NewNotifier(uri)
return err
}

View File

@ -0,0 +1,33 @@
package notification
import (
"fmt"
"net/url"
)
// NotifierCreator function for create a notifier
type NotifierCreator func(uri *url.URL) (Notifier, error)
// Registry of all Notifiers
var registry = map[string]NotifierCreator{}
// Register a Notifier to the registry
func Register(scheme string, creator NotifierCreator) {
registry[scheme] = creator
}
// NewNotifier create new Notifier
func NewNotifier(uri string) (Notifier, error) {
if uri == "" {
return nil, nil
}
u, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("invalid notification URL: %s", uri)
}
creator, ok := registry[u.Scheme]
if !ok {
return nil, fmt.Errorf("unsupported notification scheme: %s", u.Scheme)
}
return creator(u)
}

View File

@ -1,4 +1,4 @@
package notification
package smtp
import (
"crypto/tls"
@ -10,11 +10,13 @@ import (
"strings"
"time"
"github.com/ncarlier/webhookd/pkg/helper"
"github.com/ncarlier/webhookd/pkg/logger"
"github.com/ncarlier/webhookd/pkg/notification"
)
// SMTPNotifier is able to send notification to a email destination.
type SMTPNotifier struct {
// smtpNotifier is able to send notification to a email destination.
type smtpNotifier struct {
Host string
Username string
Password string
@ -25,22 +27,22 @@ type SMTPNotifier struct {
PrefixFilter string
}
func newSMTPNotifier(uri *url.URL) *SMTPNotifier {
func newSMTPNotifier(uri *url.URL) (notification.Notifier, error) {
logger.Info.Println("using SMTP notification system:", uri.Opaque)
q := uri.Query()
return &SMTPNotifier{
Host: getValueOrAlt(q, "smtp", "localhost:25"),
Username: getValueOrAlt(q, "username", ""),
Password: getValueOrAlt(q, "password", ""),
Conn: getValueOrAlt(q, "conn", "plain"),
From: getValueOrAlt(q, "from", "noreply@nunux.org"),
return &smtpNotifier{
Host: helper.GetValueOrAlt(q, "smtp", "localhost:25"),
Username: helper.GetValueOrAlt(q, "username", ""),
Password: helper.GetValueOrAlt(q, "password", ""),
Conn: helper.GetValueOrAlt(q, "conn", "plain"),
From: helper.GetValueOrAlt(q, "from", "noreply@nunux.org"),
To: uri.Opaque,
Subject: getValueOrAlt(uri.Query(), "subject", "[whd-notification] {name}#{id} {status}"),
PrefixFilter: getValueOrAlt(uri.Query(), "prefix", "notify:"),
}
Subject: helper.GetValueOrAlt(uri.Query(), "subject", "[whd-notification] {name}#{id} {status}"),
PrefixFilter: helper.GetValueOrAlt(uri.Query(), "prefix", "notify:"),
}, nil
}
func (n *SMTPNotifier) buildEmailPayload(result HookResult) string {
func (n *smtpNotifier) buildEmailPayload(result notification.HookResult) string {
// Get email body
body := result.Logs(n.PrefixFilter)
if strings.TrimSpace(body) == "" {
@ -66,7 +68,7 @@ func (n *SMTPNotifier) buildEmailPayload(result HookResult) string {
}
// Notify send a notification to a email destination.
func (n *SMTPNotifier) Notify(result HookResult) error {
func (n *smtpNotifier) Notify(result notification.HookResult) error {
hostname, _, _ := net.SplitHostPort(n.Host)
payload := n.buildEmailPayload(result)
if payload == "" {
@ -132,9 +134,13 @@ func (n *SMTPNotifier) Notify(result HookResult) error {
return client.Quit()
}
func buildSubject(template string, result HookResult) string {
func buildSubject(template string, result notification.HookResult) string {
subject := strings.ReplaceAll(template, "{name}", result.Name())
subject = strings.ReplaceAll(subject, "{id}", strconv.FormatUint(uint64(result.ID()), 10))
subject = strings.ReplaceAll(subject, "{status}", result.StatusLabel())
return subject
}
func init() {
notification.Register("mailto", newSMTPNotifier)
}