diff --git a/cli/deps.go b/cli/deps.go index b5b7add5..091e028c 100644 --- a/cli/deps.go +++ b/cli/deps.go @@ -165,30 +165,26 @@ func InitDeps( routerRegistry := map[string]notification.Router{ notification.RouterReceiver: notification.NewRouterReceiverService( notificationDeps, - notifierRegistry, ), notification.RouterSubscriber: notification.NewRouterSubscriberService( notificationDeps, - notifierRegistry, - ), - } - - dispatchServiceRegistry := map[string]notification.Dispatcher{ - notification.DispatchKindBulkNotification: notification.NewDispatchBulkNotificationService( - notificationDeps, - notifierRegistry, - routerRegistry, - ), - notification.DispatchKindSingleNotification: notification.NewDispatchSingleNotificationService( - notificationDeps, - notifierRegistry, - routerRegistry, ), } notificationService := notification.NewService( notificationDeps, - dispatchServiceRegistry, + routerRegistry, + notifierRegistry, + // notification.NewDispatchBulkNotificationService( + // notificationDeps, + // notifierRegistry, + // routerRegistry, + // ), + // notification.NewDispatchSingleNotificationService( + // notificationDeps, + // notifierRegistry, + // routerRegistry, + // ), ) alertService := alert.NewService( diff --git a/cli/server.go b/cli/server.go index b7163cd5..5d65e6e0 100644 --- a/cli/server.go +++ b/cli/server.go @@ -127,10 +127,6 @@ func StartServer(ctx context.Context, cfg config.Config) error { if err != nil { return err } - - // propagating the config explicitly - cfg.Notification.SubscriptionV2Enabled = cfg.Service.SubscriptionV2Enabled - defer cleanUpTelemetry() var queue, dlq notification.Queuer diff --git a/core/alert/mocks/alert_repository.go b/core/alert/mocks/alert_repository.go index 688abe8c..0cf25bea 100644 --- a/core/alert/mocks/alert_repository.go +++ b/core/alert/mocks/alert_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/alert/mocks/alert_transformer.go b/core/alert/mocks/alert_transformer.go index 30ee1524..c3a53ab9 100644 --- a/core/alert/mocks/alert_transformer.go +++ b/core/alert/mocks/alert_transformer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/alert/mocks/log_service.go b/core/alert/mocks/log_service.go index e4d6bfe1..72a651ea 100644 --- a/core/alert/mocks/log_service.go +++ b/core/alert/mocks/log_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/alert/mocks/notification_service.go b/core/alert/mocks/notification_service.go index 0f726a9d..08776c13 100644 --- a/core/alert/mocks/notification_service.go +++ b/core/alert/mocks/notification_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -22,9 +22,9 @@ func (_m *NotificationService) EXPECT() *NotificationService_Expecter { return &NotificationService_Expecter{mock: &_m.Mock} } -// Dispatch provides a mock function with given fields: _a0, _a1, _a2 -func (_m *NotificationService) Dispatch(_a0 context.Context, _a1 []notification.Notification, _a2 string) ([]string, error) { - ret := _m.Called(_a0, _a1, _a2) +// Dispatch provides a mock function with given fields: _a0, _a1 +func (_m *NotificationService) Dispatch(_a0 context.Context, _a1 []notification.Notification) ([]string, error) { + ret := _m.Called(_a0, _a1) if len(ret) == 0 { panic("no return value specified for Dispatch") @@ -32,19 +32,19 @@ func (_m *NotificationService) Dispatch(_a0 context.Context, _a1 []notification. var r0 []string var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []notification.Notification, string) ([]string, error)); ok { - return rf(_a0, _a1, _a2) + if rf, ok := ret.Get(0).(func(context.Context, []notification.Notification) ([]string, error)); ok { + return rf(_a0, _a1) } - if rf, ok := ret.Get(0).(func(context.Context, []notification.Notification, string) []string); ok { - r0 = rf(_a0, _a1, _a2) + if rf, ok := ret.Get(0).(func(context.Context, []notification.Notification) []string); ok { + r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) } } - if rf, ok := ret.Get(1).(func(context.Context, []notification.Notification, string) error); ok { - r1 = rf(_a0, _a1, _a2) + if rf, ok := ret.Get(1).(func(context.Context, []notification.Notification) error); ok { + r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) } @@ -60,14 +60,13 @@ type NotificationService_Dispatch_Call struct { // Dispatch is a helper method to define mock.On call // - _a0 context.Context // - _a1 []notification.Notification -// - _a2 string -func (_e *NotificationService_Expecter) Dispatch(_a0 interface{}, _a1 interface{}, _a2 interface{}) *NotificationService_Dispatch_Call { - return &NotificationService_Dispatch_Call{Call: _e.mock.On("Dispatch", _a0, _a1, _a2)} +func (_e *NotificationService_Expecter) Dispatch(_a0 interface{}, _a1 interface{}) *NotificationService_Dispatch_Call { + return &NotificationService_Dispatch_Call{Call: _e.mock.On("Dispatch", _a0, _a1)} } -func (_c *NotificationService_Dispatch_Call) Run(run func(_a0 context.Context, _a1 []notification.Notification, _a2 string)) *NotificationService_Dispatch_Call { +func (_c *NotificationService_Dispatch_Call) Run(run func(_a0 context.Context, _a1 []notification.Notification)) *NotificationService_Dispatch_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]notification.Notification), args[2].(string)) + run(args[0].(context.Context), args[1].([]notification.Notification)) }) return _c } @@ -77,7 +76,7 @@ func (_c *NotificationService_Dispatch_Call) Return(_a0 []string, _a1 error) *No return _c } -func (_c *NotificationService_Dispatch_Call) RunAndReturn(run func(context.Context, []notification.Notification, string) ([]string, error)) *NotificationService_Dispatch_Call { +func (_c *NotificationService_Dispatch_Call) RunAndReturn(run func(context.Context, []notification.Notification) ([]string, error)) *NotificationService_Dispatch_Call { _c.Call.Return(run) return _c } diff --git a/core/alert/service.go b/core/alert/service.go index 3e8e1c20..1dafc99a 100644 --- a/core/alert/service.go +++ b/core/alert/service.go @@ -20,7 +20,7 @@ type LogService interface { } type NotificationService interface { - Dispatch(context.Context, []notification.Notification, string) ([]string, error) + Dispatch(context.Context, []notification.Notification) ([]string, error) } // Service handles business logic @@ -78,10 +78,8 @@ func (s *Service) CreateAlerts(ctx context.Context, providerType string, provide } // failure on dispatch won't rollback the db changes - for _, n := range ns { - if _, err := s.notificationService.Dispatch(ctx, []notification.Notification{n}, notification.DispatchKindSingleNotification); err != nil { - s.logger.Warn("failed to send alert as notification", "err", err, "notification", n) - } + if _, err := s.notificationService.Dispatch(ctx, ns); err != nil { + s.logger.Warn("failed to send alert as notification", "err", err, "notifications", ns) } } else { diff --git a/core/alert/service_test.go b/core/alert/service_test.go index e283e560..9d034515 100644 --- a/core/alert/service_test.go +++ b/core/alert/service_test.go @@ -134,7 +134,7 @@ func TestService_CreateAlerts(t *testing.T) { }, 1, nil) ar.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("alert.Alert")).Return(alert.Alert{ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "lag", MetricValue: "20", Rule: "lagHigh", TriggeredAt: timenow}, nil) - ns.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification"), mock.AnythingOfType("string")).Return(nil, nil) + ns.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, nil) }, alertsToBeCreated: alertsToBeCreated, expectedFiringLen: 1, diff --git a/core/log/mocks/notification_log_repository.go b/core/log/mocks/notification_log_repository.go index 2e14f7a8..b8a4cc4f 100644 --- a/core/log/mocks/notification_log_repository.go +++ b/core/log/mocks/notification_log_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/namespace/mocks/config_syncer.go b/core/namespace/mocks/config_syncer.go index 01cc5db8..0a92c5ad 100644 --- a/core/namespace/mocks/config_syncer.go +++ b/core/namespace/mocks/config_syncer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/namespace/mocks/encryptor.go b/core/namespace/mocks/encryptor.go index ee212150..15638a54 100644 --- a/core/namespace/mocks/encryptor.go +++ b/core/namespace/mocks/encryptor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/namespace/mocks/namespace_repository.go b/core/namespace/mocks/namespace_repository.go index 222505e1..dc75f7f6 100644 --- a/core/namespace/mocks/namespace_repository.go +++ b/core/namespace/mocks/namespace_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/namespace/mocks/provider_service.go b/core/namespace/mocks/provider_service.go index 9e3594e9..85dde2b4 100644 --- a/core/namespace/mocks/provider_service.go +++ b/core/namespace/mocks/provider_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/namespace/mocks/transactor.go b/core/namespace/mocks/transactor.go index 50eb87d6..06476816 100644 --- a/core/namespace/mocks/transactor.go +++ b/core/namespace/mocks/transactor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/config.go b/core/notification/config.go index 212061ab..378f7fde 100644 --- a/core/notification/config.go +++ b/core/notification/config.go @@ -14,9 +14,7 @@ type Config struct { DLQHandler HandlerConfig `mapstructure:"dlq_handler" yaml:"dlq_handler"` GroupBy []string `mapstructure:"group_by" yaml:"group_by"` - // experimental: derived from service.Config - SubscriptionV2Enabled bool - EnableSilenceFeature bool + EnableSilenceFeature bool } type HandlerConfig struct { diff --git a/core/notification/dispatch_bulk_notification_service.go b/core/notification/dispatch_bulk_notification_service.go deleted file mode 100644 index a8df46fe..00000000 --- a/core/notification/dispatch_bulk_notification_service.go +++ /dev/null @@ -1,217 +0,0 @@ -package notification - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/goto/siren/core/log" - "github.com/goto/siren/pkg/errors" - "github.com/goto/siren/pkg/structure" - "github.com/mitchellh/hashstructure/v2" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric" - "golang.org/x/exp/maps" -) - -// DispatchBulkNotificationService only supports subscriber routing and not supporting direct receiver routing -type DispatchBulkNotificationService struct { - deps Deps - notifierPlugins map[string]Notifier - routerMap map[string]Router - metricGaugeNumBulkNotification metric.Int64Gauge -} - -func NewDispatchBulkNotificationService( - deps Deps, - notifierPlugins map[string]Notifier, - routerMap map[string]Router, -) *DispatchBulkNotificationService { - metricGaugeNumBulkNotification, err := otel.Meter("github.com/goto/siren/core/notification"). - Int64Gauge("siren.notification.bulk.notification_number") - if err != nil { - otel.Handle(err) - } - - return &DispatchBulkNotificationService{ - deps: deps, - notifierPlugins: notifierPlugins, - routerMap: routerMap, - metricGaugeNumBulkNotification: metricGaugeNumBulkNotification, - } -} - -func (s *DispatchBulkNotificationService) getRouter(notificationRouterKind string) (Router, error) { - selectedRouter, exist := s.routerMap[notificationRouterKind] - if !exist { - return nil, errors.ErrInvalid.WithMsgf("unsupported notification router kind: %q", notificationRouterKind) - } - return selectedRouter, nil -} - -func (s *DispatchBulkNotificationService) prepareMetaMessages(ctx context.Context, ns []Notification) (metaMessages []MetaMessage, notificationLogs []log.Notification, err error) { - for _, n := range ns { - if err := n.Validate(RouterSubscriber); err != nil { - return nil, nil, err - } - - router, err := s.getRouter(RouterSubscriber) - if err != nil { - return nil, nil, err - } - - generatedMetaMessages, generatedNotificationLogs, err := router.PrepareMetaMessages(ctx, n) - if err != nil { - if errors.Is(err, ErrRouteSubscriberNoMatchFound) { - errMessage := fmt.Sprintf("not matching any subscription for notification: %v", n) - nJson, err := json.MarshalIndent(n, "", " ") - if err == nil { - errMessage = fmt.Sprintf("not matching any subscription for notification: %s", string(nJson)) - } - s.deps.Logger.Warn(errMessage) - continue - } - return nil, nil, err - } - - metaMessages = append(metaMessages, generatedMetaMessages...) - notificationLogs = append(notificationLogs, generatedNotificationLogs...) - } - - return metaMessages, notificationLogs, nil -} - -func (s *DispatchBulkNotificationService) Dispatch(ctx context.Context, ns []Notification) (notificationIDs []string, err error) { - defer func() { - s.instrumentNumberBulkNotification(ctx, len(ns), err) - }() - - var ( - metaMessages []MetaMessage - notificationLogs []log.Notification - ) - - notifications, err := s.deps.Repository.BulkCreate(ctx, ns) - if err != nil { - return nil, err - } - - metaMessages, notificationLogs, err = s.prepareMetaMessages(ctx, notifications) - if err != nil { - return nil, err - } - - if len(metaMessages) == 0 { - s.deps.Logger.Info("no meta messages to process") - return nil, nil - } - - if err := s.deps.LogService.LogNotifications(ctx, notificationLogs...); err != nil { - return nil, fmt.Errorf("failed logging notifications: %w", err) - } - - reducedMetaMessages, err := ReduceMetaMessages(metaMessages, s.deps.Cfg.GroupBy) - if err != nil { - return nil, err - } - - messages, err := s.RenderMessages(ctx, reducedMetaMessages) - if err != nil { - return nil, err - } - - if len(messages) == 0 { - s.deps.Logger.Info("no messages to enqueue") - return nil, nil - } - - if err := s.deps.Q.Enqueue(ctx, messages...); err != nil { - return nil, fmt.Errorf("failed enqueuing messages: %w", err) - } - - for _, n := range notifications { - notificationIDs = append(notificationIDs, n.ID) - } - - return notificationIDs, nil -} - -func (s *DispatchBulkNotificationService) getNotifierPlugin(receiverType string) (Notifier, error) { - notifierPlugin, exist := s.notifierPlugins[receiverType] - if !exist { - return nil, errors.ErrInvalid.WithMsgf("unsupported receiver type: %q", receiverType) - } - return notifierPlugin, nil -} - -func (s *DispatchBulkNotificationService) RenderMessages(ctx context.Context, metaMessages []MetaMessage) (messages []Message, err error) { - for _, mm := range metaMessages { - notifierPlugin, err := s.getNotifierPlugin(mm.ReceiverType) - if err != nil { - return nil, err - } - - message, err := InitMessageByMetaMessage( - ctx, - notifierPlugin, - s.deps.TemplateService, - mm, - InitWithExpiryDuration(mm.ValidDuration), - ) - if err != nil { - return nil, err - } - - messages = append(messages, message) - } - return messages, nil -} - -func ReduceMetaMessages(metaMessages []MetaMessage, groupBy []string) ([]MetaMessage, error) { - var ( - hashedMetaMessagesMap = map[uint64]MetaMessage{} - ) - for _, mm := range metaMessages { - - groupedLabels := structure.BuildGroupLabels(mm.Labels, groupBy) - groupedLabels["_receiver.ID"] = fmt.Sprintf("%d", mm.ReceiverID) - groupedLabels["_notification.template"] = mm.Template - - hash, err := hashstructure.Hash(groupedLabels, hashstructure.FormatV2, nil) - if err != nil { - return nil, fmt.Errorf("cannot get hash from metamessage %v", mm) - } - - if _, ok := hashedMetaMessagesMap[hash]; !ok { - if mm.MergedLabels == nil { - mm.MergedLabels = map[string][]string{} - for k, v := range mm.Labels { - mm.MergedLabels[k] = append(mm.MergedLabels[k], v) - } - } - hashedMetaMessagesMap[hash] = mm - - } else { - hashedMetaMessagesMap[hash] = MergeMetaMessage(mm, hashedMetaMessagesMap[hash]) - } - - } - return maps.Values(hashedMetaMessagesMap), nil -} - -func MergeMetaMessage(from MetaMessage, to MetaMessage) MetaMessage { - var output = to - for k, v := range from.Labels { - output.MergedLabels[k] = append(output.MergedLabels[k], v) - } - output.NotificationIDs = append(output.NotificationIDs, from.NotificationIDs...) - output.SubscriptionIDs = append(output.SubscriptionIDs, from.SubscriptionIDs...) - return output -} - -func (s *DispatchBulkNotificationService) instrumentNumberBulkNotification(ctx context.Context, num int, err error) { - s.metricGaugeNumBulkNotification.Record(ctx, int64(num), metric.WithAttributes( - attribute.Bool("success", err == nil), - )) -} diff --git a/core/notification/dispatch_bulk_notification_service_test.go b/core/notification/dispatch_bulk_notification_service_test.go deleted file mode 100644 index 6577c681..00000000 --- a/core/notification/dispatch_bulk_notification_service_test.go +++ /dev/null @@ -1,479 +0,0 @@ -package notification_test - -import ( - "context" - "sort" - "testing" - - "github.com/google/go-cmp/cmp" - saltlog "github.com/goto/salt/log" - "github.com/goto/siren/core/log" - "github.com/goto/siren/core/notification" - "github.com/goto/siren/core/notification/mocks" - "github.com/goto/siren/core/template" - "github.com/goto/siren/pkg/errors" - "github.com/stretchr/testify/mock" -) - -func TestDispatchBulkNotificationServiceService_Dispatch(t *testing.T) { - tests := []struct { - name string - n []notification.Notification - setup func(*mocks.Repository, *mocks.AlertRepository, *mocks.Router, *mocks.LogService, *mocks.Queuer, *mocks.TemplateService, *mocks.Notifier) - wantErrString string - }{ - { - name: "should return error if repository return error", - n: []notification.Notification{ - { - Type: notification.TypeEvent, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer, ts *mocks.TemplateService, nt *mocks.Notifier) { - r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, errors.New("some error")) - }, - wantErrString: "some error", - }, - { - name: "should return error if notification is not valid subscriber router", - n: []notification.Notification{}, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer, ts *mocks.TemplateService, nt *mocks.Notifier) { - r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ - { - Type: notification.TypeEvent, - Labels: map[string]string{}, - }, - }, nil) - }, - wantErrString: "notification type subscriber should have labels: { 0 event map[] map[] 0s [] 0001-01-01 00:00:00 +0000 utc []}", - }, - { - name: "should return error if router prepare meta messages return error", - n: []notification.Notification{}, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer, ts *mocks.TemplateService, nt *mocks.Notifier) { - r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ - { - Type: notification.TypeEvent, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, nil) - mr.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, nil, errors.New("some error")) - }, - wantErrString: "some error", - }, - { - name: "should return error if log notifications return error", - n: []notification.Notification{}, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer, ts *mocks.TemplateService, nt *mocks.Notifier) { - r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ - { - Type: notification.TypeEvent, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, nil) - mr.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, []log.Notification{ - { - NamespaceID: 3, - SubscriptionID: 123, - ReceiverID: 12, - }, - }, nil) - ls.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(errors.New("some error 3")) - - }, - wantErrString: "failed logging notifications: some error 3", - }, - { - name: "should return no error if successfully enqueue messages", - n: []notification.Notification{}, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer, ts *mocks.TemplateService, nt *mocks.Notifier) { - r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ - { - Type: notification.TypeEvent, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, nil) - mr.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, []log.Notification{ - { - NamespaceID: 3, - SubscriptionID: 123, - ReceiverID: 12, - }, - }, nil) - ls.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - nt.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]any")).Return(map[string]any{}, nil) - nt.EXPECT().GetSystemDefaultTemplate().Return("system-template") - ts.EXPECT().GetByName(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("string")).Return(&template.Template{}, nil) - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockQueuer = new(mocks.Queuer) - mockRepository = new(mocks.Repository) - mockNotifier = new(mocks.Notifier) - mockTemplateService = new(mocks.TemplateService) - mockAlertRepository = new(mocks.AlertRepository) - mockLogService = new(mocks.LogService) - mockRouter = new(mocks.Router) - ) - - if tt.setup != nil { - tt.setup(mockRepository, mockAlertRepository, mockRouter, mockLogService, mockQueuer, mockTemplateService, mockNotifier) - } - - s := notification.NewDispatchBulkNotificationService( - notification.Deps{ - Cfg: notification.Config{ - SubscriptionV2Enabled: true, - EnableSilenceFeature: true, - }, - Logger: saltlog.NewNoop(), - Repository: mockRepository, - Q: mockQueuer, - AlertRepository: mockAlertRepository, - LogService: mockLogService, - }, - map[string]notification.Notifier{ - testType: mockNotifier, - }, - map[string]notification.Router{ - testType: mockRouter, - notification.RouterSubscriber: mockRouter, - }, - ) - if _, err := s.Dispatch(context.TODO(), tt.n); err != nil { - if err.Error() != tt.wantErrString { - t.Fatalf("error = %v, wantErr %s", err, tt.wantErrString) - } - } - }) - } -} - -func TestReduceMetaMessages(t *testing.T) { - tests := []struct { - name string - metaMessages []notification.MetaMessage - groupBy []string - want []notification.MetaMessage - wantErr bool - }{ - { - name: "should group meta messages with receiver id", - metaMessages: []notification.MetaMessage{ - { - ReceiverID: 13, - NotificationIDs: []string{"xx"}, - SubscriptionIDs: []uint64{1, 2}, - Data: map[string]any{ - "d1": "dv1", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "ab", - }, - }, - { - ReceiverID: 13, - NotificationIDs: []string{"yy"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d2": "dv2", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "cd", - "x1": "x1", - }, - }, - { - ReceiverID: 14, - NotificationIDs: []string{"zz"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k1": "receiver-14", - "k2": "ab", - }, - }, - }, - want: []notification.MetaMessage{ - { - ReceiverID: 13, - NotificationIDs: []string{"xx", "yy"}, - SubscriptionIDs: []uint64{1, 2, 3, 4}, - Data: map[string]any{ - "d1": "dv1", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "ab", - }, - MergedLabels: map[string][]string{ - "k1": {"receiver-13", "receiver-13"}, - "k2": {"ab", "cd"}, - "x1": {"x1"}, - }, - }, - { - ReceiverID: 14, - NotificationIDs: []string{"zz"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k1": "receiver-14", - "k2": "ab", - }, - MergedLabels: map[string][]string{ - "k1": {"receiver-14"}, - "k2": {"ab"}, - }, - }, - }, - }, - { - name: "should not group meta messages if receiver and template are different", - metaMessages: []notification.MetaMessage{ - { - ReceiverID: 13, - NotificationIDs: []string{"xx"}, - SubscriptionIDs: []uint64{1, 2}, - Data: map[string]any{ - "d1": "dv1", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "ab", - }, - Template: "template-1", - }, - { - ReceiverID: 14, - NotificationIDs: []string{"yy"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d2": "dv2", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "cd", - "x1": "x1", - }, - Template: "template-2", - }, - { - ReceiverID: 15, - NotificationIDs: []string{"zz"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k1": "receiver-14", - "k2": "ab", - }, - Template: "template-1", - }, - }, - want: []notification.MetaMessage{ - { - ReceiverID: 13, - NotificationIDs: []string{"xx"}, - SubscriptionIDs: []uint64{1, 2}, - Data: map[string]any{ - "d1": "dv1", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "ab", - }, - Template: "template-1", - MergedLabels: map[string][]string{"k1": {"receiver-13"}, "k2": {"ab"}}, - }, - { - ReceiverID: 14, - NotificationIDs: []string{"yy"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d2": "dv2", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "cd", - "x1": "x1", - }, - Template: "template-2", - MergedLabels: map[string][]string{"k1": {"receiver-13"}, "k2": {"cd"}, "x1": {"x1"}}, - }, - { - ReceiverID: 15, - NotificationIDs: []string{"zz"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k1": "receiver-14", - "k2": "ab", - }, - Template: "template-1", - MergedLabels: map[string][]string{"k1": {"receiver-14"}, "k2": {"ab"}}, - }, - }, - }, - { - name: "should group meta messages with group by labels, template and receiver id", - groupBy: []string{ - "k1", - "k2", - }, - metaMessages: []notification.MetaMessage{ - { - ReceiverID: 13, - NotificationIDs: []string{"xx"}, - SubscriptionIDs: []uint64{1, 2}, - Data: map[string]any{ - "d1": "dv1", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "ab", - }, - }, - { - ReceiverID: 13, - NotificationIDs: []string{"yy"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d2": "dv2", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "cd", - "x1": "x1", - }, - }, - { - ReceiverID: 13, - NotificationIDs: []string{"zz"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "ab", - }, - }, - { - ReceiverID: 13, - NotificationIDs: []string{"aa"}, - SubscriptionIDs: []uint64{5, 6}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k2": "ab", - }, - }, - { - ReceiverID: 14, - NotificationIDs: []string{"aa"}, - SubscriptionIDs: []uint64{5, 6}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k2": "ab", - }, - }, - }, - want: []notification.MetaMessage{ - { - ReceiverID: 13, - NotificationIDs: []string{"xx", "zz"}, - SubscriptionIDs: []uint64{1, 2, 3, 4}, - Data: map[string]any{ - "d1": "dv1", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "ab", - }, - MergedLabels: map[string][]string{"k1": {"receiver-13", "receiver-13"}, "k2": {"ab", "ab"}}, - }, - { - ReceiverID: 13, - NotificationIDs: []string{"yy"}, - SubscriptionIDs: []uint64{3, 4}, - Data: map[string]any{ - "d2": "dv2", - }, - Labels: map[string]string{ - "k1": "receiver-13", - "k2": "cd", - "x1": "x1", - }, - MergedLabels: map[string][]string{"k1": {"receiver-13"}, "k2": {"cd"}, "x1": {"x1"}}, - }, - { - ReceiverID: 13, - NotificationIDs: []string{"aa"}, - SubscriptionIDs: []uint64{5, 6}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k2": "ab", - }, - MergedLabels: map[string][]string{"k2": {"ab"}}, - }, - { - ReceiverID: 14, - NotificationIDs: []string{"aa"}, - SubscriptionIDs: []uint64{5, 6}, - Data: map[string]any{ - "d3": "dv3", - }, - Labels: map[string]string{ - "k2": "ab", - }, - MergedLabels: map[string][]string{"k2": {"ab"}}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := notification.ReduceMetaMessages(tt.metaMessages, tt.groupBy) - if (err != nil) != tt.wantErr { - t.Errorf("ReduceMetaMessages() error = %v, wantErr %v", err, tt.wantErr) - return - } - sort.Slice(got, func(i, j int) bool { - return got[i].ReceiverID < got[j].ReceiverID - }) - sort.Slice(tt.want, func(i, j int) bool { - return tt.want[i].ReceiverID < tt.want[j].ReceiverID - }) - if diff := cmp.Diff(got, tt.want); diff != "" { - t.Errorf("ReduceMetaMessages() diff = %v", diff) - } - }) - } -} diff --git a/core/notification/dispatch_single_notification_service.go b/core/notification/dispatch_single_notification_service.go deleted file mode 100644 index 14148195..00000000 --- a/core/notification/dispatch_single_notification_service.go +++ /dev/null @@ -1,151 +0,0 @@ -package notification - -import ( - "context" - "fmt" - - "github.com/goto/siren/core/log" - "github.com/goto/siren/core/silence" - "github.com/goto/siren/pkg/errors" -) - -// DispatchSingleNotificationService supports subscriber routing and receiver routing at the same time -type DispatchSingleNotificationService struct { - deps Deps - notifierPlugins map[string]Notifier - routerMap map[string]Router -} - -func NewDispatchSingleNotificationService( - deps Deps, - notifierPlugins map[string]Notifier, - routerMap map[string]Router, -) *DispatchSingleNotificationService { - return &DispatchSingleNotificationService{ - deps: deps, - notifierPlugins: notifierPlugins, - routerMap: routerMap, - } -} - -func (s *DispatchSingleNotificationService) getRouter(notificationRouterKind string) (Router, error) { - selectedRouter, exist := s.routerMap[notificationRouterKind] - if !exist { - return nil, errors.ErrInvalid.WithMsgf("unsupported notification router kind: %q", notificationRouterKind) - } - return selectedRouter, nil -} - -func (s *DispatchSingleNotificationService) Dispatch(ctx context.Context, ns []Notification) ([]string, error) { - if len(ns) != 1 { - return nil, errors.ErrInvalid.WithMsgf("direct single notification should only accept 1 notification but found %d", len(ns)) - } - - var ( - n = ns[0] - messages []Message - ) - - no, err := s.deps.Repository.Create(ctx, n) - if err != nil { - return nil, err - } - - n.EnrichID(no.ID) - - switch n.Type { - case TypeAlert: - messages, err = s.dispatchByRouter(ctx, n, RouterSubscriber) - if err != nil { - return nil, err - } - case TypeEvent: - messages, err = s.fetchMessagesEvents(ctx, n) - if err != nil { - return nil, err - } - default: - return nil, errors.ErrInternal.WithMsgf("unknown notification type %s", n.Type) - } - - if len(messages) == 0 { - s.deps.Logger.Info("no messages to enqueue") - return []string{n.ID}, nil - } - - if err := s.deps.Q.Enqueue(ctx, messages...); err != nil { - return nil, fmt.Errorf("failed enqueuing messages: %w", err) - } - - return []string{n.ID}, nil -} - -func (s *DispatchSingleNotificationService) dispatchByRouter(ctx context.Context, n Notification, flow string) ([]Message, error) { - if err := n.Validate(flow); err != nil { - return nil, err - } - - router, err := s.getRouter(flow) - if err != nil { - return nil, err - } - - var ( - messages []Message - notificationLogs []log.Notification - hasSilenced bool - ) - if s.deps.Cfg.SubscriptionV2Enabled { - messages, notificationLogs, hasSilenced, err = router.PrepareMessageV2(ctx, n) - if err != nil { - return nil, err - } - } else { - messages, notificationLogs, hasSilenced, err = router.PrepareMessage(ctx, n) - if err != nil { - return nil, err - } - } - - if len(messages) == 0 && len(notificationLogs) == 0 { - return nil, fmt.Errorf("something wrong and no messages will be sent with notification: %v", n) - } - - if err := s.deps.LogService.LogNotifications(ctx, notificationLogs...); err != nil { - return nil, fmt.Errorf("failed logging notifications: %w", err) - } - - // Reliability of silence feature need to be tested more - if s.deps.Cfg.EnableSilenceFeature { - if err := s.deps.AlertRepository.BulkUpdateSilence(ctx, n.AlertIDs, silence.Status(hasSilenced, len(messages) != 0)); err != nil { - return nil, fmt.Errorf("failed updating silence status: %w", err) - } - } - - return messages, nil -} - -func (s *DispatchSingleNotificationService) fetchMessagesEvents(ctx context.Context, n Notification) ([]Message, error) { - if len(n.ReceiverSelectors) == 0 && len(n.Labels) == 0 { - return nil, errors.ErrInvalid.WithMsgf("no receivers found") - } - - var messages = []Message{} - - if len(n.ReceiverSelectors) != 0 { - generatedMessages, err := s.dispatchByRouter(ctx, n, RouterReceiver) - if err != nil { - return nil, err - } - messages = append(messages, generatedMessages...) - } - - if len(n.Labels) != 0 { - generatedMessages, err := s.dispatchByRouter(ctx, n, RouterSubscriber) - if err != nil { - return nil, err - } - messages = append(messages, generatedMessages...) - } - return messages, nil -} diff --git a/core/notification/dispatch_single_notification_service_test.go b/core/notification/dispatch_single_notification_service_test.go deleted file mode 100644 index eb0d578e..00000000 --- a/core/notification/dispatch_single_notification_service_test.go +++ /dev/null @@ -1,374 +0,0 @@ -package notification_test - -import ( - "context" - "errors" - "testing" - - saltlog "github.com/goto/salt/log" - "github.com/goto/siren/core/log" - "github.com/goto/siren/core/notification" - "github.com/goto/siren/core/notification/mocks" - "github.com/stretchr/testify/mock" -) - -func TestDispatchSingleNotificationServiceService_Dispatch(t *testing.T) { - tests := []struct { - name string - n []notification.Notification - setup func(*mocks.Repository, *mocks.AlertRepository, *mocks.Router, *mocks.LogService, *mocks.Queuer) - wantErrString string - }{ - { - name: "should return error if notifications arg is not 1", - n: []notification.Notification{}, - wantErrString: "direct single notification should only accept 1 notification but found 0", - }, - { - name: "should return error if repository return error", - n: []notification.Notification{ - { - Type: notification.TypeAlert, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, errors.New("some error")) - }, - wantErrString: "some error", - }, - { - name: "should return error if notification type is unknown", - n: []notification.Notification{ - { - Type: "random", - Labels: map[string]string{}, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - }, - wantErrString: "unknown notification type random", - }, - { - name: "should return error if log notifications return error", - n: []notification.Notification{ - { - Type: notification.TypeAlert, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - mr.EXPECT().PrepareMessageV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, []log.Notification{ - { - NamespaceID: 3, - SubscriptionID: 123, - ReceiverID: 12, - }, - }, false, nil) - ls.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(errors.New("some error 3")) - }, - wantErrString: "failed logging notifications: some error 3", - }, - { - name: "should return error if update alerts silence status return error", - n: []notification.Notification{ - { - Type: notification.TypeAlert, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - mr.EXPECT().PrepareMessageV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, []log.Notification{ - { - NamespaceID: 3, - SubscriptionID: 123, - ReceiverID: 12, - }, - }, false, nil) - ls.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - ar.EXPECT().BulkUpdateSilence(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("string")).Return(errors.New("some error 4")) - }, - wantErrString: "failed updating silence status: some error 4", - }, - { - name: "should return no error if no messages to queue", - n: []notification.Notification{ - { - Type: notification.TypeAlert, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - mr.EXPECT().PrepareMessageV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, []log.Notification{ - { - NamespaceID: 3, - SubscriptionID: 123, - ReceiverID: 12, - }, - }, false, nil) - ls.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - ar.EXPECT().BulkUpdateSilence(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("string")).Return(nil) - }, - }, - { - name: "should return error if queueing error", - n: []notification.Notification{ - { - Type: notification.TypeAlert, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - mr.EXPECT().PrepareMessageV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.Message{ - { - ID: "1234", - NotificationIDs: []string{"n-1234"}, - }, - }, []log.Notification{ - { - NamespaceID: 3, - SubscriptionID: 123, - ReceiverID: 12, - }, - }, false, nil) - ls.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - ar.EXPECT().BulkUpdateSilence(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("string")).Return(nil) - q.EXPECT().Enqueue(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error 5")) - }, - wantErrString: "failed enqueuing messages: some error 5", - }, - - { - name: "should return no error if queueing succeed", - n: []notification.Notification{ - { - Type: notification.TypeAlert, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - mr.EXPECT().PrepareMessageV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.Message{ - { - ID: "1234", - NotificationIDs: []string{"n-1234"}, - }, - }, []log.Notification{ - { - NamespaceID: 3, - SubscriptionID: 123, - ReceiverID: 12, - }, - }, false, nil) - ls.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - ar.EXPECT().BulkUpdateSilence(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("string")).Return(nil) - q.EXPECT().Enqueue(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Message")).Return(nil) - }, - wantErrString: "failed enqueuing messages: some error 5", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockQueuer = new(mocks.Queuer) - mockRepository = new(mocks.Repository) - mockAlertRepository = new(mocks.AlertRepository) - mockLogService = new(mocks.LogService) - mockNotifier = new(mocks.Notifier) - mockRouter = new(mocks.Router) - ) - - if tt.setup != nil { - tt.setup(mockRepository, mockAlertRepository, mockRouter, mockLogService, mockQueuer) - } - - s := notification.NewDispatchSingleNotificationService( - notification.Deps{ - Cfg: notification.Config{ - SubscriptionV2Enabled: true, - EnableSilenceFeature: true, - }, - Logger: saltlog.NewNoop(), - Repository: mockRepository, - Q: mockQueuer, - AlertRepository: mockAlertRepository, - LogService: mockLogService, - }, - map[string]notification.Notifier{ - testType: mockNotifier, - }, - map[string]notification.Router{ - testType: mockRouter, - notification.RouterSubscriber: mockRouter, - }, - ) - if _, err := s.Dispatch(context.TODO(), tt.n); err != nil { - if err.Error() != tt.wantErrString { - t.Fatalf("Service.DispatchFailure() error = %v, wantErr %s", err, tt.wantErrString) - } - } - }) - } -} - -func TestDispatchSingleNotificationServiceService_DispatchFailureAlert(t *testing.T) { - tests := []struct { - name string - n []notification.Notification - setup func(*mocks.Repository, *mocks.AlertRepository, *mocks.Router, *mocks.LogService, *mocks.Queuer) - wantErrString string - }{ - { - name: "should return error if dispatchAlert.Validate return error", - n: []notification.Notification{ - { - Type: notification.TypeAlert, - Labels: map[string]string{}, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - }, - wantErrString: "notification type subscriber should have labels: { 0 alert map[id:] map[] 0s [] 0001-01-01 00:00:00 +0000 utc []}", - }, - { - name: "should return error if dispatchAlert no messages generated but not return subscription not found", - n: []notification.Notification{ - { - Type: notification.TypeAlert, - Labels: map[string]string{ - "k1": "v1", - }, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - mr.EXPECT().PrepareMessageV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, nil, false, nil) - }, - wantErrString: "something wrong and no messages will be sent with notification: { 0 alert map[id:] map[k1:v1] 0s [] 0001-01-01 00:00:00 +0000 UTC []}", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockQueuer = new(mocks.Queuer) - mockRepository = new(mocks.Repository) - mockAlertRepository = new(mocks.AlertRepository) - mockLogService = new(mocks.LogService) - mockNotifier = new(mocks.Notifier) - mockRouter = new(mocks.Router) - ) - - if tt.setup != nil { - tt.setup(mockRepository, mockAlertRepository, mockRouter, mockLogService, mockQueuer) - } - - s := notification.NewDispatchSingleNotificationService( - notification.Deps{ - Cfg: notification.Config{ - SubscriptionV2Enabled: true, - EnableSilenceFeature: true, - }, - Logger: saltlog.NewNoop(), - Repository: mockRepository, - Q: mockQueuer, - AlertRepository: mockAlertRepository, - LogService: mockLogService, - }, - map[string]notification.Notifier{ - testType: mockNotifier, - }, - map[string]notification.Router{ - testType: mockRouter, - notification.RouterSubscriber: mockRouter, - }, - ) - if _, err := s.Dispatch(context.TODO(), tt.n); err != nil { - if err.Error() != tt.wantErrString { - t.Fatalf("Service.DispatchFailureAlert() error = %v, wantErr %s", err, tt.wantErrString) - } - } - }) - } -} - -func TestDispatchSingleNotificationServiceService_DispatchFailureEvent(t *testing.T) { - tests := []struct { - name string - n []notification.Notification - setup func(*mocks.Repository, *mocks.AlertRepository, *mocks.Router, *mocks.LogService, *mocks.Queuer) - wantErrString string - }{ - { - name: "should return error if dispatchEvent found no receiver", - n: []notification.Notification{ - { - Type: notification.TypeEvent, - }, - }, - setup: func(r *mocks.Repository, ar *mocks.AlertRepository, mr *mocks.Router, ls *mocks.LogService, q *mocks.Queuer) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - }, - wantErrString: "no receivers found", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockQueuer = new(mocks.Queuer) - mockRepository = new(mocks.Repository) - mockAlertRepository = new(mocks.AlertRepository) - mockLogService = new(mocks.LogService) - mockNotifier = new(mocks.Notifier) - mockRouter = new(mocks.Router) - ) - - if tt.setup != nil { - tt.setup(mockRepository, mockAlertRepository, mockRouter, mockLogService, mockQueuer) - } - - s := notification.NewDispatchSingleNotificationService( - notification.Deps{ - Cfg: notification.Config{ - SubscriptionV2Enabled: true, - EnableSilenceFeature: true, - }, - Logger: saltlog.NewNoop(), - Repository: mockRepository, - Q: mockQueuer, - AlertRepository: mockAlertRepository, - LogService: mockLogService, - }, - map[string]notification.Notifier{ - testType: mockNotifier, - }, - map[string]notification.Router{ - testType: mockRouter, - notification.RouterSubscriber: mockRouter, - }, - ) - if _, err := s.Dispatch(context.TODO(), tt.n); err != nil { - if err.Error() != tt.wantErrString { - t.Fatalf("Service.DispatchFailureEvent() error = %v, wantErr %s", err, tt.wantErrString) - } - } - }) - } -} diff --git a/core/notification/errors.go b/core/notification/errors.go index 2bdabc30..374e74af 100644 --- a/core/notification/errors.go +++ b/core/notification/errors.go @@ -5,6 +5,6 @@ import ( ) var ( - ErrNoMessage = errors.New("no message found") + ErrNoMessage = errors.New("no message sent, probably because not matching any subscription or receiver") ErrRouteSubscriberNoMatchFound = errors.New("not matching any subscription") ) diff --git a/core/notification/mocks/alert_repository.go b/core/notification/mocks/alert_repository.go index aae988aa..23562e72 100644 --- a/core/notification/mocks/alert_repository.go +++ b/core/notification/mocks/alert_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/alert_service.go b/core/notification/mocks/alert_service.go deleted file mode 100644 index 8114e795..00000000 --- a/core/notification/mocks/alert_service.go +++ /dev/null @@ -1,85 +0,0 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// AlertService is an autogenerated mock type for the AlertService type -type AlertService struct { - mock.Mock -} - -type AlertService_Expecter struct { - mock *mock.Mock -} - -func (_m *AlertService) EXPECT() *AlertService_Expecter { - return &AlertService_Expecter{mock: &_m.Mock} -} - -// UpdateSilenceStatus provides a mock function with given fields: ctx, alertIDs, hasSilenced, hasNonSilenced -func (_m *AlertService) UpdateSilenceStatus(ctx context.Context, alertIDs []int64, hasSilenced bool, hasNonSilenced bool) error { - ret := _m.Called(ctx, alertIDs, hasSilenced, hasNonSilenced) - - if len(ret) == 0 { - panic("no return value specified for UpdateSilenceStatus") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []int64, bool, bool) error); ok { - r0 = rf(ctx, alertIDs, hasSilenced, hasNonSilenced) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// AlertService_UpdateSilenceStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateSilenceStatus' -type AlertService_UpdateSilenceStatus_Call struct { - *mock.Call -} - -// UpdateSilenceStatus is a helper method to define mock.On call -// - ctx context.Context -// - alertIDs []int64 -// - hasSilenced bool -// - hasNonSilenced bool -func (_e *AlertService_Expecter) UpdateSilenceStatus(ctx interface{}, alertIDs interface{}, hasSilenced interface{}, hasNonSilenced interface{}) *AlertService_UpdateSilenceStatus_Call { - return &AlertService_UpdateSilenceStatus_Call{Call: _e.mock.On("UpdateSilenceStatus", ctx, alertIDs, hasSilenced, hasNonSilenced)} -} - -func (_c *AlertService_UpdateSilenceStatus_Call) Run(run func(ctx context.Context, alertIDs []int64, hasSilenced bool, hasNonSilenced bool)) *AlertService_UpdateSilenceStatus_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]int64), args[2].(bool), args[3].(bool)) - }) - return _c -} - -func (_c *AlertService_UpdateSilenceStatus_Call) Return(_a0 error) *AlertService_UpdateSilenceStatus_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AlertService_UpdateSilenceStatus_Call) RunAndReturn(run func(context.Context, []int64, bool, bool) error) *AlertService_UpdateSilenceStatus_Call { - _c.Call.Return(run) - return _c -} - -// NewAlertService creates a new instance of AlertService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewAlertService(t interface { - mock.TestingT - Cleanup(func()) -}) *AlertService { - mock := &AlertService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/notification/mocks/dispatcher.go b/core/notification/mocks/dispatcher.go index 3c5a36c0..149e09e2 100644 --- a/core/notification/mocks/dispatcher.go +++ b/core/notification/mocks/dispatcher.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/idempotency_repository.go b/core/notification/mocks/idempotency_repository.go index b3e44062..cc047743 100644 --- a/core/notification/mocks/idempotency_repository.go +++ b/core/notification/mocks/idempotency_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/log_service.go b/core/notification/mocks/log_service.go index 7f3dd063..954ecd54 100644 --- a/core/notification/mocks/log_service.go +++ b/core/notification/mocks/log_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/notification_repository.go b/core/notification/mocks/notification_repository.go index ce27a9a3..7006c2ce 100644 --- a/core/notification/mocks/notification_repository.go +++ b/core/notification/mocks/notification_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/notifier.go b/core/notification/mocks/notifier.go index b36f7c09..870adda4 100644 --- a/core/notification/mocks/notifier.go +++ b/core/notification/mocks/notifier.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/queuer.go b/core/notification/mocks/queuer.go index 9c2c8b32..3b25b4d0 100644 --- a/core/notification/mocks/queuer.go +++ b/core/notification/mocks/queuer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/receiver_service.go b/core/notification/mocks/receiver_service.go index 264e76cd..10199362 100644 --- a/core/notification/mocks/receiver_service.go +++ b/core/notification/mocks/receiver_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/router.go b/core/notification/mocks/router.go index 216db6ad..6df2c0e6 100644 --- a/core/notification/mocks/router.go +++ b/core/notification/mocks/router.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -24,156 +24,6 @@ func (_m *Router) EXPECT() *Router_Expecter { return &Router_Expecter{mock: &_m.Mock} } -// PrepareMessage provides a mock function with given fields: ctx, n -func (_m *Router) PrepareMessage(ctx context.Context, n notification.Notification) ([]notification.Message, []log.Notification, bool, error) { - ret := _m.Called(ctx, n) - - if len(ret) == 0 { - panic("no return value specified for PrepareMessage") - } - - var r0 []notification.Message - var r1 []log.Notification - var r2 bool - var r3 error - if rf, ok := ret.Get(0).(func(context.Context, notification.Notification) ([]notification.Message, []log.Notification, bool, error)); ok { - return rf(ctx, n) - } - if rf, ok := ret.Get(0).(func(context.Context, notification.Notification) []notification.Message); ok { - r0 = rf(ctx, n) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]notification.Message) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, notification.Notification) []log.Notification); ok { - r1 = rf(ctx, n) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]log.Notification) - } - } - - if rf, ok := ret.Get(2).(func(context.Context, notification.Notification) bool); ok { - r2 = rf(ctx, n) - } else { - r2 = ret.Get(2).(bool) - } - - if rf, ok := ret.Get(3).(func(context.Context, notification.Notification) error); ok { - r3 = rf(ctx, n) - } else { - r3 = ret.Error(3) - } - - return r0, r1, r2, r3 -} - -// Router_PrepareMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrepareMessage' -type Router_PrepareMessage_Call struct { - *mock.Call -} - -// PrepareMessage is a helper method to define mock.On call -// - ctx context.Context -// - n notification.Notification -func (_e *Router_Expecter) PrepareMessage(ctx interface{}, n interface{}) *Router_PrepareMessage_Call { - return &Router_PrepareMessage_Call{Call: _e.mock.On("PrepareMessage", ctx, n)} -} - -func (_c *Router_PrepareMessage_Call) Run(run func(ctx context.Context, n notification.Notification)) *Router_PrepareMessage_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(notification.Notification)) - }) - return _c -} - -func (_c *Router_PrepareMessage_Call) Return(_a0 []notification.Message, _a1 []log.Notification, _a2 bool, _a3 error) *Router_PrepareMessage_Call { - _c.Call.Return(_a0, _a1, _a2, _a3) - return _c -} - -func (_c *Router_PrepareMessage_Call) RunAndReturn(run func(context.Context, notification.Notification) ([]notification.Message, []log.Notification, bool, error)) *Router_PrepareMessage_Call { - _c.Call.Return(run) - return _c -} - -// PrepareMessageV2 provides a mock function with given fields: ctx, n -func (_m *Router) PrepareMessageV2(ctx context.Context, n notification.Notification) ([]notification.Message, []log.Notification, bool, error) { - ret := _m.Called(ctx, n) - - if len(ret) == 0 { - panic("no return value specified for PrepareMessageV2") - } - - var r0 []notification.Message - var r1 []log.Notification - var r2 bool - var r3 error - if rf, ok := ret.Get(0).(func(context.Context, notification.Notification) ([]notification.Message, []log.Notification, bool, error)); ok { - return rf(ctx, n) - } - if rf, ok := ret.Get(0).(func(context.Context, notification.Notification) []notification.Message); ok { - r0 = rf(ctx, n) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]notification.Message) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, notification.Notification) []log.Notification); ok { - r1 = rf(ctx, n) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]log.Notification) - } - } - - if rf, ok := ret.Get(2).(func(context.Context, notification.Notification) bool); ok { - r2 = rf(ctx, n) - } else { - r2 = ret.Get(2).(bool) - } - - if rf, ok := ret.Get(3).(func(context.Context, notification.Notification) error); ok { - r3 = rf(ctx, n) - } else { - r3 = ret.Error(3) - } - - return r0, r1, r2, r3 -} - -// Router_PrepareMessageV2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrepareMessageV2' -type Router_PrepareMessageV2_Call struct { - *mock.Call -} - -// PrepareMessageV2 is a helper method to define mock.On call -// - ctx context.Context -// - n notification.Notification -func (_e *Router_Expecter) PrepareMessageV2(ctx interface{}, n interface{}) *Router_PrepareMessageV2_Call { - return &Router_PrepareMessageV2_Call{Call: _e.mock.On("PrepareMessageV2", ctx, n)} -} - -func (_c *Router_PrepareMessageV2_Call) Run(run func(ctx context.Context, n notification.Notification)) *Router_PrepareMessageV2_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(notification.Notification)) - }) - return _c -} - -func (_c *Router_PrepareMessageV2_Call) Return(_a0 []notification.Message, _a1 []log.Notification, _a2 bool, _a3 error) *Router_PrepareMessageV2_Call { - _c.Call.Return(_a0, _a1, _a2, _a3) - return _c -} - -func (_c *Router_PrepareMessageV2_Call) RunAndReturn(run func(context.Context, notification.Notification) ([]notification.Message, []log.Notification, bool, error)) *Router_PrepareMessageV2_Call { - _c.Call.Return(run) - return _c -} - // PrepareMetaMessages provides a mock function with given fields: ctx, n func (_m *Router) PrepareMetaMessages(ctx context.Context, n notification.Notification) ([]notification.MetaMessage, []log.Notification, error) { ret := _m.Called(ctx, n) diff --git a/core/notification/mocks/silence_service.go b/core/notification/mocks/silence_service.go index 22c96352..6a889705 100644 --- a/core/notification/mocks/silence_service.go +++ b/core/notification/mocks/silence_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/subscription_service.go b/core/notification/mocks/subscription_service.go index 2774a432..80c7c3ca 100644 --- a/core/notification/mocks/subscription_service.go +++ b/core/notification/mocks/subscription_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/mocks/template_service.go b/core/notification/mocks/template_service.go index 5a7e48b0..4105716d 100644 --- a/core/notification/mocks/template_service.go +++ b/core/notification/mocks/template_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/notification/router_receiver_service.go b/core/notification/router_receiver_service.go index 649d14e9..1d81a545 100644 --- a/core/notification/router_receiver_service.go +++ b/core/notification/router_receiver_service.go @@ -10,28 +10,17 @@ import ( ) type RouterReceiverService struct { - deps Deps - notifierPlugins map[string]Notifier + deps Deps } func NewRouterReceiverService( deps Deps, - notifierPlugins map[string]Notifier, ) *RouterReceiverService { return &RouterReceiverService{ - deps: deps, - notifierPlugins: notifierPlugins, + deps: deps, } } -func (s *RouterReceiverService) getNotifierPlugin(receiverType string) (Notifier, error) { - notifierPlugin, exist := s.notifierPlugins[receiverType] - if !exist { - return nil, errors.ErrInvalid.WithMsgf("unsupported receiver type: %q", receiverType) - } - return notifierPlugin, nil -} - func (s *RouterReceiverService) PrepareMetaMessages(ctx context.Context, n Notification) (metaMessages []MetaMessage, notificationLogs []log.Notification, err error) { if len(n.ReceiverSelectors) > s.deps.Cfg.MaxNumReceiverSelectors { return nil, nil, errors.ErrInvalid.WithMsgf("number of receiver selectors should be less than or equal threshold %d", s.deps.Cfg.MaxNumReceiverSelectors) @@ -70,64 +59,51 @@ func (s *RouterReceiverService) PrepareMetaMessages(ctx context.Context, n Notif return metaMessages, notificationLogs, nil } -func (s *RouterReceiverService) PrepareMessageV2(ctx context.Context, n Notification) ([]Message, []log.Notification, bool, error) { - return s.PrepareMessage(ctx, n) -} - -func (s *RouterReceiverService) PrepareMessage(ctx context.Context, n Notification) ([]Message, []log.Notification, bool, error) { - - var notificationLogs []log.Notification - - if len(n.ReceiverSelectors) > s.deps.Cfg.MaxNumReceiverSelectors { - return nil, nil, false, errors.ErrInvalid.WithMsgf("number of receiver selectors should be less than or equal threshold %d", s.deps.Cfg.MaxNumReceiverSelectors) - } - - rcvs, err := s.deps.ReceiverService.List(ctx, receiver.Filter{ - MultipleLabels: n.ReceiverSelectors, - Expanded: true, - }) - if err != nil { - return nil, nil, false, err - } - - if len(rcvs) == 0 { - return nil, nil, false, errors.ErrNotFound - } - - var messages []Message - - for _, rcv := range rcvs { - notifierPlugin, err := s.getNotifierPlugin(rcv.Type) - if err != nil { - return nil, nil, false, errors.ErrInvalid.WithMsgf("invalid receiver type: %s", err.Error()) - } - - message, err := InitMessage( - ctx, - notifierPlugin, - s.deps.TemplateService, - n, - rcv.Type, - rcv.Configurations, - InitWithExpiryDuration(n.ValidDuration), - ) - if err != nil { - return nil, nil, false, err - } - - messages = append(messages, message) - notificationLogs = append(notificationLogs, log.Notification{ - NamespaceID: n.NamespaceID, - NotificationID: n.ID, - ReceiverID: rcv.ID, - AlertIDs: n.AlertIDs, - }) - } - - var messagesNum = len(messages) - if messagesNum > s.deps.Cfg.MaxMessagesReceiverFlow { - return nil, nil, false, errors.ErrInvalid.WithMsgf("sending %d messages exceed max messages receiver flow threshold %d. this will spam and broadcast to %d channel. found %d receiver selectors passed, you might want to check your receiver selectors configuration", messagesNum, s.deps.Cfg.MaxMessagesReceiverFlow, messagesNum, len(n.ReceiverSelectors)) - } - - return messages, notificationLogs, false, nil -} +// func (s *RouterReceiverService) PrepareMessages(ctx context.Context, metaMessages []MetaMessage) ([]Message, error) { +// if len(metaMessages) == 0 { +// return []Message{}, nil +// } + +// reducedMetaMessages, err := ReduceMetaMessages(metaMessages, s.deps.Cfg.GroupBy) +// if err != nil { +// return nil, err +// } + +// messages, err := s.RenderMessages(ctx, reducedMetaMessages) +// if err != nil { +// return nil, err +// } + +// return messages, nil +// } + +// func (s *RouterReceiverService) getNotifierPlugin(receiverType string) (Notifier, error) { +// notifierPlugin, exist := s.notifierPlugins[receiverType] +// if !exist { +// return nil, errors.ErrInvalid.WithMsgf("unsupported receiver type: %q", receiverType) +// } +// return notifierPlugin, nil +// } + +// func (s *RouterReceiverService) RenderMessages(ctx context.Context, metaMessages []MetaMessage) (messages []Message, err error) { +// for _, mm := range metaMessages { +// notifierPlugin, err := s.getNotifierPlugin(mm.ReceiverType) +// if err != nil { +// return nil, err +// } + +// message, err := InitMessageByMetaMessage( +// ctx, +// notifierPlugin, +// s.deps.TemplateService, +// mm, +// InitWithExpiryDuration(mm.ValidDuration), +// ) +// if err != nil { +// return nil, err +// } + +// messages = append(messages, message) +// } +// return messages, nil +// } diff --git a/core/notification/router_receiver_service_test.go b/core/notification/router_receiver_service_test.go index b44c6f8b..f8400639 100644 --- a/core/notification/router_receiver_service_test.go +++ b/core/notification/router_receiver_service_test.go @@ -1,173 +1,157 @@ package notification_test -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - saltlog "github.com/goto/salt/log" - "github.com/goto/siren/core/log" - "github.com/goto/siren/core/notification" - "github.com/goto/siren/core/notification/mocks" - "github.com/goto/siren/core/receiver" - "github.com/goto/siren/core/template" - "github.com/goto/siren/pkg/errors" - "github.com/stretchr/testify/mock" -) - -func TestRouterReceiverService_PrepareMessage(t *testing.T) { - var notificationID = "1234-5678" - tests := []struct { - name string - setup func(*mocks.ReceiverService, *mocks.Notifier) - n notification.Notification - want []notification.Message - want1 []log.Notification - want2 bool - wantErr bool - }{ - { - name: "should return error if receiver service return error", - n: notification.Notification{}, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return error if receiver type is unknown", - n: notification.Notification{ - ID: notificationID, - ReceiverSelectors: []map[string]string{ - { - "id": "11", - }, - }, - }, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{}, nil) - }, - wantErr: true, - }, - { - name: "should return error if init message return error", - n: notification.Notification{ - ID: notificationID, - ReceiverSelectors: []map[string]string{ - { - "id": "11", - }, - }, - }, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ - { - ID: 11, - Type: testType}, - }, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return error if template is not passed", - n: notification.Notification{ - ID: notificationID, - ReceiverSelectors: []map[string]string{ - { - "id": "11", - }, - }, - }, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ - { - ID: 11, - Type: testType, - }, - }, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]any{}, nil) - }, - wantErr: true, - }, - { - name: "should return no error if all flow passed", - n: notification.Notification{ - ID: notificationID, - ReceiverSelectors: []map[string]string{ - { - "id": "11", - }, - }, - Template: template.ReservedName_SystemDefault, - }, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ - { - ID: 11, - Type: testType, - }, - }, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]any{}, nil) - n.EXPECT().GetSystemDefaultTemplate().Return("") - }, - want: []notification.Message{ - { - Status: notification.MessageStatusEnqueued, - NotificationIDs: []string{notificationID}, - ReceiverType: testType, - Configs: map[string]any{}, - Details: map[string]any{"notification_type": string("")}, - MaxTries: 3, - }, - }, - want1: []log.Notification{{ - NotificationID: notificationID, - ReceiverID: 11, - }}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockReceiverService = new(mocks.ReceiverService) - mockNotifier = new(mocks.Notifier) - mockTemplateService = new(mocks.TemplateService) - ) - s := notification.NewRouterReceiverService( - notification.Deps{ - Cfg: notification.Config{ - MaxMessagesReceiverFlow: 10, - MaxNumReceiverSelectors: 10, - }, - Logger: saltlog.NewNoop(), - ReceiverService: mockReceiverService, - TemplateService: mockTemplateService, - }, - map[string]notification.Notifier{ - testType: mockNotifier, - }, - ) - if tt.setup != nil { - tt.setup(mockReceiverService, mockNotifier) - } - got, got1, got2, err := s.PrepareMessage(context.TODO(), tt.n) - if (err != nil) != tt.wantErr { - t.Errorf("RouterReceiverService.PrepareMessage() error = %v, wantErr %v", err, tt.wantErr) - return - } - if diff := cmp.Diff(got, tt.want, - cmpopts.IgnoreFields(notification.Message{}, "ID", "CreatedAt", "UpdatedAt"), - cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { - t.Errorf("RouterReceiverService.PrepareMessage() diff = %v", diff) - } - if diff := cmp.Diff(got1, tt.want1); diff != "" { - t.Errorf("RouterReceiverService.PrepareMessage() diff = %v", diff) - } - if got2 != tt.want2 { - t.Errorf("RouterReceiverService.PrepareMessage() got2 = %v, want %v", got2, tt.want2) - } - }) - } -} +// func TestRouterReceiverService_PrepareMessage(t *testing.T) { +// var notificationID = "1234-5678" +// tests := []struct { +// name string +// setup func(*mocks.ReceiverService, *mocks.Notifier) +// n notification.Notification +// want []notification.Message +// want1 []log.Notification +// want2 bool +// wantErr bool +// }{ +// { +// name: "should return error if receiver service return error", +// n: notification.Notification{}, +// setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { +// rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return(nil, errors.New("some error")) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if receiver type is unknown", +// n: notification.Notification{ +// ID: notificationID, +// ReceiverSelectors: []map[string]string{ +// { +// "id": "11", +// }, +// }, +// }, +// setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { +// rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{}, nil) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if init message return error", +// n: notification.Notification{ +// ID: notificationID, +// ReceiverSelectors: []map[string]string{ +// { +// "id": "11", +// }, +// }, +// }, +// setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { +// rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ +// { +// ID: 11, +// Type: testType}, +// }, nil) +// n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if template is not passed", +// n: notification.Notification{ +// ID: notificationID, +// ReceiverSelectors: []map[string]string{ +// { +// "id": "11", +// }, +// }, +// }, +// setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { +// rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ +// { +// ID: 11, +// Type: testType, +// }, +// }, nil) +// n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]any{}, nil) +// }, +// wantErr: true, +// }, +// { +// name: "should return no error if all flow passed", +// n: notification.Notification{ +// ID: notificationID, +// ReceiverSelectors: []map[string]string{ +// { +// "id": "11", +// }, +// }, +// Template: template.ReservedName_SystemDefault, +// }, +// setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { +// rs.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ +// { +// ID: 11, +// Type: testType, +// }, +// }, nil) +// n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]any{}, nil) +// n.EXPECT().GetSystemDefaultTemplate().Return("") +// }, +// want: []notification.Message{ +// { +// Status: notification.MessageStatusEnqueued, +// NotificationIDs: []string{notificationID}, +// ReceiverType: testType, +// Configs: map[string]any{}, +// Details: map[string]any{"notification_type": string("")}, +// MaxTries: 3, +// }, +// }, +// want1: []log.Notification{{ +// NotificationID: notificationID, +// ReceiverID: 11, +// }}, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// var ( +// mockReceiverService = new(mocks.ReceiverService) +// mockNotifier = new(mocks.Notifier) +// mockTemplateService = new(mocks.TemplateService) +// ) +// s := notification.NewRouterReceiverService( +// notification.Deps{ +// Cfg: notification.Config{ +// MaxMessagesReceiverFlow: 10, +// MaxNumReceiverSelectors: 10, +// }, +// Logger: saltlog.NewNoop(), +// ReceiverService: mockReceiverService, +// TemplateService: mockTemplateService, +// }, +// map[string]notification.Notifier{ +// testType: mockNotifier, +// }, +// ) +// if tt.setup != nil { +// tt.setup(mockReceiverService, mockNotifier) +// } +// got, got1, got2, err := s.PrepareMessage(context.TODO(), tt.n) +// if (err != nil) != tt.wantErr { +// t.Errorf("RouterReceiverService.PrepareMessage() error = %v, wantErr %v", err, tt.wantErr) +// return +// } +// if diff := cmp.Diff(got, tt.want, +// cmpopts.IgnoreFields(notification.Message{}, "ID", "CreatedAt", "UpdatedAt"), +// cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { +// t.Errorf("RouterReceiverService.PrepareMessage() diff = %v", diff) +// } +// if diff := cmp.Diff(got1, tt.want1); diff != "" { +// t.Errorf("RouterReceiverService.PrepareMessage() diff = %v", diff) +// } +// if got2 != tt.want2 { +// t.Errorf("RouterReceiverService.PrepareMessage() got2 = %v, want %v", got2, tt.want2) +// } +// }) +// } +// } diff --git a/core/notification/router_subscriber_service.go b/core/notification/router_subscriber_service.go index 6e0c92cd..28c9d0af 100644 --- a/core/notification/router_subscriber_service.go +++ b/core/notification/router_subscriber_service.go @@ -4,9 +4,11 @@ import ( "context" "fmt" + "golang.org/x/exp/maps" + "github.com/goto/siren/core/log" - "github.com/goto/siren/core/silence" - "github.com/goto/siren/pkg/errors" + "github.com/goto/siren/pkg/structure" + "github.com/mitchellh/hashstructure/v2" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -22,13 +24,11 @@ const ( type RouterSubscriberService struct { deps Deps - notifierPlugins map[string]Notifier metricCounterRouterSubscriber metric.Int64Counter } func NewRouterSubscriberService( deps Deps, - notifierPlugins map[string]Notifier, ) *RouterSubscriberService { metricCounterRouterSubscriber, err := otel.Meter("github.com/goto/siren/core/notification"). Int64Counter("siren.notification.dispatch.subscriber") @@ -37,201 +37,10 @@ func NewRouterSubscriberService( } return &RouterSubscriberService{ deps: deps, - notifierPlugins: notifierPlugins, metricCounterRouterSubscriber: metricCounterRouterSubscriber, } } -func (s *RouterSubscriberService) getNotifierPlugin(receiverType string) (Notifier, error) { - notifierPlugin, exist := s.notifierPlugins[receiverType] - if !exist { - return nil, errors.ErrInvalid.WithMsgf("unsupported receiver type: %q", receiverType) - } - return notifierPlugin, nil -} - -func (s *RouterSubscriberService) PrepareMessage(ctx context.Context, n Notification) ([]Message, []log.Notification, bool, error) { - - var ( - messages = make([]Message, 0) - notificationLogs []log.Notification - hasSilenced bool - ) - - subscriptions, err := s.deps.SubscriptionService.MatchByLabels(ctx, n.NamespaceID, n.Labels) - if err != nil { - return nil, nil, false, err - } - - if len(subscriptions) == 0 { - return nil, nil, false, errors.ErrInvalid.WithMsgf("not matching any subscription") - } - - for _, sub := range subscriptions { - - if len(sub.Receivers) == 0 { - s.deps.Logger.Warn(fmt.Sprintf("invalid subscription with id %d, no receiver found", sub.ID)) - continue - } - - var silences []silence.Silence - - // Reliability of silence feature need to be tested more - if s.deps.Cfg.EnableSilenceFeature { - // try silencing by labels - silences, err = s.deps.SilenceService.List(ctx, silence.Filter{ - NamespaceID: n.NamespaceID, - SubscriptionMatch: sub.Match, - }) - if err != nil { - return nil, nil, false, err - } - } - - if len(silences) != 0 { - hasSilenced = true - - var silenceIDs []string - for _, sil := range silences { - silenceIDs = append(silenceIDs, sil.ID) - } - - notificationLogs = append(notificationLogs, log.Notification{ - NamespaceID: n.NamespaceID, - NotificationID: n.ID, - SubscriptionID: sub.ID, - AlertIDs: n.AlertIDs, - SilenceIDs: silenceIDs, - }) - - s.deps.Logger.Info(fmt.Sprintf("notification '%s' of alert ids '%v' is being silenced by labels '%v'", n.ID, n.AlertIDs, silences)) - continue - } - - // Reliability of silence feature need to be tested more - if s.deps.Cfg.EnableSilenceFeature { - // subscription not being silenced by label - silences, err = s.deps.SilenceService.List(ctx, silence.Filter{ - NamespaceID: n.NamespaceID, - SubscriptionID: sub.ID, - }) - if err != nil { - return nil, nil, false, err - } - } - - silencedReceiversMap, validReceivers, err := sub.SilenceReceivers(silences) - if err != nil { - return nil, nil, false, errors.ErrInvalid.WithMsgf(err.Error()) - } - - if len(silencedReceiversMap) != 0 { - hasSilenced = true - - for rcvID, sils := range silencedReceiversMap { - var silenceIDs []string - for _, sil := range sils { - silenceIDs = append(silenceIDs, sil.ID) - } - - notificationLogs = append(notificationLogs, log.Notification{ - NamespaceID: n.NamespaceID, - NotificationID: n.ID, - SubscriptionID: sub.ID, - ReceiverID: rcvID, - AlertIDs: n.AlertIDs, - SilenceIDs: silenceIDs, - }) - } - } - - for _, rcv := range validReceivers { - notifierPlugin, err := s.getNotifierPlugin(rcv.Type) - if err != nil { - return nil, nil, false, err - } - - message, err := InitMessage( - ctx, - notifierPlugin, - s.deps.TemplateService, - n, - rcv.Type, - rcv.Configuration, - InitWithExpiryDuration(n.ValidDuration), - ) - if err != nil { - return nil, nil, false, err - } - - messages = append(messages, message) - notificationLogs = append(notificationLogs, log.Notification{ - NamespaceID: n.NamespaceID, - NotificationID: n.ID, - SubscriptionID: sub.ID, - ReceiverID: rcv.ID, - AlertIDs: n.AlertIDs, - }) - } - } - - return messages, notificationLogs, hasSilenced, nil -} - -func (s *RouterSubscriberService) PrepareMessageV2(ctx context.Context, n Notification) (messages []Message, notificationLogs []log.Notification, hasSilenced bool, err error) { - var metricStatus = metricRouterSubscriberStatusSuccess - - messages = make([]Message, 0) - - defer func() { - s.instrumentDispatchSubscription(ctx, n, metricStatus, err) - }() - - receiversView, err := s.deps.SubscriptionService.MatchByLabelsV2(ctx, n.NamespaceID, n.Labels) - if err != nil { - metricStatus = metricRouterSubscriberStatusMatchError - return nil, nil, false, err - } - - if len(receiversView) == 0 { - metricStatus = metricRouterSubscriberStatusMatchNotFound - return nil, nil, false, errors.ErrInvalid.WithMsgf("not matching any subscription") - } - - for _, rcv := range receiversView { - notifierPlugin, err := s.getNotifierPlugin(rcv.Type) - if err != nil { - metricStatus = metricRouterSubscriberStatusNotifierError - return nil, nil, false, err - } - - message, err := InitMessage( - ctx, - notifierPlugin, - s.deps.TemplateService, - n, - rcv.Type, - rcv.Configurations, - InitWithExpiryDuration(n.ValidDuration), - ) - if err != nil { - metricStatus = metricRouterSubscriberStatusMessageInitError - return nil, nil, false, err - } - - messages = append(messages, message) - notificationLogs = append(notificationLogs, log.Notification{ - NamespaceID: n.NamespaceID, - NotificationID: n.ID, - SubscriptionID: rcv.SubscriptionID, - ReceiverID: rcv.ID, - AlertIDs: n.AlertIDs, - }) - } - - return messages, notificationLogs, hasSilenced, nil -} - func (s *RouterSubscriberService) instrumentDispatchSubscription(ctx context.Context, n Notification, status string, err error) { s.metricCounterRouterSubscriber.Add(ctx, 1, metric.WithAttributes( attribute.String("route.subscriber.status", status), @@ -273,3 +82,94 @@ func (s *RouterSubscriberService) PrepareMetaMessages(ctx context.Context, n Not return metaMessages, notificationLogs, nil } + +// func (s *RouterSubscriberService) PrepareMessages(ctx context.Context, metaMessages []MetaMessage) ([]Message, error) { +// if len(metaMessages) == 0 { +// return []Message{}, nil +// } + +// reducedMetaMessages, err := ReduceMetaMessages(metaMessages, s.deps.Cfg.GroupBy) +// if err != nil { +// return nil, err +// } + +// messages, err := s.RenderMessages(ctx, reducedMetaMessages) +// if err != nil { +// return nil, err +// } + +// return messages, nil +// } + +// func (s *RouterSubscriberService) getNotifierPlugin(receiverType string) (Notifier, error) { +// notifierPlugin, exist := s.notifierPlugins[receiverType] +// if !exist { +// return nil, errors.ErrInvalid.WithMsgf("unsupported receiver type: %q", receiverType) +// } +// return notifierPlugin, nil +// } + +// func (s *RouterSubscriberService) RenderMessages(ctx context.Context, metaMessages []MetaMessage) (messages []Message, err error) { +// for _, mm := range metaMessages { +// notifierPlugin, err := s.getNotifierPlugin(mm.ReceiverType) +// if err != nil { +// return nil, err +// } + +// message, err := InitMessageByMetaMessage( +// ctx, +// notifierPlugin, +// s.deps.TemplateService, +// mm, +// InitWithExpiryDuration(mm.ValidDuration), +// ) +// if err != nil { +// return nil, err +// } + +// messages = append(messages, message) +// } +// return messages, nil +// } + +func ReduceMetaMessages(metaMessages []MetaMessage, groupBy []string) ([]MetaMessage, error) { + var ( + hashedMetaMessagesMap = map[uint64]MetaMessage{} + ) + for _, mm := range metaMessages { + + groupedLabels := structure.BuildGroupLabels(mm.Labels, groupBy) + groupedLabels["_receiver.ID"] = fmt.Sprintf("%d", mm.ReceiverID) + groupedLabels["_notification.template"] = mm.Template + + hash, err := hashstructure.Hash(groupedLabels, hashstructure.FormatV2, nil) + if err != nil { + return nil, fmt.Errorf("cannot get hash from metamessage %v", mm) + } + + if _, ok := hashedMetaMessagesMap[hash]; !ok { + if mm.MergedLabels == nil { + mm.MergedLabels = map[string][]string{} + for k, v := range mm.Labels { + mm.MergedLabels[k] = append(mm.MergedLabels[k], v) + } + } + hashedMetaMessagesMap[hash] = mm + + } else { + hashedMetaMessagesMap[hash] = MergeMetaMessage(mm, hashedMetaMessagesMap[hash]) + } + + } + return maps.Values(hashedMetaMessagesMap), nil +} + +func MergeMetaMessage(from MetaMessage, to MetaMessage) MetaMessage { + var output = to + for k, v := range from.Labels { + output.MergedLabels[k] = append(output.MergedLabels[k], v) + } + output.NotificationIDs = append(output.NotificationIDs, from.NotificationIDs...) + output.SubscriptionIDs = append(output.SubscriptionIDs, from.SubscriptionIDs...) + return output +} diff --git a/core/notification/router_subscriber_service_test.go b/core/notification/router_subscriber_service_test.go index e580ba1e..169f6ca2 100644 --- a/core/notification/router_subscriber_service_test.go +++ b/core/notification/router_subscriber_service_test.go @@ -1,494 +1,476 @@ package notification_test -import ( - "context" - "errors" - "testing" +// func TestRouterSubscriberService_PrepareMessage(t *testing.T) { +// tests := []struct { +// name string +// setup func(*mocks.SubscriptionService, *mocks.SilenceService, *mocks.Notifier) +// n notification.Notification +// want []notification.Message +// want1 []log.Notification +// want2 bool +// wantErr bool +// }{ +// { +// name: "should return error if subscription service match by labels return error", +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, errors.New("some error")) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if no matching subscriptions", +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, nil) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if match subscription exist but list silences return error", +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ +// { +// ID: 123, +// Receivers: []subscription.Receiver{ +// { +// ID: 1, +// }, +// }, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if match subscription exist but list silences by label return error", +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ +// { +// ID: 123, +// Receivers: []subscription.Receiver{ +// { +// ID: 1, +// }, +// }, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) +// }, +// wantErr: true, +// }, +// { +// name: "should return no error if silenced by labels success", +// n: notification.Notification{ +// NamespaceID: 1, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ +// { +// ID: 123, +// Match: map[string]string{ +// "k1": "v1", +// }, +// Receivers: []subscription.Receiver{ +// { +// ID: 1, +// }, +// }, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionMatch: map[string]string{ +// "k1": "v1", +// }, +// }).Return([]silence.Silence{ +// { +// ID: "silence-id", +// NamespaceID: 1, +// TargetID: 123, +// }, +// }, nil) +// }, +// want: []notification.Message{}, +// want1: []log.Notification{{SubscriptionID: 123, NamespaceID: 1, SilenceIDs: []string{"silence-id"}}}, +// want2: true, +// }, +// { +// name: "should return error if silenced by subscription return error", +// n: notification.Notification{ +// NamespaceID: 1, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ +// { +// ID: 123, +// Namespace: 1, +// Match: map[string]string{ +// "k1": "v1", +// }, +// Receivers: []subscription.Receiver{ +// { +// ID: 1, +// }, +// }, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionMatch: map[string]string{ +// "k1": "v1", +// }, +// }).Return(nil, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionID: 123, +// }).Return([]silence.Silence{ +// { +// ID: "silence-id", +// NamespaceID: 1, +// }, +// }, nil) +// }, +// wantErr: true, +// }, +// { +// name: "should return no error if silenced by subscription success", +// n: notification.Notification{ +// NamespaceID: 1, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ +// { +// ID: 123, +// Namespace: 1, +// Match: map[string]string{ +// "k1": "v1", +// }, +// Receivers: []subscription.Receiver{ +// { +// ID: 1, +// }, +// }, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionMatch: map[string]string{ +// "k1": "v1", +// }, +// }).Return(nil, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionID: 123, +// }).Return([]silence.Silence{ +// { +// ID: "silence-id", +// NamespaceID: 1, +// Type: silence.TypeSubscription, +// }, +// }, nil) +// }, +// want: []notification.Message{}, +// want1: []log.Notification{{SubscriptionID: 123, NamespaceID: 1, ReceiverID: 1, SilenceIDs: []string{"silence-id"}}}, +// want2: true, +// }, +// { +// name: "should return error if receiver type is unknown", +// n: notification.Notification{ +// NamespaceID: 1, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ +// { +// ID: 123, +// Namespace: 1, +// Match: map[string]string{ +// "k1": "v1", +// }, +// Receivers: []subscription.Receiver{ +// { +// ID: 1, +// }, +// }, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionMatch: map[string]string{ +// "k1": "v1", +// }, +// }).Return(nil, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionID: 123, +// }).Return(nil, nil) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if init messages return error", +// n: notification.Notification{ +// NamespaceID: 1, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ +// { +// ID: 123, +// Namespace: 1, +// Match: map[string]string{ +// "k1": "v1", +// }, +// Receivers: []subscription.Receiver{ +// { +// ID: 1, +// Type: testType, +// }, +// }, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionMatch: map[string]string{ +// "k1": "v1", +// }, +// }).Return(nil, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionID: 123, +// }).Return(nil, nil) +// n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) +// }, +// wantErr: true, +// }, +// { +// name: "should return no error if all flow passed and no silences", +// n: notification.Notification{ +// ID: "aa", +// NamespaceID: 1, +// Type: receiver.TypeHTTP, +// Template: template.ReservedName_SystemDefault, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ +// { +// ID: 123, +// Namespace: 1, +// Match: map[string]string{ +// "k1": "v1", +// }, +// Receivers: []subscription.Receiver{ +// { +// ID: 1, +// Type: testType, +// }, +// }, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionMatch: map[string]string{ +// "k1": "v1", +// }, +// }).Return(nil, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ +// NamespaceID: 1, +// SubscriptionID: 123, +// }).Return(nil, nil) +// n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]any{}, nil) +// n.EXPECT().GetSystemDefaultTemplate().Return("") +// }, +// want: []notification.Message{ +// { +// Status: notification.MessageStatusEnqueued, +// NotificationIDs: []string{"aa"}, +// ReceiverType: testType, +// Configs: map[string]any{}, +// Details: map[string]any{"notification_type": string("http")}, +// MaxTries: 3, +// }, +// }, +// want1: []log.Notification{{NamespaceID: 1, NotificationID: "aa", SubscriptionID: 123, ReceiverID: 1}}, +// want2: false, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// var ( +// mockSubscriptionService = new(mocks.SubscriptionService) +// mockSilenceService = new(mocks.SilenceService) +// mockNotifier = new(mocks.Notifier) +// mockTemplateService = new(mocks.TemplateService) +// ) +// s := notification.NewRouterSubscriberService( +// notification.Deps{ +// Cfg: notification.Config{ +// EnableSilenceFeature: true, +// }, +// Logger: saltlog.NewNoop(), +// SubscriptionService: mockSubscriptionService, +// SilenceService: mockSilenceService, +// TemplateService: mockTemplateService, +// }, +// map[string]notification.Notifier{ +// testType: mockNotifier, +// }, +// ) - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - saltlog "github.com/goto/salt/log" - "github.com/goto/siren/core/log" - "github.com/goto/siren/core/notification" - "github.com/goto/siren/core/notification/mocks" - "github.com/goto/siren/core/receiver" - "github.com/goto/siren/core/silence" - "github.com/goto/siren/core/subscription" - "github.com/goto/siren/core/template" - "github.com/stretchr/testify/mock" -) +// if tt.setup != nil { +// tt.setup(mockSubscriptionService, mockSilenceService, mockNotifier) +// } -func TestRouterSubscriberService_PrepareMessage(t *testing.T) { - tests := []struct { - name string - setup func(*mocks.SubscriptionService, *mocks.SilenceService, *mocks.Notifier) - n notification.Notification - want []notification.Message - want1 []log.Notification - want2 bool - wantErr bool - }{ - { - name: "should return error if subscription service match by labels return error", - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return error if no matching subscriptions", - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, nil) - }, - wantErr: true, - }, - { - name: "should return error if match subscription exist but list silences return error", - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ - { - ID: 123, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return error if match subscription exist but list silences by label return error", - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ - { - ID: 123, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return no error if silenced by labels success", - n: notification.Notification{ - NamespaceID: 1, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ - { - ID: 123, - Match: map[string]string{ - "k1": "v1", - }, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return([]silence.Silence{ - { - ID: "silence-id", - NamespaceID: 1, - TargetID: 123, - }, - }, nil) - }, - want: []notification.Message{}, - want1: []log.Notification{{SubscriptionID: 123, NamespaceID: 1, SilenceIDs: []string{"silence-id"}}}, - want2: true, - }, - { - name: "should return error if silenced by subscription return error", - n: notification.Notification{ - NamespaceID: 1, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ - { - ID: 123, - Namespace: 1, - Match: map[string]string{ - "k1": "v1", - }, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionID: 123, - }).Return([]silence.Silence{ - { - ID: "silence-id", - NamespaceID: 1, - }, - }, nil) - }, - wantErr: true, - }, - { - name: "should return no error if silenced by subscription success", - n: notification.Notification{ - NamespaceID: 1, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ - { - ID: 123, - Namespace: 1, - Match: map[string]string{ - "k1": "v1", - }, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionID: 123, - }).Return([]silence.Silence{ - { - ID: "silence-id", - NamespaceID: 1, - Type: silence.TypeSubscription, - }, - }, nil) - }, - want: []notification.Message{}, - want1: []log.Notification{{SubscriptionID: 123, NamespaceID: 1, ReceiverID: 1, SilenceIDs: []string{"silence-id"}}}, - want2: true, - }, - { - name: "should return error if receiver type is unknown", - n: notification.Notification{ - NamespaceID: 1, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ - { - ID: 123, - Namespace: 1, - Match: map[string]string{ - "k1": "v1", - }, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionID: 123, - }).Return(nil, nil) - }, - wantErr: true, - }, - { - name: "should return error if init messages return error", - n: notification.Notification{ - NamespaceID: 1, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ - { - ID: 123, - Namespace: 1, - Match: map[string]string{ - "k1": "v1", - }, - Receivers: []subscription.Receiver{ - { - ID: 1, - Type: testType, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionID: 123, - }).Return(nil, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return no error if all flow passed and no silences", - n: notification.Notification{ - ID: "aa", - NamespaceID: 1, - Type: receiver.TypeHTTP, - Template: template.ReservedName_SystemDefault, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.Subscription{ - { - ID: 123, - Namespace: 1, - Match: map[string]string{ - "k1": "v1", - }, - Receivers: []subscription.Receiver{ - { - ID: 1, - Type: testType, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionID: 123, - }).Return(nil, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]any{}, nil) - n.EXPECT().GetSystemDefaultTemplate().Return("") - }, - want: []notification.Message{ - { - Status: notification.MessageStatusEnqueued, - NotificationIDs: []string{"aa"}, - ReceiverType: testType, - Configs: map[string]any{}, - Details: map[string]any{"notification_type": string("http")}, - MaxTries: 3, - }, - }, - want1: []log.Notification{{NamespaceID: 1, NotificationID: "aa", SubscriptionID: 123, ReceiverID: 1}}, - want2: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockSubscriptionService = new(mocks.SubscriptionService) - mockSilenceService = new(mocks.SilenceService) - mockNotifier = new(mocks.Notifier) - mockTemplateService = new(mocks.TemplateService) - ) - s := notification.NewRouterSubscriberService( - notification.Deps{ - Cfg: notification.Config{ - EnableSilenceFeature: true, - }, - Logger: saltlog.NewNoop(), - SubscriptionService: mockSubscriptionService, - SilenceService: mockSilenceService, - TemplateService: mockTemplateService, - }, - map[string]notification.Notifier{ - testType: mockNotifier, - }, - ) +// got, got1, got2, err := s.PrepareMessage(context.TODO(), tt.n) +// if (err != nil) != tt.wantErr { +// t.Errorf("RouterSubscriberService.PrepareMessage() error = %v, wantErr %v", err, tt.wantErr) +// return +// } +// if diff := cmp.Diff(got, tt.want, +// cmpopts.IgnoreFields(notification.Message{}, "ID", "CreatedAt", "UpdatedAt"), +// cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { +// t.Errorf("RouterSubscriberService.PrepareMessage() diff = %v", diff) +// } +// if diff := cmp.Diff(got1, tt.want1); diff != "" { +// t.Errorf("RouterSubscriberService.PrepareMessage() diff = %v", diff) +// } +// if got2 != tt.want2 { +// t.Errorf("RouterSubscriberService.PrepareMessage() got2 = %v, want %v", got2, tt.want2) +// } +// }) +// } +// } - if tt.setup != nil { - tt.setup(mockSubscriptionService, mockSilenceService, mockNotifier) - } +// func TestRouterSubscriberService_PrepareMessageV2(t *testing.T) { +// tests := []struct { +// name string +// setup func(*mocks.SubscriptionService, *mocks.SilenceService, *mocks.Notifier) +// n notification.Notification +// want []notification.Message +// want1 []log.Notification +// want2 bool +// wantErr bool +// }{ +// { +// name: "should return error if subscription service match by labels return error", +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, errors.New("some error")) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if no matching subscriptions", +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, nil) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if receiver type is unknown", +// n: notification.Notification{ +// NamespaceID: 1, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) +// ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.ReceiverView{ +// { +// ID: 1, +// }, +// }, nil) +// }, +// wantErr: true, +// }, +// { +// name: "should return error if init messages return error", +// n: notification.Notification{ +// NamespaceID: 1, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.ReceiverView{ +// { +// ID: 1, +// Type: testType, +// }, +// }, nil) +// ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) +// n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) +// }, +// wantErr: true, +// }, +// { +// name: "should return no error if all flow passed and no silences", +// n: notification.Notification{ +// ID: "aa", +// NamespaceID: 1, +// Type: receiver.TypeHTTP, +// Template: template.ReservedName_SystemDefault, +// }, +// setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { +// ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.ReceiverView{ +// { +// ID: 1, +// Type: testType, +// SubscriptionID: 123, +// }, +// }, nil) +// n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]any{}, nil) +// n.EXPECT().GetSystemDefaultTemplate().Return("") +// }, +// want: []notification.Message{ +// { +// NotificationIDs: []string{"aa"}, +// Status: notification.MessageStatusEnqueued, +// ReceiverType: testType, +// Configs: map[string]any{}, +// Details: map[string]any{"notification_type": string("http")}, +// MaxTries: 3, +// }, +// }, +// want1: []log.Notification{{NamespaceID: 1, NotificationID: "aa", SubscriptionID: 123, ReceiverID: 1}}, +// want2: false, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// var ( +// mockSubscriptionService = new(mocks.SubscriptionService) +// mockNotifier = new(mocks.Notifier) +// mockSilenceService = new(mocks.SilenceService) +// mockTemplateService = new(mocks.TemplateService) +// ) +// s := notification.NewRouterSubscriberService( +// notification.Deps{ +// Cfg: notification.Config{ +// EnableSilenceFeature: true, +// }, +// Logger: saltlog.NewNoop(), +// SubscriptionService: mockSubscriptionService, +// SilenceService: mockSilenceService, +// TemplateService: mockTemplateService, +// }, +// map[string]notification.Notifier{ +// testType: mockNotifier, +// }, +// ) - got, got1, got2, err := s.PrepareMessage(context.TODO(), tt.n) - if (err != nil) != tt.wantErr { - t.Errorf("RouterSubscriberService.PrepareMessage() error = %v, wantErr %v", err, tt.wantErr) - return - } - if diff := cmp.Diff(got, tt.want, - cmpopts.IgnoreFields(notification.Message{}, "ID", "CreatedAt", "UpdatedAt"), - cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { - t.Errorf("RouterSubscriberService.PrepareMessage() diff = %v", diff) - } - if diff := cmp.Diff(got1, tt.want1); diff != "" { - t.Errorf("RouterSubscriberService.PrepareMessage() diff = %v", diff) - } - if got2 != tt.want2 { - t.Errorf("RouterSubscriberService.PrepareMessage() got2 = %v, want %v", got2, tt.want2) - } - }) - } -} +// if tt.setup != nil { +// tt.setup(mockSubscriptionService, mockSilenceService, mockNotifier) +// } -func TestRouterSubscriberService_PrepareMessageV2(t *testing.T) { - tests := []struct { - name string - setup func(*mocks.SubscriptionService, *mocks.SilenceService, *mocks.Notifier) - n notification.Notification - want []notification.Message - want1 []log.Notification - want2 bool - wantErr bool - }{ - { - name: "should return error if subscription service match by labels return error", - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return error if no matching subscriptions", - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, nil) - }, - wantErr: true, - }, - { - name: "should return error if receiver type is unknown", - n: notification.Notification{ - NamespaceID: 1, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) - ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.ReceiverView{ - { - ID: 1, - }, - }, nil) - }, - wantErr: true, - }, - { - name: "should return error if init messages return error", - n: notification.Notification{ - NamespaceID: 1, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.ReceiverView{ - { - ID: 1, - Type: testType, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return no error if all flow passed and no silences", - n: notification.Notification{ - ID: "aa", - NamespaceID: 1, - Type: receiver.TypeHTTP, - Template: template.ReservedName_SystemDefault, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabelsV2(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return([]subscription.ReceiverView{ - { - ID: 1, - Type: testType, - SubscriptionID: 123, - }, - }, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]any{}, nil) - n.EXPECT().GetSystemDefaultTemplate().Return("") - }, - want: []notification.Message{ - { - NotificationIDs: []string{"aa"}, - Status: notification.MessageStatusEnqueued, - ReceiverType: testType, - Configs: map[string]any{}, - Details: map[string]any{"notification_type": string("http")}, - MaxTries: 3, - }, - }, - want1: []log.Notification{{NamespaceID: 1, NotificationID: "aa", SubscriptionID: 123, ReceiverID: 1}}, - want2: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockSubscriptionService = new(mocks.SubscriptionService) - mockNotifier = new(mocks.Notifier) - mockSilenceService = new(mocks.SilenceService) - mockTemplateService = new(mocks.TemplateService) - ) - s := notification.NewRouterSubscriberService( - notification.Deps{ - Cfg: notification.Config{ - EnableSilenceFeature: true, - }, - Logger: saltlog.NewNoop(), - SubscriptionService: mockSubscriptionService, - SilenceService: mockSilenceService, - TemplateService: mockTemplateService, - }, - map[string]notification.Notifier{ - testType: mockNotifier, - }, - ) - - if tt.setup != nil { - tt.setup(mockSubscriptionService, mockSilenceService, mockNotifier) - } - - got, got1, got2, err := s.PrepareMessageV2(context.TODO(), tt.n) - if (err != nil) != tt.wantErr { - t.Errorf("RouterSubscriberService.PrepareMessageV2() error = %v, wantErr %v", err, tt.wantErr) - return - } - if diff := cmp.Diff(got, tt.want, - cmpopts.IgnoreFields(notification.Message{}, "ID", "CreatedAt", "UpdatedAt"), - cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { - t.Errorf("RouterSubscriberService.PrepareMessageV2() diff = %v", diff) - } - if diff := cmp.Diff(got1, tt.want1); diff != "" { - t.Errorf("RouterSubscriberService.PrepareMessageV2() diff = %v", diff) - } - if got2 != tt.want2 { - t.Errorf("RouterSubscriberService.PrepareMessageV2() got2 = %v, want %v", got2, tt.want2) - } - }) - } -} +// got, got1, got2, err := s.PrepareMessageV2(context.TODO(), tt.n) +// if (err != nil) != tt.wantErr { +// t.Errorf("RouterSubscriberService.PrepareMessageV2() error = %v, wantErr %v", err, tt.wantErr) +// return +// } +// if diff := cmp.Diff(got, tt.want, +// cmpopts.IgnoreFields(notification.Message{}, "ID", "CreatedAt", "UpdatedAt"), +// cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { +// t.Errorf("RouterSubscriberService.PrepareMessageV2() diff = %v", diff) +// } +// if diff := cmp.Diff(got1, tt.want1); diff != "" { +// t.Errorf("RouterSubscriberService.PrepareMessageV2() diff = %v", diff) +// } +// if got2 != tt.want2 { +// t.Errorf("RouterSubscriberService.PrepareMessageV2() got2 = %v, want %v", got2, tt.want2) +// } +// }) +// } +// } diff --git a/core/notification/service.go b/core/notification/service.go index a81b4f6e..951ae12d 100644 --- a/core/notification/service.go +++ b/core/notification/service.go @@ -2,6 +2,8 @@ package notification import ( "context" + "encoding/json" + "fmt" "time" saltlog "github.com/goto/salt/log" @@ -15,8 +17,6 @@ import ( ) type Router interface { - PrepareMessage(ctx context.Context, n Notification) ([]Message, []log.Notification, bool, error) - PrepareMessageV2(ctx context.Context, n Notification) ([]Message, []log.Notification, bool, error) PrepareMetaMessages(ctx context.Context, n Notification) (metaMessages []MetaMessage, notificationLogs []log.Notification, err error) } @@ -51,8 +51,9 @@ type TemplateService interface { // Service is a service for notification domain type Service struct { - deps Deps - dispatchServiceMap map[string]Dispatcher + deps Deps + routerMap map[string]Router + notifierPlugins map[string]Notifier } type Deps struct { @@ -72,21 +73,70 @@ type Deps struct { // NewService creates a new notification service func NewService( deps Deps, - dispatchServiceMap map[string]Dispatcher, + routerMap map[string]Router, + notifierPlugins map[string]Notifier, ) *Service { return &Service{ - deps: deps, - dispatchServiceMap: dispatchServiceMap, + deps: deps, + routerMap: routerMap, + notifierPlugins: notifierPlugins, } } -func (s *Service) Dispatch(ctx context.Context, ns []Notification, dispatcherKind string) ([]string, error) { +// func (s *Service) DispatchBulkNotifications(ctx context.Context, ns []Notification) ([]string, error) { +// ctx = s.deps.Repository.WithTransaction(ctx) +// insertedNotifications, err := s.deps.Repository.BulkCreate(ctx, ns) +// if err != nil { +// return nil, err +// } + +// ids, err := s.dispatchBulkNotificationsService.Dispatch(ctx, insertedNotifications) +// if err != nil { +// if err := s.deps.Repository.Rollback(ctx, err); err != nil { +// return nil, err +// } +// return nil, err +// } + +// if err := s.deps.Repository.Commit(ctx); err != nil { +// return nil, err +// } + +// return ids, nil +// } + +// func (s *Service) DispatchSingleNotification(ctx context.Context, ns []Notification) ([]string, error) { +// ctx = s.deps.Repository.WithTransaction(ctx) + +// insertedNotifications, err := s.deps.Repository.BulkCreate(ctx, ns) +// if err != nil { +// return nil, err +// } + +// ids, err := s.dispatchSingleNotificationService.Dispatch(ctx, insertedNotifications) +// if err != nil { +// if err := s.deps.Repository.Rollback(ctx, err); err != nil { +// return nil, err +// } +// return nil, err +// } + +// if err := s.deps.Repository.Commit(ctx); err != nil { +// return nil, err +// } + +// return ids, nil +// } + +func (s *Service) Dispatch(ctx context.Context, ns []Notification) ([]string, error) { ctx = s.deps.Repository.WithTransaction(ctx) - selectedDispatcher, exist := s.dispatchServiceMap[dispatcherKind] - if !exist { - return nil, errors.ErrInvalid.WithMsgf("unsupported notification dispatcher: %q", dispatcherKind) + + insertedNotifications, err := s.deps.Repository.BulkCreate(ctx, ns) + if err != nil { + return nil, err } - ids, err := selectedDispatcher.Dispatch(ctx, ns) + + ids, err := s.dispatchInternal(ctx, insertedNotifications) if err != nil { if err := s.deps.Repository.Rollback(ctx, err); err != nil { return nil, err @@ -161,3 +211,134 @@ func (s *Service) List(ctx context.Context, flt Filter) ([]Notification, error) return notifications, err } + +func (s *Service) getRouter(notificationRouterKind string) (Router, error) { + selectedRouter, exist := s.routerMap[notificationRouterKind] + if !exist { + return nil, errors.ErrInvalid.WithMsgf("unsupported notification router kind: %q", notificationRouterKind) + } + return selectedRouter, nil +} + +func (s *Service) dispatchInternal(ctx context.Context, ns []Notification) (notificationIDs []string, err error) { + // defer func() { + // s.instrumentNumberBulkNotification(ctx, len(ns), err) + // }() + + var ( + metaMessages []MetaMessage + notificationLogs []log.Notification + ) + + for _, n := range ns { + flow := "unknown" + if len(n.ReceiverSelectors) != 0 { + flow = RouterReceiver + } else if len(n.Labels) != 0 { + flow = RouterSubscriber + } else { + return nil, errors.ErrInvalid.WithMsgf("no receivers or labels found, unknown flow") + } + + // NOTE: never invalid cause we have checked above + if err := n.Validate(flow); err != nil { + return nil, err + } + + // TODO: test if flow is not recognized + router, err := s.getRouter(flow) + if err != nil { + return nil, err + } + + generatedMetaMessages, generatedNotificationLogs, err := router.PrepareMetaMessages(ctx, n) + if err != nil { + if errors.Is(err, ErrRouteSubscriberNoMatchFound) { + errMessage := fmt.Sprintf("not matching any subscription for notification: %v", n) + nJson, err := json.MarshalIndent(n, "", " ") + if err == nil { + errMessage = fmt.Sprintf("not matching any subscription for notification: %s", string(nJson)) + } + s.deps.Logger.Warn(errMessage) + continue + } + return nil, err + } + + metaMessages = append(metaMessages, generatedMetaMessages...) + notificationLogs = append(notificationLogs, generatedNotificationLogs...) + } + + messages, err := s.PrepareMessages(ctx, metaMessages) + if err != nil { + return nil, err + } + + if len(messages) == 0 { + s.deps.Logger.Info("no messages to process") + return nil, ErrNoMessage + } + + if err := s.deps.LogService.LogNotifications(ctx, notificationLogs...); err != nil { + return nil, fmt.Errorf("failed logging notifications: %w", err) + } + + if err := s.deps.Q.Enqueue(ctx, messages...); err != nil { + return nil, fmt.Errorf("failed enqueuing messages: %w", err) + } + + for _, n := range ns { + notificationIDs = append(notificationIDs, n.ID) + } + + return notificationIDs, nil +} + +func (s *Service) PrepareMessages(ctx context.Context, metaMessages []MetaMessage) ([]Message, error) { + if len(metaMessages) == 0 { + return []Message{}, nil + } + + reducedMetaMessages, err := ReduceMetaMessages(metaMessages, s.deps.Cfg.GroupBy) + if err != nil { + return nil, err + } + + messages, err := s.RenderMessages(ctx, reducedMetaMessages) + if err != nil { + return nil, err + } + + return messages, nil +} + +func (s *Service) RenderMessages(ctx context.Context, metaMessages []MetaMessage) (messages []Message, err error) { + for _, mm := range metaMessages { + notifierPlugin, err := s.getNotifierPlugin(mm.ReceiverType) + if err != nil { + return nil, err + } + + message, err := InitMessageByMetaMessage( + ctx, + notifierPlugin, + s.deps.TemplateService, + mm, + InitWithExpiryDuration(mm.ValidDuration), + ) + if err != nil { + return nil, err + } + + messages = append(messages, message) + } + return messages, nil +} + +func (s *Service) getNotifierPlugin(receiverType string) (Notifier, error) { + notifierPlugin, exist := s.notifierPlugins[receiverType] + if !exist { + return nil, errors.ErrInvalid.WithMsgf("unsupported receiver type: %q", receiverType) + } + return notifierPlugin, nil +} diff --git a/core/notification/service_test.go b/core/notification/service_test.go index 1283f916..854abb87 100644 --- a/core/notification/service_test.go +++ b/core/notification/service_test.go @@ -2,12 +2,16 @@ package notification_test import ( "context" + "errors" "testing" + "github.com/google/go-cmp/cmp" saltlog "github.com/goto/salt/log" + "github.com/goto/siren/core/log" "github.com/goto/siren/core/notification" "github.com/goto/siren/core/notification/mocks" - "github.com/goto/siren/pkg/errors" + "github.com/goto/siren/core/receiver" + "github.com/goto/siren/core/template" "github.com/stretchr/testify/mock" ) @@ -17,13 +21,13 @@ const ( func TestService_DispatchFailure(t *testing.T) { tests := []struct { - name string - n []notification.Notification - setup func([]notification.Notification, *mocks.Repository, *mocks.AlertRepository, *mocks.LogService, *mocks.AlertService, *mocks.Queuer, *mocks.Dispatcher) - wantErr bool + name string + n []notification.Notification + setup func([]notification.Notification, *mocks.Repository, *mocks.AlertRepository, *mocks.LogService, *mocks.Queuer, *mocks.Router, *mocks.Notifier) + wantErrStr string }{ { - name: "should return error if repository return error", + name: "should return error if bulk create return error", n: []notification.Notification{ { Type: notification.TypeAlert, @@ -32,14 +36,31 @@ func TestService_DispatchFailure(t *testing.T) { }, }, }, - setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, _ *mocks.LogService, _ *mocks.AlertService, _ *mocks.Queuer, _ *mocks.Dispatcher) { + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, _ *mocks.LogService, _ *mocks.Queuer, _ *mocks.Router, _ *mocks.Notifier) { r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, errors.New("some error")) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, errors.New("some error")) }, - wantErr: true, + wantErrStr: "some error", }, { - name: "should return error if dispatcher service return error", + name: "should return error if receiver selectors and labels are empty", + n: []notification.Notification{ + { + Type: notification.TypeAlert, + }, + }, + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, _ *mocks.LogService, _ *mocks.Queuer, _ *mocks.Router, _ *mocks.Notifier) { + r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("errors.Error")).Return(nil) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ + { + Type: notification.TypeAlert, + }, + }, nil) + }, + wantErrStr: "no receivers or labels found, unknown flow", + }, + { + name: "should return error if router prepare meta messages return generic error", n: []notification.Notification{ { Type: notification.TypeAlert, @@ -48,15 +69,22 @@ func TestService_DispatchFailure(t *testing.T) { }, }, }, - setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, _ *mocks.LogService, _ *mocks.AlertService, _ *mocks.Queuer, d *mocks.Dispatcher) { + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, _ *mocks.LogService, _ *mocks.Queuer, ro *mocks.Router, _ *mocks.Notifier) { r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, errors.New("some error")) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ + { + Type: notification.TypeAlert, + Labels: map[string]string{ + "k1": "v1", + }, + }, + }, nil) + ro.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, nil, errors.New("some error")) }, - wantErr: true, + wantErrStr: "some error", }, { - name: "should return error if dispatcher service return empty results", + name: "should pass if router prepare meta messages return ErrRouteSubscriberNoMatchFound and return ErrNoMessages", n: []notification.Notification{ { Type: notification.TypeAlert, @@ -65,15 +93,22 @@ func TestService_DispatchFailure(t *testing.T) { }, }, }, - setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, _ *mocks.LogService, _ *mocks.AlertService, _ *mocks.Queuer, d *mocks.Dispatcher) { + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, _ *mocks.LogService, _ *mocks.Queuer, ro *mocks.Router, _ *mocks.Notifier) { r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, nil) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ + { + Type: notification.TypeAlert, + Labels: map[string]string{ + "k1": "v1", + }, + }, + }, nil) + ro.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, nil, notification.ErrRouteSubscriberNoMatchFound) }, - wantErr: true, + wantErrStr: notification.ErrNoMessage.Error(), }, { - name: "should return error if log notifications return error", + name: "should return error if prepare messages return error", n: []notification.Notification{ { Type: notification.TypeAlert, @@ -82,16 +117,30 @@ func TestService_DispatchFailure(t *testing.T) { }, }, }, - setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, l *mocks.LogService, _ *mocks.AlertService, _ *mocks.Queuer, d *mocks.Dispatcher) { - r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("*fmt.wrapError")).Return(nil) - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]string{"123"}, nil) - l.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(errors.New("some error")) + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, _ *mocks.LogService, _ *mocks.Queuer, ro *mocks.Router, no *mocks.Notifier) { + r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ + { + Type: notification.TypeAlert, + Labels: map[string]string{ + "k1": "v1", + }, + }, + }, nil) + ro.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.MetaMessage{ + { + ReceiverID: 1, + SubscriptionIDs: []uint64{1, 2}, + ReceiverType: receiver.TypeSlack, + }, + }, []log.Notification{}, nil) + no.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.Anything).Return(nil, errors.New("some error")) + }, - wantErr: true, + wantErrStr: "some error", }, { - name: "should return error if update alerts silence status return error", + name: "should return error if template not found", n: []notification.Notification{ { Type: notification.TypeAlert, @@ -100,14 +149,69 @@ func TestService_DispatchFailure(t *testing.T) { }, }, }, - setup: func(n []notification.Notification, r *mocks.Repository, ar *mocks.AlertRepository, l *mocks.LogService, a *mocks.AlertService, _ *mocks.Queuer, d *mocks.Dispatcher) { + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, l *mocks.LogService, _ *mocks.Queuer, ro *mocks.Router, no *mocks.Notifier) { + r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("errors.Error")).Return(nil) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ + { + Type: notification.TypeAlert, + Labels: map[string]string{ + "k1": "v1", + }, + }, + }, nil) + ro.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.MetaMessage{ + { + ReceiverID: 1, + SubscriptionIDs: []uint64{1, 2}, + ReceiverType: receiver.TypeSlack, + }, + }, []log.Notification{ + { + ID: "1", + }, + }, nil) + no.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.Anything).Return(map[string]any{}, nil) + no.EXPECT().GetSystemDefaultTemplate().Return("system-default") + }, + wantErrStr: "found no template, template is mandatory", + }, + { + name: "should return error if log notifications return error", + n: []notification.Notification{ + { + Type: notification.TypeAlert, + Labels: map[string]string{ + "k1": "v1", + }, + }, + }, + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, l *mocks.LogService, _ *mocks.Queuer, ro *mocks.Router, no *mocks.Notifier) { r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("*fmt.wrapError")).Return(nil) - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]string{"123"}, nil) - l.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - ar.EXPECT().BulkUpdateSilence(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("string")).Return(errors.New("some error")) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ + { + Type: notification.TypeAlert, + Labels: map[string]string{ + "k1": "v1", + }, + }, + }, nil) + ro.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.MetaMessage{ + { + ReceiverID: 1, + SubscriptionIDs: []uint64{1, 2}, + ReceiverType: receiver.TypeSlack, + Template: template.ReservedName_SystemDefault, + }, + }, []log.Notification{ + { + ID: "1", + }, + }, nil) + no.EXPECT().GetSystemDefaultTemplate().Return("") + no.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.Anything).Return(map[string]any{}, nil) + l.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(errors.New("some error")) }, - wantErr: true, + wantErrStr: "failed logging notifications: some error", }, { name: "should return error if enqueue return error", @@ -119,15 +223,30 @@ func TestService_DispatchFailure(t *testing.T) { }, }, }, - setup: func(n []notification.Notification, r *mocks.Repository, ar *mocks.AlertRepository, l *mocks.LogService, a *mocks.AlertService, q *mocks.Queuer, d *mocks.Dispatcher) { + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, l *mocks.LogService, q *mocks.Queuer, ro *mocks.Router, no *mocks.Notifier) { r.EXPECT().Rollback(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("*fmt.wrapError")).Return(nil) - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]string{"123"}, nil) - l.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - ar.EXPECT().BulkUpdateSilence(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("string")).Return(errors.New("some error")) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ + { + Type: notification.TypeAlert, + Labels: map[string]string{ + "k1": "v1", + }, + }, + }, nil) + ro.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.MetaMessage{ + { + ReceiverID: 1, + SubscriptionIDs: []uint64{1, 2}, + ReceiverType: receiver.TypeSlack, + Template: template.ReservedName_SystemDefault, + }, + }, []log.Notification{}, nil) + no.EXPECT().GetSystemDefaultTemplate().Return("") + no.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.Anything).Return(map[string]any{}, nil) + l.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx")).Return(nil) q.EXPECT().Enqueue(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error")) }, - wantErr: true, + wantErrStr: "failed enqueuing messages: some error", }, } for _, tt := range tests { @@ -137,13 +256,13 @@ func TestService_DispatchFailure(t *testing.T) { mockRepository = new(mocks.Repository) mockAlertRepository = new(mocks.AlertRepository) mockLogService = new(mocks.LogService) - mockAlertService = new(mocks.AlertService) - mockDispatcher = new(mocks.Dispatcher) + mockRouter = new(mocks.Router) + mockNotifier = new(mocks.Notifier) ) if tt.setup != nil { mockRepository.EXPECT().WithTransaction(mock.AnythingOfType("context.todoCtx")).Return(context.TODO()) - tt.setup(tt.n, mockRepository, mockAlertRepository, mockLogService, mockAlertService, mockQueuer, mockDispatcher) + tt.setup(tt.n, mockRepository, mockAlertRepository, mockLogService, mockQueuer, mockRouter, mockNotifier) } s := notification.NewService( @@ -157,12 +276,20 @@ func TestService_DispatchFailure(t *testing.T) { AlertRepository: mockAlertRepository, LogService: mockLogService, }, - map[string]notification.Dispatcher{ - testType: mockDispatcher, + map[string]notification.Router{ + testType: mockRouter, + notification.RouterSubscriber: mockRouter, + }, + map[string]notification.Notifier{ + receiver.TypeSlack: mockNotifier, }, ) - if _, err := s.Dispatch(context.TODO(), tt.n, notification.DispatchKindSingleNotification); (err != nil) != tt.wantErr { - t.Errorf("Service.DispatchFailure() error = %v, wantErr %v", err, tt.wantErr) + if _, err := s.Dispatch(context.TODO(), tt.n); err != nil { + if err.Error() != tt.wantErrStr { + t.Errorf("Service.DispatchFailure() error = %v, wantErr %v", err, tt.wantErrStr) + } + } else { + t.Errorf("Service.DispatchFailure() error = %v, wantErr %v", err, tt.wantErrStr) } }) } @@ -170,28 +297,66 @@ func TestService_DispatchFailure(t *testing.T) { func TestService_DispatchSuccess(t *testing.T) { tests := []struct { - name string - n []notification.Notification - setup func([]notification.Notification, *mocks.Repository, *mocks.AlertRepository, *mocks.LogService, *mocks.AlertService, *mocks.Queuer, *mocks.Dispatcher) - wantErr bool + name string + n []notification.Notification + setup func([]notification.Notification, *mocks.Repository, *mocks.AlertRepository, *mocks.LogService, *mocks.Queuer, *mocks.Router, *mocks.Notifier) + wantResult []string }{ { - name: "should return no error if enqueue success", + name: "should succesfully enqueue messages", n: []notification.Notification{ { + ID: "1", Type: notification.TypeAlert, Labels: map[string]string{ "k1": "v1", }, }, + { + ID: "2", + Type: notification.TypeAlert, + Labels: map[string]string{ + "k2": "v2", + }, + }, }, - setup: func(n []notification.Notification, r *mocks.Repository, ar *mocks.AlertRepository, l *mocks.LogService, a *mocks.AlertService, q *mocks.Queuer, d *mocks.Dispatcher) { - r.EXPECT().Create(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return(n[0], nil) - d.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]string{"123"}, nil) - l.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - ar.EXPECT().BulkUpdateSilence(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("string")).Return(nil) - q.EXPECT().Enqueue(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Message")).Return(nil) + setup: func(n []notification.Notification, r *mocks.Repository, _ *mocks.AlertRepository, l *mocks.LogService, q *mocks.Queuer, ro *mocks.Router, no *mocks.Notifier) { + r.EXPECT().Commit(mock.AnythingOfType("context.todoCtx")).Return(nil) + r.EXPECT().BulkCreate(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]notification.Notification{ + { + ID: "1", + Type: notification.TypeAlert, + Labels: map[string]string{ + "k1": "v1", + }, + }, + { + ID: "2", + Type: notification.TypeAlert, + Labels: map[string]string{ + "k2": "v2", + }, + }, + }, nil) + ro.EXPECT().PrepareMetaMessages(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.MetaMessage{ + { + ReceiverID: 1, + SubscriptionIDs: []uint64{1, 2}, + ReceiverType: receiver.TypeSlack, + Template: template.ReservedName_SystemDefault, + }, { + ReceiverID: 2, + SubscriptionIDs: []uint64{1, 2}, + ReceiverType: receiver.TypeSlack, + Template: template.ReservedName_SystemDefault, + }, + }, []log.Notification{}, nil) + no.EXPECT().GetSystemDefaultTemplate().Return("") + no.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("context.todoCtx"), mock.Anything).Return(map[string]any{}, nil) + l.EXPECT().LogNotifications(mock.AnythingOfType("context.todoCtx")).Return(nil) + q.EXPECT().Enqueue(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("notification.Message"), mock.AnythingOfType("notification.Message")).Return(nil) }, + wantResult: []string{"1", "2"}, }, } for _, tt := range tests { @@ -201,14 +366,13 @@ func TestService_DispatchSuccess(t *testing.T) { mockRepository = new(mocks.Repository) mockAlertRepository = new(mocks.AlertRepository) mockLogService = new(mocks.LogService) - mockAlertService = new(mocks.AlertService) - mockDispatcher = new(mocks.Dispatcher) + mockRouter = new(mocks.Router) + mockNotifier = new(mocks.Notifier) ) if tt.setup != nil { mockRepository.EXPECT().WithTransaction(mock.AnythingOfType("context.todoCtx")).Return(context.TODO()) - mockRepository.EXPECT().Commit(mock.AnythingOfType("context.todoCtx")).Return(nil) - tt.setup(tt.n, mockRepository, mockAlertRepository, mockLogService, mockAlertService, mockQueuer, mockDispatcher) + tt.setup(tt.n, mockRepository, mockAlertRepository, mockLogService, mockQueuer, mockRouter, mockNotifier) } s := notification.NewService( @@ -222,12 +386,20 @@ func TestService_DispatchSuccess(t *testing.T) { AlertRepository: mockAlertRepository, LogService: mockLogService, }, - map[string]notification.Dispatcher{ - testType: mockDispatcher, + map[string]notification.Router{ + testType: mockRouter, + notification.RouterSubscriber: mockRouter, + }, + map[string]notification.Notifier{ + receiver.TypeSlack: mockNotifier, }, ) - if _, err := s.Dispatch(context.TODO(), tt.n, testType); (err != nil) != tt.wantErr { - t.Errorf("Service.Dispatch() error = %v, wantErr %v", err, tt.wantErr) + if ids, err := s.Dispatch(context.TODO(), tt.n); err != nil { + t.Errorf("Service.DispatchSuccess() error = %v", err) + } else { + if diff := cmp.Diff(tt.wantResult, ids); diff != "" { + t.Errorf("Service.DispatchSuccess() diff = %v", diff) + } } }) } diff --git a/core/provider/mocks/provider_repository.go b/core/provider/mocks/provider_repository.go index 4bf93693..e040e54c 100644 --- a/core/provider/mocks/provider_repository.go +++ b/core/provider/mocks/provider_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/receiver/mocks/config_resolver.go b/core/receiver/mocks/config_resolver.go index f3e25c78..662cd87b 100644 --- a/core/receiver/mocks/config_resolver.go +++ b/core/receiver/mocks/config_resolver.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/receiver/mocks/encryptor.go b/core/receiver/mocks/encryptor.go index cdea3f1f..72760742 100644 --- a/core/receiver/mocks/encryptor.go +++ b/core/receiver/mocks/encryptor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/receiver/mocks/receiver_repository.go b/core/receiver/mocks/receiver_repository.go index fe491bc1..7b494596 100644 --- a/core/receiver/mocks/receiver_repository.go +++ b/core/receiver/mocks/receiver_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/rule/mocks/namespace_service.go b/core/rule/mocks/namespace_service.go index 05bec7ac..d4fa8b22 100644 --- a/core/rule/mocks/namespace_service.go +++ b/core/rule/mocks/namespace_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/rule/mocks/rule_repository.go b/core/rule/mocks/rule_repository.go index b5d4c850..b95eba8e 100644 --- a/core/rule/mocks/rule_repository.go +++ b/core/rule/mocks/rule_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/rule/mocks/rule_uploader.go b/core/rule/mocks/rule_uploader.go index 18c31cab..649c07f7 100644 --- a/core/rule/mocks/rule_uploader.go +++ b/core/rule/mocks/rule_uploader.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/rule/mocks/template_service.go b/core/rule/mocks/template_service.go index ecaa0cb9..b025a678 100644 --- a/core/rule/mocks/template_service.go +++ b/core/rule/mocks/template_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/rule/mocks/transactor.go b/core/rule/mocks/transactor.go index 50eb87d6..06476816 100644 --- a/core/rule/mocks/transactor.go +++ b/core/rule/mocks/transactor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/silence/mocks/silence_repository.go b/core/silence/mocks/silence_repository.go index 9ec5fd53..62d9dc45 100644 --- a/core/silence/mocks/silence_repository.go +++ b/core/silence/mocks/silence_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/subscription/mocks/log_service.go b/core/subscription/mocks/log_service.go index c32943aa..f66f264e 100644 --- a/core/subscription/mocks/log_service.go +++ b/core/subscription/mocks/log_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/subscription/mocks/namespace_service.go b/core/subscription/mocks/namespace_service.go index 05bec7ac..d4fa8b22 100644 --- a/core/subscription/mocks/namespace_service.go +++ b/core/subscription/mocks/namespace_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/subscription/mocks/receiver_service.go b/core/subscription/mocks/receiver_service.go index 3b5b68ad..34348c55 100644 --- a/core/subscription/mocks/receiver_service.go +++ b/core/subscription/mocks/receiver_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/subscription/mocks/subscription_receiver_service.go b/core/subscription/mocks/subscription_receiver_service.go index 2955be5e..4997b271 100644 --- a/core/subscription/mocks/subscription_receiver_service.go +++ b/core/subscription/mocks/subscription_receiver_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/subscription/mocks/subscription_repository.go b/core/subscription/mocks/subscription_repository.go index 9afa4cd9..b0a8d05d 100644 --- a/core/subscription/mocks/subscription_repository.go +++ b/core/subscription/mocks/subscription_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/subscription/mocks/transactor.go b/core/subscription/mocks/transactor.go index 50eb87d6..06476816 100644 --- a/core/subscription/mocks/transactor.go +++ b/core/subscription/mocks/transactor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/subscriptionreceiver/mocks/repository.go b/core/subscriptionreceiver/mocks/repository.go index b1476ff4..66910d13 100644 --- a/core/subscriptionreceiver/mocks/repository.go +++ b/core/subscriptionreceiver/mocks/repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/subscriptionreceiver/mocks/transactor.go b/core/subscriptionreceiver/mocks/transactor.go index 50eb87d6..06476816 100644 --- a/core/subscriptionreceiver/mocks/transactor.go +++ b/core/subscriptionreceiver/mocks/transactor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/core/template/mocks/template_repository.go b/core/template/mocks/template_repository.go index 6ed22634..58121fd3 100644 --- a/core/template/mocks/template_repository.go +++ b/core/template/mocks/template_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/api.go b/internal/api/api.go index 4d433219..5349b0f5 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -80,7 +80,7 @@ type TemplateService interface { } type NotificationService interface { - Dispatch(context.Context, []notification.Notification, string) ([]string, error) + Dispatch(context.Context, []notification.Notification) ([]string, error) RemoveIdempotencies(ctx context.Context, TTL time.Duration) error CheckIdempotency(ctx context.Context, scope, key string) (string, error) InsertIdempotency(ctx context.Context, scope, key, notificationID string) error diff --git a/internal/api/mocks/alert_service.go b/internal/api/mocks/alert_service.go index 0363faa0..bf51cdd0 100644 --- a/internal/api/mocks/alert_service.go +++ b/internal/api/mocks/alert_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/mocks/namespace_service.go b/internal/api/mocks/namespace_service.go index 05bec7ac..d4fa8b22 100644 --- a/internal/api/mocks/namespace_service.go +++ b/internal/api/mocks/namespace_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/mocks/notification_service.go b/internal/api/mocks/notification_service.go index 2a5941cd..7e5f2813 100644 --- a/internal/api/mocks/notification_service.go +++ b/internal/api/mocks/notification_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -82,9 +82,9 @@ func (_c *NotificationService_CheckIdempotency_Call) RunAndReturn(run func(conte return _c } -// Dispatch provides a mock function with given fields: _a0, _a1, _a2 -func (_m *NotificationService) Dispatch(_a0 context.Context, _a1 []notification.Notification, _a2 string) ([]string, error) { - ret := _m.Called(_a0, _a1, _a2) +// Dispatch provides a mock function with given fields: _a0, _a1 +func (_m *NotificationService) Dispatch(_a0 context.Context, _a1 []notification.Notification) ([]string, error) { + ret := _m.Called(_a0, _a1) if len(ret) == 0 { panic("no return value specified for Dispatch") @@ -92,19 +92,19 @@ func (_m *NotificationService) Dispatch(_a0 context.Context, _a1 []notification. var r0 []string var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []notification.Notification, string) ([]string, error)); ok { - return rf(_a0, _a1, _a2) + if rf, ok := ret.Get(0).(func(context.Context, []notification.Notification) ([]string, error)); ok { + return rf(_a0, _a1) } - if rf, ok := ret.Get(0).(func(context.Context, []notification.Notification, string) []string); ok { - r0 = rf(_a0, _a1, _a2) + if rf, ok := ret.Get(0).(func(context.Context, []notification.Notification) []string); ok { + r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) } } - if rf, ok := ret.Get(1).(func(context.Context, []notification.Notification, string) error); ok { - r1 = rf(_a0, _a1, _a2) + if rf, ok := ret.Get(1).(func(context.Context, []notification.Notification) error); ok { + r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) } @@ -120,14 +120,13 @@ type NotificationService_Dispatch_Call struct { // Dispatch is a helper method to define mock.On call // - _a0 context.Context // - _a1 []notification.Notification -// - _a2 string -func (_e *NotificationService_Expecter) Dispatch(_a0 interface{}, _a1 interface{}, _a2 interface{}) *NotificationService_Dispatch_Call { - return &NotificationService_Dispatch_Call{Call: _e.mock.On("Dispatch", _a0, _a1, _a2)} +func (_e *NotificationService_Expecter) Dispatch(_a0 interface{}, _a1 interface{}) *NotificationService_Dispatch_Call { + return &NotificationService_Dispatch_Call{Call: _e.mock.On("Dispatch", _a0, _a1)} } -func (_c *NotificationService_Dispatch_Call) Run(run func(_a0 context.Context, _a1 []notification.Notification, _a2 string)) *NotificationService_Dispatch_Call { +func (_c *NotificationService_Dispatch_Call) Run(run func(_a0 context.Context, _a1 []notification.Notification)) *NotificationService_Dispatch_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]notification.Notification), args[2].(string)) + run(args[0].(context.Context), args[1].([]notification.Notification)) }) return _c } @@ -137,7 +136,7 @@ func (_c *NotificationService_Dispatch_Call) Return(_a0 []string, _a1 error) *No return _c } -func (_c *NotificationService_Dispatch_Call) RunAndReturn(run func(context.Context, []notification.Notification, string) ([]string, error)) *NotificationService_Dispatch_Call { +func (_c *NotificationService_Dispatch_Call) RunAndReturn(run func(context.Context, []notification.Notification) ([]string, error)) *NotificationService_Dispatch_Call { _c.Call.Return(run) return _c } diff --git a/internal/api/mocks/provider_service.go b/internal/api/mocks/provider_service.go index 58d537b6..f110f862 100644 --- a/internal/api/mocks/provider_service.go +++ b/internal/api/mocks/provider_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/mocks/receiver_service.go b/internal/api/mocks/receiver_service.go index b5cc4b7e..245d4c94 100644 --- a/internal/api/mocks/receiver_service.go +++ b/internal/api/mocks/receiver_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/mocks/rule_service.go b/internal/api/mocks/rule_service.go index a4b820b8..15e0677e 100644 --- a/internal/api/mocks/rule_service.go +++ b/internal/api/mocks/rule_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/mocks/silence_service.go b/internal/api/mocks/silence_service.go index e51cd67c..f2a9b6a4 100644 --- a/internal/api/mocks/silence_service.go +++ b/internal/api/mocks/silence_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/mocks/subscription_service.go b/internal/api/mocks/subscription_service.go index 2cf16d04..cb5b2cef 100644 --- a/internal/api/mocks/subscription_service.go +++ b/internal/api/mocks/subscription_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/mocks/template_service.go b/internal/api/mocks/template_service.go index 80ffa29c..c55338cf 100644 --- a/internal/api/mocks/template_service.go +++ b/internal/api/mocks/template_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/api/v1beta1/alert_test.go b/internal/api/v1beta1/alert_test.go index e41f94bd..88cac008 100644 --- a/internal/api/v1beta1/alert_test.go +++ b/internal/api/v1beta1/alert_test.go @@ -171,7 +171,7 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { }} mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), mock.AnythingOfType("uint64"), payload). Return(dummyAlerts, nil).Once() - mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification"), mock.AnythingOfType("string")).Return(nil, nil) + mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, nil) dummyGRPCServer := v1beta1.NewGRPCServer(log.NewNoop(), api.HeadersConfig{}, &api.Deps{AlertService: mockedAlertService, NotificationService: mockNotificationService}) @@ -279,7 +279,7 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { }} mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), mock.AnythingOfType("uint64"), payload). Return(dummyAlerts, nil).Once() - mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification"), mock.AnythingOfType("string")).Return(nil, nil) + mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, nil) dummyGRPCServer := v1beta1.NewGRPCServer(log.NewNoop(), api.HeadersConfig{}, &api.Deps{AlertService: mockedAlertService, NotificationService: mockNotificationService}) @@ -460,7 +460,7 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), mock.AnythingOfType("uint64"), payload). Return(dummyAlerts, nil).Once() - mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification"), mock.AnythingOfType("string")).Return(nil, nil) + mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, nil) res, err := dummyGRPCServer.CreateAlerts(context.TODO(), dummyReq) assert.Equal(t, 1, len(res.GetAlerts())) diff --git a/internal/api/v1beta1/notification.go b/internal/api/v1beta1/notification.go index 38f3168c..005f0807 100644 --- a/internal/api/v1beta1/notification.go +++ b/internal/api/v1beta1/notification.go @@ -75,7 +75,7 @@ func (s *GRPCServer) PostNotification(ctx context.Context, req *sirenv1beta1.Pos Template: notificationTemplate, ReceiverSelectors: receiverSelectors, }, - }, notification.DispatchKindSingleNotification) + }) if err != nil { return nil, s.generateRPCErr(err) } @@ -135,7 +135,7 @@ func (s *GRPCServer) PostBulkNotifications(ctx context.Context, req *sirenv1beta }) } - notificationIDs, err := s.notificationService.Dispatch(ctx, notifications, notification.DispatchKindBulkNotification) + notificationIDs, err := s.notificationService.Dispatch(ctx, notifications) if err != nil { return nil, s.generateRPCErr(err) } diff --git a/internal/api/v1beta1/notification_test.go b/internal/api/v1beta1/notification_test.go index e6a281f0..7930986a 100644 --- a/internal/api/v1beta1/notification_test.go +++ b/internal/api/v1beta1/notification_test.go @@ -32,7 +32,7 @@ func TestGRPCServer_PostNotification(t *testing.T) { idempotencyKey: "test", setup: func(ns *mocks.NotificationService) { ns.EXPECT().CheckIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return("", errors.ErrNotFound) - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]notification.Notification"), mock.AnythingOfType("string")).Return(nil, errors.ErrInvalid) + ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, errors.ErrInvalid) }, errString: "rpc error: code = InvalidArgument desc = request is not valid", }, @@ -41,7 +41,7 @@ func TestGRPCServer_PostNotification(t *testing.T) { idempotencyKey: "test", setup: func(ns *mocks.NotificationService) { ns.EXPECT().CheckIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return("", errors.ErrNotFound) - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]notification.Notification"), mock.AnythingOfType("string")).Return(nil, errors.New("some error")) + ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]notification.Notification")).Return(nil, errors.New("some error")) }, errString: "rpc error: code = Internal desc = some unexpected error occurred", }, @@ -65,7 +65,7 @@ func TestGRPCServer_PostNotification(t *testing.T) { idempotencyKey: "test", setup: func(ns *mocks.NotificationService) { ns.EXPECT().CheckIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return("", errors.ErrNotFound) - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]notification.Notification"), mock.AnythingOfType("string")).Return([]string{notificationID}, nil) + ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]string{notificationID}, nil) ns.EXPECT().InsertIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(errors.New("some error")) }, errString: "rpc error: code = Internal desc = some unexpected error occurred", @@ -75,7 +75,7 @@ func TestGRPCServer_PostNotification(t *testing.T) { idempotencyKey: "test", setup: func(ns *mocks.NotificationService) { ns.EXPECT().CheckIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return("", errors.ErrNotFound) - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]notification.Notification"), mock.AnythingOfType("string")).Return([]string{notificationID}, nil) + ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]notification.Notification")).Return([]string{notificationID}, nil) ns.EXPECT().InsertIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil) }, }, diff --git a/plugins/providers/cortex/v1/mocks/cortex_caller.go b/plugins/providers/cortex/v1/mocks/cortex_caller.go index f09c2af9..e198a189 100644 --- a/plugins/providers/cortex/v1/mocks/cortex_caller.go +++ b/plugins/providers/cortex/v1/mocks/cortex_caller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/plugins/receivers/pagerduty/mocks/pagerduty_caller.go b/plugins/receivers/pagerduty/mocks/pagerduty_caller.go index ef6e133f..a553c181 100644 --- a/plugins/receivers/pagerduty/mocks/pagerduty_caller.go +++ b/plugins/receivers/pagerduty/mocks/pagerduty_caller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/plugins/receivers/slack/mocks/encryptor.go b/plugins/receivers/slack/mocks/encryptor.go index e5fff092..89cc2627 100644 --- a/plugins/receivers/slack/mocks/encryptor.go +++ b/plugins/receivers/slack/mocks/encryptor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/plugins/receivers/slack/mocks/goslack_caller.go b/plugins/receivers/slack/mocks/goslack_caller.go index cc0ee3ce..673d1810 100644 --- a/plugins/receivers/slack/mocks/goslack_caller.go +++ b/plugins/receivers/slack/mocks/goslack_caller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/plugins/receivers/slack/mocks/slack_caller.go b/plugins/receivers/slack/mocks/slack_caller.go index 7da37b3d..71efce0a 100644 --- a/plugins/receivers/slack/mocks/slack_caller.go +++ b/plugins/receivers/slack/mocks/slack_caller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/test/e2e_test/notification_bulk_subscription_test.go b/test/e2e_test/notification_bulk_subscription_test.go index d3744c0d..d8881275 100644 --- a/test/e2e_test/notification_bulk_subscription_test.go +++ b/test/e2e_test/notification_bulk_subscription_test.go @@ -67,7 +67,6 @@ func (s *BulkNotificationSubscriptionTestSuite) SetupTest() { "team", "service", }, - SubscriptionV2Enabled: true, } s.appConfig.Telemetry.OpenTelemetry.Enabled = false diff --git a/test/e2e_test/notification_receiver_test.go b/test/e2e_test/notification_receiver_test.go index 5696dd1b..8e0e2710 100644 --- a/test/e2e_test/notification_receiver_test.go +++ b/test/e2e_test/notification_receiver_test.go @@ -53,7 +53,6 @@ func (s *NotificationReceiverTestSuite) SetupTest() { } s.appConfig.Notification.MessageHandler.Enabled = true s.appConfig.Notification.DLQHandler.Enabled = false - s.appConfig.Notification.SubscriptionV2Enabled = true s.testBench, err = InitCortexEnvironment(s.appConfig) s.Require().NoError(err)