chore: cleans up types for notificaitons (#4367)

This commit is contained in:
Amir Raminfar
2026-01-21 10:18:48 -08:00
committed by GitHub
parent 9f6d24523c
commit af0407157b
7 changed files with 60 additions and 51 deletions
+3 -2
View File
@@ -14,6 +14,7 @@ import (
"github.com/amir20/dozzle/internal/notification"
"github.com/amir20/dozzle/internal/notification/dispatcher"
"github.com/amir20/dozzle/internal/releases"
"github.com/amir20/dozzle/types"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
)
@@ -195,7 +196,7 @@ func (r *mutationResolver) PreviewExpression(ctx context.Context, input model.Pr
// Compile and test container expression
var containerProgram *vm.Program
if input.ContainerExpression != "" {
program, err := expr.Compile(input.ContainerExpression, expr.Env(notification.Container{}))
program, err := expr.Compile(input.ContainerExpression, expr.Env(types.NotificationContainer{}))
if err != nil {
errStr := err.Error()
result.ContainerError = &errStr
@@ -207,7 +208,7 @@ func (r *mutationResolver) PreviewExpression(ctx context.Context, input model.Pr
// Compile and test log expression
var logProgram *vm.Program
if input.LogExpression != nil && *input.LogExpression != "" {
program, err := expr.Compile(*input.LogExpression, expr.Env(notification.Log{}))
program, err := expr.Compile(*input.LogExpression, expr.Env(types.NotificationLog{}))
if err != nil {
errStr := err.Error()
result.LogError = &errStr
@@ -2,10 +2,12 @@ package dispatcher
import (
"context"
"github.com/amir20/dozzle/types"
)
// Dispatcher is responsible for sending notifications to external systems
type Dispatcher interface {
// Send sends a notification to the configured destination
Send(ctx context.Context, notification any) error
Send(ctx context.Context, notification types.Notification) error
}
+2 -1
View File
@@ -10,6 +10,7 @@ import (
"text/template"
"time"
"github.com/amir20/dozzle/types"
"github.com/rs/zerolog/log"
)
@@ -46,7 +47,7 @@ func NewWebhookDispatcher(name, url, templateStr string) (*WebhookDispatcher, er
}
// Send sends a notification to the webhook URL
func (w *WebhookDispatcher) Send(ctx context.Context, notification any) error {
func (w *WebhookDispatcher) Send(ctx context.Context, notification types.Notification) error {
var payload []byte
var err error
+11 -10
View File
@@ -10,6 +10,7 @@ import (
"github.com/amir20/dozzle/internal/container"
"github.com/amir20/dozzle/internal/notification/dispatcher"
"github.com/amir20/dozzle/types"
"github.com/expr-lang/expr"
"github.com/puzpuzpuz/xsync/v4"
"github.com/rs/zerolog/log"
@@ -75,7 +76,7 @@ func (m *Manager) AddSubscription(sub *Subscription) error {
// Compile container expression if provided
if sub.ContainerExpression != "" {
program, err := expr.Compile(sub.ContainerExpression, expr.Env(Container{}))
program, err := expr.Compile(sub.ContainerExpression, expr.Env(types.NotificationContainer{}))
if err != nil {
return fmt.Errorf("failed to compile container expression: %w", err)
}
@@ -84,7 +85,7 @@ func (m *Manager) AddSubscription(sub *Subscription) error {
// Compile log expression if provided
if sub.LogExpression != "" {
program, err := expr.Compile(sub.LogExpression, expr.Env(Log{}))
program, err := expr.Compile(sub.LogExpression, expr.Env(types.NotificationLog{}))
if err != nil {
return fmt.Errorf("failed to compile log expression: %w", err)
}
@@ -122,7 +123,7 @@ func (m *Manager) RemoveSubscription(id int) {
func (m *Manager) ReplaceSubscription(sub *Subscription) error {
// Compile container expression if provided
if sub.ContainerExpression != "" {
program, err := expr.Compile(sub.ContainerExpression, expr.Env(Container{}))
program, err := expr.Compile(sub.ContainerExpression, expr.Env(types.NotificationContainer{}))
if err != nil {
return fmt.Errorf("failed to compile container expression: %w", err)
}
@@ -131,7 +132,7 @@ func (m *Manager) ReplaceSubscription(sub *Subscription) error {
// Compile log expression if provided
if sub.LogExpression != "" {
program, err := expr.Compile(sub.LogExpression, expr.Env(Log{}))
program, err := expr.Compile(sub.LogExpression, expr.Env(types.NotificationLog{}))
if err != nil {
return fmt.Errorf("failed to compile log expression: %w", err)
}
@@ -196,7 +197,7 @@ func (m *Manager) UpdateSubscription(id int, updates map[string]any) error {
}
case "containerExpression":
if exprStr, ok := value.(string); ok {
program, err := expr.Compile(exprStr, expr.Env(Container{}))
program, err := expr.Compile(exprStr, expr.Env(types.NotificationContainer{}))
if err != nil {
updateErr = fmt.Errorf("failed to compile container expression: %w", err)
return nil, xsync.CancelOp
@@ -207,7 +208,7 @@ func (m *Manager) UpdateSubscription(id int, updates map[string]any) error {
case "logExpression":
if exprStr, ok := value.(string); ok {
if exprStr != "" {
program, err := expr.Compile(exprStr, expr.Env(Log{}))
program, err := expr.Compile(exprStr, expr.Env(types.NotificationLog{}))
if err != nil {
updateErr = fmt.Errorf("failed to compile log expression: %w", err)
return nil, xsync.CancelOp
@@ -354,7 +355,7 @@ func (m *Manager) processLogEvent(logEvent *container.LogEvent) {
log.Debug().Str("containerID", notificationContainer.ID).Interface("log", notificationLog.Message).Msg("Matched subscription")
// Create notification
notification := Notification{
notification := types.Notification{
ID: fmt.Sprintf("%s-%d", c.ID, time.Now().UnixNano()),
Container: notificationContainer,
Log: notificationLog,
@@ -370,7 +371,7 @@ func (m *Manager) processLogEvent(logEvent *container.LogEvent) {
}
// sendNotification sends a notification using the dispatcher
func (m *Manager) sendNotification(d dispatcher.Dispatcher, notification Notification, id int) {
func (m *Manager) sendNotification(d dispatcher.Dispatcher, notification types.Notification, id int) {
ctx, cancel := context.WithTimeout(m.ctx, 30*time.Second)
defer cancel()
@@ -454,7 +455,7 @@ func (m *Manager) LoadConfig(r io.Reader) error {
func (m *Manager) loadSubscription(sub *Subscription) error {
// Compile container expression if provided
if sub.ContainerExpression != "" {
program, err := expr.Compile(sub.ContainerExpression, expr.Env(Container{}))
program, err := expr.Compile(sub.ContainerExpression, expr.Env(types.NotificationContainer{}))
if err != nil {
return fmt.Errorf("failed to compile container expression: %w", err)
}
@@ -463,7 +464,7 @@ func (m *Manager) loadSubscription(sub *Subscription) error {
// Compile log expression if provided
if sub.LogExpression != "" {
program, err := expr.Compile(sub.LogExpression, expr.Env(Log{}))
program, err := expr.Compile(sub.LogExpression, expr.Env(types.NotificationLog{}))
if err != nil {
return fmt.Errorf("failed to compile log expression: %w", err)
}
+9 -37
View File
@@ -6,33 +6,15 @@ import (
"time"
"github.com/amir20/dozzle/internal/container"
"github.com/amir20/dozzle/types"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/puzpuzpuz/xsync/v4"
)
// Notification represents a notification event that can be filtered and sent
type Notification struct {
ID string `json:"id"`
Container Container `json:"container"`
Log Log `json:"log"`
Timestamp time.Time `json:"timestamp"`
}
// Container represents a simplified container structure optimized for expr filtering
type Container struct {
ID string `json:"id" expr:"id"`
Name string `json:"name" expr:"name"`
Image string `json:"image" expr:"image"`
State string `json:"state" expr:"state"`
Health string `json:"health" expr:"health"`
Host string `json:"host" expr:"host"`
Labels map[string]string `json:"labels" expr:"labels"`
}
// FromContainerModel converts internal container.Container to notification.Container
func FromContainerModel(c container.Container) Container {
return Container{
// FromContainerModel converts internal container.Container to types.NotificationContainer
func FromContainerModel(c container.Container) types.NotificationContainer {
return types.NotificationContainer{
ID: c.ID,
Name: c.Name,
Image: c.Image,
@@ -43,21 +25,11 @@ func FromContainerModel(c container.Container) Container {
}
}
// Log represents a log entry with message that can be string or object
type Log struct {
ID uint32 `json:"id" expr:"id"`
Message any `json:"message" expr:"message"` // string for simple/grouped logs, map for complex logs
Timestamp int64 `json:"timestamp" expr:"timestamp"`
Level string `json:"level" expr:"level"`
Stream string `json:"stream" expr:"stream"`
Type string `json:"type" expr:"type"`
}
// FromLogEvent converts container.LogEvent to notification.Log
func FromLogEvent(l container.LogEvent) Log {
// FromLogEvent converts container.LogEvent to types.NotificationLog
func FromLogEvent(l container.LogEvent) types.NotificationLog {
message := extractMessage(l)
return Log{
return types.NotificationLog{
ID: l.Id,
Message: message,
Timestamp: l.Timestamp,
@@ -138,7 +110,7 @@ type Config struct {
}
// MatchesContainer checks if a container matches this subscription's container filter
func (s *Subscription) MatchesContainer(c Container) bool {
func (s *Subscription) MatchesContainer(c types.NotificationContainer) bool {
if s.ContainerProgram == nil {
return false
}
@@ -153,7 +125,7 @@ func (s *Subscription) MatchesContainer(c Container) bool {
}
// MatchesLog checks if a log matches this subscription's log filter
func (s *Subscription) MatchesLog(l Log) bool {
func (s *Subscription) MatchesLog(l types.NotificationLog) bool {
if s.LogProgram == nil {
return false
}
View File
+32
View File
@@ -0,0 +1,32 @@
package types
import "time"
// Notification represents a notification event that can be filtered and sent
type Notification struct {
ID string `json:"id"`
Container NotificationContainer `json:"container"`
Log NotificationLog `json:"log"`
Timestamp time.Time `json:"timestamp"`
}
// NotificationContainer represents a simplified container structure for notifications
type NotificationContainer struct {
ID string `json:"id" expr:"id"`
Name string `json:"name" expr:"name"`
Image string `json:"image" expr:"image"`
State string `json:"state" expr:"state"`
Health string `json:"health" expr:"health"`
Host string `json:"host" expr:"host"`
Labels map[string]string `json:"labels" expr:"labels"`
}
// NotificationLog represents a log entry with message that can be string or object
type NotificationLog struct {
ID uint32 `json:"id" expr:"id"`
Message any `json:"message" expr:"message"` // string for simple/grouped logs, map for complex logs
Timestamp int64 `json:"timestamp" expr:"timestamp"`
Level string `json:"level" expr:"level"`
Stream string `json:"stream" expr:"stream"`
Type string `json:"type" expr:"type"`
}