mirror of
https://github.com/ncarlier/webhookd.git
synced 2025-04-07 18:16:12 +00:00
refactor(notification): modularize the package
This commit is contained in:
parent
dba7604a43
commit
13194eb0ca
1
main.go
1
main.go
|
@ -14,6 +14,7 @@ import (
|
||||||
configflag "github.com/ncarlier/webhookd/pkg/config/flag"
|
configflag "github.com/ncarlier/webhookd/pkg/config/flag"
|
||||||
"github.com/ncarlier/webhookd/pkg/logger"
|
"github.com/ncarlier/webhookd/pkg/logger"
|
||||||
"github.com/ncarlier/webhookd/pkg/notification"
|
"github.com/ncarlier/webhookd/pkg/notification"
|
||||||
|
_ "github.com/ncarlier/webhookd/pkg/notification/all"
|
||||||
"github.com/ncarlier/webhookd/pkg/server"
|
"github.com/ncarlier/webhookd/pkg/server"
|
||||||
"github.com/ncarlier/webhookd/pkg/version"
|
"github.com/ncarlier/webhookd/pkg/version"
|
||||||
"github.com/ncarlier/webhookd/pkg/worker"
|
"github.com/ncarlier/webhookd/pkg/worker"
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ncarlier/webhookd/pkg/strcase"
|
"github.com/ncarlier/webhookd/pkg/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPParamsToShellVars convert URL values to shell vars.
|
// HTTPParamsToShellVars convert URL values to shell vars.
|
||||||
|
@ -20,7 +20,7 @@ func HTTPParamsToShellVars[T url.Values | http.Header](params T) []string {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
buf.WriteString(strcase.ToSnake(k))
|
buf.WriteString(helper.ToSnake(k))
|
||||||
buf.WriteString("=")
|
buf.WriteString("=")
|
||||||
buf.WriteString(value)
|
buf.WriteString(value)
|
||||||
result = append(result, buf.String())
|
result = append(result, buf.String())
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ncarlier/webhookd/pkg/strcase"
|
"github.com/ncarlier/webhookd/pkg/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bind conf struct tags with flags
|
// Bind conf struct tags with flags
|
||||||
|
@ -103,7 +103,7 @@ func Bind(conf interface{}, prefix string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnvKey(prefix, key string) string {
|
func getEnvKey(prefix, key string) string {
|
||||||
return strcase.ToScreamingSnake(prefix + "_" + key)
|
return helper.ToScreamingSnake(prefix + "_" + key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnvValue(prefix, key, fallback string) string {
|
func getEnvValue(prefix, key, fallback string) string {
|
||||||
|
|
|
@ -23,8 +23,7 @@
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Package strcase converts strings to snake_case or CamelCase
|
package helper
|
||||||
package strcase
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ncarlier/webhookd/pkg/assert"
|
"github.com/ncarlier/webhookd/pkg/assert"
|
||||||
"github.com/ncarlier/webhookd/pkg/strcase"
|
"github.com/ncarlier/webhookd/pkg/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestToSnakeCase(t *testing.T) {
|
func TestToSnakeCase(t *testing.T) {
|
||||||
|
@ -19,7 +19,7 @@ func TestToSnakeCase(t *testing.T) {
|
||||||
{"Hello/world", "hello_world"},
|
{"Hello/world", "hello_world"},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
value := strcase.ToSnake(tc.value)
|
value := helper.ToSnake(tc.value)
|
||||||
assert.Equal(t, tc.expected, value, "")
|
assert.Equal(t, tc.expected, value, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
14
pkg/helper/values.go
Normal file
14
pkg/helper/values.go
Normal 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
|
||||||
|
}
|
|
@ -15,8 +15,8 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ncarlier/webhookd/pkg/helper"
|
||||||
"github.com/ncarlier/webhookd/pkg/logger"
|
"github.com/ncarlier/webhookd/pkg/logger"
|
||||||
"github.com/ncarlier/webhookd/pkg/strcase"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var hookID uint64
|
var hookID uint64
|
||||||
|
@ -54,7 +54,7 @@ func NewHookJob(request *Request) (*Job, error) {
|
||||||
MessageChan: make(chan []byte),
|
MessageChan: make(chan []byte),
|
||||||
status: Idle,
|
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
|
return job, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ncarlier/webhookd/pkg/strcase"
|
"github.com/ncarlier/webhookd/pkg/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logs get hook log with its name and id
|
// Logs get hook log with its name and id
|
||||||
func Logs(id, name, base string) (*os.File, error) {
|
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)
|
files, err := filepath.Glob(logPattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
8
pkg/notification/all/all.go
Normal file
8
pkg/notification/all/all.go
Normal 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"
|
||||||
|
)
|
|
@ -1,4 +1,4 @@
|
||||||
package notification
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -8,7 +8,9 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ncarlier/webhookd/pkg/helper"
|
||||||
"github.com/ncarlier/webhookd/pkg/logger"
|
"github.com/ncarlier/webhookd/pkg/logger"
|
||||||
|
"github.com/ncarlier/webhookd/pkg/notification"
|
||||||
)
|
)
|
||||||
|
|
||||||
type notifPayload struct {
|
type notifPayload struct {
|
||||||
|
@ -18,22 +20,22 @@ type notifPayload struct {
|
||||||
Error error `json:"error,omitempty"`
|
Error error `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPNotifier is able to send a notification to a HTTP endpoint.
|
// httpNotifier is able to send a notification to a HTTP endpoint.
|
||||||
type HTTPNotifier struct {
|
type httpNotifier struct {
|
||||||
URL *url.URL
|
URL *url.URL
|
||||||
PrefixFilter string
|
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())
|
logger.Info.Println("using HTTP notification system: ", uri.String())
|
||||||
return &HTTPNotifier{
|
return &httpNotifier{
|
||||||
URL: uri,
|
URL: uri,
|
||||||
PrefixFilter: getValueOrAlt(uri.Query(), "prefix", "notify:"),
|
PrefixFilter: helper.GetValueOrAlt(uri.Query(), "prefix", "notify:"),
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify send a notification to a HTTP endpoint.
|
// 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)
|
payload := result.Logs(n.PrefixFilter)
|
||||||
if strings.TrimSpace(payload) == "" {
|
if strings.TrimSpace(payload) == "" {
|
||||||
// Nothing to notify, abort
|
// 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())
|
logger.Info.Printf("job %s#%d notification sent to %s\n", result.Name(), result.ID(), n.URL.String())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
notification.Register("http", newHTTPNotifier)
|
||||||
|
notification.Register("https", newHTTPNotifier)
|
||||||
|
}
|
|
@ -1,10 +1,6 @@
|
||||||
package notification
|
package notification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ncarlier/webhookd/pkg/logger"
|
"github.com/ncarlier/webhookd/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,30 +21,8 @@ func Notify(result HookResult) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init creates a notifier regarding the URI.
|
// Init creates the notifier singleton regarding the URI.
|
||||||
func Init(uri string) error {
|
func Init(uri string) (err error) {
|
||||||
if uri == "" {
|
notifier, err = NewNotifier(uri)
|
||||||
return nil
|
return err
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
33
pkg/notification/registry.go
Normal file
33
pkg/notification/registry.go
Normal 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)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package notification
|
package smtp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
@ -10,11 +10,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ncarlier/webhookd/pkg/helper"
|
||||||
"github.com/ncarlier/webhookd/pkg/logger"
|
"github.com/ncarlier/webhookd/pkg/logger"
|
||||||
|
"github.com/ncarlier/webhookd/pkg/notification"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SMTPNotifier is able to send notification to a email destination.
|
// smtpNotifier is able to send notification to a email destination.
|
||||||
type SMTPNotifier struct {
|
type smtpNotifier struct {
|
||||||
Host string
|
Host string
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
|
@ -25,22 +27,22 @@ type SMTPNotifier struct {
|
||||||
PrefixFilter string
|
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)
|
logger.Info.Println("using SMTP notification system:", uri.Opaque)
|
||||||
q := uri.Query()
|
q := uri.Query()
|
||||||
return &SMTPNotifier{
|
return &smtpNotifier{
|
||||||
Host: getValueOrAlt(q, "smtp", "localhost:25"),
|
Host: helper.GetValueOrAlt(q, "smtp", "localhost:25"),
|
||||||
Username: getValueOrAlt(q, "username", ""),
|
Username: helper.GetValueOrAlt(q, "username", ""),
|
||||||
Password: getValueOrAlt(q, "password", ""),
|
Password: helper.GetValueOrAlt(q, "password", ""),
|
||||||
Conn: getValueOrAlt(q, "conn", "plain"),
|
Conn: helper.GetValueOrAlt(q, "conn", "plain"),
|
||||||
From: getValueOrAlt(q, "from", "noreply@nunux.org"),
|
From: helper.GetValueOrAlt(q, "from", "noreply@nunux.org"),
|
||||||
To: uri.Opaque,
|
To: uri.Opaque,
|
||||||
Subject: getValueOrAlt(uri.Query(), "subject", "[whd-notification] {name}#{id} {status}"),
|
Subject: helper.GetValueOrAlt(uri.Query(), "subject", "[whd-notification] {name}#{id} {status}"),
|
||||||
PrefixFilter: getValueOrAlt(uri.Query(), "prefix", "notify:"),
|
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
|
// Get email body
|
||||||
body := result.Logs(n.PrefixFilter)
|
body := result.Logs(n.PrefixFilter)
|
||||||
if strings.TrimSpace(body) == "" {
|
if strings.TrimSpace(body) == "" {
|
||||||
|
@ -66,7 +68,7 @@ func (n *SMTPNotifier) buildEmailPayload(result HookResult) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify send a notification to a email destination.
|
// 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)
|
hostname, _, _ := net.SplitHostPort(n.Host)
|
||||||
payload := n.buildEmailPayload(result)
|
payload := n.buildEmailPayload(result)
|
||||||
if payload == "" {
|
if payload == "" {
|
||||||
|
@ -132,9 +134,13 @@ func (n *SMTPNotifier) Notify(result HookResult) error {
|
||||||
return client.Quit()
|
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(template, "{name}", result.Name())
|
||||||
subject = strings.ReplaceAll(subject, "{id}", strconv.FormatUint(uint64(result.ID()), 10))
|
subject = strings.ReplaceAll(subject, "{id}", strconv.FormatUint(uint64(result.ID()), 10))
|
||||||
subject = strings.ReplaceAll(subject, "{status}", result.StatusLabel())
|
subject = strings.ReplaceAll(subject, "{status}", result.StatusLabel())
|
||||||
return subject
|
return subject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
notification.Register("mailto", newSMTPNotifier)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user