diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e5bd92de..1593b809 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest services: postgres: - image: postgres:13 + image: postgres:12 ports: - 5432:5432 env: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 82cd9c79..c1e11d6b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest services: postgres: - image: postgres:13 + image: postgres:12 ports: - 5432:5432 env: diff --git a/Makefile b/Makefile index ba821f68..86462c41 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ NAME="github.com/odpf/siren" LAST_COMMIT := $(shell git rev-parse --short HEAD) LAST_TAG := "$(shell git rev-list --tags --max-count=1)" APP_VERSION := "$(shell git describe --tags ${LAST_TAG})-next" -PROTON_COMMIT := "546b6368150b4c12e24f25cf4657bc76dc3177a1" +PROTON_COMMIT := "9cdffc3c1838ec72b35b2a1b9a170ca9c138db66" .PHONY: all build test clean dist vet proto install diff --git a/cli/deps.go b/cli/deps.go index ce00c3c3..db1bcc95 100644 --- a/cli/deps.go +++ b/cli/deps.go @@ -6,16 +6,14 @@ import ( "github.com/newrelic/go-agent/v3/newrelic" "github.com/odpf/salt/db" - saltlog "github.com/odpf/salt/log" + "github.com/odpf/salt/log" "github.com/odpf/siren/config" "github.com/odpf/siren/core/alert" - "github.com/odpf/siren/core/log" "github.com/odpf/siren/core/namespace" "github.com/odpf/siren/core/notification" "github.com/odpf/siren/core/provider" "github.com/odpf/siren/core/receiver" "github.com/odpf/siren/core/rule" - "github.com/odpf/siren/core/silence" "github.com/odpf/siren/core/subscription" "github.com/odpf/siren/core/template" "github.com/odpf/siren/internal/api" @@ -32,19 +30,18 @@ import ( func InitDeps( ctx context.Context, - logger saltlog.Logger, + logger log.Logger, cfg config.Config, queue notification.Queuer, ) (*api.Deps, *newrelic.Application, *pgc.Client, map[string]notification.Notifier, error) { telemetry.Init(ctx, cfg.Telemetry, logger) - nrApp, err := newrelic.NewApplication( - newrelic.ConfigAppName(cfg.Telemetry.NewRelicAppName), + newrelic.ConfigAppName(cfg.Telemetry.ServiceName), newrelic.ConfigLicense(cfg.Telemetry.NewRelicAPIKey), ) if err != nil { - logger.Warn("failed to init newrelic", "err", err) + return nil, nil, nil, nil, err } dbClient, err := db.New(cfg.DB) @@ -62,26 +59,22 @@ func InitDeps( return nil, nil, nil, nil, fmt.Errorf("cannot initialize encryptor: %w", err) } + idempotencyRepository := postgres.NewIdempotencyRepository(pgClient) templateRepository := postgres.NewTemplateRepository(pgClient) templateService := template.NewService(templateRepository) + alertRepository := postgres.NewAlertRepository(pgClient) + providerRepository := postgres.NewProviderRepository(pgClient) providerService := provider.NewService(providerRepository) - logRepository := postgres.NewLogRepository(pgClient) - logService := log.NewService(logRepository) + namespaceRepository := postgres.NewNamespaceRepository(pgClient) cortexPluginService := cortex.NewPluginService(logger, cfg.Providers.Cortex) - alertRepository := postgres.NewAlertRepository(pgClient) - alertService := alert.NewService( - alertRepository, - logService, - map[string]alert.AlertTransformer{ - provider.TypeCortex: cortexPluginService, - }, - ) - namespaceRepository := postgres.NewNamespaceRepository(pgClient) + alertHistoryService := alert.NewService(alertRepository, map[string]alert.AlertTransformer{ + provider.TypeCortex: cortexPluginService, + }) namespaceService := namespace.NewService(encryptor, namespaceRepository, providerService, map[string]namespace.ConfigSyncer{ provider.TypeCortex: cortexPluginService, }) @@ -96,9 +89,6 @@ func InitDeps( }, ) - silenceRepository := postgres.NewSilenceRepository(pgClient) - silenceService := silence.NewService(silenceRepository) - // plugin receiver services slackPluginService := slack.NewPluginService(cfg.Receivers.Slack, encryptor) pagerDutyPluginService := pagerduty.NewPluginService(cfg.Receivers.Pagerduty) @@ -119,7 +109,6 @@ func InitDeps( subscriptionRepository := postgres.NewSubscriptionRepository(pgClient) subscriptionService := subscription.NewService( subscriptionRepository, - logService, namespaceService, receiverService, ) @@ -132,33 +121,17 @@ func InitDeps( receiver.TypeFile: filePluginService, } - idempotencyRepository := postgres.NewIdempotencyRepository(pgClient) - notificationRepository := postgres.NewNotificationRepository(pgClient) - notificationService := notification.NewService( - logger, - notificationRepository, - queue, - notifierRegistry, - notification.Deps{ - LogService: logService, - IdempotencyRepository: idempotencyRepository, - ReceiverService: receiverService, - SubscriptionService: subscriptionService, - SilenceService: silenceService, - AlertService: alertService, - }, - ) + notificationService := notification.NewService(logger, queue, idempotencyRepository, receiverService, subscriptionService, notifierRegistry) return &api.Deps{ TemplateService: templateService, RuleService: ruleService, - AlertService: alertService, + AlertService: alertHistoryService, ProviderService: providerService, NamespaceService: namespaceService, ReceiverService: receiverService, SubscriptionService: subscriptionService, NotificationService: notificationService, - SilenceService: silenceService, }, nrApp, pgClient, notifierRegistry, nil } diff --git a/cli/rule.go b/cli/rule.go index d3294850..a7ea9c7f 100644 --- a/cli/rule.go +++ b/cli/rule.go @@ -333,7 +333,7 @@ func UploadRules(client sirenv1beta1.SirenServiceClient, yamlFile []byte) ([]uin payload.Namespace, payload.GroupName, payload.Template), err) return successfullyUpsertedRulesID, err } - successfullyUpsertedRulesID = append(successfullyUpsertedRulesID, result.GetId()) + successfullyUpsertedRulesID = append(successfullyUpsertedRulesID, result.GetRule().GetId()) fmt.Printf("successfully uploaded %s/%s/%s", payload.Namespace, payload.GroupName, payload.Template) diff --git a/config/init.go b/config/init.go index 949acdd7..a2c0b330 100644 --- a/config/init.go +++ b/config/init.go @@ -5,7 +5,7 @@ import ( "github.com/mcuadros/go-defaults" "github.com/odpf/siren/core/receiver" - "gopkg.in/yaml.v3" + "gopkg.in/yaml.v2" ) func Init(configFile string) error { diff --git a/core/alert/alert.go b/core/alert/alert.go index b3ae43d8..38506994 100644 --- a/core/alert/alert.go +++ b/core/alert/alert.go @@ -9,22 +9,20 @@ import ( type Repository interface { Create(context.Context, Alert) (Alert, error) List(context.Context, Filter) ([]Alert, error) - BulkUpdateSilence(context.Context, []int64, string) error } type Alert struct { - ID uint64 `json:"id"` - ProviderID uint64 `json:"provider_id"` - NamespaceID uint64 `json:"namespace_id"` - ResourceName string `json:"resource_name"` - MetricName string `json:"metric_name"` - MetricValue string `json:"metric_value"` - Severity string `json:"severity"` - Rule string `json:"rule"` - TriggeredAt time.Time `json:"triggered_at"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - SilenceStatus string `json:"silence_status"` + ID uint64 `json:"id"` + ProviderID uint64 `json:"provider_id"` + NamespaceID uint64 `json:"namespace_id"` + ResourceName string `json:"resource_name"` + MetricName string `json:"metric_name"` + MetricValue string `json:"metric_value"` + Severity string `json:"severity"` + Rule string `json:"rule"` + TriggeredAt time.Time `json:"triggered_at"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` // These fields won't be stored in the DB // these are additional information for notification purposes diff --git a/core/alert/filter.go b/core/alert/filter.go index 1bfa92a3..7892e7b8 100644 --- a/core/alert/filter.go +++ b/core/alert/filter.go @@ -6,6 +6,4 @@ type Filter struct { NamespaceID uint64 StartTime int64 EndTime int64 - SilenceID string - IDs []int64 } diff --git a/core/alert/mocks/alert_repository.go b/core/alert/mocks/alert_repository.go index 313bd0c6..9208af0e 100644 --- a/core/alert/mocks/alert_repository.go +++ b/core/alert/mocks/alert_repository.go @@ -23,45 +23,6 @@ func (_m *AlertRepository) EXPECT() *AlertRepository_Expecter { return &AlertRepository_Expecter{mock: &_m.Mock} } -// BulkUpdateSilence provides a mock function with given fields: _a0, _a1, _a2 -func (_m *AlertRepository) BulkUpdateSilence(_a0 context.Context, _a1 []int64, _a2 string) error { - ret := _m.Called(_a0, _a1, _a2) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []int64, string) error); ok { - r0 = rf(_a0, _a1, _a2) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// AlertRepository_BulkUpdateSilence_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BulkUpdateSilence' -type AlertRepository_BulkUpdateSilence_Call struct { - *mock.Call -} - -// BulkUpdateSilence is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 []int64 -// - _a2 string -func (_e *AlertRepository_Expecter) BulkUpdateSilence(_a0 interface{}, _a1 interface{}, _a2 interface{}) *AlertRepository_BulkUpdateSilence_Call { - return &AlertRepository_BulkUpdateSilence_Call{Call: _e.mock.On("BulkUpdateSilence", _a0, _a1, _a2)} -} - -func (_c *AlertRepository_BulkUpdateSilence_Call) Run(run func(_a0 context.Context, _a1 []int64, _a2 string)) *AlertRepository_BulkUpdateSilence_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]int64), args[2].(string)) - }) - return _c -} - -func (_c *AlertRepository_BulkUpdateSilence_Call) Return(_a0 error) *AlertRepository_BulkUpdateSilence_Call { - _c.Call.Return(_a0) - return _c -} - // Create provides a mock function with given fields: _a0, _a1 func (_m *AlertRepository) Create(_a0 context.Context, _a1 alert.Alert) (alert.Alert, error) { ret := _m.Called(_a0, _a1) diff --git a/core/alert/mocks/log_service.go b/core/alert/mocks/log_service.go deleted file mode 100644 index 1a059738..00000000 --- a/core/alert/mocks/log_service.go +++ /dev/null @@ -1,84 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// LogService is an autogenerated mock type for the LogService type -type LogService struct { - mock.Mock -} - -type LogService_Expecter struct { - mock *mock.Mock -} - -func (_m *LogService) EXPECT() *LogService_Expecter { - return &LogService_Expecter{mock: &_m.Mock} -} - -// ListNotificationAlertIDsBySilenceID provides a mock function with given fields: ctx, silenceID -func (_m *LogService) ListNotificationAlertIDsBySilenceID(ctx context.Context, silenceID string) ([]int64, error) { - ret := _m.Called(ctx, silenceID) - - var r0 []int64 - if rf, ok := ret.Get(0).(func(context.Context, string) []int64); ok { - r0 = rf(ctx, silenceID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]int64) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, silenceID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// LogService_ListNotificationAlertIDsBySilenceID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListNotificationAlertIDsBySilenceID' -type LogService_ListNotificationAlertIDsBySilenceID_Call struct { - *mock.Call -} - -// ListNotificationAlertIDsBySilenceID is a helper method to define mock.On call -// - ctx context.Context -// - silenceID string -func (_e *LogService_Expecter) ListNotificationAlertIDsBySilenceID(ctx interface{}, silenceID interface{}) *LogService_ListNotificationAlertIDsBySilenceID_Call { - return &LogService_ListNotificationAlertIDsBySilenceID_Call{Call: _e.mock.On("ListNotificationAlertIDsBySilenceID", ctx, silenceID)} -} - -func (_c *LogService_ListNotificationAlertIDsBySilenceID_Call) Run(run func(ctx context.Context, silenceID string)) *LogService_ListNotificationAlertIDsBySilenceID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *LogService_ListNotificationAlertIDsBySilenceID_Call) Return(_a0 []int64, _a1 error) *LogService_ListNotificationAlertIDsBySilenceID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -type mockConstructorTestingTNewLogService interface { - mock.TestingT - Cleanup(func()) -} - -// NewLogService creates a new instance of LogService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewLogService(t mockConstructorTestingTNewLogService) *LogService { - mock := &LogService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/alert/service.go b/core/alert/service.go index ae259a0d..012922ba 100644 --- a/core/alert/service.go +++ b/core/alert/service.go @@ -7,21 +7,15 @@ import ( "github.com/odpf/siren/pkg/errors" ) -//go:generate mockery --name=LogService -r --case underscore --with-expecter --structname LogService --filename log_service.go --output=./mocks -type LogService interface { - ListAlertIDsBySilenceID(ctx context.Context, silenceID string) ([]int64, error) -} - // Service handles business logic type Service struct { repository Repository - logService LogService registry map[string]AlertTransformer } // NewService returns repository struct -func NewService(repository Repository, logService LogService, registry map[string]AlertTransformer) *Service { - return &Service{repository, logService, registry} +func NewService(repository Repository, registry map[string]AlertTransformer) *Service { + return &Service{repository, registry} } func (s *Service) CreateAlerts(ctx context.Context, providerType string, providerID uint64, namespaceID uint64, body map[string]interface{}) ([]Alert, int, error) { @@ -35,15 +29,15 @@ func (s *Service) CreateAlerts(ctx context.Context, providerType string, provide return nil, 0, err } - for i := 0; i < len(alerts); i++ { - createdAlert, err := s.repository.Create(ctx, alerts[i]) + for _, alrt := range alerts { + createdAlert, err := s.repository.Create(ctx, alrt) if err != nil { if errors.Is(err, ErrRelation) { return nil, 0, errors.ErrNotFound.WithMsgf(err.Error()) } return nil, 0, err } - alerts[i].ID = createdAlert.ID + alrt.ID = createdAlert.ID } return alerts, firingLen, nil @@ -54,21 +48,9 @@ func (s *Service) List(ctx context.Context, flt Filter) ([]Alert, error) { flt.EndTime = time.Now().Unix() } - if flt.SilenceID != "" { - alertIDs, err := s.logService.ListAlertIDsBySilenceID(ctx, flt.SilenceID) - if err != nil { - return nil, err - } - flt.IDs = alertIDs - } - return s.repository.List(ctx, flt) } -func (s *Service) UpdateSilenceStatus(ctx context.Context, alertIDs []int64, hasSilenced bool, hasNonSilenced bool) error { - return s.repository.BulkUpdateSilence(ctx, alertIDs, silenceStatus(hasSilenced, hasNonSilenced)) -} - func (s *Service) getProviderPluginService(providerType string) (AlertTransformer, error) { pluginService, exist := s.registry[providerType] if !exist { diff --git a/core/alert/service_test.go b/core/alert/service_test.go index 88785020..176505d1 100644 --- a/core/alert/service_test.go +++ b/core/alert/service_test.go @@ -13,12 +13,12 @@ import ( "github.com/stretchr/testify/mock" ) -func TestService_List(t *testing.T) { +func TestService_Get(t *testing.T) { ctx := context.TODO() t.Run("should call repository List method with proper arguments and return result in domain's type", func(t *testing.T) { repositoryMock := &mocks.AlertRepository{} - dummyService := alert.NewService(repositoryMock, nil, nil) + dummyService := alert.NewService(repositoryMock, nil) timenow := time.Now() dummyAlerts := []alert.Alert{ {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "baz", MetricValue: "20", @@ -45,7 +45,7 @@ func TestService_List(t *testing.T) { t.Run("should call repository List method with proper arguments if endtime is zero", func(t *testing.T) { repositoryMock := &mocks.AlertRepository{} - dummyService := alert.NewService(repositoryMock, nil, nil) + dummyService := alert.NewService(repositoryMock, nil) timenow := time.Now() dummyAlerts := []alert.Alert{ {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "baz", MetricValue: "20", @@ -67,7 +67,7 @@ func TestService_List(t *testing.T) { t.Run("should call repository List method and handle errors", func(t *testing.T) { repositoryMock := &mocks.AlertRepository{} - dummyService := alert.NewService(repositoryMock, nil, nil) + dummyService := alert.NewService(repositoryMock, nil) repositoryMock.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.Anything). Return(nil, errors.New("random error")) actualAlerts, err := dummyService.List(ctx, alert.Filter{ @@ -81,7 +81,7 @@ func TestService_List(t *testing.T) { }) } -func TestService_CreateAlerts(t *testing.T) { +func TestService_Create(t *testing.T) { var ( ctx = context.TODO() timenow = time.Now() @@ -175,7 +175,7 @@ func TestService_CreateAlerts(t *testing.T) { tc.setup(repositoryMock, alertTransformerMock) } - svc := alert.NewService(repositoryMock, nil, map[string]alert.AlertTransformer{ + svc := alert.NewService(repositoryMock, map[string]alert.AlertTransformer{ testType: alertTransformerMock, }) actualAlerts, firingLen, err := svc.CreateAlerts(ctx, testType, 1, 1, tc.alertsToBeCreated) diff --git a/core/alert/silence.go b/core/alert/silence.go deleted file mode 100644 index f7329e89..00000000 --- a/core/alert/silence.go +++ /dev/null @@ -1,15 +0,0 @@ -package alert - -const ( - SilenceStatusTotal = "total" - SilenceStatusPartial = "partial" -) - -func silenceStatus(hasSilenced, hasNonSilenced bool) string { - if hasSilenced && !hasNonSilenced { - return SilenceStatusTotal - } else if hasSilenced && hasNonSilenced { - return SilenceStatusPartial - } - return "" -} diff --git a/core/idempotency/idempotency.go b/core/idempotency/idempotency.go new file mode 100644 index 00000000..e5e8458b --- /dev/null +++ b/core/idempotency/idempotency.go @@ -0,0 +1,18 @@ +package idempotency + +import ( + "time" +) + +type Filter struct { + TTL time.Duration +} + +type Idempotency struct { + ID uint64 + Scope string + Key string + Success bool + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/core/log/filter.go b/core/log/filter.go deleted file mode 100644 index 9587f445..00000000 --- a/core/log/filter.go +++ /dev/null @@ -1,5 +0,0 @@ -package log - -type NotificationFilter struct { - SilenceID string -} diff --git a/core/log/mocks/notification_log_repository.go b/core/log/mocks/notification_log_repository.go deleted file mode 100644 index d24a59fc..00000000 --- a/core/log/mocks/notification_log_repository.go +++ /dev/null @@ -1,170 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - log "github.com/odpf/siren/core/log" - mock "github.com/stretchr/testify/mock" -) - -// NotificationLogRepository is an autogenerated mock type for the NotificationLogRepository type -type NotificationLogRepository struct { - mock.Mock -} - -type NotificationLogRepository_Expecter struct { - mock *mock.Mock -} - -func (_m *NotificationLogRepository) EXPECT() *NotificationLogRepository_Expecter { - return &NotificationLogRepository_Expecter{mock: &_m.Mock} -} - -// BulkCreate provides a mock function with given fields: _a0, _a1 -func (_m *NotificationLogRepository) BulkCreate(_a0 context.Context, _a1 []log.Notification) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []log.Notification) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// NotificationLogRepository_BulkCreate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BulkCreate' -type NotificationLogRepository_BulkCreate_Call struct { - *mock.Call -} - -// BulkCreate is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 []log.Notification -func (_e *NotificationLogRepository_Expecter) BulkCreate(_a0 interface{}, _a1 interface{}) *NotificationLogRepository_BulkCreate_Call { - return &NotificationLogRepository_BulkCreate_Call{Call: _e.mock.On("BulkCreate", _a0, _a1)} -} - -func (_c *NotificationLogRepository_BulkCreate_Call) Run(run func(_a0 context.Context, _a1 []log.Notification)) *NotificationLogRepository_BulkCreate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]log.Notification)) - }) - return _c -} - -func (_c *NotificationLogRepository_BulkCreate_Call) Return(_a0 error) *NotificationLogRepository_BulkCreate_Call { - _c.Call.Return(_a0) - return _c -} - -// ListAlertIDsBySilenceID provides a mock function with given fields: _a0, _a1 -func (_m *NotificationLogRepository) ListAlertIDsBySilenceID(_a0 context.Context, _a1 string) ([]int64, error) { - ret := _m.Called(_a0, _a1) - - var r0 []int64 - if rf, ok := ret.Get(0).(func(context.Context, string) []int64); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]int64) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NotificationLogRepository_ListAlertIDsBySilenceID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListAlertIDsBySilenceID' -type NotificationLogRepository_ListAlertIDsBySilenceID_Call struct { - *mock.Call -} - -// ListAlertIDsBySilenceID is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -func (_e *NotificationLogRepository_Expecter) ListAlertIDsBySilenceID(_a0 interface{}, _a1 interface{}) *NotificationLogRepository_ListAlertIDsBySilenceID_Call { - return &NotificationLogRepository_ListAlertIDsBySilenceID_Call{Call: _e.mock.On("ListAlertIDsBySilenceID", _a0, _a1)} -} - -func (_c *NotificationLogRepository_ListAlertIDsBySilenceID_Call) Run(run func(_a0 context.Context, _a1 string)) *NotificationLogRepository_ListAlertIDsBySilenceID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *NotificationLogRepository_ListAlertIDsBySilenceID_Call) Return(_a0 []int64, _a1 error) *NotificationLogRepository_ListAlertIDsBySilenceID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// ListSubscriptionIDsBySilenceID provides a mock function with given fields: _a0, _a1 -func (_m *NotificationLogRepository) ListSubscriptionIDsBySilenceID(_a0 context.Context, _a1 string) ([]int64, error) { - ret := _m.Called(_a0, _a1) - - var r0 []int64 - if rf, ok := ret.Get(0).(func(context.Context, string) []int64); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]int64) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NotificationLogRepository_ListSubscriptionIDsBySilenceID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListSubscriptionIDsBySilenceID' -type NotificationLogRepository_ListSubscriptionIDsBySilenceID_Call struct { - *mock.Call -} - -// ListSubscriptionIDsBySilenceID is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -func (_e *NotificationLogRepository_Expecter) ListSubscriptionIDsBySilenceID(_a0 interface{}, _a1 interface{}) *NotificationLogRepository_ListSubscriptionIDsBySilenceID_Call { - return &NotificationLogRepository_ListSubscriptionIDsBySilenceID_Call{Call: _e.mock.On("ListSubscriptionIDsBySilenceID", _a0, _a1)} -} - -func (_c *NotificationLogRepository_ListSubscriptionIDsBySilenceID_Call) Run(run func(_a0 context.Context, _a1 string)) *NotificationLogRepository_ListSubscriptionIDsBySilenceID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *NotificationLogRepository_ListSubscriptionIDsBySilenceID_Call) Return(_a0 []int64, _a1 error) *NotificationLogRepository_ListSubscriptionIDsBySilenceID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -type mockConstructorTestingTNewNotificationLogRepository interface { - mock.TestingT - Cleanup(func()) -} - -// NewNotificationLogRepository creates a new instance of NotificationLogRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewNotificationLogRepository(t mockConstructorTestingTNewNotificationLogRepository) *NotificationLogRepository { - mock := &NotificationLogRepository{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/log/notification.go b/core/log/notification.go deleted file mode 100644 index 3cec5905..00000000 --- a/core/log/notification.go +++ /dev/null @@ -1,24 +0,0 @@ -package log - -import ( - "context" - "time" -) - -//go:generate mockery --name=NotificationLogRepository -r --case underscore --with-expecter --structname NotificationLogRepository --filename notification_log_repository.go --output=./mocks -type NotificationLogRepository interface { - BulkCreate(context.Context, []Notification) error - ListAlertIDsBySilenceID(context.Context, string) ([]int64, error) - ListSubscriptionIDsBySilenceID(context.Context, string) ([]int64, error) -} - -type Notification struct { - ID string - NamespaceID uint64 - NotificationID string - SubscriptionID uint64 - ReceiverID uint64 - AlertIDs []int64 - SilenceIDs []string - CreatedAt time.Time -} diff --git a/core/log/service.go b/core/log/service.go deleted file mode 100644 index 07565912..00000000 --- a/core/log/service.go +++ /dev/null @@ -1,23 +0,0 @@ -package log - -import "context" - -type Service struct { - notificationLogRepo NotificationLogRepository -} - -func NewService(nlr NotificationLogRepository) *Service { - return &Service{nlr} -} - -func (s *Service) LogNotifications(ctx context.Context, nlogs ...Notification) error { - return s.notificationLogRepo.BulkCreate(ctx, nlogs) -} - -func (s *Service) ListAlertIDsBySilenceID(ctx context.Context, silenceID string) ([]int64, error) { - return s.notificationLogRepo.ListAlertIDsBySilenceID(ctx, silenceID) -} - -func (s *Service) ListSubscriptionIDsBySilenceID(ctx context.Context, silenceID string) ([]int64, error) { - return s.notificationLogRepo.ListSubscriptionIDsBySilenceID(ctx, silenceID) -} diff --git a/core/notification/builder.go b/core/notification/builder.go deleted file mode 100644 index 4cb90154..00000000 --- a/core/notification/builder.go +++ /dev/null @@ -1,130 +0,0 @@ -package notification - -import ( - "fmt" - "time" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/core/alert" - "github.com/odpf/siren/core/template" - "github.com/odpf/siren/pkg/errors" -) - -// Transform alerts and populate Data and Labels to be interpolated to the system-default template -// .Data -// - id -// - status "FIRING"/"RESOLVED" -// - resource -// - template -// - metric_value -// - metric_name -// - generator_url -// - num_alerts_firing -// - dashboard -// - playbook -// - summary -// .Labels -// - severity "WARNING"/"CRITICAL" -// - alertname -// - (others labels defined in rules) -func BuildFromAlerts( - as []alert.Alert, - firingLen int, - createdTime time.Time, -) Notification { - if len(as) == 0 { - return Notification{} - } - - sampleAlert := as[0] - - data := map[string]interface{}{} - - mergedAnnotations := map[string][]string{} - for _, a := range as { - for k, v := range a.Annotations { - mergedAnnotations[k] = append(mergedAnnotations[k], v) - } - } - // make unique - for k, v := range mergedAnnotations { - mergedAnnotations[k] = removeDuplicateStringValues(v) - } - // render annotations - for k, vSlice := range mergedAnnotations { - for _, v := range vSlice { - if _, ok := data[k]; ok { - data[k] = fmt.Sprintf("%s\n%s", data[k], v) - } else { - data[k] = v - } - } - } - - data["status"] = sampleAlert.Status - data["generator_url"] = sampleAlert.GeneratorURL - data["num_alerts_firing"] = firingLen - - labels := map[string]string{} - alertIDs := []int64{} - - for _, a := range as { - alertIDs = append(alertIDs, int64(a.ID)) - for k, v := range a.Labels { - labels[k] = v - } - } - - return Notification{ - NamespaceID: sampleAlert.NamespaceID, - Type: TypeSubscriber, - Data: data, - Labels: labels, - Template: template.ReservedName_SystemDefault, - CreatedAt: createdTime, - AlertIDs: alertIDs, - } -} - -// BuildTypeReceiver builds a notification struct with receiver type flow -func BuildTypeReceiver(receiverID uint64, payloadMap map[string]interface{}) (Notification, error) { - n := Notification{} - if err := mapstructure.Decode(payloadMap, &n); err != nil { - return Notification{}, errors.ErrInvalid.WithMsgf("failed to parse payload to notification: %s", err.Error()) - } - - if val, ok := payloadMap[ValidDurationRequestKey]; ok { - valString, ok := val.(string) - if !ok { - return Notification{}, fmt.Errorf("cannot parse %s value: %v", ValidDurationRequestKey, val) - } - parsedDur, err := time.ParseDuration(valString) - if err != nil { - return Notification{}, err - } - n.ValidDuration = parsedDur - } - - n.Type = TypeReceiver - - if len(n.Labels) == 0 { - n.Labels = map[string]string{} - } - - n.Labels[ReceiverIDLabelKey] = fmt.Sprintf("%d", receiverID) - - return n, nil -} - -func removeDuplicateStringValues(strSlice []string) []string { - keys := make(map[string]bool) - list := []string{} - - for _, v := range strSlice { - if _, value := keys[v]; !value { - keys[v] = true - list = append(list, v) - } - } - return list -} diff --git a/core/notification/builder_test.go b/core/notification/builder_test.go deleted file mode 100644 index b72e5840..00000000 --- a/core/notification/builder_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package notification_test - -import ( - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/odpf/siren/core/alert" - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/template" -) - -func TestBuildTypeReceiver(t *testing.T) { - sampleReceiverID := uint64(11) - - tests := []struct { - name string - receiverID uint64 - payloadMap map[string]interface{} - want notification.Notification - wantErr bool - }{ - { - name: "should build a correct notification", - receiverID: sampleReceiverID, - payloadMap: map[string]interface{}{ - "data": map[string]interface{}{ - "key1": "key2", - }, - "valid_duration": "10m", - "template": "some-template", - }, - want: notification.Notification{ - Type: notification.TypeReceiver, - Data: map[string]interface{}{ - "key1": "key2", - }, - Labels: map[string]string{ - "receiver_id": "11", - }, - ValidDuration: time.Duration(10 * time.Minute), - Template: "some-template", - }, - }, - { - name: "should return error if payload is not decodable", - receiverID: sampleReceiverID, - payloadMap: map[string]interface{}{ - "template": 1, - }, - wantErr: true, - }, - { - name: "should return error if 'valid_duration' is not string", - receiverID: sampleReceiverID, - payloadMap: map[string]interface{}{ - "valid_duration": 1, - }, - wantErr: true, - }, - { - name: "should return error if 'valid_duration' is not parsable", - receiverID: sampleReceiverID, - payloadMap: map[string]interface{}{ - "valid_duration": "xzx", - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := notification.BuildTypeReceiver(tt.receiverID, tt.payloadMap) - if (err != nil) != tt.wantErr { - t.Errorf("BuildTypeReceiver() error = %v, wantErr %v", err, tt.wantErr) - return - } - if diff := cmp.Diff(got, tt.want); diff != "" { - t.Errorf("BuildTypeReceiver() diff = %v", diff) - } - }) - } -} - -func TestBuildFromAlerts(t *testing.T) { - tests := []struct { - name string - alerts []alert.Alert - firingLen int - want notification.Notification - }{ - - { - name: "should return empty notification if alerts slice is empty", - want: notification.Notification{}, - }, - { - name: `should properly return notification - - same annotations are joined by newline - - labels are merged - `, - alerts: []alert.Alert{ - { - ID: 14, - ProviderID: 1, - NamespaceID: 1, - ResourceName: "test-alert-host-1", - MetricName: "test-alert", - MetricValue: "15", - Severity: "WARNING", - Rule: "test-alert-template", - Labels: map[string]string{"lk1": "lv1"}, - Annotations: map[string]string{"ak1": "akv1"}, - Status: "FIRING", - }, - { - ID: 15, - ProviderID: 1, - NamespaceID: 1, - ResourceName: "test-alert-host-2", - MetricName: "test-alert", - MetricValue: "16", - Severity: "WARNING", - Rule: "test-alert-template", - Labels: map[string]string{"lk1": "lv11", "lk2": "lv2"}, - Annotations: map[string]string{"ak1": "akv11", "ak2": "akv2"}, - Status: "FIRING", - }, - }, - firingLen: 2, - want: notification.Notification{ - NamespaceID: 1, - Type: notification.TypeSubscriber, - - Data: map[string]interface{}{ - "generator_url": "", - "num_alerts_firing": 2, - "status": "FIRING", - "ak1": "akv1\nakv11", - "ak2": "akv2", - }, - Labels: map[string]string{ - "lk1": "lv11", - "lk2": "lv2", - }, - Template: template.ReservedName_SystemDefault, - AlertIDs: []int64{14, 15}, - }, - }, - {}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := notification.BuildFromAlerts(tt.alerts, tt.firingLen, time.Time{}) - - if diff := cmp.Diff(got, tt.want); diff != "" { - t.Errorf("BuildFromAlerts() got diff = %v", diff) - } - }) - } -} diff --git a/core/notification/dispatch_receiver_service.go b/core/notification/dispatch_receiver_service.go deleted file mode 100644 index f53bdc70..00000000 --- a/core/notification/dispatch_receiver_service.go +++ /dev/null @@ -1,73 +0,0 @@ -package notification - -import ( - "context" - "strconv" - - "github.com/odpf/siren/core/log" - "github.com/odpf/siren/core/receiver" - "github.com/odpf/siren/pkg/errors" -) - -type DispatchReceiverService struct { - receiverService ReceiverService - notifierPlugins map[string]Notifier -} - -func NewDispatchReceiverService(receiverService ReceiverService, notifierPlugins map[string]Notifier) *DispatchReceiverService { - return &DispatchReceiverService{ - receiverService: receiverService, - notifierPlugins: notifierPlugins, - } -} - -func (s *DispatchReceiverService) 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 *DispatchReceiverService) PrepareMessage(ctx context.Context, n Notification) ([]Message, []log.Notification, bool, error) { - - var notificationLogs []log.Notification - - receiverID, err := strconv.ParseUint(n.Labels[ReceiverIDLabelKey], 0, 64) - if err != nil { - // should not goes here as this already have been checked - return nil, nil, false, err - } - - rcv, err := s.receiverService.Get(ctx, receiverID, receiver.GetWithData(false)) - if err != nil { - return nil, nil, false, err - } - - 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, - n, - rcv.Type, - rcv.Configurations, - InitWithExpiryDuration(n.ValidDuration), - ) - if err != nil { - return nil, nil, false, err - } - - messages := []Message{message} - notificationLogs = append(notificationLogs, log.Notification{ - NamespaceID: n.NamespaceID, - NotificationID: n.ID, - ReceiverID: rcv.ID, - AlertIDs: n.AlertIDs, - }) - - return messages, notificationLogs, false, nil -} diff --git a/core/notification/dispatch_receiver_service_test.go b/core/notification/dispatch_receiver_service_test.go deleted file mode 100644 index 95cf231f..00000000 --- a/core/notification/dispatch_receiver_service_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package notification_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/odpf/siren/core/log" - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/notification/mocks" - "github.com/odpf/siren/core/receiver" - "github.com/odpf/siren/pkg/errors" - "github.com/stretchr/testify/mock" -) - -func TestDispatchReceiverService_PrepareMessage(t *testing.T) { - 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 id in label is not parsable", - n: notification.Notification{ - Labels: map[string]string{ - notification.ReceiverIDLabelKey: "x", - }, - }, - wantErr: true, - }, - { - name: "should return error if receiver service return error", - n: notification.Notification{ - Labels: map[string]string{ - notification.ReceiverIDLabelKey: "11", - }, - }, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return error if receiver type is unknown", - n: notification.Notification{ - Labels: map[string]string{ - notification.ReceiverIDLabelKey: "11", - }, - }, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(&receiver.Receiver{}, nil) - }, - wantErr: true, - }, - { - name: "should return error if init message return error", - n: notification.Notification{ - Labels: map[string]string{ - notification.ReceiverIDLabelKey: "11", - }, - }, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(&receiver.Receiver{ - Type: testPluginType, - }, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return no error if all flow passed", - n: notification.Notification{ - Labels: map[string]string{ - notification.ReceiverIDLabelKey: "11", - }, - }, - setup: func(rs *mocks.ReceiverService, n *mocks.Notifier) { - rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(&receiver.Receiver{ - ID: 11, - Type: testPluginType, - }, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) - }, - want: []notification.Message{ - { - Status: notification.MessageStatusEnqueued, - ReceiverType: testPluginType, - Configs: map[string]interface{}{}, - Details: map[string]interface{}{"notification_type": string(""), "receiver_id": string("11")}, - MaxTries: 3, - }, - }, - want1: []log.Notification{{ReceiverID: 11}}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockReceiverService = new(mocks.ReceiverService) - mockNotifier = new(mocks.Notifier) - ) - s := notification.NewDispatchReceiverService( - mockReceiverService, - map[string]notification.Notifier{ - testPluginType: 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("DispatchReceiverService.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("DispatchReceiverService.PrepareMessage() diff = %v", diff) - } - if diff := cmp.Diff(got1, tt.want1); diff != "" { - t.Errorf("DispatchReceiverService.PrepareMessage() diff = %v", diff) - } - if got2 != tt.want2 { - t.Errorf("DispatchReceiverService.PrepareMessage() got2 = %v, want %v", got2, tt.want2) - } - }) - } -} diff --git a/core/notification/dispatch_subscriber_service.go b/core/notification/dispatch_subscriber_service.go deleted file mode 100644 index 151a697c..00000000 --- a/core/notification/dispatch_subscriber_service.go +++ /dev/null @@ -1,160 +0,0 @@ -package notification - -import ( - "context" - "fmt" - - saltlog "github.com/odpf/salt/log" - "github.com/odpf/siren/core/log" - "github.com/odpf/siren/core/silence" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/pkg/telemetry" -) - -type DispatchSubscriberService struct { - logger saltlog.Logger - subscriptionService SubscriptionService - silenceService SilenceService - notifierPlugins map[string]Notifier -} - -func NewDispatchSubscriberService( - logger saltlog.Logger, - subscriptionService SubscriptionService, - silenceService SilenceService, - notifierPlugins map[string]Notifier) *DispatchSubscriberService { - return &DispatchSubscriberService{ - logger: logger, - subscriptionService: subscriptionService, - silenceService: silenceService, - notifierPlugins: notifierPlugins, - } -} - -func (s *DispatchSubscriberService) 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 *DispatchSubscriberService) PrepareMessage(ctx context.Context, n Notification) ([]Message, []log.Notification, bool, error) { - - var ( - messages = make([]Message, 0) - notificationLogs []log.Notification - hasSilenced bool - ) - - subscriptions, err := s.subscriptionService.MatchByLabels(ctx, n.NamespaceID, n.Labels) - if err != nil { - return nil, nil, false, err - } - - if len(subscriptions) == 0 { - telemetry.IncrementInt64Counter(ctx, telemetry.MetricNotificationSubscriberNotFound) - return nil, nil, false, errors.ErrInvalid.WithMsgf("not matching any subscription") - } - - for _, sub := range subscriptions { - - if len(sub.Receivers) == 0 { - s.logger.Warn(fmt.Sprintf("invalid subscription with id %d, no receiver found", sub.ID)) - continue - } - - // try silencing by labels - silences, err := s.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.logger.Info(fmt.Sprintf("notification '%s' of alert ids '%v' is being silenced by labels '%v'", n.ID, n.AlertIDs, silences)) - continue - } - - // subscription not being silenced by label - silences, err = s.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, - 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 -} diff --git a/core/notification/dispatch_subscriber_service_test.go b/core/notification/dispatch_subscriber_service_test.go deleted file mode 100644 index c7648ac9..00000000 --- a/core/notification/dispatch_subscriber_service_test.go +++ /dev/null @@ -1,342 +0,0 @@ -package notification_test - -import ( - "context" - "errors" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - saltlog "github.com/odpf/salt/log" - "github.com/odpf/siren/core/log" - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/notification/mocks" - "github.com/odpf/siren/core/silence" - "github.com/odpf/siren/core/subscription" - "github.com/stretchr/testify/mock" -) - -func TestDispatchSubscriberService_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.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), 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.emptyCtx"), 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.emptyCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), 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.emptyCtx"), 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: testPluginType, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionID: 123, - }).Return(nil, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), 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{ - NamespaceID: 1, - }, - setup: func(ss1 *mocks.SubscriptionService, ss2 *mocks.SilenceService, n *mocks.Notifier) { - ss1.EXPECT().MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), 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: testPluginType, - }, - }, - }, - }, nil) - ss2.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionMatch: map[string]string{ - "k1": "v1", - }, - }).Return(nil, nil) - ss2.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), silence.Filter{ - NamespaceID: 1, - SubscriptionID: 123, - }).Return(nil, nil) - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) - }, - want: []notification.Message{ - { - Status: notification.MessageStatusEnqueued, - ReceiverType: testPluginType, - Configs: map[string]interface{}{}, - Details: map[string]interface{}{"notification_type": string("")}, - MaxTries: 3, - }, - }, - want1: []log.Notification{{NamespaceID: 1, 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) - ) - s := notification.NewDispatchSubscriberService( - saltlog.NewNoop(), - mockSubscriptionService, - mockSilenceService, map[string]notification.Notifier{ - testPluginType: mockNotifier, - }) - - if tt.setup != nil { - tt.setup(mockSubscriptionService, mockSilenceService, mockNotifier) - } - - got, got1, got2, err := s.PrepareMessage(context.TODO(), tt.n) - if (err != nil) != tt.wantErr { - t.Errorf("DispatchSubscriberService.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("DispatchSubscriberService.PrepareMessage() diff = %v", diff) - } - if diff := cmp.Diff(got1, tt.want1); diff != "" { - t.Errorf("DispatchSubscriberService.PrepareMessage() diff = %v", diff) - } - if got2 != tt.want2 { - t.Errorf("DispatchSubscriberService.PrepareMessage() got2 = %v, want %v", got2, tt.want2) - } - }) - } -} diff --git a/core/notification/idempotency.go b/core/notification/idempotency.go deleted file mode 100644 index 9f02bf34..00000000 --- a/core/notification/idempotency.go +++ /dev/null @@ -1,26 +0,0 @@ -package notification - -import ( - "context" - "time" -) - -type IdempotencyFilter struct { - TTL time.Duration -} - -//go:generate mockery --name=IdempotencyRepository -r --case underscore --with-expecter --structname IdempotencyRepository --filename idempotency_repository.go --output=./mocks -type IdempotencyRepository interface { - InsertOnConflictReturning(context.Context, string, string) (*Idempotency, error) - UpdateSuccess(context.Context, uint64, bool) error - Delete(context.Context, IdempotencyFilter) error -} - -type Idempotency struct { - ID uint64 - Scope string - Key string - Success bool - CreatedAt time.Time - UpdatedAt time.Time -} diff --git a/core/notification/message.go b/core/notification/message.go index 90fb9a8e..af3f0329 100644 --- a/core/notification/message.go +++ b/core/notification/message.go @@ -1,25 +1,19 @@ package notification import ( - "context" "time" "github.com/google/uuid" - "github.com/odpf/siren/core/template" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/pkg/telemetry" - "go.opencensus.io/tag" - "gopkg.in/yaml.v3" ) // MessageStatus determines the state of the message type MessageStatus string const ( - defaultMaxTries int = 3 + DefaultMaxTries = 3 // additional details - DetailsKeyNotificationType = "notification_type" + DetailsKeyRoutingMethod = "routing_method" MessageStatusEnqueued MessageStatus = "enqueued" MessageStatusFailed MessageStatus = "failed" @@ -65,53 +59,42 @@ func InitWithMaxTries(mt int) MessageOption { // Message is the model to be sent for a specific subscription's receiver type Message struct { - ID string - Status MessageStatus + ID string + Status MessageStatus + ReceiverType string Configs map[string]interface{} // the datasource to build vendor-specific configs Details map[string]interface{} // the datasource to build vendor-specific message - MaxTries int - ExpiredAt time.Time - CreatedAt time.Time - UpdatedAt time.Time + LastError string - LastError string + MaxTries int TryCount int Retryable bool + ExpiredAt time.Time + CreatedAt time.Time + UpdatedAt time.Time + expiryDuration time.Duration } // Initialize initializes the message with some default value // or the customized value -func InitMessage( - ctx context.Context, - notifierPlugin Notifier, +func (m *Message) Initialize( n Notification, receiverType string, - messageConfig map[string]interface{}, + notificationConfigs map[string]interface{}, opts ...MessageOption, -) (Message, error) { - if notifierPlugin == nil { - return Message{}, errors.New("notifierPlugin cannot be nil") - } - - newConfigs, err := notifierPlugin.PreHookQueueTransformConfigs(ctx, messageConfig) - if err != nil { - telemetry.IncrementInt64Counter(ctx, telemetry.MetricReceiverHookFailed, - tag.Upsert(telemetry.TagNotificationType, n.Type), - tag.Upsert(telemetry.TagReceiverType, receiverType), - tag.Upsert(telemetry.TagHookCondition, telemetry.HookConditionPreHookQueue), - ) +) { + var timeNow = time.Now() - return Message{}, err - } + m.ID = uuid.NewString() + m.Status = MessageStatusEnqueued - var ( - timeNow = time.Now() - details = make(map[string]interface{}) - ) + m.ReceiverType = receiverType + m.Configs = notificationConfigs + details := make(map[string]interface{}) for k, v := range n.Labels { details[k] = v } @@ -119,16 +102,12 @@ func InitMessage( details[k] = v } - m := &Message{ - ID: uuid.NewString(), - Status: MessageStatusEnqueued, - ReceiverType: receiverType, - Configs: newConfigs, - Details: details, - MaxTries: defaultMaxTries, - CreatedAt: timeNow, - UpdatedAt: timeNow, - } + m.Details = details + + m.MaxTries = DefaultMaxTries + + m.CreatedAt = timeNow + m.UpdatedAt = timeNow for _, opt := range opts { opt(m) @@ -137,38 +116,6 @@ func InitMessage( if m.expiryDuration != 0 { m.ExpiredAt = m.CreatedAt.Add(m.expiryDuration) } - - //TODO fetch template if any, if not exist, check provider type, if exist use the default template, if not pass as-is - // if there is template, render and replace detail with the new one - if n.Template != "" { - var templateBody string - - if template.IsReservedName(n.Template) { - templateBody = notifierPlugin.GetSystemDefaultTemplate() - } - - if templateBody != "" { - renderedDetailString, err := template.RenderBody(templateBody, n) - if err != nil { - return Message{}, errors.ErrInvalid.WithMsgf("failed to render template receiver %s: %s", receiverType, err.Error()) - } - - var messageDetails map[string]interface{} - if err := yaml.Unmarshal([]byte(renderedDetailString), &messageDetails); err != nil { - return Message{}, errors.ErrInvalid.WithMsgf("failed to unmarshal rendered template receiver %s: %s, rendered template: %v", receiverType, err.Error(), renderedDetailString) - } - m.Details = messageDetails - } - } - - m.Details[DetailsKeyNotificationType] = n.Type - - telemetry.IncrementInt64Counter(ctx, telemetry.MetricNotificationMessageCounter, - tag.Upsert(telemetry.TagNotificationType, TypeSubscriber), - tag.Upsert(telemetry.TagMessageStatus, m.Status.String()), - tag.Upsert(telemetry.TagReceiverType, m.ReceiverType)) - - return *m, nil } // MarkFailed update message to the failed state @@ -192,3 +139,8 @@ func (m *Message) MarkPublished(updatedAt time.Time) { m.Status = MessageStatusPublished m.UpdatedAt = updatedAt } + +// AddDetail adds a custom kv string detail +func (m *Message) AddStringDetail(key, value string) { + m.Details[key] = value +} diff --git a/core/notification/message_test.go b/core/notification/message_test.go index 4ad3b035..5b896e93 100644 --- a/core/notification/message_test.go +++ b/core/notification/message_test.go @@ -1,7 +1,6 @@ package notification_test import ( - "context" "errors" "testing" "time" @@ -9,11 +8,9 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/notification/mocks" - "github.com/stretchr/testify/mock" ) -func TestMessage_InitMessage(t *testing.T) { +func TestMessage_Initialize(t *testing.T) { var ( testID = "some-id" testTimeNow = time.Now() @@ -21,20 +18,16 @@ func TestMessage_InitMessage(t *testing.T) { ) testCases := []struct { name string - setup func(*mocks.Notifier) n notification.Notification receiverType string notificationConfigs map[string]interface{} - want notification.Message - errString string + want *notification.Message + wantErr bool }{ { name: "all notification labels and data should be merged to message detail and data takes precedence if key conflict", - setup: func(n *mocks.Notifier) { - n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, nil) - }, n: notification.Notification{ - Type: notification.TypeSubscriber, + ID: "notification-id", Labels: map[string]string{ "labelkey1": "value1", "samekey": "label_value", @@ -44,15 +37,15 @@ func TestMessage_InitMessage(t *testing.T) { "samekey": "var_value", }, }, - want: notification.Message{ + want: ¬ification.Message{ ID: testID, Status: notification.MessageStatusEnqueued, Details: map[string]interface{}{ - "labelkey1": "value1", - "varkey1": "value1", - "samekey": "var_value", - notification.DetailsKeyNotificationType: notification.TypeSubscriber, + "labelkey1": "value1", + "varkey1": "value1", + "samekey": "var_value", }, + MaxTries: notification.DefaultMaxTries, CreatedAt: testTimeNow, UpdatedAt: testTimeNow, ExpiredAt: testTimeNow.Add(testExpiryDuration), @@ -61,15 +54,8 @@ func TestMessage_InitMessage(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - mockNotifierPlugin := new(mocks.Notifier) - - if tc.setup != nil { - tc.setup(mockNotifierPlugin) - } - - m, err := notification.InitMessage( - context.TODO(), - mockNotifierPlugin, + m := ¬ification.Message{} + m.Initialize( tc.n, tc.receiverType, tc.notificationConfigs, @@ -77,15 +63,8 @@ func TestMessage_InitMessage(t *testing.T) { notification.InitWithCreateTime(testTimeNow), notification.InitWithExpiryDuration(testExpiryDuration), ) - if err != nil { - if err.Error() != tc.errString { - t.Fatalf("got error %s, expected was %s", err.Error(), tc.errString) - } - } - if diff := cmp.Diff(m, tc.want, - cmpopts.IgnoreUnexported(notification.Message{}), - cmpopts.IgnoreFields(notification.Message{}, "MaxTries")); diff != "" { + if diff := cmp.Diff(m, tc.want, cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { t.Errorf("Notification.ToMessage() diff = %v", diff) } }) @@ -105,6 +84,7 @@ func TestMessage_Mark(t *testing.T) { "labelkey1": "value1", "varkey1": "value1", }, + MaxTries: notification.DefaultMaxTries, CreatedAt: createTime, UpdatedAt: createTime, ExpiredAt: expiredAt, @@ -124,9 +104,7 @@ func TestMessage_Mark(t *testing.T) { m.MarkFailed(testTimeNow, true, err) - if diff := cmp.Diff(m, expectedMessage, - cmpopts.IgnoreUnexported(notification.Message{}), - cmpopts.IgnoreFields(notification.Message{}, "MaxTries")); diff != "" { + if diff := cmp.Diff(m, expectedMessage, cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { t.Errorf("result not match, diff = %v", diff) } }) @@ -142,9 +120,7 @@ func TestMessage_Mark(t *testing.T) { m.MarkPending(testTimeNow) - if diff := cmp.Diff(m, expectedMessage, - cmpopts.IgnoreUnexported(notification.Message{}), - cmpopts.IgnoreFields(notification.Message{}, "MaxTries")); diff != "" { + if diff := cmp.Diff(m, expectedMessage, cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { t.Errorf("result not match, diff = %v", diff) } }) @@ -160,9 +136,7 @@ func TestMessage_Mark(t *testing.T) { m.MarkPublished(testTimeNow) - if diff := cmp.Diff(m, expectedMessage, - cmpopts.IgnoreUnexported(notification.Message{}), - cmpopts.IgnoreFields(notification.Message{}, "MaxTries")); diff != "" { + if diff := cmp.Diff(m, expectedMessage, cmpopts.IgnoreUnexported(notification.Message{})); diff != "" { t.Errorf("result not match, diff = %v", diff) } }) diff --git a/core/notification/mocks/alert_service.go b/core/notification/mocks/alert_service.go deleted file mode 100644 index ca4be0b4..00000000 --- a/core/notification/mocks/alert_service.go +++ /dev/null @@ -1,77 +0,0 @@ -// Code generated by mockery v2.16.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) - - 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 -} - -type mockConstructorTestingTNewAlertService interface { - mock.TestingT - Cleanup(func()) -} - -// 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. -func NewAlertService(t mockConstructorTestingTNewAlertService) *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 deleted file mode 100644 index d869a9f4..00000000 --- a/core/notification/mocks/dispatcher.go +++ /dev/null @@ -1,103 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - log "github.com/odpf/siren/core/log" - mock "github.com/stretchr/testify/mock" - - notification "github.com/odpf/siren/core/notification" -) - -// Dispatcher is an autogenerated mock type for the Dispatcher type -type Dispatcher struct { - mock.Mock -} - -type Dispatcher_Expecter struct { - mock *mock.Mock -} - -func (_m *Dispatcher) EXPECT() *Dispatcher_Expecter { - return &Dispatcher_Expecter{mock: &_m.Mock} -} - -// PrepareMessage provides a mock function with given fields: ctx, n -func (_m *Dispatcher) PrepareMessage(ctx context.Context, n notification.Notification) ([]notification.Message, []log.Notification, bool, error) { - ret := _m.Called(ctx, n) - - var r0 []notification.Message - 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) - } - } - - var r1 []log.Notification - 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) - } - } - - var r2 bool - if rf, ok := ret.Get(2).(func(context.Context, notification.Notification) bool); ok { - r2 = rf(ctx, n) - } else { - r2 = ret.Get(2).(bool) - } - - var r3 error - 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 -} - -// Dispatcher_PrepareMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrepareMessage' -type Dispatcher_PrepareMessage_Call struct { - *mock.Call -} - -// PrepareMessage is a helper method to define mock.On call -// - ctx context.Context -// - n notification.Notification -func (_e *Dispatcher_Expecter) PrepareMessage(ctx interface{}, n interface{}) *Dispatcher_PrepareMessage_Call { - return &Dispatcher_PrepareMessage_Call{Call: _e.mock.On("PrepareMessage", ctx, n)} -} - -func (_c *Dispatcher_PrepareMessage_Call) Run(run func(ctx context.Context, n notification.Notification)) *Dispatcher_PrepareMessage_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(notification.Notification)) - }) - return _c -} - -func (_c *Dispatcher_PrepareMessage_Call) Return(_a0 []notification.Message, _a1 []log.Notification, _a2 bool, _a3 error) *Dispatcher_PrepareMessage_Call { - _c.Call.Return(_a0, _a1, _a2, _a3) - return _c -} - -type mockConstructorTestingTNewDispatcher interface { - mock.TestingT - Cleanup(func()) -} - -// NewDispatcher creates a new instance of Dispatcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewDispatcher(t mockConstructorTestingTNewDispatcher) *Dispatcher { - mock := &Dispatcher{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/notification/mocks/idempotency_repository.go b/core/notification/mocks/idempotency_repository.go index 0ed1f893..b54634c2 100644 --- a/core/notification/mocks/idempotency_repository.go +++ b/core/notification/mocks/idempotency_repository.go @@ -1,11 +1,11 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks import ( context "context" - notification "github.com/odpf/siren/core/notification" + idempotency "github.com/odpf/siren/core/idempotency" mock "github.com/stretchr/testify/mock" ) @@ -23,11 +23,11 @@ func (_m *IdempotencyRepository) EXPECT() *IdempotencyRepository_Expecter { } // Delete provides a mock function with given fields: _a0, _a1 -func (_m *IdempotencyRepository) Delete(_a0 context.Context, _a1 notification.IdempotencyFilter) error { +func (_m *IdempotencyRepository) Delete(_a0 context.Context, _a1 idempotency.Filter) error { ret := _m.Called(_a0, _a1) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, notification.IdempotencyFilter) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, idempotency.Filter) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) @@ -42,15 +42,15 @@ type IdempotencyRepository_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 notification.IdempotencyFilter +// - _a0 context.Context +// - _a1 idempotency.Filter func (_e *IdempotencyRepository_Expecter) Delete(_a0 interface{}, _a1 interface{}) *IdempotencyRepository_Delete_Call { return &IdempotencyRepository_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} } -func (_c *IdempotencyRepository_Delete_Call) Run(run func(_a0 context.Context, _a1 notification.IdempotencyFilter)) *IdempotencyRepository_Delete_Call { +func (_c *IdempotencyRepository_Delete_Call) Run(run func(_a0 context.Context, _a1 idempotency.Filter)) *IdempotencyRepository_Delete_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(notification.IdempotencyFilter)) + run(args[0].(context.Context), args[1].(idempotency.Filter)) }) return _c } @@ -61,15 +61,15 @@ func (_c *IdempotencyRepository_Delete_Call) Return(_a0 error) *IdempotencyRepos } // InsertOnConflictReturning provides a mock function with given fields: _a0, _a1, _a2 -func (_m *IdempotencyRepository) InsertOnConflictReturning(_a0 context.Context, _a1 string, _a2 string) (*notification.Idempotency, error) { +func (_m *IdempotencyRepository) InsertOnConflictReturning(_a0 context.Context, _a1 string, _a2 string) (*idempotency.Idempotency, error) { ret := _m.Called(_a0, _a1, _a2) - var r0 *notification.Idempotency - if rf, ok := ret.Get(0).(func(context.Context, string, string) *notification.Idempotency); ok { + var r0 *idempotency.Idempotency + if rf, ok := ret.Get(0).(func(context.Context, string, string) *idempotency.Idempotency); ok { r0 = rf(_a0, _a1, _a2) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*notification.Idempotency) + r0 = ret.Get(0).(*idempotency.Idempotency) } } @@ -89,9 +89,9 @@ type IdempotencyRepository_InsertOnConflictReturning_Call struct { } // InsertOnConflictReturning is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -// - _a2 string +// - _a0 context.Context +// - _a1 string +// - _a2 string func (_e *IdempotencyRepository_Expecter) InsertOnConflictReturning(_a0 interface{}, _a1 interface{}, _a2 interface{}) *IdempotencyRepository_InsertOnConflictReturning_Call { return &IdempotencyRepository_InsertOnConflictReturning_Call{Call: _e.mock.On("InsertOnConflictReturning", _a0, _a1, _a2)} } @@ -103,7 +103,7 @@ func (_c *IdempotencyRepository_InsertOnConflictReturning_Call) Run(run func(_a0 return _c } -func (_c *IdempotencyRepository_InsertOnConflictReturning_Call) Return(_a0 *notification.Idempotency, _a1 error) *IdempotencyRepository_InsertOnConflictReturning_Call { +func (_c *IdempotencyRepository_InsertOnConflictReturning_Call) Return(_a0 *idempotency.Idempotency, _a1 error) *IdempotencyRepository_InsertOnConflictReturning_Call { _c.Call.Return(_a0, _a1) return _c } @@ -128,9 +128,9 @@ type IdempotencyRepository_UpdateSuccess_Call struct { } // UpdateSuccess is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -// - _a2 bool +// - _a0 context.Context +// - _a1 uint64 +// - _a2 bool func (_e *IdempotencyRepository_Expecter) UpdateSuccess(_a0 interface{}, _a1 interface{}, _a2 interface{}) *IdempotencyRepository_UpdateSuccess_Call { return &IdempotencyRepository_UpdateSuccess_Call{Call: _e.mock.On("UpdateSuccess", _a0, _a1, _a2)} } diff --git a/core/notification/mocks/log_service.go b/core/notification/mocks/log_service.go deleted file mode 100644 index 683460e8..00000000 --- a/core/notification/mocks/log_service.go +++ /dev/null @@ -1,90 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - log "github.com/odpf/siren/core/log" - mock "github.com/stretchr/testify/mock" -) - -// LogService is an autogenerated mock type for the LogService type -type LogService struct { - mock.Mock -} - -type LogService_Expecter struct { - mock *mock.Mock -} - -func (_m *LogService) EXPECT() *LogService_Expecter { - return &LogService_Expecter{mock: &_m.Mock} -} - -// LogNotifications provides a mock function with given fields: ctx, nlogs -func (_m *LogService) LogNotifications(ctx context.Context, nlogs ...log.Notification) error { - _va := make([]interface{}, len(nlogs)) - for _i := range nlogs { - _va[_i] = nlogs[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, ...log.Notification) error); ok { - r0 = rf(ctx, nlogs...) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// LogService_LogNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LogNotifications' -type LogService_LogNotifications_Call struct { - *mock.Call -} - -// LogNotifications is a helper method to define mock.On call -// - ctx context.Context -// - nlogs ...log.Notification -func (_e *LogService_Expecter) LogNotifications(ctx interface{}, nlogs ...interface{}) *LogService_LogNotifications_Call { - return &LogService_LogNotifications_Call{Call: _e.mock.On("LogNotifications", - append([]interface{}{ctx}, nlogs...)...)} -} - -func (_c *LogService_LogNotifications_Call) Run(run func(ctx context.Context, nlogs ...log.Notification)) *LogService_LogNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]log.Notification, len(args)-1) - for i, a := range args[1:] { - if a != nil { - variadicArgs[i] = a.(log.Notification) - } - } - run(args[0].(context.Context), variadicArgs...) - }) - return _c -} - -func (_c *LogService_LogNotifications_Call) Return(_a0 error) *LogService_LogNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewLogService interface { - mock.TestingT - Cleanup(func()) -} - -// NewLogService creates a new instance of LogService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewLogService(t mockConstructorTestingTNewLogService) *LogService { - mock := &LogService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/notification/mocks/repository.go b/core/notification/mocks/repository.go deleted file mode 100644 index b0097f02..00000000 --- a/core/notification/mocks/repository.go +++ /dev/null @@ -1,83 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - notification "github.com/odpf/siren/core/notification" - mock "github.com/stretchr/testify/mock" -) - -// Repository is an autogenerated mock type for the Repository type -type Repository struct { - mock.Mock -} - -type Repository_Expecter struct { - mock *mock.Mock -} - -func (_m *Repository) EXPECT() *Repository_Expecter { - return &Repository_Expecter{mock: &_m.Mock} -} - -// Create provides a mock function with given fields: _a0, _a1 -func (_m *Repository) Create(_a0 context.Context, _a1 notification.Notification) (notification.Notification, error) { - ret := _m.Called(_a0, _a1) - - var r0 notification.Notification - if rf, ok := ret.Get(0).(func(context.Context, notification.Notification) notification.Notification); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Get(0).(notification.Notification) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, notification.Notification) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Repository_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type Repository_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 notification.Notification -func (_e *Repository_Expecter) Create(_a0 interface{}, _a1 interface{}) *Repository_Create_Call { - return &Repository_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} -} - -func (_c *Repository_Create_Call) Run(run func(_a0 context.Context, _a1 notification.Notification)) *Repository_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(notification.Notification)) - }) - return _c -} - -func (_c *Repository_Create_Call) Return(_a0 notification.Notification, _a1 error) *Repository_Create_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -type mockConstructorTestingTNewRepository interface { - mock.TestingT - Cleanup(func()) -} - -// NewRepository creates a new instance of Repository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewRepository(t mockConstructorTestingTNewRepository) *Repository { - mock := &Repository{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/notification/mocks/silence_service.go b/core/notification/mocks/silence_service.go deleted file mode 100644 index 29d0338a..00000000 --- a/core/notification/mocks/silence_service.go +++ /dev/null @@ -1,86 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - - silence "github.com/odpf/siren/core/silence" -) - -// SilenceService is an autogenerated mock type for the SilenceService type -type SilenceService struct { - mock.Mock -} - -type SilenceService_Expecter struct { - mock *mock.Mock -} - -func (_m *SilenceService) EXPECT() *SilenceService_Expecter { - return &SilenceService_Expecter{mock: &_m.Mock} -} - -// List provides a mock function with given fields: ctx, filter -func (_m *SilenceService) List(ctx context.Context, filter silence.Filter) ([]silence.Silence, error) { - ret := _m.Called(ctx, filter) - - var r0 []silence.Silence - if rf, ok := ret.Get(0).(func(context.Context, silence.Filter) []silence.Silence); ok { - r0 = rf(ctx, filter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]silence.Silence) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, silence.Filter) error); ok { - r1 = rf(ctx, filter) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SilenceService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type SilenceService_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - ctx context.Context -// - filter silence.Filter -func (_e *SilenceService_Expecter) List(ctx interface{}, filter interface{}) *SilenceService_List_Call { - return &SilenceService_List_Call{Call: _e.mock.On("List", ctx, filter)} -} - -func (_c *SilenceService_List_Call) Run(run func(ctx context.Context, filter silence.Filter)) *SilenceService_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(silence.Filter)) - }) - return _c -} - -func (_c *SilenceService_List_Call) Return(_a0 []silence.Silence, _a1 error) *SilenceService_List_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -type mockConstructorTestingTNewSilenceService interface { - mock.TestingT - Cleanup(func()) -} - -// NewSilenceService creates a new instance of SilenceService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSilenceService(t mockConstructorTestingTNewSilenceService) *SilenceService { - mock := &SilenceService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/notification/notification.go b/core/notification/notification.go index 8c15532f..aad7489f 100644 --- a/core/notification/notification.go +++ b/core/notification/notification.go @@ -1,71 +1,42 @@ package notification import ( - "context" - "strconv" "time" "github.com/odpf/siren/pkg/errors" ) -const ( - ReceiverIDLabelKey string = "receiver_id" - ValidDurationRequestKey string = "valid_duration" - - TypeReceiver string = "receiver" - TypeSubscriber string = "subscriber" -) - -//go:generate mockery --name=Repository -r --case underscore --with-expecter --structname Repository --filename repository.go --output=./mocks -type Repository interface { - Create(context.Context, Notification) (Notification, error) -} - // Notification is a model of notification -// if type is `receiver`, it is expected for the labels to have -// receiver_id = int type Notification struct { - ID string `json:"id"` - NamespaceID uint64 `json:"namespace_id"` - Type string `json:"type"` - Data map[string]interface{} `json:"data"` - Labels map[string]string `json:"labels"` - ValidDuration time.Duration `json:"valid_duration"` - Template string `json:"template"` - CreatedAt time.Time `json:"created_at"` - - // won't be stored in notification table, only to propaget this to notification_subscriber - AlertIDs []int64 -} - -func (n *Notification) EnrichID(id string) { - if n == nil { - return - } - n.ID = id - - if len(n.Data) == 0 { - n.Data = map[string]interface{}{} - } - - n.Data["id"] = id + ID string `json:"id"` + Data map[string]interface{} `json:"data"` + Labels map[string]string `json:"labels"` + ValidDurationString string `json:"valid_duration"` + Template string `json:"template"` + CreatedAt time.Time } -func (n Notification) Validate() error { - if n.Type == TypeReceiver { - if v, ok := n.Labels[ReceiverIDLabelKey]; ok { - intVar, err := strconv.ParseInt(v, 0, 64) - if err == nil && intVar != 0 { - return nil - } - } - return errors.ErrInvalid.WithCausef("notification type receiver should have valid receiver_id: %v", n) - } else if n.Type == TypeSubscriber { - if len(n.Labels) != 0 { - return nil +// ToMessage transforms Notification model to one or several Messages +func (n Notification) ToMessage(receiverType string, notificationConfigMap map[string]interface{}) (*Message, error) { + var ( + expiryDuration time.Duration + err error + ) + + if n.ValidDurationString != "" { + expiryDuration, err = time.ParseDuration(n.ValidDurationString) + if err != nil { + return nil, errors.ErrInvalid.WithMsgf(err.Error()) } - return errors.ErrInvalid.WithCausef("notification type subscriber should have labels: %v", n) } - return errors.ErrInvalid.WithCausef("invalid notification type: %v", n) + nm := &Message{} + nm.Initialize( + n, + receiverType, + notificationConfigMap, + InitWithExpiryDuration(expiryDuration), + ) + + return nm, nil } diff --git a/core/notification/notification_test.go b/core/notification/notification_test.go index 1c34f8aa..9b34a3d4 100644 --- a/core/notification/notification_test.go +++ b/core/notification/notification_test.go @@ -3,28 +3,30 @@ package notification_test import ( "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/odpf/siren/core/notification" ) -func TestNotification_Validate(t *testing.T) { +func TestNotification_ToMessage(t *testing.T) { testCases := []struct { name string n notification.Notification receiverType string notificationConfigs map[string]interface{} + want *notification.Message wantErr bool }{ { - name: "should return error if type is unknown", + name: "should return error if expiry duration is not parsable", n: notification.Notification{ - Type: "random", + ValidDurationString: "xxx", }, wantErr: true, }, { - name: "should return error if type receiver but has no 'receiver_id' key label", + name: "should return message if expiry duration is empty", n: notification.Notification{ - Type: notification.TypeReceiver, Labels: map[string]string{ "labelkey1": "value1", }, @@ -32,76 +34,55 @@ func TestNotification_Validate(t *testing.T) { "varkey1": "value1", }, }, - wantErr: true, - }, - { - name: "should return error if type receiver but has empty 'receiver_id' label value", - n: notification.Notification{ - Type: notification.TypeReceiver, - Labels: map[string]string{ - "receiver_id": "", - }, - Data: map[string]interface{}{ - "varkey1": "value1", - }, - }, - wantErr: true, - }, - { - name: "should return error if type receiver but has 'receiver_id' value is non parsable to integer", - n: notification.Notification{ - Type: notification.TypeReceiver, - Labels: map[string]string{ - "receiver_id": "xxx", - }, - Data: map[string]interface{}{ - "varkey1": "value1", + want: ¬ification.Message{ + Status: notification.MessageStatusEnqueued, + Details: map[string]interface{}{ + "labelkey1": "value1", + "varkey1": "value1", }, }, - wantErr: true, }, { - name: "should return nil error if type receiver and 'receiver_id' is valid", + name: "should return message if expiry duration is parsable", n: notification.Notification{ - Type: notification.TypeReceiver, Labels: map[string]string{ - "receiver_id": "2", - }, - Data: map[string]interface{}{ - "varkey1": "value1", + "labelkey1": "value1", }, - }, - }, - { - name: "should return error if type subscriber but has no kv labels", - n: notification.Notification{ - Type: notification.TypeSubscriber, Data: map[string]interface{}{ "varkey1": "value1", }, + ValidDurationString: "10m", }, - wantErr: true, - }, - { - name: "should return nil error if type subscriber and has kv labels", - n: notification.Notification{ - Type: notification.TypeSubscriber, - Labels: map[string]string{ - "receiver_id": "xxx", - }, - Data: map[string]interface{}{ - "varkey1": "value1", + want: ¬ification.Message{ + Status: notification.MessageStatusEnqueued, + Details: map[string]interface{}{ + "labelkey1": "value1", + "varkey1": "value1", }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := tc.n.Validate() + got, err := tc.n.ToMessage(tc.receiverType, tc.notificationConfigs) if (err != nil) != tc.wantErr { t.Errorf("Notification.ToMessage() error = %v, wantErr %v", err, tc.wantErr) return } + + if diff := cmp.Diff(got, tc.want, + cmpopts.IgnoreUnexported(notification.Message{}), + cmpopts.IgnoreFields( + notification.Message{}, + "ID", + "MaxTries", + "ExpiredAt", + "CreatedAt", + "UpdatedAt", + ), + ); diff != "" { + t.Errorf("Notification.ToMessage() diff = %v", diff) + } }) } } diff --git a/core/notification/routing.go b/core/notification/routing.go new file mode 100644 index 00000000..b62f9054 --- /dev/null +++ b/core/notification/routing.go @@ -0,0 +1,12 @@ +package notification + +type RoutingMethod string + +const ( + RoutingMethodReceiver RoutingMethod = "receiver" + RoutingMethodSubscribers RoutingMethod = "subscribers" +) + +func (rm RoutingMethod) String() string { + return string(rm) +} diff --git a/core/notification/service.go b/core/notification/service.go index 5ee5d8e0..6374e713 100644 --- a/core/notification/service.go +++ b/core/notification/service.go @@ -2,23 +2,26 @@ package notification import ( "context" - "fmt" "time" - saltlog "github.com/odpf/salt/log" + "github.com/odpf/salt/log" + "go.opencensus.io/tag" "go.opencensus.io/trace" + "gopkg.in/yaml.v3" - "github.com/odpf/siren/core/log" + "github.com/odpf/siren/core/idempotency" "github.com/odpf/siren/core/receiver" - "github.com/odpf/siren/core/silence" "github.com/odpf/siren/core/subscription" + "github.com/odpf/siren/core/template" "github.com/odpf/siren/pkg/errors" "github.com/odpf/siren/pkg/telemetry" ) -//go:generate mockery --name=Dispatcher -r --case underscore --with-expecter --structname Dispatcher --filename dispatcher.go --output=./mocks -type Dispatcher interface { - PrepareMessage(ctx context.Context, n Notification) ([]Message, []log.Notification, bool, error) +//go:generate mockery --name=IdempotencyRepository -r --case underscore --with-expecter --structname IdempotencyRepository --filename idempotency_repository.go --output=./mocks +type IdempotencyRepository interface { + InsertOnConflictReturning(context.Context, string, string) (*idempotency.Idempotency, error) + UpdateSuccess(context.Context, uint64, bool) error + Delete(context.Context, idempotency.Filter) error } //go:generate mockery --name=SubscriptionService -r --case underscore --with-expecter --structname SubscriptionService --filename subscription_service.go --output=./mocks @@ -31,82 +34,33 @@ type ReceiverService interface { Get(ctx context.Context, id uint64, gopts ...receiver.GetOption) (*receiver.Receiver, error) } -//go:generate mockery --name=SilenceService -r --case underscore --with-expecter --structname SilenceService --filename silence_service.go --output=./mocks -type SilenceService interface { - List(ctx context.Context, filter silence.Filter) ([]silence.Silence, error) -} - -//go:generate mockery --name=AlertService -r --case underscore --with-expecter --structname AlertService --filename alert_service.go --output=./mocks -type AlertService interface { - UpdateSilenceStatus(ctx context.Context, alertIDs []int64, hasSilenced bool, hasNonSilenced bool) error -} - -//go:generate mockery --name=LogService -r --case underscore --with-expecter --structname LogService --filename log_service.go --output=./mocks -type LogService interface { - LogNotifications(ctx context.Context, nlogs ...log.Notification) error -} - // Service is a service for notification domain type Service struct { - logger saltlog.Logger + logger log.Logger q Queuer idempotencyRepository IdempotencyRepository - logService LogService - repository Repository receiverService ReceiverService subscriptionService SubscriptionService - silenceService SilenceService - alertService AlertService notifierPlugins map[string]Notifier - dispatcher map[string]Dispatcher messagingTracer *telemetry.MessagingTracer } -type Deps struct { - IdempotencyRepository IdempotencyRepository - LogService LogService - ReceiverService ReceiverService - SubscriptionService SubscriptionService - SilenceService SilenceService - AlertService AlertService - DispatchReceiverService Dispatcher - DispatchSubscriberService Dispatcher -} - // NewService creates a new notification service func NewService( - logger saltlog.Logger, - repository Repository, + logger log.Logger, q Queuer, + idempotencyRepository IdempotencyRepository, + receiverService ReceiverService, + subscriptionService SubscriptionService, notifierPlugins map[string]Notifier, - deps Deps, ) *Service { - var ( - dispatchReceiverService = deps.DispatchReceiverService - dispatchSubscriberService = deps.DispatchSubscriberService - ) - if deps.DispatchReceiverService == nil { - dispatchReceiverService = NewDispatchReceiverService(deps.ReceiverService, notifierPlugins) - } - if deps.DispatchSubscriberService == nil { - dispatchSubscriberService = NewDispatchSubscriberService(logger, deps.SubscriptionService, deps.SilenceService, notifierPlugins) - } - ns := &Service{ logger: logger, q: q, - repository: repository, - idempotencyRepository: deps.IdempotencyRepository, - logService: deps.LogService, - receiverService: deps.ReceiverService, - subscriptionService: deps.SubscriptionService, - silenceService: deps.SilenceService, - alertService: deps.AlertService, - dispatcher: map[string]Dispatcher{ - TypeReceiver: dispatchReceiverService, - TypeSubscriber: dispatchSubscriberService, - }, - notifierPlugins: notifierPlugins, + idempotencyRepository: idempotencyRepository, + receiverService: receiverService, + subscriptionService: subscriptionService, + notifierPlugins: notifierPlugins, } ns.messagingTracer = telemetry.NewMessagingTracer("default") @@ -117,60 +71,148 @@ func NewService( return ns } -func (s *Service) getDispatcherService(notificationType string) (Dispatcher, error) { - selectedDispatcher, exist := s.dispatcher[notificationType] +func (ns *Service) getNotifierPlugin(receiverType string) (Notifier, error) { + notifierPlugin, exist := ns.notifierPlugins[receiverType] if !exist { - return nil, errors.ErrInvalid.WithMsgf("unsupported notification type: %q", notificationType) + return nil, errors.ErrInvalid.WithMsgf("unsupported receiver type: %q", receiverType) } - return selectedDispatcher, nil + return notifierPlugin, nil } -func (s *Service) Dispatch(ctx context.Context, n Notification) error { - if err := n.Validate(); err != nil { +func (ns *Service) DispatchToReceiver(ctx context.Context, n Notification, receiverID uint64) error { + rcv, err := ns.receiverService.Get(ctx, receiverID, receiver.GetWithData(false)) + if err != nil { return err } - no, err := s.repository.Create(ctx, n) + ctx, span := ns.messagingTracer.StartSpan(ctx, "prepare_enqueue", + trace.StringAttribute("messaging.notification_id", n.ID), + trace.StringAttribute("messaging.routing_method", RoutingMethodReceiver.String()), + ) + defer span.End() + + notifierPlugin, err := ns.getNotifierPlugin(rcv.Type) if err != nil { - return err + return errors.ErrInvalid.WithMsgf("invalid receiver type: %s", err.Error()) } - n.EnrichID(no.ID) - - dispatcherService, err := s.getDispatcherService(n.Type) + message, err := n.ToMessage(rcv.Type, rcv.Configurations) if err != nil { return err } - ctx, span := s.messagingTracer.StartSpan(ctx, "prepare_message", - trace.StringAttribute("messaging.notification_id", n.ID), - trace.StringAttribute("messaging.routing_method", n.Type), - ) - messages, notificationLogs, hasSilenced, err := dispatcherService.PrepareMessage(ctx, n) - span.End() + newConfigs, err := notifierPlugin.PreHookQueueTransformConfigs(ctx, message.Configs) if err != nil { + telemetry.IncrementInt64Counter(ctx, telemetry.MetricReceiverHookFailed, + tag.Upsert(telemetry.TagRoutingMethod, RoutingMethodReceiver.String()), + tag.Upsert(telemetry.TagReceiverType, message.ReceiverType), + tag.Upsert(telemetry.TagHookCondition, telemetry.HookConditionPreHookQueue), + ) + return err } + message.Configs = newConfigs + + message.AddStringDetail(DetailsKeyRoutingMethod, RoutingMethodReceiver.String()) - if len(messages) == 0 && len(notificationLogs) == 0 { - return fmt.Errorf("something wrong and no messages will be sent with notification: %v", n) + span.End() + + telemetry.IncrementInt64Counter(ctx, telemetry.MetricNotificationMessageCounter, + tag.Upsert(telemetry.TagRoutingMethod, RoutingMethodReceiver.String()), + tag.Upsert(telemetry.TagMessageStatus, message.Status.String()), + tag.Upsert(telemetry.TagReceiverType, message.ReceiverType), + ) + + // supported no templating for now + if err := ns.q.Enqueue(ctx, *message); err != nil { + return err } - if err := s.logService.LogNotifications(ctx, notificationLogs...); err != nil { - return fmt.Errorf("failed logging notifications: %w", err) + return nil +} + +func (ns *Service) DispatchToSubscribers(ctx context.Context, namespaceID uint64, n Notification) error { + subscriptions, err := ns.subscriptionService.MatchByLabels(ctx, namespaceID, n.Labels) + if err != nil { + return err } - if err := s.alertService.UpdateSilenceStatus(ctx, n.AlertIDs, hasSilenced, len(messages) != 0); err != nil { - return fmt.Errorf("failed updating silence status: %w", err) + if len(subscriptions) == 0 { + telemetry.IncrementInt64Counter(ctx, telemetry.MetricNotificationSubscriberNotFound) + return errors.ErrInvalid.WithMsgf("not matching any subscription") } - if len(messages) == 0 { - s.logger.Info("no messages to enqueue") - return nil + ctx, span := ns.messagingTracer.StartSpan(ctx, "prepare_enqueue", + trace.StringAttribute("messaging.notification_id", n.ID), + trace.StringAttribute("messaging.routing_method", RoutingMethodSubscribers.String()), + ) + defer span.End() + + var messages = make([]Message, 0) + + for _, s := range subscriptions { + for _, rcv := range s.Receivers { + + notifierPlugin, err := ns.getNotifierPlugin(rcv.Type) + if err != nil { + return err + } + + message, err := n.ToMessage(rcv.Type, rcv.Configuration) + if err != nil { + return err + } + + newConfigs, err := notifierPlugin.PreHookQueueTransformConfigs(ctx, message.Configs) + if err != nil { + telemetry.IncrementInt64Counter(ctx, telemetry.MetricReceiverHookFailed, + tag.Upsert(telemetry.TagReceiverType, message.ReceiverType), + tag.Upsert(telemetry.TagRoutingMethod, RoutingMethodSubscribers.String()), + tag.Upsert(telemetry.TagHookCondition, telemetry.HookConditionPreHookQueue), + ) + + return err + } + message.Configs = newConfigs + + message.AddStringDetail(DetailsKeyRoutingMethod, RoutingMethodSubscribers.String()) + + //TODO fetch template if any, if not exist, check provider type, if exist use the default template, if not pass as-is + // if there is template, render and replace detail with the new one + if n.Template != "" { + var templateBody string + + if template.IsReservedName(n.Template) { + templateBody = notifierPlugin.GetSystemDefaultTemplate() + } + + if templateBody != "" { + renderedDetailString, err := template.RenderBody(templateBody, n) + if err != nil { + return errors.ErrInvalid.WithMsgf("failed to render template receiver %s: %s", rcv.Type, err.Error()) + } + + var messageDetails map[string]interface{} + if err := yaml.Unmarshal([]byte(renderedDetailString), &messageDetails); err != nil { + return errors.ErrInvalid.WithMsgf("failed to unmarshal rendered template receiver %s: %s, rendered template: %v", rcv.Type, err.Error(), renderedDetailString) + } + message.Details = messageDetails + } + } + + telemetry.IncrementInt64Counter(ctx, telemetry.MetricNotificationMessageCounter, + tag.Upsert(telemetry.TagRoutingMethod, RoutingMethodSubscribers.String()), + tag.Upsert(telemetry.TagMessageStatus, message.Status.String()), + tag.Upsert(telemetry.TagReceiverType, message.ReceiverType)) + + messages = append(messages, *message) + } } - if err := s.q.Enqueue(ctx, messages...); err != nil { - return fmt.Errorf("failed enqueuing messages: %w", err) + span.End() + + if err := ns.q.Enqueue(ctx, messages...); err != nil { + return err } return nil @@ -194,7 +236,7 @@ func (s *Service) MarkIdempotencyAsSuccess(ctx context.Context, id uint64) error } func (s *Service) RemoveIdempotencies(ctx context.Context, TTL time.Duration) error { - return s.idempotencyRepository.Delete(ctx, IdempotencyFilter{ + return s.idempotencyRepository.Delete(ctx, idempotency.Filter{ TTL: TTL, }) } diff --git a/core/notification/service_test.go b/core/notification/service_test.go index 51b546ee..32b60647 100644 --- a/core/notification/service_test.go +++ b/core/notification/service_test.go @@ -4,228 +4,345 @@ import ( "context" "testing" - saltlog "github.com/odpf/salt/log" - "github.com/odpf/siren/core/log" + "github.com/odpf/salt/log" + "github.com/stretchr/testify/mock" + + "github.com/odpf/siren/core/idempotency" "github.com/odpf/siren/core/notification" "github.com/odpf/siren/core/notification/mocks" + "github.com/odpf/siren/core/receiver" + "github.com/odpf/siren/core/subscription" "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/plugins/queues" - "github.com/stretchr/testify/mock" ) const testPluginType = "test" -func TestService_CheckAndInsertIdempotency(t *testing.T) { - var ( - scope = "test-scope" - key = "test-key" - ) +func TestService_DispatchToReceiver(t *testing.T) { testCases := []struct { name string - setup func(*mocks.IdempotencyRepository) - scope string - key string + setup func(*mocks.ReceiverService, *mocks.Queuer, *mocks.Notifier) + n notification.Notification wantErr bool }{ { - name: "should return error if idempotency exist and success", - setup: func(ir *mocks.IdempotencyRepository) { - ir.EXPECT().InsertOnConflictReturning(mock.AnythingOfType("*context.emptyCtx"), scope, key).Return(nil, errors.ErrConflict) + name: "should return error if failed to transform notification to messages", + setup: func(rs *mocks.ReceiverService, q *mocks.Queuer, _ *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(&receiver.Receiver{}, nil) + }, + n: notification.Notification{ + ValidDurationString: "xxx", }, - scope: scope, - key: key, wantErr: true, }, { - name: "should return error if repository returning some error", - setup: func(ir *mocks.IdempotencyRepository) { - ir.EXPECT().InsertOnConflictReturning(mock.AnythingOfType("*context.emptyCtx"), scope, key).Return(nil, errors.New("some error")) + name: "should return error if there is an error when fetching receiver", + setup: func(rs *mocks.ReceiverService, q *mocks.Queuer, _ *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(nil, errors.New("some error")) }, - scope: scope, - key: key, wantErr: true, }, { - name: "should return id and nil error if no idempotency exists", - setup: func(ir *mocks.IdempotencyRepository) { - ir.EXPECT().InsertOnConflictReturning(mock.AnythingOfType("*context.emptyCtx"), scope, key).Return(¬ification.Idempotency{ - ID: 1, + name: "should return error if prehook transform config return error", + setup: func(rs *mocks.ReceiverService, q *mocks.Queuer, n *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(&receiver.Receiver{ + Type: testPluginType, + Configurations: map[string]interface{}{ + "key": "value", + }, }, nil) + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("invalid config")) + }, + wantErr: true, + }, + { + name: "should return error if enqueue error", + setup: func(rs *mocks.ReceiverService, q *mocks.Queuer, n *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(&receiver.Receiver{ + Type: testPluginType, + Configurations: map[string]interface{}{ + "key": "value", + }, + }, nil) + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ + "key": "value", + }, nil) + q.EXPECT().Enqueue(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error")) + }, + wantErr: true, + }, + { + name: "should return no error if enqueue success", + setup: func(rs *mocks.ReceiverService, q *mocks.Queuer, n *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + rs.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("receiver.GetOption")).Return(&receiver.Receiver{ + Type: testPluginType, + Configurations: map[string]interface{}{ + "key": "value", + }, + }, nil) + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ + "key": "value", + }, nil) + q.EXPECT().Enqueue(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Message")).Return(nil) }, - scope: scope, - key: key, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - mockIdempotencyRepository := new(mocks.IdempotencyRepository) + var ( + mockReceiverService = new(mocks.ReceiverService) + mockQueuer = new(mocks.Queuer) + mockNotifier = new(mocks.Notifier) + ) if tc.setup != nil { - tc.setup(mockIdempotencyRepository) + tc.setup(mockReceiverService, mockQueuer, mockNotifier) } - ns := notification.NewService(saltlog.NewNoop(), nil, nil, nil, notification.Deps{IdempotencyRepository: mockIdempotencyRepository}) + ns := notification.NewService( + log.NewNoop(), mockQueuer, nil, mockReceiverService, nil, map[string]notification.Notifier{ + testPluginType: mockNotifier, + }) - _, err := ns.CheckAndInsertIdempotency(context.Background(), tc.scope, tc.key) - - if (err != nil) != tc.wantErr { - t.Errorf("NotificationService.CheckAndInsertIdempotency() error = %v, wantErr %v", err, tc.wantErr) + if err := ns.DispatchToReceiver(context.Background(), tc.n, 1); (err != nil) != tc.wantErr { + t.Errorf("NotificationService.Dispatch() error = %v, wantErr %v", err, tc.wantErr) } - mockIdempotencyRepository.AssertExpectations(t) + mockReceiverService.AssertExpectations(t) + mockQueuer.AssertExpectations(t) + mockNotifier.AssertExpectations(t) }) } } -func TestService_Dispatch(t *testing.T) { - tests := []struct { +func TestService_DispatchToSubscribers(t *testing.T) { + testCases := []struct { name string + setup func(*mocks.SubscriptionService, *mocks.Queuer, *mocks.Notifier) n notification.Notification - setup func(notification.Notification, *mocks.Repository, *mocks.LogService, *mocks.AlertService, *mocks.Queuer, *mocks.Dispatcher) wantErr bool }{ { - name: "should return error if notification type is unknown", - n: notification.Notification{}, - wantErr: true, - }, - { - name: "should return error if repository return error", - n: notification.Notification{ - Type: notification.TypeSubscriber, - Labels: map[string]string{ - "k1": "v1", - }, - }, - setup: func(n notification.Notification, r *mocks.Repository, _ *mocks.LogService, _ *mocks.AlertService, _ *mocks.Queuer, _ *mocks.Dispatcher) { - r.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, errors.New("some error")) + name: "should return error if there is an error when matching labels", + setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, _ *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + ss.EXPECT().MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, errors.New("some error")) }, wantErr: true, }, { - name: "should return error if dispatcher service return error", - n: notification.Notification{ - Type: notification.TypeSubscriber, - Labels: map[string]string{ - "k1": "v1", - }, - }, - setup: func(n notification.Notification, r *mocks.Repository, _ *mocks.LogService, _ *mocks.AlertService, _ *mocks.Queuer, d *mocks.Dispatcher) { - r.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().PrepareMessage(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, nil, false, errors.New("some error")) + name: "should return error if receiver type of a receiver is unknown", + setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, _ *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + ss.EXPECT(). + MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")). + Return([]subscription.Subscription{ + { + Receivers: []subscription.Receiver{ + { + Type: "random", + }, + }, + }, + }, nil) }, wantErr: true, }, { - name: "should return error if dispatcher service return empty results", - n: notification.Notification{ - Type: notification.TypeSubscriber, - Labels: map[string]string{ - "k1": "v1", - }, - }, - setup: func(n notification.Notification, r *mocks.Repository, _ *mocks.LogService, _ *mocks.AlertService, _ *mocks.Queuer, d *mocks.Dispatcher) { - r.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().PrepareMessage(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return(nil, nil, false, nil) + name: "should return error if there is no matching subscription", + setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, _ *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + ss.EXPECT().MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")).Return(nil, nil) }, wantErr: true, }, { - name: "should return error if log notifications return error", - n: notification.Notification{ - Type: notification.TypeSubscriber, - Labels: map[string]string{ - "k1": "v1", - }, + name: "should return error if failed to transform notification to messages", + setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, _ *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + ss.EXPECT(). + MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")). + Return([]subscription.Subscription{ + { + Receivers: []subscription.Receiver{ + { + Type: testPluginType, + }, + }, + }, + }, nil) }, - setup: func(n notification.Notification, r *mocks.Repository, l *mocks.LogService, _ *mocks.AlertService, _ *mocks.Queuer, d *mocks.Dispatcher) { - r.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().PrepareMessage(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.Message{{ID: "123"}}, []log.Notification{{ReceiverID: 123}}, false, nil) - l.EXPECT().LogNotifications(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("log.Notification")).Return(errors.New("some error")) + n: notification.Notification{ + ValidDurationString: "xxx", }, wantErr: true, }, { - name: "should return error if update alerts silence status return error", - n: notification.Notification{ - Type: notification.TypeSubscriber, - Labels: map[string]string{ - "k1": "v1", - }, - }, - setup: func(n notification.Notification, r *mocks.Repository, l *mocks.LogService, a *mocks.AlertService, _ *mocks.Queuer, d *mocks.Dispatcher) { - r.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().PrepareMessage(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.Message{{ID: "123"}}, []log.Notification{{ReceiverID: 123}}, false, nil) - l.EXPECT().LogNotifications(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - a.EXPECT().UpdateSilenceStatus(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(errors.New("some error")) + name: "should return error if receiver config is invalid", + setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, n *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + ss.EXPECT(). + MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")). + Return([]subscription.Subscription{ + { + Receivers: []subscription.Receiver{ + { + Type: testPluginType, + Configuration: map[string]interface{}{ + "key": "value", + }, + }, + }, + }, + }, nil) + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("invalid config")) }, wantErr: true, }, { - name: "should return error if enqueue return error", - n: notification.Notification{ - Type: notification.TypeSubscriber, - Labels: map[string]string{ - "k1": "v1", - }, - }, - setup: func(n notification.Notification, r *mocks.Repository, l *mocks.LogService, a *mocks.AlertService, q *mocks.Queuer, d *mocks.Dispatcher) { - r.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(notification.Notification{}, nil) - d.EXPECT().PrepareMessage(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.Message{{ID: "123"}}, []log.Notification{{ReceiverID: 123}}, false, nil) - l.EXPECT().LogNotifications(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - a.EXPECT().UpdateSilenceStatus(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(nil) + name: "should return error if enqueue error", + setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, n *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + ss.EXPECT(). + MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")). + Return([]subscription.Subscription{ + { + Receivers: []subscription.Receiver{ + { + Type: testPluginType, + Configuration: map[string]interface{}{ + "key": "value", + }, + }, + }, + }, + }, nil) + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ + "key": "value", + }, nil) q.EXPECT().Enqueue(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error")) }, wantErr: true, }, { name: "should return no error if enqueue success", - n: notification.Notification{ - Type: notification.TypeSubscriber, - Labels: map[string]string{ - "k1": "v1", - }, - }, - setup: func(n notification.Notification, r *mocks.Repository, l *mocks.LogService, a *mocks.AlertService, q *mocks.Queuer, d *mocks.Dispatcher) { - r.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(n, nil) - d.EXPECT().PrepareMessage(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return([]notification.Message{{ID: "123"}}, []log.Notification{{ReceiverID: 123}}, false, nil) - l.EXPECT().LogNotifications(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("log.Notification")).Return(nil) - a.EXPECT().UpdateSilenceStatus(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("[]int64"), mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(nil) + setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, n *mocks.Notifier) { + q.EXPECT().Type().Return("postgresql") + ss.EXPECT(). + MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]string")). + Return([]subscription.Subscription{ + { + Receivers: []subscription.Receiver{ + { + Type: testPluginType, + Configuration: map[string]interface{}{ + "key": "value", + }, + }, + }, + }, + }, nil) + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ + "key": "value", + }, nil) q.EXPECT().Enqueue(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Message")).Return(nil) }, + wantErr: false, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { var ( - mockQueuer = new(mocks.Queuer) - mockRepository = new(mocks.Repository) - mockDispatcher = new(mocks.Dispatcher) - mockLogService = new(mocks.LogService) - mockAlertService = new(mocks.AlertService) + mockSubscriptionService = new(mocks.SubscriptionService) + mockQueuer = new(mocks.Queuer) + mockNotifier = new(mocks.Notifier) ) - if tt.setup != nil { - tt.setup(tt.n, mockRepository, mockLogService, mockAlertService, mockQueuer, mockDispatcher) + if tc.setup != nil { + tc.setup(mockSubscriptionService, mockQueuer, mockNotifier) } - mockQueuer.EXPECT().Type().Return(queues.KindPostgres.String()) - s := notification.NewService( - saltlog.NewNoop(), - mockRepository, - mockQueuer, - nil, - notification.Deps{ - AlertService: mockAlertService, - LogService: mockLogService, - DispatchReceiverService: mockDispatcher, - DispatchSubscriberService: mockDispatcher, - }, - ) - if err := s.Dispatch(context.TODO(), tt.n); (err != nil) != tt.wantErr { - t.Errorf("Service.Dispatch() error = %v, wantErr %v", err, tt.wantErr) + ns := notification.NewService( + log.NewNoop(), mockQueuer, nil, nil, mockSubscriptionService, map[string]notification.Notifier{ + testPluginType: mockNotifier, + }) + + if err := ns.DispatchToSubscribers(context.Background(), 1, tc.n); (err != nil) != tc.wantErr { + t.Errorf("NotificationService.Dispatch() error = %v, wantErr %v", err, tc.wantErr) } + + mockSubscriptionService.AssertExpectations(t) + mockQueuer.AssertExpectations(t) + mockNotifier.AssertExpectations(t) + }) + } +} + +func TestService_CheckAndInsertIdempotency(t *testing.T) { + var ( + scope = "test-scope" + key = "test-key" + ) + testCases := []struct { + name string + setup func(*mocks.IdempotencyRepository) + scope string + key string + wantErr bool + }{ + { + name: "should return error if idempotency exist and success", + setup: func(ir *mocks.IdempotencyRepository) { + ir.EXPECT().InsertOnConflictReturning(mock.AnythingOfType("*context.emptyCtx"), scope, key).Return(nil, errors.ErrConflict) + }, + scope: scope, + key: key, + wantErr: true, + }, + { + name: "should return error if repository returning some error", + setup: func(ir *mocks.IdempotencyRepository) { + ir.EXPECT().InsertOnConflictReturning(mock.AnythingOfType("*context.emptyCtx"), scope, key).Return(nil, errors.New("some error")) + }, + scope: scope, + key: key, + wantErr: true, + }, + { + name: "should return id and nil error if no idempotency exists", + setup: func(ir *mocks.IdempotencyRepository) { + ir.EXPECT().InsertOnConflictReturning(mock.AnythingOfType("*context.emptyCtx"), scope, key).Return(&idempotency.Idempotency{ + ID: 1, + }, nil) + }, + scope: scope, + key: key, + wantErr: false, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mockIdempotencyRepository := new(mocks.IdempotencyRepository) + + if tc.setup != nil { + tc.setup(mockIdempotencyRepository) + } + + ns := notification.NewService( + log.NewNoop(), nil, mockIdempotencyRepository, nil, nil, nil) + + _, err := ns.CheckAndInsertIdempotency(context.Background(), tc.scope, tc.key) + + if (err != nil) != tc.wantErr { + t.Errorf("NotificationService.CheckAndInsertIdempotency() error = %v, wantErr %v", err, tc.wantErr) + } + + mockIdempotencyRepository.AssertExpectations(t) }) } } diff --git a/core/provider/mocks/provider_repository.go b/core/provider/mocks/provider_repository.go index 594fbf72..91694fc5 100644 --- a/core/provider/mocks/provider_repository.go +++ b/core/provider/mocks/provider_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -42,8 +42,8 @@ type ProviderRepository_Create_Call struct { } // Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *provider.Provider +// - _a0 context.Context +// - _a1 *provider.Provider func (_e *ProviderRepository_Expecter) Create(_a0 interface{}, _a1 interface{}) *ProviderRepository_Create_Call { return &ProviderRepository_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} } @@ -80,8 +80,8 @@ type ProviderRepository_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *ProviderRepository_Expecter) Delete(_a0 interface{}, _a1 interface{}) *ProviderRepository_Delete_Call { return &ProviderRepository_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} } @@ -127,8 +127,8 @@ type ProviderRepository_Get_Call struct { } // Get is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *ProviderRepository_Expecter) Get(_a0 interface{}, _a1 interface{}) *ProviderRepository_Get_Call { return &ProviderRepository_Get_Call{Call: _e.mock.On("Get", _a0, _a1)} } @@ -174,8 +174,8 @@ type ProviderRepository_List_Call struct { } // List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 provider.Filter +// - _a0 context.Context +// - _a1 provider.Filter func (_e *ProviderRepository_Expecter) List(_a0 interface{}, _a1 interface{}) *ProviderRepository_List_Call { return &ProviderRepository_List_Call{Call: _e.mock.On("List", _a0, _a1)} } @@ -212,8 +212,8 @@ type ProviderRepository_Update_Call struct { } // Update is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *provider.Provider +// - _a0 context.Context +// - _a1 *provider.Provider func (_e *ProviderRepository_Expecter) Update(_a0 interface{}, _a1 interface{}) *ProviderRepository_Update_Call { return &ProviderRepository_Update_Call{Call: _e.mock.On("Update", _a0, _a1)} } diff --git a/core/receiver/mocks/config_resolver.go b/core/receiver/mocks/config_resolver.go index aa9fbd35..504358fb 100644 --- a/core/receiver/mocks/config_resolver.go +++ b/core/receiver/mocks/config_resolver.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -50,8 +50,8 @@ type ConfigResolver_BuildData_Call struct { } // BuildData is a helper method to define mock.On call -// - ctx context.Context -// - configs map[string]interface{} +// - ctx context.Context +// - configs map[string]interface{} func (_e *ConfigResolver_Expecter) BuildData(ctx interface{}, configs interface{}) *ConfigResolver_BuildData_Call { return &ConfigResolver_BuildData_Call{Call: _e.mock.On("BuildData", ctx, configs)} } @@ -97,8 +97,8 @@ type ConfigResolver_PostHookDBTransformConfigs_Call struct { } // PostHookDBTransformConfigs is a helper method to define mock.On call -// - ctx context.Context -// - configs map[string]interface{} +// - ctx context.Context +// - configs map[string]interface{} func (_e *ConfigResolver_Expecter) PostHookDBTransformConfigs(ctx interface{}, configs interface{}) *ConfigResolver_PostHookDBTransformConfigs_Call { return &ConfigResolver_PostHookDBTransformConfigs_Call{Call: _e.mock.On("PostHookDBTransformConfigs", ctx, configs)} } @@ -144,8 +144,8 @@ type ConfigResolver_PreHookDBTransformConfigs_Call struct { } // PreHookDBTransformConfigs is a helper method to define mock.On call -// - ctx context.Context -// - configs map[string]interface{} +// - ctx context.Context +// - configs map[string]interface{} func (_e *ConfigResolver_Expecter) PreHookDBTransformConfigs(ctx interface{}, configs interface{}) *ConfigResolver_PreHookDBTransformConfigs_Call { return &ConfigResolver_PreHookDBTransformConfigs_Call{Call: _e.mock.On("PreHookDBTransformConfigs", ctx, configs)} } diff --git a/core/receiver/mocks/encryptor.go b/core/receiver/mocks/encryptor.go index 8bb1be7f..6659fae9 100644 --- a/core/receiver/mocks/encryptor.go +++ b/core/receiver/mocks/encryptor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -44,7 +44,7 @@ type Encryptor_Decrypt_Call struct { } // Decrypt is a helper method to define mock.On call -// - str string +// - str string func (_e *Encryptor_Expecter) Decrypt(str interface{}) *Encryptor_Decrypt_Call { return &Encryptor_Decrypt_Call{Call: _e.mock.On("Decrypt", str)} } @@ -88,7 +88,7 @@ type Encryptor_Encrypt_Call struct { } // Encrypt is a helper method to define mock.On call -// - str string +// - str string func (_e *Encryptor_Expecter) Encrypt(str interface{}) *Encryptor_Encrypt_Call { return &Encryptor_Encrypt_Call{Call: _e.mock.On("Encrypt", str)} } diff --git a/core/receiver/mocks/receiver_repository.go b/core/receiver/mocks/receiver_repository.go index eb1948c2..da1b234d 100644 --- a/core/receiver/mocks/receiver_repository.go +++ b/core/receiver/mocks/receiver_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -42,8 +42,8 @@ type ReceiverRepository_Create_Call struct { } // Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *receiver.Receiver +// - _a0 context.Context +// - _a1 *receiver.Receiver func (_e *ReceiverRepository_Expecter) Create(_a0 interface{}, _a1 interface{}) *ReceiverRepository_Create_Call { return &ReceiverRepository_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} } @@ -80,8 +80,8 @@ type ReceiverRepository_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *ReceiverRepository_Expecter) Delete(_a0 interface{}, _a1 interface{}) *ReceiverRepository_Delete_Call { return &ReceiverRepository_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} } @@ -127,8 +127,8 @@ type ReceiverRepository_Get_Call struct { } // Get is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *ReceiverRepository_Expecter) Get(_a0 interface{}, _a1 interface{}) *ReceiverRepository_Get_Call { return &ReceiverRepository_Get_Call{Call: _e.mock.On("Get", _a0, _a1)} } @@ -174,8 +174,8 @@ type ReceiverRepository_List_Call struct { } // List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 receiver.Filter +// - _a0 context.Context +// - _a1 receiver.Filter func (_e *ReceiverRepository_Expecter) List(_a0 interface{}, _a1 interface{}) *ReceiverRepository_List_Call { return &ReceiverRepository_List_Call{Call: _e.mock.On("List", _a0, _a1)} } @@ -212,8 +212,8 @@ type ReceiverRepository_Update_Call struct { } // Update is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *receiver.Receiver +// - _a0 context.Context +// - _a1 *receiver.Receiver func (_e *ReceiverRepository_Expecter) Update(_a0 interface{}, _a1 interface{}) *ReceiverRepository_Update_Call { return &ReceiverRepository_Update_Call{Call: _e.mock.On("Update", _a0, _a1)} } diff --git a/core/rule/mocks/namespace_service.go b/core/rule/mocks/namespace_service.go index c86da659..daa564ea 100644 --- a/core/rule/mocks/namespace_service.go +++ b/core/rule/mocks/namespace_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -42,8 +42,8 @@ type NamespaceService_Create_Call struct { } // Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *namespace.Namespace +// - _a0 context.Context +// - _a1 *namespace.Namespace func (_e *NamespaceService_Expecter) Create(_a0 interface{}, _a1 interface{}) *NamespaceService_Create_Call { return &NamespaceService_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} } @@ -80,8 +80,8 @@ type NamespaceService_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *NamespaceService_Expecter) Delete(_a0 interface{}, _a1 interface{}) *NamespaceService_Delete_Call { return &NamespaceService_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} } @@ -127,8 +127,8 @@ type NamespaceService_Get_Call struct { } // Get is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *NamespaceService_Expecter) Get(_a0 interface{}, _a1 interface{}) *NamespaceService_Get_Call { return &NamespaceService_Get_Call{Call: _e.mock.On("Get", _a0, _a1)} } @@ -174,7 +174,7 @@ type NamespaceService_List_Call struct { } // List is a helper method to define mock.On call -// - _a0 context.Context +// - _a0 context.Context func (_e *NamespaceService_Expecter) List(_a0 interface{}) *NamespaceService_List_Call { return &NamespaceService_List_Call{Call: _e.mock.On("List", _a0)} } @@ -211,8 +211,8 @@ type NamespaceService_Update_Call struct { } // Update is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *namespace.Namespace +// - _a0 context.Context +// - _a1 *namespace.Namespace func (_e *NamespaceService_Expecter) Update(_a0 interface{}, _a1 interface{}) *NamespaceService_Update_Call { return &NamespaceService_Update_Call{Call: _e.mock.On("Update", _a0, _a1)} } diff --git a/core/rule/mocks/rule_repository.go b/core/rule/mocks/rule_repository.go index e335bf59..0686d0b1 100644 --- a/core/rule/mocks/rule_repository.go +++ b/core/rule/mocks/rule_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -42,7 +42,7 @@ type RuleRepository_Commit_Call struct { } // Commit is a helper method to define mock.On call -// - ctx context.Context +// - ctx context.Context func (_e *RuleRepository_Expecter) Commit(ctx interface{}) *RuleRepository_Commit_Call { return &RuleRepository_Commit_Call{Call: _e.mock.On("Commit", ctx)} } @@ -88,8 +88,8 @@ type RuleRepository_List_Call struct { } // List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 rule.Filter +// - _a0 context.Context +// - _a1 rule.Filter func (_e *RuleRepository_Expecter) List(_a0 interface{}, _a1 interface{}) *RuleRepository_List_Call { return &RuleRepository_List_Call{Call: _e.mock.On("List", _a0, _a1)} } @@ -126,8 +126,8 @@ type RuleRepository_Rollback_Call struct { } // Rollback is a helper method to define mock.On call -// - ctx context.Context -// - err error +// - ctx context.Context +// - err error func (_e *RuleRepository_Expecter) Rollback(ctx interface{}, err interface{}) *RuleRepository_Rollback_Call { return &RuleRepository_Rollback_Call{Call: _e.mock.On("Rollback", ctx, err)} } @@ -164,8 +164,8 @@ type RuleRepository_Upsert_Call struct { } // Upsert is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *rule.Rule +// - _a0 context.Context +// - _a1 *rule.Rule func (_e *RuleRepository_Expecter) Upsert(_a0 interface{}, _a1 interface{}) *RuleRepository_Upsert_Call { return &RuleRepository_Upsert_Call{Call: _e.mock.On("Upsert", _a0, _a1)} } @@ -204,7 +204,7 @@ type RuleRepository_WithTransaction_Call struct { } // WithTransaction is a helper method to define mock.On call -// - ctx context.Context +// - ctx context.Context func (_e *RuleRepository_Expecter) WithTransaction(ctx interface{}) *RuleRepository_WithTransaction_Call { return &RuleRepository_WithTransaction_Call{Call: _e.mock.On("WithTransaction", ctx)} } diff --git a/core/rule/mocks/rule_uploader.go b/core/rule/mocks/rule_uploader.go index bcfd32e9..3f00dd47 100644 --- a/core/rule/mocks/rule_uploader.go +++ b/core/rule/mocks/rule_uploader.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -46,11 +46,11 @@ type RuleUploader_UpsertRule_Call struct { } // UpsertRule is a helper method to define mock.On call -// - ctx context.Context -// - namespaceURN string -// - prov provider.Provider -// - rl *rule.Rule -// - templateToUpdate *template.Template +// - ctx context.Context +// - namespaceURN string +// - prov provider.Provider +// - rl *rule.Rule +// - templateToUpdate *template.Template func (_e *RuleUploader_Expecter) UpsertRule(ctx interface{}, namespaceURN interface{}, prov interface{}, rl interface{}, templateToUpdate interface{}) *RuleUploader_UpsertRule_Call { return &RuleUploader_UpsertRule_Call{Call: _e.mock.On("UpsertRule", ctx, namespaceURN, prov, rl, templateToUpdate)} } diff --git a/core/rule/mocks/template_service.go b/core/rule/mocks/template_service.go index cc038cae..97ef0f72 100644 --- a/core/rule/mocks/template_service.go +++ b/core/rule/mocks/template_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -43,8 +43,8 @@ type TemplateService_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string +// - _a0 context.Context +// - _a1 string func (_e *TemplateService_Expecter) Delete(_a0 interface{}, _a1 interface{}) *TemplateService_Delete_Call { return &TemplateService_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} } @@ -90,8 +90,8 @@ type TemplateService_GetByName_Call struct { } // GetByName is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string +// - _a0 context.Context +// - _a1 string func (_e *TemplateService_Expecter) GetByName(_a0 interface{}, _a1 interface{}) *TemplateService_GetByName_Call { return &TemplateService_GetByName_Call{Call: _e.mock.On("GetByName", _a0, _a1)} } @@ -137,8 +137,8 @@ type TemplateService_List_Call struct { } // List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 template.Filter +// - _a0 context.Context +// - _a1 template.Filter func (_e *TemplateService_Expecter) List(_a0 interface{}, _a1 interface{}) *TemplateService_List_Call { return &TemplateService_List_Call{Call: _e.mock.On("List", _a0, _a1)} } @@ -175,8 +175,8 @@ type TemplateService_Upsert_Call struct { } // Upsert is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *template.Template +// - _a0 context.Context +// - _a1 *template.Template func (_e *TemplateService_Expecter) Upsert(_a0 interface{}, _a1 interface{}) *TemplateService_Upsert_Call { return &TemplateService_Upsert_Call{Call: _e.mock.On("Upsert", _a0, _a1)} } diff --git a/core/silence/filter.go b/core/silence/filter.go deleted file mode 100644 index 87bbd9cd..00000000 --- a/core/silence/filter.go +++ /dev/null @@ -1,9 +0,0 @@ -package silence - -type Filter struct { - ID string - NamespaceID uint64 - SubscriptionID uint64 - Match map[string]string - SubscriptionMatch map[string]string -} diff --git a/core/silence/mocks/subscription_repository.go b/core/silence/mocks/subscription_repository.go deleted file mode 100644 index 15534f55..00000000 --- a/core/silence/mocks/subscription_repository.go +++ /dev/null @@ -1,213 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - silence "github.com/odpf/siren/core/silence" - mock "github.com/stretchr/testify/mock" -) - -// SubscriptionRepository is an autogenerated mock type for the Repository type -type SubscriptionRepository struct { - mock.Mock -} - -type SubscriptionRepository_Expecter struct { - mock *mock.Mock -} - -func (_m *SubscriptionRepository) EXPECT() *SubscriptionRepository_Expecter { - return &SubscriptionRepository_Expecter{mock: &_m.Mock} -} - -// Create provides a mock function with given fields: _a0, _a1 -func (_m *SubscriptionRepository) Create(_a0 context.Context, _a1 silence.Silence) (string, error) { - ret := _m.Called(_a0, _a1) - - var r0 string - if rf, ok := ret.Get(0).(func(context.Context, silence.Silence) string); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, silence.Silence) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SubscriptionRepository_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type SubscriptionRepository_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 silence.Silence -func (_e *SubscriptionRepository_Expecter) Create(_a0 interface{}, _a1 interface{}) *SubscriptionRepository_Create_Call { - return &SubscriptionRepository_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} -} - -func (_c *SubscriptionRepository_Create_Call) Run(run func(_a0 context.Context, _a1 silence.Silence)) *SubscriptionRepository_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(silence.Silence)) - }) - return _c -} - -func (_c *SubscriptionRepository_Create_Call) Return(_a0 string, _a1 error) *SubscriptionRepository_Create_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Get provides a mock function with given fields: ctx, id -func (_m *SubscriptionRepository) Get(ctx context.Context, id string) (silence.Silence, error) { - ret := _m.Called(ctx, id) - - var r0 silence.Silence - if rf, ok := ret.Get(0).(func(context.Context, string) silence.Silence); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Get(0).(silence.Silence) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SubscriptionRepository_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' -type SubscriptionRepository_Get_Call struct { - *mock.Call -} - -// Get is a helper method to define mock.On call -// - ctx context.Context -// - id string -func (_e *SubscriptionRepository_Expecter) Get(ctx interface{}, id interface{}) *SubscriptionRepository_Get_Call { - return &SubscriptionRepository_Get_Call{Call: _e.mock.On("Get", ctx, id)} -} - -func (_c *SubscriptionRepository_Get_Call) Run(run func(ctx context.Context, id string)) *SubscriptionRepository_Get_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *SubscriptionRepository_Get_Call) Return(_a0 silence.Silence, _a1 error) *SubscriptionRepository_Get_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// List provides a mock function with given fields: _a0, _a1 -func (_m *SubscriptionRepository) List(_a0 context.Context, _a1 silence.Filter) ([]silence.Silence, error) { - ret := _m.Called(_a0, _a1) - - var r0 []silence.Silence - if rf, ok := ret.Get(0).(func(context.Context, silence.Filter) []silence.Silence); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]silence.Silence) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, silence.Filter) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SubscriptionRepository_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type SubscriptionRepository_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 silence.Filter -func (_e *SubscriptionRepository_Expecter) List(_a0 interface{}, _a1 interface{}) *SubscriptionRepository_List_Call { - return &SubscriptionRepository_List_Call{Call: _e.mock.On("List", _a0, _a1)} -} - -func (_c *SubscriptionRepository_List_Call) Run(run func(_a0 context.Context, _a1 silence.Filter)) *SubscriptionRepository_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(silence.Filter)) - }) - return _c -} - -func (_c *SubscriptionRepository_List_Call) Return(_a0 []silence.Silence, _a1 error) *SubscriptionRepository_List_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// SoftDelete provides a mock function with given fields: ctx, id -func (_m *SubscriptionRepository) SoftDelete(ctx context.Context, id string) error { - ret := _m.Called(ctx, id) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionRepository_SoftDelete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SoftDelete' -type SubscriptionRepository_SoftDelete_Call struct { - *mock.Call -} - -// SoftDelete is a helper method to define mock.On call -// - ctx context.Context -// - id string -func (_e *SubscriptionRepository_Expecter) SoftDelete(ctx interface{}, id interface{}) *SubscriptionRepository_SoftDelete_Call { - return &SubscriptionRepository_SoftDelete_Call{Call: _e.mock.On("SoftDelete", ctx, id)} -} - -func (_c *SubscriptionRepository_SoftDelete_Call) Run(run func(ctx context.Context, id string)) *SubscriptionRepository_SoftDelete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *SubscriptionRepository_SoftDelete_Call) Return(_a0 error) *SubscriptionRepository_SoftDelete_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewSubscriptionRepository interface { - mock.TestingT - Cleanup(func()) -} - -// NewSubscriptionRepository creates a new instance of SubscriptionRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSubscriptionRepository(t mockConstructorTestingTNewSubscriptionRepository) *SubscriptionRepository { - mock := &SubscriptionRepository{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/silence/service.go b/core/silence/service.go deleted file mode 100644 index 6afd671f..00000000 --- a/core/silence/service.go +++ /dev/null @@ -1,34 +0,0 @@ -package silence - -import ( - "context" -) - -type Service struct { - repository Repository -} - -func NewService(repo Repository) *Service { - return &Service{ - repository: repo, - } -} - -func (s *Service) Create(ctx context.Context, sil Silence) (string, error) { - if err := sil.Validate(); err != nil { - return "", err - } - return s.repository.Create(ctx, sil) -} - -func (s *Service) List(ctx context.Context, filter Filter) ([]Silence, error) { - return s.repository.List(ctx, filter) -} - -func (s *Service) Get(ctx context.Context, id string) (Silence, error) { - return s.repository.Get(ctx, id) -} - -func (s *Service) Delete(ctx context.Context, id string) error { - return s.repository.SoftDelete(ctx, id) -} diff --git a/core/silence/service_test.go b/core/silence/service_test.go deleted file mode 100644 index c6e8dc2c..00000000 --- a/core/silence/service_test.go +++ /dev/null @@ -1 +0,0 @@ -package silence_test diff --git a/core/silence/silence.go b/core/silence/silence.go deleted file mode 100644 index b8debee5..00000000 --- a/core/silence/silence.go +++ /dev/null @@ -1,85 +0,0 @@ -package silence - -import ( - "context" - "fmt" - "time" - - "github.com/antonmedv/expr" -) - -const TargetExpressionRuleKey = "rule" - -//go:generate mockery --name=Repository -r --case underscore --with-expecter --structname SubscriptionRepository --filename subscription_repository.go --output=./mocks -type Repository interface { - Create(context.Context, Silence) (string, error) - List(context.Context, Filter) ([]Silence, error) - Get(ctx context.Context, id string) (Silence, error) - SoftDelete(ctx context.Context, id string) error -} - -type Silence struct { - ID string `json:"id"` - NamespaceID uint64 `json:"namespace_id"` - Type string `json:"type"` - TargetID uint64 `json:"target_id"` - TargetExpression map[string]interface{} `json:"target_expression"` - Creator string `json:"creator"` - Comment string `json:"comment"` - CreatedAt time.Time `json:"created_at"` - DeletedAt time.Time `json:"deleted_at"` -} - -func (s Silence) Validate() error { - switch s.Type { - case TypeSubscription: - if s.TargetID == 0 { - return fmt.Errorf("target id cannot be empty or zero for type '%s'", TypeSubscription) - } - case TypeMatchers: - if len(s.TargetExpression) == 0 { - return fmt.Errorf("target expression cannot be empty and should be kv labels for type '%s'", TypeMatchers) - } - default: - return fmt.Errorf("unknown silence type '%s', should be '%s' or '%s'", s.Type, TypeMatchers, TypeSubscription) - } - return nil -} - -func (s Silence) subscriptionRule() (string, error) { - if s.Type != TypeSubscription { - return "", fmt.Errorf("silence id '%s' type is not subscription, type is '%s' instead", s.ID, s.Type) - } - - rule, ok := s.TargetExpression[TargetExpressionRuleKey] - if !ok { - return "", nil - } - - ruleStr := fmt.Sprintf("%s", rule) - - return ruleStr, nil -} - -func (s Silence) EvaluateSubscriptionRule(env interface{}) (bool, error) { - rule, err := s.subscriptionRule() - if err != nil { - return false, err - } - - if rule == "" { - return true, nil - } - - res, err := expr.Eval(rule, env) - if err != nil { - return false, err - } - - resBool, ok := res.(bool) - if !ok { - return false, fmt.Errorf("rule evaluation result is not boolean: %v", res) - } - - return resBool, nil -} diff --git a/core/silence/silence_test.go b/core/silence/silence_test.go deleted file mode 100644 index 5eb5963c..00000000 --- a/core/silence/silence_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package silence_test - -import ( - "testing" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/core/receiver" - "github.com/odpf/siren/core/silence" - "github.com/odpf/siren/core/subscription" - "github.com/stretchr/testify/require" -) - -func TestSilence_Evaluate(t *testing.T) { - failedCases := []struct { - name string - silence silence.Silence - rcv subscription.Receiver - want bool - errString string - }{ - { - name: "silence type that is not subscription type would return error", - silence: silence.Silence{ - ID: "silence-id", - Type: "test", - }, - errString: "silence id 'silence-id' type is not subscription, type is 'test' instead", - }, - { - name: "rule that is not evaluated to boolean would return error", - silence: silence.Silence{ - ID: "silence-id", - Type: silence.TypeSubscription, - TargetID: 12, - TargetExpression: map[string]interface{}{ - "rule": "1 + 1", - }, - }, - rcv: subscription.Receiver{ - ID: 12, - Type: receiver.TypePagerDuty, - }, - errString: "rule evaluation result is not boolean: 2", - }, - { - name: "rule that cannot be evaluated would return error", - silence: silence.Silence{ - ID: "silence-id", - Type: silence.TypeSubscription, - TargetID: 12, - TargetExpression: map[string]interface{}{ - "rule": "test", - }, - }, - rcv: subscription.Receiver{ - ID: 12, - Type: receiver.TypePagerDuty, - }, - errString: "rule evaluation result is not boolean: ", - }, - } - - sucessCases := []struct { - name string - silence silence.Silence - rcv subscription.Receiver - want bool - errString string - }{ - { - name: "match by empty rule would pass", - silence: silence.Silence{ - Type: silence.TypeSubscription, - TargetID: 12, - TargetExpression: map[string]interface{}{ - "rule": "", - }, - }, - rcv: subscription.Receiver{ - ID: 12, - Type: receiver.TypePagerDuty, - }, - want: true, - }, - { - name: "no rule key in target expression would return empty string", - silence: silence.Silence{ - ID: "silence-id", - Type: silence.TypeSubscription, - TargetID: 12, - TargetExpression: map[string]interface{}{}, - }, - rcv: subscription.Receiver{ - ID: 12, - Type: receiver.TypePagerDuty, - }, - want: true, - }, - { - name: "match by `true` rule would pass", - silence: silence.Silence{ - Type: silence.TypeSubscription, - TargetID: 12, - TargetExpression: map[string]interface{}{ - "rule": "true", - }, - }, - rcv: subscription.Receiver{ - ID: 12, - Type: receiver.TypePagerDuty, - }, - want: true, - }, - { - name: "match by receiver id and type would pass", - silence: silence.Silence{ - Type: silence.TypeSubscription, - TargetID: 12, - TargetExpression: map[string]interface{}{ - "rule": "(ID == 12) and (Type == 'pagerduty')", - }, - }, - rcv: subscription.Receiver{ - ID: 12, - Type: receiver.TypePagerDuty, - }, - want: true, - }, - { - name: "match multiple receivers would pass", - silence: silence.Silence{ - Type: silence.TypeSubscription, - TargetID: 12, - TargetExpression: map[string]interface{}{ - "rule": "(ID == 12) or (ID == 16)", - }, - }, - rcv: subscription.Receiver{ - ID: 12, - Type: receiver.TypePagerDuty, - }, - want: true, - }, - } - - tests := append(sucessCases, failedCases...) - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mapSubscriptionReceiver := map[string]interface{}{} - - err := mapstructure.Decode(tt.rcv, &mapSubscriptionReceiver) - require.NoError(t, err) - - got, err := tt.silence.EvaluateSubscriptionRule(mapSubscriptionReceiver) - if err != nil { - if err.Error() != tt.errString { - t.Errorf("silence.Silence.Evaluate() error = %v, expected was %v", err, tt.errString) - } - } - if got != tt.want { - t.Errorf("silence.Silence.Evaluate() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSilence_Validate(t *testing.T) { - tests := []struct { - name string - sil silence.Silence - wantErr bool - }{ - { - name: "should return error if type subscription and target id is empty or zero", - sil: silence.Silence{ - Type: silence.TypeSubscription, - }, - wantErr: true, - }, - { - name: "should return error if type labels and target expression is empty", - sil: silence.Silence{ - Type: silence.TypeMatchers, - }, - wantErr: true, - }, - { - name: "should return no error if type subscription and target id is not empty or zero", - sil: silence.Silence{ - Type: silence.TypeSubscription, - TargetID: 1, - }, - }, - { - name: "should return error if type labels and target expression is not empty", - sil: silence.Silence{ - Type: silence.TypeMatchers, - TargetExpression: map[string]interface{}{ - "k1": "v1", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.sil.Validate(); (err != nil) != tt.wantErr { - t.Errorf("Silence.Validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/core/silence/type.go b/core/silence/type.go deleted file mode 100644 index b0582219..00000000 --- a/core/silence/type.go +++ /dev/null @@ -1,14 +0,0 @@ -package silence - -const ( - TypeMatchers = "Matchers" - TypeSubscription = "subscription" -) - -func IsTypeValid(silenceTypeStr string) bool { - if silenceTypeStr == TypeMatchers || - silenceTypeStr == TypeSubscription { - return true - } - return false -} diff --git a/core/subscription/filter.go b/core/subscription/filter.go index 8c1f2262..a5671c86 100644 --- a/core/subscription/filter.go +++ b/core/subscription/filter.go @@ -1,9 +1,6 @@ package subscription type Filter struct { - NamespaceID uint64 - Match map[string]string - NotificationMatch map[string]string - SilenceID string - IDs []int64 + NamespaceID uint64 + Labels map[string]string } diff --git a/core/subscription/mocks/log_service.go b/core/subscription/mocks/log_service.go deleted file mode 100644 index 763f712c..00000000 --- a/core/subscription/mocks/log_service.go +++ /dev/null @@ -1,84 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// LogService is an autogenerated mock type for the LogService type -type LogService struct { - mock.Mock -} - -type LogService_Expecter struct { - mock *mock.Mock -} - -func (_m *LogService) EXPECT() *LogService_Expecter { - return &LogService_Expecter{mock: &_m.Mock} -} - -// ListSubscriptionIDsBySilenceID provides a mock function with given fields: ctx, silenceID -func (_m *LogService) ListSubscriptionIDsBySilenceID(ctx context.Context, silenceID string) ([]int64, error) { - ret := _m.Called(ctx, silenceID) - - var r0 []int64 - if rf, ok := ret.Get(0).(func(context.Context, string) []int64); ok { - r0 = rf(ctx, silenceID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]int64) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, silenceID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// LogService_ListSubscriptionIDsBySilenceID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListSubscriptionIDsBySilenceID' -type LogService_ListSubscriptionIDsBySilenceID_Call struct { - *mock.Call -} - -// ListSubscriptionIDsBySilenceID is a helper method to define mock.On call -// - ctx context.Context -// - silenceID string -func (_e *LogService_Expecter) ListSubscriptionIDsBySilenceID(ctx interface{}, silenceID interface{}) *LogService_ListSubscriptionIDsBySilenceID_Call { - return &LogService_ListSubscriptionIDsBySilenceID_Call{Call: _e.mock.On("ListSubscriptionIDsBySilenceID", ctx, silenceID)} -} - -func (_c *LogService_ListSubscriptionIDsBySilenceID_Call) Run(run func(ctx context.Context, silenceID string)) *LogService_ListSubscriptionIDsBySilenceID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *LogService_ListSubscriptionIDsBySilenceID_Call) Return(_a0 []int64, _a1 error) *LogService_ListSubscriptionIDsBySilenceID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -type mockConstructorTestingTNewLogService interface { - mock.TestingT - Cleanup(func()) -} - -// NewLogService creates a new instance of LogService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewLogService(t mockConstructorTestingTNewLogService) *LogService { - mock := &LogService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/subscription/mocks/namespace_service.go b/core/subscription/mocks/namespace_service.go index c86da659..daa564ea 100644 --- a/core/subscription/mocks/namespace_service.go +++ b/core/subscription/mocks/namespace_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -42,8 +42,8 @@ type NamespaceService_Create_Call struct { } // Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *namespace.Namespace +// - _a0 context.Context +// - _a1 *namespace.Namespace func (_e *NamespaceService_Expecter) Create(_a0 interface{}, _a1 interface{}) *NamespaceService_Create_Call { return &NamespaceService_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} } @@ -80,8 +80,8 @@ type NamespaceService_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *NamespaceService_Expecter) Delete(_a0 interface{}, _a1 interface{}) *NamespaceService_Delete_Call { return &NamespaceService_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} } @@ -127,8 +127,8 @@ type NamespaceService_Get_Call struct { } // Get is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *NamespaceService_Expecter) Get(_a0 interface{}, _a1 interface{}) *NamespaceService_Get_Call { return &NamespaceService_Get_Call{Call: _e.mock.On("Get", _a0, _a1)} } @@ -174,7 +174,7 @@ type NamespaceService_List_Call struct { } // List is a helper method to define mock.On call -// - _a0 context.Context +// - _a0 context.Context func (_e *NamespaceService_Expecter) List(_a0 interface{}) *NamespaceService_List_Call { return &NamespaceService_List_Call{Call: _e.mock.On("List", _a0)} } @@ -211,8 +211,8 @@ type NamespaceService_Update_Call struct { } // Update is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *namespace.Namespace +// - _a0 context.Context +// - _a1 *namespace.Namespace func (_e *NamespaceService_Expecter) Update(_a0 interface{}, _a1 interface{}) *NamespaceService_Update_Call { return &NamespaceService_Update_Call{Call: _e.mock.On("Update", _a0, _a1)} } diff --git a/core/subscription/mocks/receiver_service.go b/core/subscription/mocks/receiver_service.go index 7e13371b..76b8fa58 100644 --- a/core/subscription/mocks/receiver_service.go +++ b/core/subscription/mocks/receiver_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -51,8 +51,8 @@ type ReceiverService_List_Call struct { } // List is a helper method to define mock.On call -// - ctx context.Context -// - flt receiver.Filter +// - ctx context.Context +// - flt receiver.Filter func (_e *ReceiverService_Expecter) List(ctx interface{}, flt interface{}) *ReceiverService_List_Call { return &ReceiverService_List_Call{Call: _e.mock.On("List", ctx, flt)} } diff --git a/core/subscription/mocks/subscription_repository.go b/core/subscription/mocks/subscription_repository.go index f4cc9dd5..fa1fd0c2 100644 --- a/core/subscription/mocks/subscription_repository.go +++ b/core/subscription/mocks/subscription_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -42,8 +42,8 @@ type SubscriptionRepository_Create_Call struct { } // Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *subscription.Subscription +// - _a0 context.Context +// - _a1 *subscription.Subscription func (_e *SubscriptionRepository_Expecter) Create(_a0 interface{}, _a1 interface{}) *SubscriptionRepository_Create_Call { return &SubscriptionRepository_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} } @@ -80,8 +80,8 @@ type SubscriptionRepository_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *SubscriptionRepository_Expecter) Delete(_a0 interface{}, _a1 interface{}) *SubscriptionRepository_Delete_Call { return &SubscriptionRepository_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} } @@ -127,8 +127,8 @@ type SubscriptionRepository_Get_Call struct { } // Get is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 +// - _a0 context.Context +// - _a1 uint64 func (_e *SubscriptionRepository_Expecter) Get(_a0 interface{}, _a1 interface{}) *SubscriptionRepository_Get_Call { return &SubscriptionRepository_Get_Call{Call: _e.mock.On("Get", _a0, _a1)} } @@ -174,8 +174,8 @@ type SubscriptionRepository_List_Call struct { } // List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 subscription.Filter +// - _a0 context.Context +// - _a1 subscription.Filter func (_e *SubscriptionRepository_Expecter) List(_a0 interface{}, _a1 interface{}) *SubscriptionRepository_List_Call { return &SubscriptionRepository_List_Call{Call: _e.mock.On("List", _a0, _a1)} } @@ -212,8 +212,8 @@ type SubscriptionRepository_Update_Call struct { } // Update is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *subscription.Subscription +// - _a0 context.Context +// - _a1 *subscription.Subscription func (_e *SubscriptionRepository_Expecter) Update(_a0 interface{}, _a1 interface{}) *SubscriptionRepository_Update_Call { return &SubscriptionRepository_Update_Call{Call: _e.mock.On("Update", _a0, _a1)} } diff --git a/core/subscription/service.go b/core/subscription/service.go index d6aa0986..fd2fa36c 100644 --- a/core/subscription/service.go +++ b/core/subscription/service.go @@ -8,11 +8,6 @@ import ( "github.com/odpf/siren/pkg/errors" ) -//go:generate mockery --name=LogService -r --case underscore --with-expecter --structname LogService --filename log_service.go --output=./mocks -type LogService interface { - ListSubscriptionIDsBySilenceID(ctx context.Context, silenceID string) ([]int64, error) -} - //go:generate mockery --name=NamespaceService -r --case underscore --with-expecter --structname NamespaceService --filename namespace_service.go --output=./mocks type NamespaceService interface { List(context.Context) ([]namespace.Namespace, error) @@ -30,16 +25,14 @@ type ReceiverService interface { // Service handles business logic type Service struct { repository Repository - logService LogService namespaceService NamespaceService receiverService ReceiverService } // NewService returns service struct -func NewService(repository Repository, logService LogService, namespaceService NamespaceService, receiverService ReceiverService) *Service { +func NewService(repository Repository, namespaceService NamespaceService, receiverService ReceiverService) *Service { svc := &Service{ repository: repository, - logService: logService, namespaceService: namespaceService, receiverService: receiverService, } @@ -48,15 +41,6 @@ func NewService(repository Repository, logService LogService, namespaceService N } func (s *Service) List(ctx context.Context, flt Filter) ([]Subscription, error) { - - if flt.SilenceID != "" { - subscriptionIDs, err := s.logService.ListSubscriptionIDsBySilenceID(ctx, flt.SilenceID) - if err != nil { - return nil, err - } - flt.IDs = subscriptionIDs - } - subscriptions, err := s.repository.List(ctx, flt) if err != nil { return nil, err @@ -116,11 +100,11 @@ func (s *Service) Delete(ctx context.Context, id uint64) error { return nil } -func (s *Service) MatchByLabels(ctx context.Context, namespaceID uint64, notificationLabels map[string]string) ([]Subscription, error) { +func (s *Service) MatchByLabels(ctx context.Context, namespaceID uint64, labels map[string]string) ([]Subscription, error) { // fetch all subscriptions by matching labels. subscriptionsByLabels, err := s.repository.List(ctx, Filter{ - NamespaceID: namespaceID, - NotificationMatch: notificationLabels, + NamespaceID: namespaceID, + Labels: labels, }) if err != nil { return nil, err diff --git a/core/subscription/service_test.go b/core/subscription/service_test.go index 32c3d228..7e847e3d 100644 --- a/core/subscription/service_test.go +++ b/core/subscription/service_test.go @@ -41,9 +41,8 @@ func TestService_List(t *testing.T) { t.Run(tc.Description, func(t *testing.T) { var ( repositoryMock = new(mocks.SubscriptionRepository) - logServiceMock = new(mocks.LogService) ) - svc := subscription.NewService(repositoryMock, logServiceMock, nil, nil) + svc := subscription.NewService(repositoryMock, nil, nil) tc.Setup(repositoryMock) @@ -95,9 +94,8 @@ func TestService_Get(t *testing.T) { t.Run(tc.Description, func(t *testing.T) { var ( repositoryMock = new(mocks.SubscriptionRepository) - logServiceMock = new(mocks.LogService) ) - svc := subscription.NewService(repositoryMock, logServiceMock, nil, nil) + svc := subscription.NewService(repositoryMock, nil, nil) tc.Setup(repositoryMock) @@ -158,13 +156,11 @@ func TestService_Create(t *testing.T) { t.Run(tc.Description, func(t *testing.T) { var ( repositoryMock = new(mocks.SubscriptionRepository) - logServiceMock = new(mocks.LogService) namespaceServiceMock = new(mocks.NamespaceService) receiverServiceMock = new(mocks.ReceiverService) ) svc := subscription.NewService( repositoryMock, - logServiceMock, namespaceServiceMock, receiverServiceMock, ) @@ -235,13 +231,11 @@ func TestService_Update(t *testing.T) { t.Run(tc.Description, func(t *testing.T) { var ( repositoryMock = new(mocks.SubscriptionRepository) - logServiceMock = new(mocks.LogService) namespaceServiceMock = new(mocks.NamespaceService) receiverServiceMock = new(mocks.ReceiverService) ) svc := subscription.NewService( repositoryMock, - logServiceMock, namespaceServiceMock, receiverServiceMock, ) @@ -298,13 +292,11 @@ func TestService_Delete(t *testing.T) { t.Run(tc.Description, func(t *testing.T) { var ( repositoryMock = new(mocks.SubscriptionRepository) - logServiceMock = new(mocks.LogService) namespaceServiceMock = new(mocks.NamespaceService) receiverServiceMock = new(mocks.ReceiverService) ) svc := subscription.NewService( repositoryMock, - logServiceMock, namespaceServiceMock, receiverServiceMock, ) diff --git a/core/subscription/subscription.go b/core/subscription/subscription.go index 982d688f..9b285e79 100644 --- a/core/subscription/subscription.go +++ b/core/subscription/subscription.go @@ -2,10 +2,7 @@ package subscription import ( context "context" - "fmt" "time" - - "github.com/odpf/siren/core/silence" ) //go:generate mockery --name=Repository -r --case underscore --with-expecter --structname SubscriptionRepository --filename subscription_repository.go --output=./mocks @@ -34,51 +31,3 @@ type Subscription struct { CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } - -func (s Subscription) ReceiversAsMap() map[uint64]Receiver { - var m = make(map[uint64]Receiver) - for _, rcv := range s.Receivers { - m[rcv.ID] = rcv - } - return m -} - -func (s Subscription) SilenceReceivers(silences []silence.Silence) (map[uint64][]silence.Silence, []Receiver, error) { - var ( - nonSilencedReceiversMap = map[uint64]Receiver{} - silencedReceiversMap = map[uint64][]silence.Silence{} - ) - - if len(silences) == 0 { - return nil, s.Receivers, nil - } - - // evaluate all receivers of subscribers with all matched silences - for _, sil := range silences { - for _, rcv := range s.Receivers { - isSilenced, err := sil.EvaluateSubscriptionRule(rcv) - if err != nil { - return nil, nil, fmt.Errorf("error evaluating subscription receiver %v: %w", rcv, err) - } - - if isSilenced { - if len(silencedReceiversMap) == 0 { - silencedReceiversMap = make(map[uint64][]silence.Silence) - } - silencedReceiversMap[rcv.ID] = append(silencedReceiversMap[rcv.ID], sil) - } else { - nonSilencedReceiversMap[rcv.ID] = rcv - } - } - } - - var nonSilencedReceivers []Receiver - for k, v := range nonSilencedReceiversMap { - // remove if non silenced receivers are part of silenced receivers - if _, ok := silencedReceiversMap[k]; !ok { - nonSilencedReceivers = append(nonSilencedReceivers, v) - } - } - - return silencedReceiversMap, nonSilencedReceivers, nil -} diff --git a/core/template/mocks/template_repository.go b/core/template/mocks/template_repository.go index 73a43756..339441e5 100644 --- a/core/template/mocks/template_repository.go +++ b/core/template/mocks/template_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -42,8 +42,8 @@ type TemplateRepository_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string +// - _a0 context.Context +// - _a1 string func (_e *TemplateRepository_Expecter) Delete(_a0 interface{}, _a1 interface{}) *TemplateRepository_Delete_Call { return &TemplateRepository_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} } @@ -89,8 +89,8 @@ type TemplateRepository_GetByName_Call struct { } // GetByName is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string +// - _a0 context.Context +// - _a1 string func (_e *TemplateRepository_Expecter) GetByName(_a0 interface{}, _a1 interface{}) *TemplateRepository_GetByName_Call { return &TemplateRepository_GetByName_Call{Call: _e.mock.On("GetByName", _a0, _a1)} } @@ -136,8 +136,8 @@ type TemplateRepository_List_Call struct { } // List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 template.Filter +// - _a0 context.Context +// - _a1 template.Filter func (_e *TemplateRepository_Expecter) List(_a0 interface{}, _a1 interface{}) *TemplateRepository_List_Call { return &TemplateRepository_List_Call{Call: _e.mock.On("List", _a0, _a1)} } @@ -174,8 +174,8 @@ type TemplateRepository_Upsert_Call struct { } // Upsert is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *template.Template +// - _a0 context.Context +// - _a1 *template.Template func (_e *TemplateRepository_Expecter) Upsert(_a0 interface{}, _a1 interface{}) *TemplateRepository_Upsert_Call { return &TemplateRepository_Upsert_Call{Call: _e.mock.On("Upsert", _a0, _a1)} } diff --git a/docker-compose.yaml b/docker-compose.yaml index 18dc4e70..afea3feb 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,7 +1,7 @@ version: "3" services: db: - image: "postgres:13" + image: "postgres:12" container_name: "siren_postgres" ports: - "5432:5432" diff --git a/docker/otel-collector-config.yaml b/docker/otel-collector-config.yaml index 5e8ac5e1..7fa58188 100644 --- a/docker/otel-collector-config.yaml +++ b/docker/otel-collector-config.yaml @@ -5,6 +5,10 @@ processors: batch: exporters: + prometheusremotewrite: + endpoint: "http://localhost:9000/api/v1/push" + tls: + insecure: true otlp: endpoint: https://otlp.nr-data.net:4317 headers: diff --git a/docs/docs/concepts/plugin.md b/docs/docs/concepts/plugin.md index 502fa9fe..b98c8c12 100644 --- a/docs/docs/concepts/plugin.md +++ b/docs/docs/concepts/plugin.md @@ -103,8 +103,8 @@ Data - template - metricValue - metricName -- generator_url -- num_alerts_firing +- generatorUrl +- numAlertsFiring - dashboard - playbook - summary diff --git a/docs/docs/guides/deployment.md b/docs/docs/guides/deployment.md index 3ada363b..07cc1b2a 100644 --- a/docs/docs/guides/deployment.md +++ b/docs/docs/guides/deployment.md @@ -8,7 +8,7 @@ There are several approaches to setup Siren Server ## General pre-requisites -- PostgreSQL (version 13 or above) +- PostgreSQL (version 12 or above) - Monitoring Providers - Ex: CortexMetrics diff --git a/docs/docs/reference/api.md b/docs/docs/reference/api.md index 3b9bb4f9..ef782bdc 100644 --- a/docs/docs/reference/api.md +++ b/docs/docs/reference/api.md @@ -1089,7 +1089,7 @@ render a template | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | -| id | string (uint64) | | No | +| rule | [Rule](#rule) | | No | #### UpdateSubscriptionResponse diff --git a/docs/docs/tour/2alerting_rules_subscriptions_overview.md b/docs/docs/tour/2alerting_rules_subscriptions_overview.md index 5cb93b51..9b711cfd 100644 --- a/docs/docs/tour/2alerting_rules_subscriptions_overview.md +++ b/docs/docs/tour/2alerting_rules_subscriptions_overview.md @@ -527,7 +527,7 @@ For details on a alert, try: siren alert view We also expect notifications have been published to the receiver id `2`. You can check a new notification is already added in `./out-file-sink2.json` with this value. ```json -{"environment":"integration","generator_url":"","groupKey":"{}:{severity=\"WARNING\"}","metricName":"test_alert","metricValue":"1","num_alerts_firing":1,"resource":"test_alert","routing_method":"subscribers","service":"some-service","severity":"WARNING","status":"firing","team":"odpf","template":"alert_test"} +{"environment":"integration","generatorUrl":"","groupKey":"{}:{severity=\"WARNING\"}","metricName":"test_alert","metricValue":"1","numAlertsFiring":1,"resource":"test_alert","routing_method":"subscribers","service":"some-service","severity":"WARNING","status":"firing","team":"odpf","template":"alert_test"} ``` ## What Next? diff --git a/go.mod b/go.mod index 27f16a5d..f4d4929b 100644 --- a/go.mod +++ b/go.mod @@ -21,8 +21,8 @@ require ( github.com/lib/pq v1.10.4 github.com/mcuadros/go-defaults v1.2.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/newrelic/go-agent/v3 v3.20.2 - github.com/newrelic/go-agent/v3/integrations/nrgrpc v1.3.2 + github.com/newrelic/go-agent/v3 v3.12.0 + github.com/newrelic/go-agent/v3/integrations/nrgrpc v1.3.1 github.com/newrelic/newrelic-opencensus-exporter-go v0.4.0 github.com/odpf/salt v0.2.5-0.20221122033807-b6caa1b617bf github.com/ory/dockertest/v3 v3.9.1 @@ -38,11 +38,10 @@ require ( google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 google.golang.org/grpc v1.51.0 google.golang.org/protobuf v1.28.1 + gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 ) -require github.com/antonmedv/expr v1.9.0 - require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/huandu/xstrings v1.3.3 // indirect @@ -183,5 +182,4 @@ require ( google.golang.org/api v0.103.0 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.66.6 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 705c438a..8acd3768 100644 --- a/go.sum +++ b/go.sum @@ -262,8 +262,6 @@ github.com/amir/raidman v0.0.0-20170415203553-1ccc43bfb9c9/go.mod h1:eliMa/PW+RD github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU= -github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY= github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= @@ -620,7 +618,6 @@ github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CL github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -762,8 +759,6 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXt github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -1510,8 +1505,6 @@ github.com/lightstep/lightstep-tracer-go v0.18.0/go.mod h1:jlF1pusYV4pidLvZ+XD0U github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/lovoo/gcloud-opentracing v0.3.0/go.mod h1:ZFqk2y38kMDDikZPAK7ynTTGuyt17nSPdS3K5e+ZTBY= -github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/iostat v1.1.0/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg= @@ -1567,7 +1560,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= @@ -1702,10 +1694,10 @@ github.com/ncw/swift v1.0.50/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ github.com/ncw/swift v1.0.52/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= github.com/newrelic/go-agent/v3 v3.3.0/go.mod h1:H28zDNUC0U/b7kLoY4EFOhuth10Xu/9dchozUiOseQQ= -github.com/newrelic/go-agent/v3 v3.20.2 h1:EqFMriW3Bv3on4tqKzI+fJmNYOEG55yw54v6yv8L+x8= -github.com/newrelic/go-agent/v3 v3.20.2/go.mod h1:rT6ZUxJc5rQbWLyCtjqQCOcfb01lKRFbc1yMQkcboWM= -github.com/newrelic/go-agent/v3/integrations/nrgrpc v1.3.2 h1:SBtZAkWapxfAxYVZHagHxsrbRt+ULkgxi0mbn6TMRM8= -github.com/newrelic/go-agent/v3/integrations/nrgrpc v1.3.2/go.mod h1:h5GGcju9OuzmRdKa5glKh/SrMs1HhZyvXrf0mEPv70k= +github.com/newrelic/go-agent/v3 v3.12.0 h1:tcDo0Q8qRWAJqb9uykfmM8pxGSbv0HqSS3q1+PzdhAo= +github.com/newrelic/go-agent/v3 v3.12.0/go.mod h1:1A1dssWBwzB7UemzRU6ZVaGDsI+cEn5/bNxI0wiYlIc= +github.com/newrelic/go-agent/v3/integrations/nrgrpc v1.3.1 h1:/ar1Omo9luapTJYWXt86oQGBpWwpWF92x+UuYU9v/7o= +github.com/newrelic/go-agent/v3/integrations/nrgrpc v1.3.1/go.mod h1:2q0u6qkNJ4ClDt920A4r+NpcO370lFze1NF4OPJjAks= github.com/newrelic/go-agent/v3/integrations/nrpq v1.1.1 h1:HlVcLXw7ZZPjeRx3lQUAN8qfpJVDmuq4L237M1+PS8A= github.com/newrelic/go-agent/v3/integrations/nrpq v1.1.1/go.mod h1:UvI7Z0Dok/36E44UiTysh9HQZudDdpiChbe3+eqSB0I= github.com/newrelic/newrelic-opencensus-exporter-go v0.4.0 h1:BjzhyzSrzc8/WtyZDWBF8XATW4M92EoZiy38kgL3gfo= @@ -1971,7 +1963,6 @@ github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqn github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -2002,7 +1993,6 @@ github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75/go.mod h1:gi+0 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/samuel/go-zookeeper v0.0.0-20200724154423-2164a8ac840e/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4= github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -2110,7 +2100,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -2632,7 +2621,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/api/api.go b/internal/api/api.go index 7817f3fa..635bfdc1 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -10,7 +10,6 @@ import ( "github.com/odpf/siren/core/provider" "github.com/odpf/siren/core/receiver" "github.com/odpf/siren/core/rule" - "github.com/odpf/siren/core/silence" "github.com/odpf/siren/core/subscription" "github.com/odpf/siren/core/template" ) @@ -74,20 +73,13 @@ type TemplateService interface { //go:generate mockery --name=NotificationService -r --case underscore --with-expecter --structname NotificationService --filename notification_service.go --output=./mocks type NotificationService interface { - Dispatch(ctx context.Context, n notification.Notification) error + DispatchToReceiver(ctx context.Context, n notification.Notification, receiverID uint64) error + DispatchToSubscribers(ctx context.Context, namespaceID uint64, n notification.Notification) error CheckAndInsertIdempotency(ctx context.Context, scope, key string) (uint64, error) MarkIdempotencyAsSuccess(ctx context.Context, id uint64) error RemoveIdempotencies(ctx context.Context, TTL time.Duration) error } -//go:generate mockery --name=SilenceService -r --case underscore --with-expecter --structname SilenceService --filename silence_service.go --output=./mocks -type SilenceService interface { - Create(ctx context.Context, sil silence.Silence) (string, error) - List(ctx context.Context, filter silence.Filter) ([]silence.Silence, error) - Get(ctx context.Context, id string) (silence.Silence, error) - Delete(ctx context.Context, id string) error -} - type Deps struct { TemplateService TemplateService RuleService RuleService @@ -97,5 +89,4 @@ type Deps struct { ReceiverService ReceiverService SubscriptionService SubscriptionService NotificationService NotificationService - SilenceService SilenceService } diff --git a/internal/api/mocks/notification_service.go b/internal/api/mocks/notification_service.go index 00fd9488..fe71f3d3 100644 --- a/internal/api/mocks/notification_service.go +++ b/internal/api/mocks/notification_service.go @@ -70,13 +70,13 @@ func (_c *NotificationService_CheckAndInsertIdempotency_Call) Return(_a0 uint64, return _c } -// Dispatch provides a mock function with given fields: ctx, n -func (_m *NotificationService) Dispatch(ctx context.Context, n notification.Notification) error { - ret := _m.Called(ctx, n) +// DispatchToReceiver provides a mock function with given fields: ctx, n, receiverID +func (_m *NotificationService) DispatchToReceiver(ctx context.Context, n notification.Notification, receiverID uint64) error { + ret := _m.Called(ctx, n, receiverID) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, notification.Notification) error); ok { - r0 = rf(ctx, n) + if rf, ok := ret.Get(0).(func(context.Context, notification.Notification, uint64) error); ok { + r0 = rf(ctx, n, receiverID) } else { r0 = ret.Error(0) } @@ -84,26 +84,66 @@ func (_m *NotificationService) Dispatch(ctx context.Context, n notification.Noti return r0 } -// NotificationService_Dispatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dispatch' -type NotificationService_Dispatch_Call struct { +// NotificationService_DispatchToReceiver_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchToReceiver' +type NotificationService_DispatchToReceiver_Call struct { *mock.Call } -// Dispatch is a helper method to define mock.On call +// DispatchToReceiver is a helper method to define mock.On call // - ctx context.Context // - n notification.Notification -func (_e *NotificationService_Expecter) Dispatch(ctx interface{}, n interface{}) *NotificationService_Dispatch_Call { - return &NotificationService_Dispatch_Call{Call: _e.mock.On("Dispatch", ctx, n)} +// - receiverID uint64 +func (_e *NotificationService_Expecter) DispatchToReceiver(ctx interface{}, n interface{}, receiverID interface{}) *NotificationService_DispatchToReceiver_Call { + return &NotificationService_DispatchToReceiver_Call{Call: _e.mock.On("DispatchToReceiver", ctx, n, receiverID)} } -func (_c *NotificationService_Dispatch_Call) Run(run func(ctx context.Context, n notification.Notification)) *NotificationService_Dispatch_Call { +func (_c *NotificationService_DispatchToReceiver_Call) Run(run func(ctx context.Context, n notification.Notification, receiverID uint64)) *NotificationService_DispatchToReceiver_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(notification.Notification)) + run(args[0].(context.Context), args[1].(notification.Notification), args[2].(uint64)) }) return _c } -func (_c *NotificationService_Dispatch_Call) Return(_a0 error) *NotificationService_Dispatch_Call { +func (_c *NotificationService_DispatchToReceiver_Call) Return(_a0 error) *NotificationService_DispatchToReceiver_Call { + _c.Call.Return(_a0) + return _c +} + +// DispatchToSubscribers provides a mock function with given fields: ctx, namespaceID, n +func (_m *NotificationService) DispatchToSubscribers(ctx context.Context, namespaceID uint64, n notification.Notification) error { + ret := _m.Called(ctx, namespaceID, n) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, notification.Notification) error); ok { + r0 = rf(ctx, namespaceID, n) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NotificationService_DispatchToSubscribers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DispatchToSubscribers' +type NotificationService_DispatchToSubscribers_Call struct { + *mock.Call +} + +// DispatchToSubscribers is a helper method to define mock.On call +// - ctx context.Context +// - namespaceID uint64 +// - n notification.Notification +func (_e *NotificationService_Expecter) DispatchToSubscribers(ctx interface{}, namespaceID interface{}, n interface{}) *NotificationService_DispatchToSubscribers_Call { + return &NotificationService_DispatchToSubscribers_Call{Call: _e.mock.On("DispatchToSubscribers", ctx, namespaceID, n)} +} + +func (_c *NotificationService_DispatchToSubscribers_Call) Run(run func(ctx context.Context, namespaceID uint64, n notification.Notification)) *NotificationService_DispatchToSubscribers_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(notification.Notification)) + }) + return _c +} + +func (_c *NotificationService_DispatchToSubscribers_Call) Return(_a0 error) *NotificationService_DispatchToSubscribers_Call { _c.Call.Return(_a0) return _c } diff --git a/internal/api/mocks/silence_service.go b/internal/api/mocks/silence_service.go deleted file mode 100644 index 0715715d..00000000 --- a/internal/api/mocks/silence_service.go +++ /dev/null @@ -1,213 +0,0 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - silence "github.com/odpf/siren/core/silence" - mock "github.com/stretchr/testify/mock" -) - -// SilenceService is an autogenerated mock type for the SilenceService type -type SilenceService struct { - mock.Mock -} - -type SilenceService_Expecter struct { - mock *mock.Mock -} - -func (_m *SilenceService) EXPECT() *SilenceService_Expecter { - return &SilenceService_Expecter{mock: &_m.Mock} -} - -// Create provides a mock function with given fields: ctx, sil -func (_m *SilenceService) Create(ctx context.Context, sil silence.Silence) (string, error) { - ret := _m.Called(ctx, sil) - - var r0 string - if rf, ok := ret.Get(0).(func(context.Context, silence.Silence) string); ok { - r0 = rf(ctx, sil) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, silence.Silence) error); ok { - r1 = rf(ctx, sil) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SilenceService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type SilenceService_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - ctx context.Context -// - sil silence.Silence -func (_e *SilenceService_Expecter) Create(ctx interface{}, sil interface{}) *SilenceService_Create_Call { - return &SilenceService_Create_Call{Call: _e.mock.On("Create", ctx, sil)} -} - -func (_c *SilenceService_Create_Call) Run(run func(ctx context.Context, sil silence.Silence)) *SilenceService_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(silence.Silence)) - }) - return _c -} - -func (_c *SilenceService_Create_Call) Return(_a0 string, _a1 error) *SilenceService_Create_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Delete provides a mock function with given fields: ctx, id -func (_m *SilenceService) Delete(ctx context.Context, id string) error { - ret := _m.Called(ctx, id) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SilenceService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type SilenceService_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -// - ctx context.Context -// - id string -func (_e *SilenceService_Expecter) Delete(ctx interface{}, id interface{}) *SilenceService_Delete_Call { - return &SilenceService_Delete_Call{Call: _e.mock.On("Delete", ctx, id)} -} - -func (_c *SilenceService_Delete_Call) Run(run func(ctx context.Context, id string)) *SilenceService_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *SilenceService_Delete_Call) Return(_a0 error) *SilenceService_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -// Get provides a mock function with given fields: ctx, id -func (_m *SilenceService) Get(ctx context.Context, id string) (silence.Silence, error) { - ret := _m.Called(ctx, id) - - var r0 silence.Silence - if rf, ok := ret.Get(0).(func(context.Context, string) silence.Silence); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Get(0).(silence.Silence) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SilenceService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' -type SilenceService_Get_Call struct { - *mock.Call -} - -// Get is a helper method to define mock.On call -// - ctx context.Context -// - id string -func (_e *SilenceService_Expecter) Get(ctx interface{}, id interface{}) *SilenceService_Get_Call { - return &SilenceService_Get_Call{Call: _e.mock.On("Get", ctx, id)} -} - -func (_c *SilenceService_Get_Call) Run(run func(ctx context.Context, id string)) *SilenceService_Get_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *SilenceService_Get_Call) Return(_a0 silence.Silence, _a1 error) *SilenceService_Get_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// List provides a mock function with given fields: ctx, filter -func (_m *SilenceService) List(ctx context.Context, filter silence.Filter) ([]silence.Silence, error) { - ret := _m.Called(ctx, filter) - - var r0 []silence.Silence - if rf, ok := ret.Get(0).(func(context.Context, silence.Filter) []silence.Silence); ok { - r0 = rf(ctx, filter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]silence.Silence) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, silence.Filter) error); ok { - r1 = rf(ctx, filter) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SilenceService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type SilenceService_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - ctx context.Context -// - filter silence.Filter -func (_e *SilenceService_Expecter) List(ctx interface{}, filter interface{}) *SilenceService_List_Call { - return &SilenceService_List_Call{Call: _e.mock.On("List", ctx, filter)} -} - -func (_c *SilenceService_List_Call) Run(run func(ctx context.Context, filter silence.Filter)) *SilenceService_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(silence.Filter)) - }) - return _c -} - -func (_c *SilenceService_List_Call) Return(_a0 []silence.Silence, _a1 error) *SilenceService_List_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -type mockConstructorTestingTNewSilenceService interface { - mock.TestingT - Cleanup(func()) -} - -// NewSilenceService creates a new instance of SilenceService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSilenceService(t mockConstructorTestingTNewSilenceService) *SilenceService { - mock := &SilenceService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/api/v1beta1/alert.go b/internal/api/v1beta1/alert.go index 23df4b60..8e83453d 100644 --- a/internal/api/v1beta1/alert.go +++ b/internal/api/v1beta1/alert.go @@ -2,10 +2,12 @@ package v1beta1 import ( "context" + "fmt" "time" "github.com/odpf/siren/core/alert" "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/core/template" sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -16,7 +18,6 @@ func (s *GRPCServer) ListAlerts(ctx context.Context, req *sirenv1beta1.ListAlert ProviderID: req.GetProviderId(), StartTime: int64(req.GetStartTime()), EndTime: int64(req.GetEndTime()), - // SilenceID: req.GetSilenced(), }) if err != nil { return nil, s.generateRPCErr(err) @@ -25,15 +26,14 @@ func (s *GRPCServer) ListAlerts(ctx context.Context, req *sirenv1beta1.ListAlert items := []*sirenv1beta1.Alert{} for _, alert := range alerts { item := &sirenv1beta1.Alert{ - Id: alert.ID, - ProviderId: alert.ProviderID, - ResourceName: alert.ResourceName, - MetricName: alert.MetricName, - MetricValue: alert.MetricValue, - Severity: alert.Severity, - Rule: alert.Rule, - TriggeredAt: timestamppb.New(alert.TriggeredAt), - SilenceStatus: alert.SilenceStatus, + Id: alert.ID, + ProviderId: alert.ProviderID, + ResourceName: alert.ResourceName, + MetricName: alert.MetricName, + MetricValue: alert.MetricValue, + Severity: alert.Severity, + Rule: alert.Rule, + TriggeredAt: timestamppb.New(alert.TriggeredAt), } items = append(items, item) } @@ -87,14 +87,99 @@ func (s *GRPCServer) createAlerts(ctx context.Context, providerType string, prov if len(createdAlerts) > 0 { // Publish to notification service - n := notification.BuildFromAlerts(createdAlerts, firingLen, time.Now()) + n := AlertsToNotification(createdAlerts, firingLen, time.Now()) - if err := s.notificationService.Dispatch(ctx, n); err != nil { + if err := s.notificationService.DispatchToSubscribers(ctx, namespaceID, n); err != nil { s.logger.Warn("failed to send alert as notification", "err", err, "notification", n) } } else { - s.logger.Warn("failed to send alert as notification, empty created alerts") + s.logger.Warn("failed to send alert a as notification, empty created alerts") } return items, nil } + +// Transform alerts and populate Data and Labels to be interpolated to the system-default template +// .Data +// - id +// - status "FIRING"/"RESOLVED" +// - resource +// - template +// - metric_value +// - metric_name +// - generatorUrl +// - numAlertsFiring +// - dashboard +// - playbook +// - summary +// .Labels +// - severity "WARNING"/"CRITICAL" +// - alertname +// - (others labels defined in rules) +func AlertsToNotification( + as []alert.Alert, + firingLen int, + createdTime time.Time, +) notification.Notification { + sampleAlert := as[0] + id := "cortex-" + sampleAlert.Fingerprint + + data := map[string]interface{}{} + + mergedAnnotations := map[string][]string{} + for _, a := range as { + for k, v := range a.Annotations { + mergedAnnotations[k] = append(mergedAnnotations[k], v) + } + } + + // make unique + for k, v := range mergedAnnotations { + mergedAnnotations[k] = removeDuplicateStringValues(v) + } + + // render annotations + for k, vSlice := range mergedAnnotations { + for _, v := range vSlice { + if _, ok := data[k]; ok { + data[k] = fmt.Sprintf("%s\n%s", data[k], v) + } else { + data[k] = v + } + } + } + + data["status"] = sampleAlert.Status + data["generatorUrl"] = sampleAlert.GeneratorURL + data["numAlertsFiring"] = firingLen + data["id"] = id + + labels := map[string]string{} + + for _, a := range as { + for k, v := range a.Labels { + labels[k] = v + } + } + + return notification.Notification{ + ID: id, + Data: data, + Labels: labels, + Template: template.ReservedName_SystemDefault, + CreatedAt: createdTime, + } +} + +func removeDuplicateStringValues(strSlice []string) []string { + keys := make(map[string]bool) + list := []string{} + + for _, v := range strSlice { + if _, value := keys[v]; !value { + keys[v] = true + list = append(list, v) + } + } + return list +} diff --git a/internal/api/v1beta1/alert_test.go b/internal/api/v1beta1/alert_test.go index 1ef929af..ac08840a 100644 --- a/internal/api/v1beta1/alert_test.go +++ b/internal/api/v1beta1/alert_test.go @@ -162,7 +162,6 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { dummyAlerts := []alert.Alert{{ ID: 1, ProviderID: 1, - NamespaceID: 1, ResourceName: "foo", MetricName: "bar", MetricValue: "30", @@ -172,7 +171,7 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { }} mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), mock.AnythingOfType("uint64"), payload). Return(dummyAlerts, 1, nil).Once() - mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) + mockNotificationService.EXPECT().DispatchToSubscribers(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("notification.Notification")).Return(nil) dummyGRPCServer := v1beta1.NewGRPCServer(nil, log.NewNoop(), api.HeadersConfig{}, &api.Deps{AlertService: mockedAlertService, NotificationService: mockNotificationService}) @@ -270,7 +269,6 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { dummyAlerts := []alert.Alert{{ ID: 1, ProviderID: 1, - NamespaceID: 1, ResourceName: "foo", MetricName: "bar", MetricValue: "30", @@ -280,7 +278,7 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { }} mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), mock.AnythingOfType("uint64"), payload). Return(dummyAlerts, 1, nil).Once() - mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) + mockNotificationService.EXPECT().DispatchToSubscribers(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("notification.Notification")).Return(nil) dummyGRPCServer := v1beta1.NewGRPCServer(nil, log.NewNoop(), api.HeadersConfig{}, &api.Deps{AlertService: mockedAlertService, NotificationService: mockNotificationService}) @@ -448,7 +446,6 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { dummyAlerts := []alert.Alert{{ ProviderID: 1, - NamespaceID: 1, ResourceName: "foo", MetricName: "bar", MetricValue: "30", @@ -461,7 +458,7 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), mock.AnythingOfType("uint64"), payload). Return(dummyAlerts, 2, nil).Once() - mockNotificationService.EXPECT().Dispatch(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) + mockNotificationService.EXPECT().DispatchToSubscribers(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("notification.Notification")).Return(nil) res, err := dummyGRPCServer.CreateAlerts(context.Background(), dummyReq) assert.Equal(t, 1, len(res.GetAlerts())) diff --git a/internal/api/v1beta1/notification.go b/internal/api/v1beta1/notification.go index 1abef106..4ecd7c6a 100644 --- a/internal/api/v1beta1/notification.go +++ b/internal/api/v1beta1/notification.go @@ -3,6 +3,8 @@ package v1beta1 import ( "context" + "github.com/mitchellh/mapstructure" + "github.com/odpf/siren/core/notification" "github.com/odpf/siren/internal/api" "github.com/odpf/siren/pkg/errors" @@ -31,12 +33,13 @@ func (s *GRPCServer) NotifyReceiver(ctx context.Context, req *sirenv1beta1.Notif } } - n, err := notification.BuildTypeReceiver(req.GetId(), payloadMap) - if err != nil { + n := notification.Notification{} + if err := mapstructure.Decode(payloadMap, &n); err != nil { + err = errors.ErrInvalid.WithMsgf("failed to parse payload to notification: %s", err.Error()) return nil, s.generateRPCErr(err) } - if err := s.notificationService.Dispatch(ctx, n); err != nil { + if err := s.notificationService.DispatchToReceiver(ctx, n, req.GetId()); err != nil { return nil, s.generateRPCErr(err) } diff --git a/internal/api/v1beta1/notification_test.go b/internal/api/v1beta1/notification_test.go index 452faab9..73b7ce1c 100644 --- a/internal/api/v1beta1/notification_test.go +++ b/internal/api/v1beta1/notification_test.go @@ -27,7 +27,7 @@ func TestGRPCServer_NotifyReceiver(t *testing.T) { idempotencyKey: "test", setup: func(ns *mocks.NotificationService) { ns.EXPECT().CheckAndInsertIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(1, nil) - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return(errors.ErrInvalid) + ns.EXPECT().DispatchToReceiver(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification"), mock.AnythingOfType("uint64")).Return(errors.ErrInvalid) }, errString: "rpc error: code = InvalidArgument desc = request is not valid", }, @@ -36,7 +36,7 @@ func TestGRPCServer_NotifyReceiver(t *testing.T) { idempotencyKey: "test", setup: func(ns *mocks.NotificationService) { ns.EXPECT().CheckAndInsertIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(1, nil) - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return(errors.New("some error")) + ns.EXPECT().DispatchToReceiver(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification"), mock.AnythingOfType("uint64")).Return(errors.New("some error")) }, errString: "rpc error: code = Internal desc = some unexpected error occurred", }, @@ -61,7 +61,7 @@ func TestGRPCServer_NotifyReceiver(t *testing.T) { setup: func(ns *mocks.NotificationService) { ns.EXPECT().CheckAndInsertIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(1, nil) ns.EXPECT().MarkIdempotencyAsSuccess(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("uint64")).Return(errors.New("some error")) - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) + ns.EXPECT().DispatchToReceiver(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification"), mock.AnythingOfType("uint64")).Return(nil) }, errString: "rpc error: code = Internal desc = some unexpected error occurred", }, @@ -71,13 +71,13 @@ func TestGRPCServer_NotifyReceiver(t *testing.T) { setup: func(ns *mocks.NotificationService) { ns.EXPECT().CheckAndInsertIdempotency(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(1, nil) ns.EXPECT().MarkIdempotencyAsSuccess(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("uint64")).Return(nil) - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) + ns.EXPECT().DispatchToReceiver(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification"), mock.AnythingOfType("uint64")).Return(nil) }, }, { name: "should return OK response if notify receiver succeed without idempotency", setup: func(ns *mocks.NotificationService) { - ns.EXPECT().Dispatch(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) + ns.EXPECT().DispatchToReceiver(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("notification.Notification"), mock.AnythingOfType("uint64")).Return(nil) }, }, } diff --git a/internal/api/v1beta1/rule.go b/internal/api/v1beta1/rule.go index 4807b1a7..2c83352b 100644 --- a/internal/api/v1beta1/rule.go +++ b/internal/api/v1beta1/rule.go @@ -76,7 +76,29 @@ func (s *GRPCServer) UpdateRule(ctx context.Context, req *sirenv1beta1.UpdateRul return nil, s.generateRPCErr(err) } - return &sirenv1beta1.UpdateRuleResponse{ - Id: rl.ID, - }, nil + responseVariables := make([]*sirenv1beta1.Variables, 0) + for _, variable := range rl.Variables { + responseVariables = append(responseVariables, &sirenv1beta1.Variables{ + Name: variable.Name, + Type: variable.Type, + Value: variable.Value, + Description: variable.Description, + }) + } + + res := &sirenv1beta1.UpdateRuleResponse{ + Rule: &sirenv1beta1.Rule{ + Id: rl.ID, + Name: rl.Name, + Enabled: rl.Enabled, + GroupName: rl.GroupName, + Namespace: rl.Namespace, + Template: rl.Template, + Variables: responseVariables, + ProviderNamespace: rl.ProviderNamespace, + CreatedAt: timestamppb.New(rl.CreatedAt), + UpdatedAt: timestamppb.New(rl.UpdatedAt), + }, + } + return res, nil } diff --git a/internal/api/v1beta1/rule_test.go b/internal/api/v1beta1/rule_test.go index adef1025..812a9f64 100644 --- a/internal/api/v1beta1/rule_test.go +++ b/internal/api/v1beta1/rule_test.go @@ -137,7 +137,7 @@ func TestGRPCServer_UpdateRules(t *testing.T) { res, err := dummyGRPCServer.UpdateRule(ctx, dummyReq) assert.Nil(t, err) - assert.Equal(t, testID, res.GetId()) + assert.Equal(t, testID, res.GetRule().GetId()) mockedRuleService.AssertExpectations(t) }) diff --git a/internal/api/v1beta1/silence.go b/internal/api/v1beta1/silence.go deleted file mode 100644 index 2b426f4d..00000000 --- a/internal/api/v1beta1/silence.go +++ /dev/null @@ -1,92 +0,0 @@ -package v1beta1 - -import ( - "context" - - "github.com/odpf/siren/core/silence" - sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func (s *GRPCServer) CreateSilence(ctx context.Context, req *sirenv1beta1.CreateSilenceRequest) (*sirenv1beta1.CreateSilenceResponse, error) { - id, err := s.silenceService.Create(ctx, silence.Silence{ - NamespaceID: req.GetNamespaceId(), - Type: req.GetType(), - TargetID: req.GetTargetId(), - TargetExpression: req.GetTargetExpression().AsMap(), - }) - if err != nil { - return nil, s.generateRPCErr(err) - } - - return &sirenv1beta1.CreateSilenceResponse{ - Id: id, - }, nil -} - -func (s *GRPCServer) ListSilences(ctx context.Context, req *sirenv1beta1.ListSilencesRequest) (*sirenv1beta1.ListSilencesResponse, error) { - silences, err := s.silenceService.List(ctx, silence.Filter{ - NamespaceID: req.GetNamespaceId(), - SubscriptionID: req.GetSubscriptionId(), - Match: req.GetMatch(), - SubscriptionMatch: req.GetSubscriptionMatch(), - }) - if err != nil { - return nil, s.generateRPCErr(err) - } - - var silencesProto []*sirenv1beta1.Silence - for _, si := range silences { - targetExpression, err := structpb.NewStruct(si.TargetExpression) - if err != nil { - return nil, s.generateRPCErr(err) - } - - silencesProto = append(silencesProto, &sirenv1beta1.Silence{ - Id: si.ID, - NamespaceId: si.NamespaceID, - Type: si.Type, - TargetId: si.TargetID, - TargetExpression: targetExpression, - CreatedAt: timestamppb.New(si.CreatedAt), - DeletedAt: timestamppb.New(si.DeletedAt), - }) - } - - return &sirenv1beta1.ListSilencesResponse{ - Silences: silencesProto, - }, nil -} - -func (s *GRPCServer) GetSilence(ctx context.Context, req *sirenv1beta1.GetSilenceRequest) (*sirenv1beta1.GetSilenceResponse, error) { - sil, err := s.silenceService.Get(ctx, req.GetId()) - if err != nil { - return nil, s.generateRPCErr(err) - } - - targetExpression, err := structpb.NewStruct(sil.TargetExpression) - if err != nil { - return nil, s.generateRPCErr(err) - } - - return &sirenv1beta1.GetSilenceResponse{ - Silence: &sirenv1beta1.Silence{ - Id: sil.ID, - NamespaceId: sil.NamespaceID, - Type: sil.Type, - TargetId: sil.TargetID, - TargetExpression: targetExpression, - CreatedAt: timestamppb.New(sil.CreatedAt), - DeletedAt: timestamppb.New(sil.DeletedAt), - }, - }, nil -} - -func (s *GRPCServer) ExpireSilence(ctx context.Context, req *sirenv1beta1.ExpireSilenceRequest) (*sirenv1beta1.ExpireSilenceResponse, error) { - if err := s.silenceService.Delete(ctx, req.GetId()); err != nil { - return nil, s.generateRPCErr(err) - } - - return &sirenv1beta1.ExpireSilenceResponse{}, nil -} diff --git a/internal/api/v1beta1/silence_test.go b/internal/api/v1beta1/silence_test.go deleted file mode 100644 index 3503bf9e..00000000 --- a/internal/api/v1beta1/silence_test.go +++ /dev/null @@ -1,289 +0,0 @@ -package v1beta1_test - -import ( - "context" - "reflect" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/odpf/salt/log" - "github.com/odpf/siren/core/silence" - "github.com/odpf/siren/internal/api" - "github.com/odpf/siren/internal/api/mocks" - "github.com/odpf/siren/internal/api/v1beta1" - "github.com/odpf/siren/pkg/errors" - sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" - "github.com/stretchr/testify/mock" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func TestGRPCServer_CreateSilence(t *testing.T) { - mockSilenceData := silence.Silence{ - NamespaceID: 1, - Type: silence.TypeMatchers, - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - } - - tests := []struct { - name string - setup func(*mocks.SilenceService) - req *sirenv1beta1.CreateSilenceRequest - want *sirenv1beta1.CreateSilenceResponse - wantErr bool - }{ - { - name: "return silence id when successfully created silence", - setup: func(ss *mocks.SilenceService) { - ss.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mockSilenceData).Return("123", nil) - }, - req: &sirenv1beta1.CreateSilenceRequest{ - NamespaceId: mockSilenceData.NamespaceID, - Type: mockSilenceData.Type, - TargetExpression: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "key1": structpb.NewStringValue("value1"), - }, - }, - }, - want: &sirenv1beta1.CreateSilenceResponse{ - Id: "123", - }, - }, - { - name: "return error if service create return error", - setup: func(ss *mocks.SilenceService) { - ss.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mockSilenceData).Return("", errors.New("some error")) - }, - req: &sirenv1beta1.CreateSilenceRequest{ - NamespaceId: mockSilenceData.NamespaceID, - Type: mockSilenceData.Type, - TargetExpression: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "key1": structpb.NewStringValue("value1"), - }, - }, - }, - wantErr: true, - }, - } - for _, tt := range tests { - ctx := context.TODO() - t.Run(tt.name, func(t *testing.T) { - mockSilenceService := new(mocks.SilenceService) - - if tt.setup != nil { - tt.setup(mockSilenceService) - } - - s := v1beta1.NewGRPCServer(nil, log.NewNoop(), api.HeadersConfig{}, &api.Deps{SilenceService: mockSilenceService}) - got, err := s.CreateSilence(ctx, tt.req) - if (err != nil) != tt.wantErr { - t.Errorf("GRPCServer.CreateSilence() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GRPCServer.CreateSilence() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestGRPCServer_ListSilences(t *testing.T) { - mockSilenceData := silence.Silence{ - NamespaceID: 1, - Type: silence.TypeMatchers, - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - } - - tests := []struct { - name string - setup func(*mocks.SilenceService) - want []*sirenv1beta1.Silence - wantErr bool - }{ - { - name: "return silences when successfully list silences", - setup: func(ss *mocks.SilenceService) { - ss.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("silence.Filter")).Return([]silence.Silence{ - mockSilenceData, - }, nil) - }, - want: []*sirenv1beta1.Silence{ - { - NamespaceId: mockSilenceData.NamespaceID, - Type: mockSilenceData.Type, - TargetExpression: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "key1": structpb.NewStringValue("value1"), - }, - }, - CreatedAt: timestamppb.New(time.Time{}), - }, - }, - }, - { - name: "return error if service list return error", - setup: func(ss *mocks.SilenceService) { - ss.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("silence.Filter")).Return(nil, errors.New("some error")) - }, - wantErr: true, - }, - } - for _, tt := range tests { - ctx := context.TODO() - t.Run(tt.name, func(t *testing.T) { - mockSilenceService := new(mocks.SilenceService) - - if tt.setup != nil { - tt.setup(mockSilenceService) - } - - s := v1beta1.NewGRPCServer(nil, log.NewNoop(), api.HeadersConfig{}, &api.Deps{SilenceService: mockSilenceService}) - got, err := s.ListSilences(ctx, &sirenv1beta1.ListSilencesRequest{}) - if (err != nil) != tt.wantErr { - t.Errorf("GRPCServer.ListSilences() error = %v, wantErr %v", err, tt.wantErr) - return - } - - if diff := cmp.Diff(got.GetSilences(), tt.want, protocmp.Transform(), protocmp.IgnoreFields(&sirenv1beta1.Silence{}, "id", "deleted_at")); diff != "" { - t.Errorf("GRPCServer.ListSilences() diff = %v", diff) - } - }) - } -} - -func TestGRPCServer_GetSilence(t *testing.T) { - mockSilenceData := silence.Silence{ - ID: "silence-id", - NamespaceID: 1, - Type: silence.TypeMatchers, - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - } - - tests := []struct { - name string - setup func(*mocks.SilenceService) - req *sirenv1beta1.GetSilenceRequest - want *sirenv1beta1.Silence - wantErr bool - }{ - { - name: "return silence when successfully get silence", - setup: func(ss *mocks.SilenceService) { - ss.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mockSilenceData.ID).Return(mockSilenceData, nil) - }, - req: &sirenv1beta1.GetSilenceRequest{ - Id: mockSilenceData.ID, - }, - want: &sirenv1beta1.Silence{ - NamespaceId: mockSilenceData.NamespaceID, - Type: mockSilenceData.Type, - TargetExpression: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "key1": structpb.NewStringValue("value1"), - }, - }, - CreatedAt: timestamppb.New(time.Time{}), - }, - }, - { - name: "return error if service get return error", - setup: func(ss *mocks.SilenceService) { - ss.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mockSilenceData.ID).Return(silence.Silence{}, errors.New("some error")) - }, - req: &sirenv1beta1.GetSilenceRequest{ - Id: mockSilenceData.ID, - }, - wantErr: true, - }, - } - for _, tt := range tests { - ctx := context.TODO() - t.Run(tt.name, func(t *testing.T) { - mockSilenceService := new(mocks.SilenceService) - - if tt.setup != nil { - tt.setup(mockSilenceService) - } - - s := v1beta1.NewGRPCServer(nil, log.NewNoop(), api.HeadersConfig{}, &api.Deps{SilenceService: mockSilenceService}) - got, err := s.GetSilence(ctx, tt.req) - if (err != nil) != tt.wantErr { - t.Errorf("GRPCServer.GetSilence() error = %v, wantErr %v", err, tt.wantErr) - return - } - if diff := cmp.Diff(got.GetSilence(), tt.want, protocmp.Transform(), protocmp.IgnoreFields(&sirenv1beta1.Silence{}, "id", "deleted_at")); diff != "" { - t.Errorf("GRPCServer.GetSilence() diff = %v", diff) - } - }) - } -} - -func TestGRPCServer_ExpireSilence(t *testing.T) { - mockSilenceData := silence.Silence{ - ID: "silence-id", - NamespaceID: 1, - Type: silence.TypeMatchers, - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - } - - tests := []struct { - name string - setup func(*mocks.SilenceService) - req *sirenv1beta1.ExpireSilenceRequest - want *sirenv1beta1.ExpireSilenceResponse - wantErr bool - }{ - { - name: "return success when successfully deleted silence", - setup: func(ss *mocks.SilenceService) { - ss.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mockSilenceData.ID).Return(nil) - }, - req: &sirenv1beta1.ExpireSilenceRequest{ - Id: mockSilenceData.ID, - }, - want: &sirenv1beta1.ExpireSilenceResponse{}, - }, - { - name: "return error if service delete return error", - setup: func(ss *mocks.SilenceService) { - ss.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mockSilenceData.ID).Return(errors.New("some error")) - }, - req: &sirenv1beta1.ExpireSilenceRequest{ - Id: mockSilenceData.ID, - }, - wantErr: true, - }, - } - for _, tt := range tests { - ctx := context.TODO() - t.Run(tt.name, func(t *testing.T) { - mockSilenceService := new(mocks.SilenceService) - - if tt.setup != nil { - tt.setup(mockSilenceService) - } - - s := v1beta1.NewGRPCServer(nil, log.NewNoop(), api.HeadersConfig{}, &api.Deps{SilenceService: mockSilenceService}) - got, err := s.ExpireSilence(ctx, tt.req) - if (err != nil) != tt.wantErr { - t.Errorf("GRPCServer.ExpireSilence() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GRPCServer.ExpireSilence() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/api/v1beta1/subscription.go b/internal/api/v1beta1/subscription.go index 0ca6eab5..c18ba457 100644 --- a/internal/api/v1beta1/subscription.go +++ b/internal/api/v1beta1/subscription.go @@ -9,13 +9,8 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -func (s *GRPCServer) ListSubscriptions(ctx context.Context, req *sirenv1beta1.ListSubscriptionsRequest) (*sirenv1beta1.ListSubscriptionsResponse, error) { - subscriptions, err := s.subscriptionService.List(ctx, subscription.Filter{ - NamespaceID: req.GetNamespaceId(), - SilenceID: req.GetSilenceId(), - Match: req.GetMatch(), - NotificationMatch: req.GetNotificationMatch(), - }) +func (s *GRPCServer) ListSubscriptions(ctx context.Context, _ *sirenv1beta1.ListSubscriptionsRequest) (*sirenv1beta1.ListSubscriptionsResponse, error) { + subscriptions, err := s.subscriptionService.List(ctx, subscription.Filter{}) if err != nil { return nil, s.generateRPCErr(err) } diff --git a/internal/api/v1beta1/v1beta1.go b/internal/api/v1beta1/v1beta1.go index 3df04096..eb2b1e58 100644 --- a/internal/api/v1beta1/v1beta1.go +++ b/internal/api/v1beta1/v1beta1.go @@ -24,7 +24,6 @@ type GRPCServer struct { receiverService api.ReceiverService subscriptionService api.SubscriptionService notificationService api.NotificationService - silenceService api.SilenceService } func NewGRPCServer( @@ -44,7 +43,6 @@ func NewGRPCServer( receiverService: apiDeps.ReceiverService, subscriptionService: apiDeps.SubscriptionService, notificationService: apiDeps.NotificationService, - silenceService: apiDeps.SilenceService, } } diff --git a/internal/jobs/mocks/notification_service.go b/internal/jobs/mocks/notification_service.go index c5203263..ce6164ba 100644 --- a/internal/jobs/mocks/notification_service.go +++ b/internal/jobs/mocks/notification_service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -43,8 +43,8 @@ type NotificationService_RemoveIdempotencies_Call struct { } // RemoveIdempotencies is a helper method to define mock.On call -// - ctx context.Context -// - TTL time.Duration +// - ctx context.Context +// - TTL time.Duration func (_e *NotificationService_Expecter) RemoveIdempotencies(ctx interface{}, TTL interface{}) *NotificationService_RemoveIdempotencies_Call { return &NotificationService_RemoveIdempotencies_Call{Call: _e.mock.On("RemoveIdempotencies", ctx, TTL)} } diff --git a/internal/store/model/alerts.go b/internal/store/model/alerts.go index c65cb465..4dd8539f 100644 --- a/internal/store/model/alerts.go +++ b/internal/store/model/alerts.go @@ -8,18 +8,17 @@ import ( ) type Alert struct { - ID uint64 `db:"id"` - NamespaceID sql.NullInt64 `db:"namespace_id"` - ProviderID uint64 `db:"provider_id"` - ResourceName string `db:"resource_name"` - MetricName string `db:"metric_name"` - MetricValue string `db:"metric_value"` - Severity string `db:"severity"` - Rule string `db:"rule"` - TriggeredAt time.Time `db:"triggered_at"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` - SilenceStatus sql.NullString `db:"silence_status"` + ID uint64 `db:"id"` + NamespaceID sql.NullInt64 `db:"namespace_id"` + ProviderID uint64 `db:"provider_id"` + ResourceName string `db:"resource_name"` + MetricName string `db:"metric_name"` + MetricValue string `db:"metric_value"` + Severity string `db:"severity"` + Rule string `db:"rule"` + TriggeredAt time.Time `db:"triggered_at"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` } func (a *Alert) FromDomain(alrt alert.Alert) { @@ -33,7 +32,6 @@ func (a *Alert) FromDomain(alrt alert.Alert) { a.TriggeredAt = alrt.TriggeredAt a.CreatedAt = alrt.CreatedAt a.UpdatedAt = alrt.UpdatedAt - a.SilenceStatus = sql.NullString{Valid: true, String: alrt.SilenceStatus} if alrt.NamespaceID == 0 { a.NamespaceID = sql.NullInt64{ @@ -49,17 +47,16 @@ func (a *Alert) FromDomain(alrt alert.Alert) { func (a *Alert) ToDomain() *alert.Alert { alrt := &alert.Alert{ - ID: a.ID, - ProviderID: a.ProviderID, - ResourceName: a.ResourceName, - MetricName: a.MetricName, - MetricValue: a.MetricValue, - Severity: a.Severity, - Rule: a.Rule, - TriggeredAt: a.TriggeredAt, - CreatedAt: a.CreatedAt, - UpdatedAt: a.UpdatedAt, - SilenceStatus: a.SilenceStatus.String, + ID: a.ID, + ProviderID: a.ProviderID, + ResourceName: a.ResourceName, + MetricName: a.MetricName, + MetricValue: a.MetricValue, + Severity: a.Severity, + Rule: a.Rule, + TriggeredAt: a.TriggeredAt, + CreatedAt: a.CreatedAt, + UpdatedAt: a.UpdatedAt, } if a.NamespaceID.Valid { diff --git a/internal/store/model/idempotency.go b/internal/store/model/idempotency.go index a683b30d..ff3c8dc4 100644 --- a/internal/store/model/idempotency.go +++ b/internal/store/model/idempotency.go @@ -3,7 +3,7 @@ package model import ( "time" - "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/core/idempotency" ) type Idempotency struct { @@ -15,8 +15,8 @@ type Idempotency struct { UpdatedAt time.Time `db:"updated_at"` } -func (i *Idempotency) ToDomain() *notification.Idempotency { - return ¬ification.Idempotency{ +func (i *Idempotency) ToDomain() *idempotency.Idempotency { + return &idempotency.Idempotency{ ID: i.ID, Scope: i.Scope, Key: i.Key, diff --git a/internal/store/model/log.go b/internal/store/model/log.go deleted file mode 100644 index 9ecad2cc..00000000 --- a/internal/store/model/log.go +++ /dev/null @@ -1,56 +0,0 @@ -package model - -import ( - "database/sql" - "time" - - "github.com/lib/pq" - "github.com/odpf/siren/core/log" -) - -type NotificationLog struct { - ID string `db:"id"` - NamespaceID sql.NullInt64 `db:"namespace_id"` - NotificationID string `db:"notification_id"` - SubscriptionID uint64 `db:"subscription_id"` - ReceiverID sql.NullInt64 `db:"receiver_id"` - AlertIDs pq.Int64Array `db:"alert_ids"` - SilenceIDs pq.StringArray `db:"silence_ids"` - CreatedAt time.Time `db:"created_at"` -} - -func (ns *NotificationLog) FromDomain(d log.Notification) { - ns.ID = d.ID - - if d.NamespaceID == 0 { - ns.NamespaceID = sql.NullInt64{Valid: false} - } else { - ns.NamespaceID = sql.NullInt64{Valid: true, Int64: int64(d.NamespaceID)} - } - - ns.NotificationID = d.NotificationID - ns.SubscriptionID = d.SubscriptionID - ns.AlertIDs = pq.Int64Array(d.AlertIDs) - ns.SilenceIDs = pq.StringArray(d.SilenceIDs) - - if d.ReceiverID == 0 { - ns.ReceiverID = sql.NullInt64{Valid: false} - } else { - ns.ReceiverID = sql.NullInt64{Valid: true, Int64: int64(d.ReceiverID)} - } - - ns.CreatedAt = d.CreatedAt -} - -func (ns *NotificationLog) ToDomain() log.Notification { - return log.Notification{ - ID: ns.ID, - NamespaceID: uint64(ns.NamespaceID.Int64), - NotificationID: ns.NotificationID, - SubscriptionID: ns.SubscriptionID, - ReceiverID: uint64(ns.ReceiverID.Int64), - AlertIDs: ns.AlertIDs, - SilenceIDs: ns.SilenceIDs, - CreatedAt: ns.CreatedAt, - } -} diff --git a/internal/store/model/notification.go b/internal/store/model/notification.go deleted file mode 100644 index 12a00920..00000000 --- a/internal/store/model/notification.go +++ /dev/null @@ -1,55 +0,0 @@ -package model - -import ( - "database/sql" - "time" - - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/pkg/pgc" -) - -type Notification struct { - ID string `db:"id"` - NamespaceID sql.NullInt64 `db:"namespace_id"` - Type string `db:"type"` - Data pgc.StringInterfaceMap `db:"data"` - Labels pgc.StringStringMap `db:"labels"` - ValidDuration pgc.TimeDuration `db:"valid_duration"` - Template sql.NullString `db:"template"` - CreatedAt time.Time `db:"created_at"` -} - -func (n *Notification) FromDomain(d notification.Notification) { - n.ID = d.ID - n.Type = d.Type - n.Data = d.Data - n.Labels = d.Labels - n.ValidDuration = pgc.TimeDuration(d.ValidDuration) - - if d.NamespaceID == 0 { - n.NamespaceID = sql.NullInt64{Valid: false} - } else { - n.NamespaceID = sql.NullInt64{Int64: int64(d.NamespaceID), Valid: true} - } - - if d.Template == "" { - n.Template = sql.NullString{Valid: false} - } else { - n.Template = sql.NullString{String: d.Template, Valid: true} - } - - n.CreatedAt = d.CreatedAt -} - -func (n *Notification) ToDomain() notification.Notification { - return notification.Notification{ - ID: n.ID, - NamespaceID: uint64(n.NamespaceID.Int64), - Type: n.Type, - Data: n.Data, - Labels: n.Labels, - ValidDuration: time.Duration(n.ValidDuration), - Template: n.Template.String, - CreatedAt: n.CreatedAt, - } -} diff --git a/internal/store/model/silence.go b/internal/store/model/silence.go deleted file mode 100644 index 50e48c53..00000000 --- a/internal/store/model/silence.go +++ /dev/null @@ -1,67 +0,0 @@ -package model - -import ( - "database/sql" - "time" - - "github.com/odpf/siren/core/silence" - "github.com/odpf/siren/pkg/pgc" -) - -type Silence struct { - ID string `db:"id"` - NamespaceID uint64 `db:"namespace_id"` - Type string `db:"type"` - TargetID sql.NullInt64 `db:"target_id"` - TargetExpression pgc.StringInterfaceMap `db:"target_expression"` - Creator sql.NullString `db:"creator"` - Comment sql.NullString `db:"comment"` - CreatedAt time.Time `db:"created_at"` - DeletedAt sql.NullTime `db:"deleted_at"` -} - -func (s *Silence) FromDomain(sil silence.Silence) { - s.ID = sil.ID - s.NamespaceID = sil.NamespaceID - s.Type = sil.Type - - if sil.TargetID == 0 { - s.TargetID = sql.NullInt64{Valid: false} - } else { - s.TargetID = sql.NullInt64{Int64: int64(sil.TargetID), Valid: true} - } - - s.TargetExpression = pgc.StringInterfaceMap(sil.TargetExpression) - - if sil.Creator == "" { - s.Creator = sql.NullString{Valid: false} - } else { - s.Creator = sql.NullString{String: sil.Creator, Valid: true} - } - - if sil.Comment == "" { - s.Comment = sql.NullString{Valid: false} - } else { - s.Comment = sql.NullString{String: sil.Comment, Valid: true} - } - - s.CreatedAt = sil.CreatedAt - - if sil.DeletedAt.IsZero() { - s.DeletedAt = sql.NullTime{Valid: false} - } else { - s.DeletedAt = sql.NullTime{Time: sil.DeletedAt, Valid: true} - } -} - -func (s *Silence) ToDomain() *silence.Silence { - return &silence.Silence{ - ID: s.ID, - NamespaceID: s.NamespaceID, - Type: s.Type, - TargetID: uint64(s.TargetID.Int64), - TargetExpression: s.TargetExpression, - CreatedAt: s.CreatedAt, - DeletedAt: s.DeletedAt.Time, - } -} diff --git a/internal/store/postgres/alerts.go b/internal/store/postgres/alerts.go index 3d2d8421..6b04b524 100644 --- a/internal/store/postgres/alerts.go +++ b/internal/store/postgres/alerts.go @@ -5,7 +5,6 @@ import ( "time" sq "github.com/Masterminds/squirrel" - "github.com/lib/pq" "github.com/odpf/siren/core/alert" "github.com/odpf/siren/internal/store/model" "github.com/odpf/siren/pkg/errors" @@ -18,9 +17,6 @@ INSERT INTO alerts (provider_id, namespace_id, resource_name, metric_name, metri RETURNING * ` -const alertUpdateBulkSilenceQuery = ` -UPDATE alerts SET silence_status = $1, updated_at = now() WHERE id = any($2)` - var alertListQueryBuilder = sq.Select( "id", "provider_id", @@ -32,7 +28,6 @@ var alertListQueryBuilder = sq.Select( "triggered_at", "created_at", "updated_at", - "silence_status", ).From("alerts") // AlertRepository talks to the store to read or insert data @@ -73,11 +68,6 @@ func (r AlertRepository) Create(ctx context.Context, alrt alert.Alert) (alert.Al func (r AlertRepository) List(ctx context.Context, flt alert.Filter) ([]alert.Alert, error) { var queryBuilder = alertListQueryBuilder - - if len(flt.IDs) != 0 { - queryBuilder = queryBuilder.Where("id = any(?)", pq.Array(flt.IDs)) - } - if flt.NamespaceID != 0 { queryBuilder = queryBuilder.Where("namespace_id = ?", flt.NamespaceID) } @@ -116,15 +106,3 @@ func (r AlertRepository) List(ctx context.Context, flt alert.Filter) ([]alert.Al return alertsDomain, nil } - -func (r AlertRepository) BulkUpdateSilence(ctx context.Context, alertIDs []int64, silenceStatus string) error { - sqlAlertIDs := pq.Array(alertIDs) - if _, err := r.client.ExecContext(ctx, pgc.OpUpdate, r.tableName, alertUpdateBulkSilenceQuery, - silenceStatus, - sqlAlertIDs, - ); err != nil { - return err - } - - return nil -} diff --git a/internal/store/postgres/alerts_test.go b/internal/store/postgres/alerts_test.go index 97022bae..e86cc04c 100644 --- a/internal/store/postgres/alerts_test.go +++ b/internal/store/postgres/alerts_test.go @@ -34,7 +34,6 @@ func (s *AlertsRepositoryTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) @@ -56,7 +55,6 @@ func (s *AlertsRepositoryTestSuite) SetupSuite() { s.ctx = context.TODO() s.Require().NoError(migrate(s.ctx, logger, s.client, dbConfig)) - s.repository = postgres.NewAlertRepository(s.client) _, err = bootstrapProvider(s.client) @@ -66,8 +64,8 @@ func (s *AlertsRepositoryTestSuite) SetupSuite() { } func (s *AlertsRepositoryTestSuite) SetupTest() { - _, err := bootstrapAlert(s.client) - if err != nil { + var err error + if err = bootstrapAlert(s.client); err != nil { s.T().Fatal(err) } } @@ -204,76 +202,6 @@ func (s *AlertsRepositoryTestSuite) TestCreate() { } } -func (s *AlertsRepositoryTestSuite) TestBulkUpdateSilence() { - type testCase struct { - Description string - SilenceStatus string - ExpectedAlerts []alert.Alert - ErrString string - } - - var testCases = []testCase{ - { - Description: "should update 2 alerts to silence", - SilenceStatus: alert.SilenceStatusTotal, - ExpectedAlerts: []alert.Alert{ - { - ID: 2, - ProviderID: 1, - ResourceName: "odpf-kafka-2", - MetricName: "cpu_usage_user", - MetricValue: "97.95", - Severity: "WARNING", - Rule: "cpu-usage", - SilenceStatus: alert.SilenceStatusTotal, - }, - { - ID: 3, - ProviderID: 1, - ResourceName: "odpf-kafka-1", - MetricName: "cpu_usage_user", - MetricValue: "98.30", - Severity: "CRITICAL", - Rule: "cpu-usage", - SilenceStatus: alert.SilenceStatusTotal, - }, - }, - }, - { - Description: "should return error foreign key if provider id does not exist", - ErrString: "err", - }, - } - - for _, tc := range testCases { - s.Run(tc.Description, func() { - err := s.repository.BulkUpdateSilence(s.ctx, []int64{2, 3}, tc.SilenceStatus) - if err != nil { - if err.Error() != tc.ErrString { - s.T().Fatalf("got error %s, expected was %s", err.Error(), tc.ErrString) - } - } - if len(tc.ExpectedAlerts) != 0 { - alerts, err := s.repository.List(s.ctx, alert.Filter{ - SilenceID: "", - }) - s.Assert().NoError(err) - - var silencedAlerts []alert.Alert - for _, al := range alerts { - if al.SilenceStatus != "" { - silencedAlerts = append(silencedAlerts, al) - } - } - - if diff := cmp.Diff(silencedAlerts, tc.ExpectedAlerts, cmpopts.IgnoreFields(alert.Alert{}, "TriggeredAt", "CreatedAt", "UpdatedAt")); diff != "" { - s.T().Fatalf("got diff %v", diff) - } - } - }) - } -} - func TestAlertsRepository(t *testing.T) { suite.Run(t, new(AlertsRepositoryTestSuite)) } diff --git a/internal/store/postgres/bootstrap_test.go b/internal/store/postgres/bootstrap_test.go index 5661f8d4..cd0a3e84 100644 --- a/internal/store/postgres/bootstrap_test.go +++ b/internal/store/postgres/bootstrap_test.go @@ -7,15 +7,12 @@ import ( "os" "github.com/odpf/salt/db" - saltlog "github.com/odpf/salt/log" + "github.com/odpf/salt/log" "github.com/odpf/siren/core/alert" - "github.com/odpf/siren/core/log" "github.com/odpf/siren/core/namespace" - "github.com/odpf/siren/core/notification" "github.com/odpf/siren/core/provider" "github.com/odpf/siren/core/receiver" "github.com/odpf/siren/core/rule" - "github.com/odpf/siren/core/silence" "github.com/odpf/siren/core/subscription" "github.com/odpf/siren/core/template" "github.com/odpf/siren/internal/store/postgres" @@ -46,7 +43,7 @@ func purgeDocker(pool *dockertest.Pool, resource *dockertest.Resource) error { return nil } -func migrate(ctx context.Context, logger saltlog.Logger, client *pgc.Client, dbConf db.Config) error { +func migrate(ctx context.Context, logger log.Logger, client *pgc.Client, dbConf db.Config) error { var queries = []string{ "DROP SCHEMA public CASCADE", "CREATE SCHEMA public", @@ -159,30 +156,28 @@ func bootstrapReceiver(client *pgc.Client) ([]receiver.Receiver, error) { return insertedData, nil } -func bootstrapAlert(client *pgc.Client) ([]alert.Alert, error) { +func bootstrapAlert(client *pgc.Client) error { filePath := "./testdata/mock-alert.json" testFixtureJSON, err := os.ReadFile(filePath) if err != nil { - return nil, err + return err } var data []alert.Alert if err = json.Unmarshal(testFixtureJSON, &data); err != nil { - return nil, err + return err } repo := postgres.NewAlertRepository(client) - var createdAlerts []alert.Alert for _, d := range data { - alrt, err := repo.Create(context.Background(), d) + _, err := repo.Create(context.Background(), d) if err != nil { - return nil, err + return err } - createdAlerts = append(createdAlerts, alrt) } - return createdAlerts, nil + return nil } func bootstrapTemplate(client *pgc.Client) ([]template.Template, error) { @@ -265,107 +260,3 @@ func bootstrapSubscription(client *pgc.Client) ([]subscription.Subscription, err return insertedData, nil } - -func bootstrapNotification(client *pgc.Client) ([]notification.Notification, error) { - filePath := "./testdata/mock-notification.json" - testFixtureJSON, err := os.ReadFile(filePath) - if err != nil { - return nil, err - } - - var data []notification.Notification - if err = json.Unmarshal(testFixtureJSON, &data); err != nil { - return nil, err - } - - repo := postgres.NewNotificationRepository(client) - - var insertedData []notification.Notification - for _, d := range data { - newD, err := repo.Create(context.Background(), d) - if err != nil { - return nil, err - } - - insertedData = append(insertedData, newD) - } - - return insertedData, nil -} - -func bootstrapSilence(client *pgc.Client) ([]string, error) { - filePath := "./testdata/mock-silence.json" - testFixtureJSON, err := os.ReadFile(filePath) - if err != nil { - return nil, err - } - - var data []silence.Silence - if err = json.Unmarshal(testFixtureJSON, &data); err != nil { - return nil, err - } - - repo := postgres.NewSilenceRepository(client) - - var silenceIDs []string - for _, d := range data { - id, err := repo.Create(context.Background(), d) - if err != nil { - return nil, err - } - - silenceIDs = append(silenceIDs, id) - } - - return silenceIDs, nil -} - -func bootstrapNotificationLog( - client *pgc.Client, - namespaces []namespace.EncryptedNamespace, - subscriptions []subscription.Subscription, - receivers []receiver.Receiver, - silenceIDs []string, - notifications []notification.Notification, - alerts []alert.Alert, -) error { - var data = []log.Notification{ - { - NamespaceID: namespaces[0].ID, - NotificationID: notifications[0].ID, - SubscriptionID: subscriptions[0].ID, - ReceiverID: receivers[0].ID, - AlertIDs: []int64{int64(alerts[0].ID)}, - SilenceIDs: []string{silenceIDs[0], silenceIDs[1]}, - }, - { - NamespaceID: namespaces[1].ID, - NotificationID: notifications[1].ID, - SubscriptionID: subscriptions[1].ID, - ReceiverID: receivers[1].ID, - AlertIDs: []int64{int64(alerts[1].ID)}, - SilenceIDs: []string{silenceIDs[1]}, - }, - { - NamespaceID: namespaces[2].ID, - NotificationID: notifications[1].ID, - SubscriptionID: subscriptions[2].ID, - AlertIDs: []int64{int64(alerts[0].ID), int64(alerts[2].ID)}, - SilenceIDs: []string{silenceIDs[0]}, - }, - { - NamespaceID: namespaces[2].ID, - NotificationID: notifications[1].ID, - SubscriptionID: subscriptions[2].ID, - AlertIDs: []int64{int64(alerts[0].ID), int64(alerts[2].ID)}, - }, - } - - repo := postgres.NewLogRepository(client) - - if err := repo.BulkCreate(context.Background(), data); err != nil { - return err - } - - return nil -} diff --git a/internal/store/postgres/idempotency.go b/internal/store/postgres/idempotency.go index 81826df1..68f248ac 100644 --- a/internal/store/postgres/idempotency.go +++ b/internal/store/postgres/idempotency.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/core/idempotency" "github.com/odpf/siren/internal/store/model" "github.com/odpf/siren/pkg/errors" "github.com/odpf/siren/pkg/pgc" @@ -36,7 +36,7 @@ func NewIdempotencyRepository(client *pgc.Client) *IdempotencyRepository { return &IdempotencyRepository{client, "idempotencies"} } -func (r *IdempotencyRepository) InsertOnConflictReturning(ctx context.Context, scope, key string) (*notification.Idempotency, error) { +func (r *IdempotencyRepository) InsertOnConflictReturning(ctx context.Context, scope, key string) (*idempotency.Idempotency, error) { var idempotencyModel model.Idempotency if err := r.client.QueryRowxContext(ctx, pgc.OpInsert, r.tableName, idempotencyInsertQuery, scope, key, @@ -67,7 +67,7 @@ func (r *IdempotencyRepository) UpdateSuccess(ctx context.Context, id uint64, su return nil } -func (r *IdempotencyRepository) Delete(ctx context.Context, filter notification.IdempotencyFilter) error { +func (r *IdempotencyRepository) Delete(ctx context.Context, filter idempotency.Filter) error { if filter.TTL == 0 { return errors.ErrInvalid.WithCausef("cannot delete with ttl 0") diff --git a/internal/store/postgres/idempotency_test.go b/internal/store/postgres/idempotency_test.go index 0a559a9c..45ef1fdf 100644 --- a/internal/store/postgres/idempotency_test.go +++ b/internal/store/postgres/idempotency_test.go @@ -13,7 +13,7 @@ import ( "github.com/ory/dockertest/v3" "github.com/stretchr/testify/suite" - "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/core/idempotency" "github.com/odpf/siren/internal/store/postgres" "github.com/odpf/siren/pkg/errors" "github.com/odpf/siren/pkg/pgc" @@ -36,7 +36,6 @@ func (s *IdempotencyRepositoryTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) @@ -81,7 +80,7 @@ func (s *IdempotencyRepositoryTestSuite) cleanup() error { } func (s *IdempotencyRepositoryTestSuite) TestInsertReturnOnConflict() { - data1 := ¬ification.Idempotency{ + data1 := &idempotency.Idempotency{ Scope: "a-scope", Key: "key-1", } @@ -97,14 +96,14 @@ func (s *IdempotencyRepositoryTestSuite) TestInsertReturnOnConflict() { res, err := s.repository.InsertOnConflictReturning(context.Background(), data1.Scope, data1.Key) s.Assert().NoError(err) - if diff := cmp.Diff(data1, res, cmpopts.IgnoreFields(notification.Idempotency{}, "UpdatedAt")); diff != "" { + if diff := cmp.Diff(data1, res, cmpopts.IgnoreFields(idempotency.Idempotency{}, "UpdatedAt")); diff != "" { s.T().Error(diff) } }) } func (s *IdempotencyRepositoryTestSuite) TestUpdateSuccess() { - data := ¬ification.Idempotency{ + data := &idempotency.Idempotency{ Scope: "a-scope", Key: "existing-key-1", } @@ -154,14 +153,14 @@ func (s *IdempotencyRepositoryTestSuite) TestDelete() { s.Require().Nil(err) s.Run("should return not found if no rows outside TTL", func() { - err := s.repository.Delete(s.ctx, notification.IdempotencyFilter{ + err := s.repository.Delete(s.ctx, idempotency.Filter{ TTL: time.Hour * time.Duration(10000), }) s.Assert().EqualError(err, errors.ErrNotFound.Error()) }) s.Run("should remove all idempotencies that are outside TTL", func() { - err := s.repository.Delete(s.ctx, notification.IdempotencyFilter{ + err := s.repository.Delete(s.ctx, idempotency.Filter{ TTL: time.Second * time.Duration(60), }) s.Assert().Nil(err) diff --git a/internal/store/postgres/log.go b/internal/store/postgres/log.go deleted file mode 100644 index 269311bf..00000000 --- a/internal/store/postgres/log.go +++ /dev/null @@ -1,156 +0,0 @@ -package postgres - -import ( - "context" - "fmt" - - "github.com/odpf/siren/core/log" - "github.com/odpf/siren/internal/store/model" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/pkg/pgc" -) - -const notificationLogTableName = "notification_log" - -const notificationLogInsertNamedQuery = ` -INSERT INTO notification_log - (namespace_id, notification_id, subscription_id, alert_ids, receiver_id, silence_ids, created_at) - VALUES (:namespace_id, :notification_id, :subscription_id, :alert_ids, :receiver_id, :silence_ids, now()) -` - -// LogRepository talks to the store to read or insert data -type LogRepository struct { - client *pgc.Client -} - -// NewLogRepository returns LogRepository struct -func NewLogRepository(client *pgc.Client) *LogRepository { - return &LogRepository{ - client: client, - } -} - -func (r *LogRepository) ListAlertIDsBySilenceID(ctx context.Context, silenceID string) ([]int64, error) { - rows, err := r.client.QueryxContext(ctx, pgc.OpSelectAll, notificationLogTableName, fmt.Sprintf(` - SELECT distinct unnest(alert_ids) AS alert_ids FROM %s WHERE silence_ids @> '{%s}' - `, notificationLogTableName, silenceID)) - if err != nil { - return nil, err - } - defer rows.Close() - - var alertIDs []int64 - for rows.Next() { - var alertID int64 - if err := rows.Scan(&alertID); err != nil { - return nil, err - } - - alertIDs = append(alertIDs, alertID) - } - - return alertIDs, nil -} - -func (r *LogRepository) ListSubscriptionIDsBySilenceID(ctx context.Context, silenceID string) ([]int64, error) { - rows, err := r.client.QueryxContext(ctx, pgc.OpSelectAll, notificationLogTableName, fmt.Sprintf(` - SELECT distinct subscription_id FROM %s WHERE silence_ids @> '{%s}' - `, notificationLogTableName, silenceID)) - if err != nil { - return nil, err - } - defer rows.Close() - - var subscriptionIDs []int64 - for rows.Next() { - var subscriptionID int64 - if err := rows.Scan(&subscriptionID); err != nil { - return nil, err - } - - subscriptionIDs = append(subscriptionIDs, subscriptionID) - } - - return subscriptionIDs, nil -} - -func (r *LogRepository) BulkCreate(ctx context.Context, nss []log.Notification) error { - nssModel := []model.NotificationLog{} - for _, ns := range nss { - nsModel := new(model.NotificationLog) - nsModel.FromDomain(ns) - - nssModel = append(nssModel, *nsModel) - } - - res, err := r.client.NamedExecContext(ctx, pgc.OpInsert, notificationLogTableName, notificationLogInsertNamedQuery, nssModel) - if err != nil { - return err - } - rowsAffected, err := res.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return errors.New("no rows affected when inserting notification subscribers") - } - return nil -} - -// func (r *SubscriptionRepository) Get(ctx context.Context, id uint64) (*subscription.Subscription, error) { -// query, args, err := subscriptionListQueryBuilder.Where("id = ?", id).PlaceholderFormat(sq.Dollar).ToSql() -// if err != nil { -// return nil, err -// } - -// var subscriptionModel model.Subscription -// if err := r.client.QueryRowxContext(ctx, pgc.OpSelect, r.tableName, query, args...).StructScan(&subscriptionModel); err != nil { -// if errors.Is(err, sql.ErrNoRows) { -// return nil, subscription.NotFoundError{ID: id} -// } -// return nil, err -// } - -// return subscriptionModel.ToDomain(), nil -// } - -// func (r *SubscriptionRepository) Update(ctx context.Context, sub *subscription.Subscription) error { -// if sub == nil { -// return errors.New("subscription domain is nil") -// } - -// subscriptionModel := new(model.Subscription) -// subscriptionModel.FromDomain(*sub) - -// var newSubscriptionModel model.Subscription -// if err := r.client.QueryRowxContext(ctx, pgc.OpUpdate, r.tableName, subscriptionUpdateQuery, -// subscriptionModel.ID, -// subscriptionModel.NamespaceID, -// subscriptionModel.URN, -// subscriptionModel.Receiver, -// subscriptionModel.Match, -// ).StructScan(&newSubscriptionModel); err != nil { -// err := pgc.CheckError(err) -// if errors.Is(err, sql.ErrNoRows) { -// return subscription.NotFoundError{ID: subscriptionModel.ID} -// } -// if errors.Is(err, pgc.ErrDuplicateKey) { -// return subscription.ErrDuplicate -// } -// if errors.Is(err, pgc.ErrForeignKeyViolation) { -// return subscription.ErrRelation -// } -// return err -// } - -// *sub = *newSubscriptionModel.ToDomain() - -// return nil -// } - -// func (r *SubscriptionRepository) Delete(ctx context.Context, id uint64) error { -// if _, err := r.client.ExecContext(ctx, pgc.OpDelete, r.tableName, subscriptionDeleteQuery, id); err != nil { -// return err -// } -// return nil -// } diff --git a/internal/store/postgres/log_test.go b/internal/store/postgres/log_test.go deleted file mode 100644 index 72e0a1ca..00000000 --- a/internal/store/postgres/log_test.go +++ /dev/null @@ -1,254 +0,0 @@ -package postgres_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/odpf/salt/db" - "github.com/odpf/salt/dockertestx" - saltlog "github.com/odpf/salt/log" - "github.com/odpf/siren/core/alert" - "github.com/odpf/siren/core/log" - "github.com/odpf/siren/core/namespace" - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/receiver" - "github.com/odpf/siren/core/subscription" - "github.com/odpf/siren/internal/store/postgres" - "github.com/odpf/siren/pkg/pgc" - "github.com/ory/dockertest/v3" - "github.com/stretchr/testify/suite" -) - -type LogRepositoryTestSuite struct { - suite.Suite - ctx context.Context - client *pgc.Client - pool *dockertest.Pool - resource *dockertest.Resource - repository *postgres.LogRepository - - namespaces []namespace.EncryptedNamespace - subscriptions []subscription.Subscription - receivers []receiver.Receiver - silencesIDs []string - notifications []notification.Notification - alerts []alert.Alert -} - -func (s *LogRepositoryTestSuite) SetupSuite() { - var err error - - logger := saltlog.NewZap() - dpg, err := dockertestx.CreatePostgres( - dockertestx.PostgresWithDetail( - pgUser, pgPass, pgDBName, - ), - dockertestx.PostgresWithVersionTag("13"), - ) - if err != nil { - s.T().Fatal(err) - } - - s.pool = dpg.GetPool() - s.resource = dpg.GetResource() - - dbConfig.URL = dpg.GetExternalConnString() - dbc, err := db.New(dbConfig) - if err != nil { - s.T().Fatal(err) - } - - s.client, err = pgc.NewClient(logger, dbc) - if err != nil { - s.T().Fatal(err) - } - - s.ctx = context.TODO() - s.Require().NoError(migrate(s.ctx, logger, s.client, dbConfig)) - s.repository = postgres.NewLogRepository(s.client) - - _, err = bootstrapProvider(s.client) - if err != nil { - s.T().Fatal(err) - } - s.namespaces, err = bootstrapNamespace(s.client) - if err != nil { - s.T().Fatal(err) - } - s.subscriptions, err = bootstrapSubscription(s.client) - if err != nil { - s.T().Fatal(err) - } - s.receivers, err = bootstrapReceiver(s.client) - if err != nil { - s.T().Fatal(err) - } - s.notifications, err = bootstrapNotification(s.client) - if err != nil { - s.T().Fatal(err) - } - s.alerts, err = bootstrapAlert(s.client) - if err != nil { - s.T().Fatal(err) - } - s.silencesIDs, err = bootstrapSilence(s.client) - if err != nil { - s.T().Fatal(err) - } -} - -func (s *LogRepositoryTestSuite) SetupTest() { - if err := bootstrapNotificationLog( - s.client, - s.namespaces, - s.subscriptions, - s.receivers, - s.silencesIDs, - s.notifications, - s.alerts, - ); err != nil { - s.T().Fatal(err) - } -} - -func (s *LogRepositoryTestSuite) TearDownSuite() { - // Clean tests - if err := purgeDocker(s.pool, s.resource); err != nil { - s.T().Fatal(err) - } -} - -func (s *LogRepositoryTestSuite) TearDownTest() { - if err := s.cleanup(); err != nil { - s.T().Fatal(err) - } -} - -func (s *LogRepositoryTestSuite) cleanup() error { - queries := []string{ - "TRUNCATE TABLE notification_log RESTART IDENTITY CASCADE", - } - return execQueries(context.TODO(), s.client, queries) -} - -func (s *LogRepositoryTestSuite) TestCreate() { - type testCase struct { - Description string - ItemsToCreate []log.Notification - ErrString string - } - - var testCases = []testCase{ - { - Description: "should create a notification_log", - ItemsToCreate: []log.Notification{ - { - NamespaceID: 1, - NotificationID: s.notifications[0].ID, - SubscriptionID: 1, - }, - }, - }, - { - Description: "should return error if a notification_log is invalid", - ItemsToCreate: []log.Notification{{ - NamespaceID: 1111, - NotificationID: "nid", - SubscriptionID: 1111, - AlertIDs: []int64{11}, - }}, - ErrString: "pq: insert or update on table \"notification_log\" violates foreign key constraint \"notification_log_notification_id_fkey\"", - }, - } - - for _, tc := range testCases { - s.Run(tc.Description, func() { - err := s.repository.BulkCreate(s.ctx, tc.ItemsToCreate) - if err != nil { - if err.Error() != tc.ErrString { - s.T().Fatalf("got error %s, expected was %s", err.Error(), tc.ErrString) - } - } - }) - } -} - -func (s *LogRepositoryTestSuite) TestListAlertIDsBySilenceID() { - tests := []struct { - name string - silenceID string - want []int64 - wantErrString string - }{ - { - name: "should return list of alert id if silence id exist", - silenceID: s.silencesIDs[0], - want: []int64{ - int64(s.alerts[0].ID), - int64(s.alerts[2].ID), - }, - }, - { - name: "should return nil if silence id does not exist", - silenceID: "abc", - }, - } - for _, tt := range tests { - s.Run(tt.name, func() { - r := postgres.NewLogRepository(s.client) - got, err := r.ListAlertIDsBySilenceID(context.TODO(), tt.silenceID) - if err != nil { - s.Assert().Equal(tt.wantErrString, err.Error()) - return - } - if diff := cmp.Diff(got, tt.want, cmpopts.SortSlices(func(x int64, y int64) bool { - return x < y - })); diff != "" { - s.T().Errorf("LogRepository.ListAlertIDsBySilenceID() diff = %v", diff) - } - }) - } -} - -func (s *LogRepositoryTestSuite) TestListSubscriptionIDsBySilenceID() { - tests := []struct { - name string - silenceID string - want []int64 - wantErrString string - }{ - { - name: "should return list of subscription id if silence id exist", - silenceID: s.silencesIDs[0], - want: []int64{ - int64(s.subscriptions[0].ID), - int64(s.subscriptions[2].ID), - }, - }, - { - name: "should return nil if silence id does not exist", - silenceID: "abc", - }, - } - for _, tt := range tests { - s.Run(tt.name, func() { - r := postgres.NewLogRepository(s.client) - got, err := r.ListSubscriptionIDsBySilenceID(context.TODO(), tt.silenceID) - if err != nil { - s.Assert().Equal(tt.wantErrString, err.Error()) - return - } - if diff := cmp.Diff(got, tt.want, cmpopts.SortSlices(func(x int64, y int64) bool { - return x < y - })); diff != "" { - s.T().Errorf("LogRepository.ListSubscriptionIDsBySilenceID() diff = %v", diff) - } - }) - } -} - -func TestLogRepository(t *testing.T) { - suite.Run(t, new(LogRepositoryTestSuite)) -} diff --git a/internal/store/postgres/migrations/000002_create_index_subscription_matcher.up.sql b/internal/store/postgres/migrations/000002_create_index_subscription_matcher.up.sql index 1c7ba8d6..b8897bdd 100644 --- a/internal/store/postgres/migrations/000002_create_index_subscription_matcher.up.sql +++ b/internal/store/postgres/migrations/000002_create_index_subscription_matcher.up.sql @@ -1 +1 @@ -CREATE INDEX IF NOT EXISTS subscriptions_idx_match ON subscriptions USING GIN(match jsonb_path_ops); \ No newline at end of file +CREATE INDEX IF NOT EXISTS subscriptions_idx_match ON subscriptions USING GIN(match jsonb_path_ops); \ No newline at end of file diff --git a/internal/store/postgres/migrations/000004_create_index_subscription_namespace.up.sql b/internal/store/postgres/migrations/000004_create_index_subscription_namespace.up.sql index eb09d05a..21bf8b6a 100644 --- a/internal/store/postgres/migrations/000004_create_index_subscription_namespace.up.sql +++ b/internal/store/postgres/migrations/000004_create_index_subscription_namespace.up.sql @@ -1 +1 @@ -CREATE INDEX IF NOT EXISTS subscriptions_idx_namespace ON subscriptions(namespace_id); +CREATE INDEX IF NOT EXISTS subscriptions_idx_namespace ON subscriptions(namespace_id); \ No newline at end of file diff --git a/internal/store/postgres/migrations/000006_create_notifications.down.sql b/internal/store/postgres/migrations/000006_create_notifications.down.sql deleted file mode 100644 index 6df35ec6..00000000 --- a/internal/store/postgres/migrations/000006_create_notifications.down.sql +++ /dev/null @@ -1,4 +0,0 @@ -DROP INDEX IF EXISTS notifications_idx_labels; -DROP INDEX IF EXISTS notifications_idx_template; - -DROP TABLE IF EXISTS notifications; \ No newline at end of file diff --git a/internal/store/postgres/migrations/000006_create_notifications.up.sql b/internal/store/postgres/migrations/000006_create_notifications.up.sql deleted file mode 100644 index 84025457..00000000 --- a/internal/store/postgres/migrations/000006_create_notifications.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE IF NOT EXISTS notifications ( - id text PRIMARY KEY DEFAULT gen_random_uuid(), - namespace_id bigint, - type text, - data jsonb, - labels jsonb, - valid_duration text, - template text, - created_at timestamptz NOT NULL -); - -CREATE INDEX IF NOT EXISTS notifications_idx_labels ON notifications USING GIN(labels jsonb_path_ops); -CREATE INDEX IF NOT EXISTS notifications_idx_template ON notifications (template); \ No newline at end of file diff --git a/internal/store/postgres/migrations/000007_create_silences.down.sql b/internal/store/postgres/migrations/000007_create_silences.down.sql deleted file mode 100644 index 8cc484c5..00000000 --- a/internal/store/postgres/migrations/000007_create_silences.down.sql +++ /dev/null @@ -1,4 +0,0 @@ -DROP INDEX IF EXISTS silences_idx_namespace_id_type_target_id; -DROP INDEX IF EXISTS silences_idx_namespace_id_type_target_expression; - -DROP TABLE IF EXISTS subscriptions; \ No newline at end of file diff --git a/internal/store/postgres/migrations/000007_create_silences.up.sql b/internal/store/postgres/migrations/000007_create_silences.up.sql deleted file mode 100644 index f4dbdcd5..00000000 --- a/internal/store/postgres/migrations/000007_create_silences.up.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TABLE IF NOT EXISTS silences ( - id text PRIMARY KEY DEFAULT gen_random_uuid(), - namespace_id bigint REFERENCES namespaces(id), - type text, - target_id text, - target_expression jsonb, - creator text, - comment text, - created_at timestamptz NOT NULL, - deleted_at timestamptz -); - -CREATE INDEX IF NOT EXISTS silences_idx_namespace_id_type_target_id ON silences (namespace_id, type, target_id) WHERE type = 'subscription'; -CREATE INDEX IF NOT EXISTS silences_idx_namespace_id_type ON silences (namespace_id, type); -CREATE INDEX IF NOT EXISTS silences_idx_target_expression ON silences USING GIN(target_expression jsonb_path_ops) WHERE type = 'labels'; diff --git a/internal/store/postgres/migrations/000008_create_notification_log.down.sql b/internal/store/postgres/migrations/000008_create_notification_log.down.sql deleted file mode 100644 index 3ebb9220..00000000 --- a/internal/store/postgres/migrations/000008_create_notification_log.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS notification_log; \ No newline at end of file diff --git a/internal/store/postgres/migrations/000008_create_notification_log.up.sql b/internal/store/postgres/migrations/000008_create_notification_log.up.sql deleted file mode 100644 index 4729ae21..00000000 --- a/internal/store/postgres/migrations/000008_create_notification_log.up.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE IF NOT EXISTS notification_log ( - id text PRIMARY KEY DEFAULT gen_random_uuid(), - namespace_id bigint, - notification_id text REFERENCES notifications(id), - subscription_id bigint, - receiver_id bigint, - alert_ids bigint[], - silence_ids text[], - created_at timestamptz NOT NULL -); - -CREATE INDEX IF NOT EXISTS notification_log_idx_silence_ids ON notification_log USING GIN(silence_ids); \ No newline at end of file diff --git a/internal/store/postgres/migrations/000009_add_alert_silence_status_column.down.sql b/internal/store/postgres/migrations/000009_add_alert_silence_status_column.down.sql deleted file mode 100644 index 15bcacb0..00000000 --- a/internal/store/postgres/migrations/000009_add_alert_silence_status_column.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE - alerts -DROP COLUMN IF EXISTS silence_status; \ No newline at end of file diff --git a/internal/store/postgres/migrations/000009_add_alert_silence_status_column.up.sql b/internal/store/postgres/migrations/000009_add_alert_silence_status_column.up.sql deleted file mode 100644 index e679e280..00000000 --- a/internal/store/postgres/migrations/000009_add_alert_silence_status_column.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE - alerts -ADD COLUMN IF NOT EXISTS silence_status text; \ No newline at end of file diff --git a/internal/store/postgres/namespace_test.go b/internal/store/postgres/namespace_test.go index aac920ec..904402e9 100644 --- a/internal/store/postgres/namespace_test.go +++ b/internal/store/postgres/namespace_test.go @@ -34,7 +34,6 @@ func (s *NamespaceRepositoryTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) diff --git a/internal/store/postgres/notification.go b/internal/store/postgres/notification.go deleted file mode 100644 index 11eaea68..00000000 --- a/internal/store/postgres/notification.go +++ /dev/null @@ -1,48 +0,0 @@ -package postgres - -import ( - "context" - - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/internal/store/model" - "github.com/odpf/siren/pkg/pgc" -) - -const notificationInsertQuery = ` -INSERT INTO notifications (namespace_id, type, data, labels, valid_duration, template, created_at) - VALUES ($1, $2, $3, $4, $5, $6, now()) -RETURNING * -` - -// NotificationRepository talks to the store to read or insert data -type NotificationRepository struct { - client *pgc.Client - tableName string -} - -// NewNotificationRepository returns NotificationRepository struct -func NewNotificationRepository(client *pgc.Client) *NotificationRepository { - return &NotificationRepository{ - client: client, - tableName: "notifications", - } -} - -func (r *NotificationRepository) Create(ctx context.Context, n notification.Notification) (notification.Notification, error) { - nModel := new(model.Notification) - nModel.FromDomain(n) - - var newNModel model.Notification - if err := r.client.QueryRowxContext(ctx, pgc.OpInsert, r.tableName, notificationInsertQuery, - nModel.NamespaceID, - nModel.Type, - nModel.Data, - nModel.Labels, - nModel.ValidDuration, - nModel.Template, - ).StructScan(&newNModel); err != nil { - return notification.Notification{}, err - } - - return newNModel.ToDomain(), nil -} diff --git a/internal/store/postgres/notification_test.go b/internal/store/postgres/notification_test.go deleted file mode 100644 index 1e3db292..00000000 --- a/internal/store/postgres/notification_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package postgres_test - -import ( - "context" - "testing" - "time" - - "github.com/odpf/salt/db" - "github.com/odpf/salt/dockertestx" - "github.com/odpf/salt/log" - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/internal/store/postgres" - "github.com/odpf/siren/pkg/pgc" - "github.com/ory/dockertest/v3" - "github.com/stretchr/testify/suite" -) - -type NotificationRepositoryTestSuite struct { - suite.Suite - ctx context.Context - client *pgc.Client - pool *dockertest.Pool - resource *dockertest.Resource - repository *postgres.NotificationRepository -} - -func (s *NotificationRepositoryTestSuite) SetupSuite() { - var err error - - logger := log.NewZap() - dpg, err := dockertestx.CreatePostgres( - dockertestx.PostgresWithDetail( - pgUser, pgPass, pgDBName, - ), - dockertestx.PostgresWithVersionTag("13"), - ) - if err != nil { - s.T().Fatal(err) - } - - s.pool = dpg.GetPool() - s.resource = dpg.GetResource() - - dbConfig.URL = dpg.GetExternalConnString() - dbc, err := db.New(dbConfig) - if err != nil { - s.T().Fatal(err) - } - - s.client, err = pgc.NewClient(logger, dbc) - if err != nil { - s.T().Fatal(err) - } - - s.ctx = context.TODO() - s.Require().NoError(migrate(s.ctx, logger, s.client, dbConfig)) - s.repository = postgres.NewNotificationRepository(s.client) -} - -func (s *NotificationRepositoryTestSuite) TearDownSuite() { - // Clean tests - if err := purgeDocker(s.pool, s.resource); err != nil { - s.T().Fatal(err) - } -} - -func (s *NotificationRepositoryTestSuite) TearDownTest() { - if err := s.cleanup(); err != nil { - s.T().Fatal(err) - } -} - -func (s *NotificationRepositoryTestSuite) cleanup() error { - queries := []string{ - "TRUNCATE TABLE notifications RESTART IDENTITY CASCADE", - } - return execQueries(context.TODO(), s.client, queries) -} - -func (s *NotificationRepositoryTestSuite) TestCreate() { - type testCase struct { - Description string - NotificationToCreate notification.Notification - ErrString string - } - - var testCases = []testCase{ - { - Description: "should create a notification", - NotificationToCreate: notification.Notification{ - NamespaceID: 1, - Type: notification.TypeReceiver, - Data: map[string]interface{}{}, - Labels: map[string]string{}, - CreatedAt: time.Now(), - }, - }, - { - Description: "should return error if a notification is invalid", - NotificationToCreate: notification.Notification{ - NamespaceID: 1, - Type: notification.TypeReceiver, - Data: map[string]interface{}{ - "k1": func(x chan struct{}) { - <-x - }, - }, - Labels: map[string]string{}, - CreatedAt: time.Now(), - }, - ErrString: "sql: converting argument $3 type: json: unsupported type: func(chan struct {})", - }, - } - - for _, tc := range testCases { - s.Run(tc.Description, func() { - _, err := s.repository.Create(s.ctx, tc.NotificationToCreate) - if err != nil { - if err.Error() != tc.ErrString { - s.T().Fatalf("got error %s, expected was %s", err.Error(), tc.ErrString) - } - } - }) - } -} - -func TestNotificationRepository(t *testing.T) { - suite.Run(t, new(NotificationRepositoryTestSuite)) -} diff --git a/internal/store/postgres/provider_test.go b/internal/store/postgres/provider_test.go index 13838c7f..da6b5a75 100644 --- a/internal/store/postgres/provider_test.go +++ b/internal/store/postgres/provider_test.go @@ -33,7 +33,6 @@ func (s *ProviderRepositoryTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) diff --git a/internal/store/postgres/receiver_test.go b/internal/store/postgres/receiver_test.go index 03d58f19..01196f95 100644 --- a/internal/store/postgres/receiver_test.go +++ b/internal/store/postgres/receiver_test.go @@ -33,7 +33,6 @@ func (s *ReceiverRepositoryTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) diff --git a/internal/store/postgres/rule_test.go b/internal/store/postgres/rule_test.go index 5fef9e55..41d4eb02 100644 --- a/internal/store/postgres/rule_test.go +++ b/internal/store/postgres/rule_test.go @@ -33,7 +33,6 @@ func (s *RuleRepositoryTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) diff --git a/internal/store/postgres/silence.go b/internal/store/postgres/silence.go deleted file mode 100644 index 735bb48f..00000000 --- a/internal/store/postgres/silence.go +++ /dev/null @@ -1,148 +0,0 @@ -package postgres - -import ( - "context" - "encoding/json" - "fmt" - - sq "github.com/Masterminds/squirrel" - "github.com/odpf/siren/core/silence" - "github.com/odpf/siren/internal/store/model" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/pkg/pgc" -) - -const silenceInsertQuery = ` -INSERT INTO silences (namespace_id, type, target_id, target_expression, creator, comment, created_at) - VALUES ($1, $2, $3, $4, $5, $6, now()) -RETURNING * -` - -var silenceListQueryBuilder = sq.Select( - "id", - "namespace_id", - "type", - "target_id", - "target_expression", - "creator", - "comment", - "created_at", - "deleted_at", -).From("silences") - -const silenceSoftDeleteQuery = ` -UPDATE silences SET deleted_at=now() -WHERE id = $1 AND deleted_at IS NULL -RETURNING * -` - -// SilenceRepository talks to the store to read or insert data -type SilenceRepository struct { - client *pgc.Client - tableName string -} - -// NewSilenceRepository returns repository struct -func NewSilenceRepository(client *pgc.Client) *SilenceRepository { - return &SilenceRepository{client, "silences"} -} - -func (r *SilenceRepository) Create(ctx context.Context, s silence.Silence) (string, error) { - sModel := new(model.Silence) - sModel.FromDomain(s) - - var newSModel model.Silence - if err := r.client.QueryRowxContext(ctx, pgc.OpInsert, r.tableName, silenceInsertQuery, - sModel.NamespaceID, - sModel.Type, - sModel.TargetID, - sModel.TargetExpression, - sModel.Creator, - sModel.Comment, - ).StructScan(&newSModel); err != nil { - err = pgc.CheckError(err) - if errors.Is(err, pgc.ErrForeignKeyViolation) { - return "", errors.ErrInvalid.WithMsgf(err.Error()) - } - return "", err - } - - return newSModel.ID, nil -} - -func (r *SilenceRepository) List(ctx context.Context, flt silence.Filter) ([]silence.Silence, error) { - var queryBuilder = silenceListQueryBuilder - - queryBuilder = queryBuilder.Where("deleted_at IS NULL") - - if flt.NamespaceID != 0 { - queryBuilder = queryBuilder.Where("namespace_id = ?", flt.NamespaceID) - } - - if flt.SubscriptionID != 0 { - queryBuilder = queryBuilder.Where("target_id = ?", flt.SubscriptionID) - } - - if len(flt.Match) != 0 { - labelsJSON, err := json.Marshal(flt.Match) - if err != nil { - return nil, errors.ErrInvalid.WithCausef("problem marshalling json match to string with err: %s", err.Error()) - } - queryBuilder = queryBuilder.Where(fmt.Sprintf("target_expression @> '%s'::jsonb", string(json.RawMessage(labelsJSON)))) - } - - if len(flt.SubscriptionMatch) != 0 { - labelsJSON, err := json.Marshal(flt.SubscriptionMatch) - if err != nil { - return nil, errors.ErrInvalid.WithCausef("problem marshalling json subscription labels to string with err: %s", err.Error()) - } - queryBuilder = queryBuilder.Where(fmt.Sprintf("target_expression <@ '%s'::jsonb", string(json.RawMessage(labelsJSON)))) - } - - query, args, err := queryBuilder.PlaceholderFormat(sq.Dollar).ToSql() - if err != nil { - return nil, err - } - - rows, err := r.client.QueryxContext(ctx, pgc.OpSelectAll, r.tableName, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var silencesDomain []silence.Silence - for rows.Next() { - var silenceModel model.Silence - if err := rows.StructScan(&silenceModel); err != nil { - return nil, err - } - - silencesDomain = append(silencesDomain, *silenceModel.ToDomain()) - } - - return silencesDomain, nil -} - -func (r *SilenceRepository) Get(ctx context.Context, id string) (silence.Silence, error) { - queryBuilder := silenceListQueryBuilder.Where("deleted_at IS NULL") - - query, args, err := queryBuilder.Where("id = ?", id).PlaceholderFormat(sq.Dollar).ToSql() - if err != nil { - return silence.Silence{}, err - } - - var modelSilence model.Silence - if err := r.client.GetContext(ctx, pgc.OpSelect, r.tableName, &modelSilence, query, args...); err != nil { - return silence.Silence{}, err - } - - return *modelSilence.ToDomain(), nil -} - -func (r *SilenceRepository) SoftDelete(ctx context.Context, id string) error { - if _, err := r.client.ExecContext(ctx, pgc.OpDelete, r.tableName, silenceSoftDeleteQuery, id); err != nil { - return err - } - - return nil -} diff --git a/internal/store/postgres/silence_test.go b/internal/store/postgres/silence_test.go deleted file mode 100644 index f61b37aa..00000000 --- a/internal/store/postgres/silence_test.go +++ /dev/null @@ -1,416 +0,0 @@ -package postgres_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/odpf/salt/db" - "github.com/odpf/salt/dockertestx" - "github.com/odpf/salt/log" - "github.com/odpf/siren/core/silence" - "github.com/odpf/siren/internal/store/postgres" - "github.com/odpf/siren/pkg/pgc" - "github.com/ory/dockertest/v3" - "github.com/stretchr/testify/suite" -) - -type SilenceRepositoryTestSuite struct { - suite.Suite - ctx context.Context - client *pgc.Client - pool *dockertest.Pool - resource *dockertest.Resource - repository *postgres.SilenceRepository - silenceIDs []string -} - -func (s *SilenceRepositoryTestSuite) SetupSuite() { - var err error - - logger := log.NewZap() - dpg, err := dockertestx.CreatePostgres( - dockertestx.PostgresWithDetail( - pgUser, pgPass, pgDBName, - ), - dockertestx.PostgresWithVersionTag("13"), - ) - if err != nil { - s.T().Fatal(err) - } - - s.pool = dpg.GetPool() - s.resource = dpg.GetResource() - - dbConfig.URL = dpg.GetExternalConnString() - dbc, err := db.New(dbConfig) - if err != nil { - s.T().Fatal(err) - } - - s.client, err = pgc.NewClient(logger, dbc) - if err != nil { - s.T().Fatal(err) - } - - s.ctx = context.TODO() - s.Require().NoError(migrate(s.ctx, logger, s.client, dbConfig)) - s.repository = postgres.NewSilenceRepository(s.client) - - _, err = bootstrapProvider(s.client) - if err != nil { - s.T().Fatal(err) - } - _, err = bootstrapNamespace(s.client) - if err != nil { - s.T().Fatal(err) - } - _, err = bootstrapSubscription(s.client) - if err != nil { - s.T().Fatal(err) - } -} - -func (s *SilenceRepositoryTestSuite) SetupTest() { - - var err error - s.silenceIDs, err = bootstrapSilence(s.client) - if err != nil { - s.T().Fatal(err) - } -} - -func (s *SilenceRepositoryTestSuite) TearDownSuite() { - // Clean tests - if err := purgeDocker(s.pool, s.resource); err != nil { - s.T().Fatal(err) - } -} - -func (s *SilenceRepositoryTestSuite) TearDownTest() { - if err := s.cleanup(); err != nil { - s.T().Fatal(err) - } -} - -func (s *SilenceRepositoryTestSuite) cleanup() error { - queries := []string{ - "TRUNCATE TABLE silences RESTART IDENTITY CASCADE", - } - return execQueries(context.TODO(), s.client, queries) -} - -func (s *SilenceRepositoryTestSuite) TestCreate() { - type testCase struct { - Description string - SilenceToCreate silence.Silence - ErrString string - } - - var testCases = []testCase{ - { - Description: "should create a silence type subscription", - SilenceToCreate: silence.Silence{ - NamespaceID: 1, - Type: silence.TypeSubscription, - TargetID: 1, - TargetExpression: map[string]interface{}{ - "rule": "true", - }, - }, - }, - { - Description: "should create a silence type labels", - SilenceToCreate: silence.Silence{ - NamespaceID: 1, - Type: silence.TypeMatchers, - TargetExpression: map[string]interface{}{ - "key1": "val1", - }, - }, - }, - { - Description: "should return error if a silence is invalid", - SilenceToCreate: silence.Silence{ - NamespaceID: 111, - Type: silence.TypeMatchers, - TargetExpression: map[string]interface{}{ - "key1": "val1", - }, - }, - ErrString: "foreign key violation [key (namespace_id)=(111) is not present in table \"namespaces\".]", - }, - } - - for _, tc := range testCases { - s.Run(tc.Description, func() { - _, err := s.repository.Create(s.ctx, tc.SilenceToCreate) - if err != nil { - if err.Error() != tc.ErrString { - s.T().Fatalf("got error %s, expected was %s", err.Error(), tc.ErrString) - } - } - }) - } -} - -func (s *SilenceRepositoryTestSuite) TestList() { - type testCase struct { - description string - expectedSilence []silence.Silence - filter silence.Filter - errString string - } - - var testCases = []testCase{ - { - description: "should get all silences", - expectedSilence: []silence.Silence{ - { - NamespaceID: 1, - Type: "subscription", - TargetID: 2, - TargetExpression: map[string]interface{}{ - "rule": "", - }, - }, - { - NamespaceID: 2, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - }, - { - NamespaceID: 3, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - }, - }, - { - NamespaceID: 2, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - "key3": "value3", - }, - }, - }, - }, - { - description: "should return correct silences when filtered with namespace_id", - filter: silence.Filter{ - NamespaceID: 2, - }, - expectedSilence: []silence.Silence{ - { - NamespaceID: 2, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - }, - { - NamespaceID: 2, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - "key3": "value3", - }, - }, - }, - }, - { - description: "should return correct silences when filtered with subscription_id", - filter: silence.Filter{ - SubscriptionID: 2, - }, - expectedSilence: []silence.Silence{ - { - NamespaceID: 1, - Type: "subscription", - TargetID: 2, - TargetExpression: map[string]interface{}{ - "rule": "", - }, - }, - }, - }, - { - description: "should return correct silences when filtered with labels", - filter: silence.Filter{ - Match: map[string]string{ - "key1": "value1", - }, - }, - expectedSilence: []silence.Silence{ - { - NamespaceID: 2, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - }, - { - NamespaceID: 3, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - }, - }, - { - NamespaceID: 2, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - "key3": "value3", - }, - }, - }, - }, - { - description: "should return correct silences when filtered with subscription labels", - filter: silence.Filter{ - SubscriptionMatch: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - expectedSilence: []silence.Silence{ - { - NamespaceID: 2, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - }, - { - NamespaceID: 3, - Type: "labels", - TargetExpression: map[string]interface{}{ - "key1": "value1", - "key2": "value2", - }, - }, - }, - }, - } - - for _, tc := range testCases { - s.Run(tc.description, func() { - got, err := s.repository.List(s.ctx, tc.filter) - if tc.errString != "" { - if err.Error() != tc.errString { - s.T().Fatalf("got error %s, expected was %s", err.Error(), tc.errString) - } - } - if diff := cmp.Diff(got, tc.expectedSilence, cmpopts.IgnoreFields(silence.Silence{}, - "CreatedAt", "ID")); diff != "" { - s.T().Fatalf("got diff %+v", diff) - } - }) - } -} - -func (s *SilenceRepositoryTestSuite) TestGet() { - type testCase struct { - Description string - ID string - ExpectedSilence silence.Silence - ErrString string - } - - var testCases = []testCase{ - { - - Description: "should get a silences", - ID: s.silenceIDs[0], - ExpectedSilence: silence.Silence{ - ID: "", - NamespaceID: 1, - Type: "subscription", - TargetID: 2, - TargetExpression: map[string]interface{}{ - "rule": "", - }, - }, - }, - { - - Description: "should return error not found when id not exist", - ID: "not-exist", - ExpectedSilence: silence.Silence{ - ID: "", - NamespaceID: 1, - Type: "subscription", - TargetID: 2, - TargetExpression: map[string]interface{}{ - "rule": "", - }, - }, - ErrString: "sql: no rows in result set", - }, - } - - for _, tc := range testCases { - s.Run(tc.Description, func() { - got, err := s.repository.Get(s.ctx, tc.ID) - if tc.ErrString != "" { - if err.Error() != tc.ErrString { - s.T().Fatalf("got error %s, expected was %s", err.Error(), tc.ErrString) - } - } else if diff := cmp.Diff(got, tc.ExpectedSilence, cmpopts.IgnoreFields(silence.Silence{}, - "CreatedAt", "ID")); diff != "" { - s.T().Fatalf("got diff %+v", diff) - } - }) - } -} - -func (s *SilenceRepositoryTestSuite) TestSoftDelete() { - s.Run("should delete an entry if success", func() { - id, err := s.repository.Create(s.ctx, silence.Silence{ - NamespaceID: 1, - Type: silence.TypeMatchers, - TargetExpression: map[string]interface{}{ - "key1": "value1", - }, - }) - s.Require().NoError(err) - - silences, err := s.repository.List(s.ctx, silence.Filter{}) - s.Require().NoError(err) - s.Require().Equal(5, len(silences)) - - err = s.repository.SoftDelete(s.ctx, id) - s.Assert().NoError(err) - - silences, err = s.repository.List(s.ctx, silence.Filter{}) - s.Require().NoError(err) - s.Require().Equal(4, len(silences)) - }) - - s.Run("should not delete anything and return nil error if id not found", func() { - silences, err := s.repository.List(s.ctx, silence.Filter{}) - s.Require().NoError(err) - s.Require().Equal(4, len(silences)) - - err = s.repository.SoftDelete(s.ctx, "random") - s.Assert().NoError(err) - - silences, err = s.repository.List(s.ctx, silence.Filter{}) - s.Require().NoError(err) - s.Require().Equal(4, len(silences)) - }) -} - -func TestSilenceRepository(t *testing.T) { - suite.Run(t, new(SilenceRepositoryTestSuite)) -} diff --git a/internal/store/postgres/subscription.go b/internal/store/postgres/subscription.go index 3af9bb06..9bc058ef 100644 --- a/internal/store/postgres/subscription.go +++ b/internal/store/postgres/subscription.go @@ -7,7 +7,6 @@ import ( "fmt" sq "github.com/Masterminds/squirrel" - "github.com/lib/pq" "github.com/odpf/siren/core/subscription" "github.com/odpf/siren/internal/store/model" "github.com/odpf/siren/pkg/errors" @@ -57,18 +56,14 @@ func NewSubscriptionRepository(client *pgc.Client) *SubscriptionRepository { func (r *SubscriptionRepository) List(ctx context.Context, flt subscription.Filter) ([]subscription.Subscription, error) { var queryBuilder = subscriptionListQueryBuilder - if len(flt.IDs) != 0 { - queryBuilder = queryBuilder.Where("id = any(?)", pq.Array(flt.IDs)) - } - if flt.NamespaceID != 0 { queryBuilder = queryBuilder.Where("namespace_id = ?", flt.NamespaceID) } - if len(flt.NotificationMatch) != 0 { - labelsJSON, err := json.Marshal(flt.NotificationMatch) + if len(flt.Labels) != 0 { + labelsJSON, err := json.Marshal(flt.Labels) if err != nil { - return nil, errors.ErrInvalid.WithCausef("problem marshalling notification labels json to string with err: %s", err.Error()) + return nil, errors.ErrInvalid.WithCausef("problem marshalling json to string with err: %s", err.Error()) } queryBuilder = queryBuilder.Where(fmt.Sprintf("match <@ '%s'::jsonb", string(json.RawMessage(labelsJSON)))) } @@ -91,6 +86,14 @@ func (r *SubscriptionRepository) List(ctx context.Context, flt subscription.Filt return nil, err } + // If filter by Labels and namespace ID exist, filter by namespace should be done in app + // to make use of search by labels with GIN index + if len(flt.Labels) != 0 && flt.NamespaceID != 0 { + if subscriptionModel.NamespaceID != flt.NamespaceID { + continue + } + } + subscriptionsDomain = append(subscriptionsDomain, *subscriptionModel.ToDomain()) } diff --git a/internal/store/postgres/subscription_test.go b/internal/store/postgres/subscription_test.go index 4ae4da8f..e39dd6b7 100644 --- a/internal/store/postgres/subscription_test.go +++ b/internal/store/postgres/subscription_test.go @@ -33,7 +33,6 @@ func (s *SubscriptionRepositoryTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) @@ -55,7 +54,6 @@ func (s *SubscriptionRepositoryTestSuite) SetupSuite() { s.ctx = context.TODO() s.Require().NoError(migrate(s.ctx, logger, s.client, dbConfig)) - s.repository = postgres.NewSubscriptionRepository(s.client) _, err = bootstrapProvider(s.client) @@ -190,7 +188,7 @@ func (s *SubscriptionRepositoryTestSuite) TestList() { { Description: "should get filtered subscriptions by match labels", Filter: subscription.Filter{ - NotificationMatch: map[string]string{ + Labels: map[string]string{ "environment": "production", "severity": "CRITICAL", "team": "odpf-app", diff --git a/internal/store/postgres/template_test.go b/internal/store/postgres/template_test.go index 6e9d4376..6614b791 100644 --- a/internal/store/postgres/template_test.go +++ b/internal/store/postgres/template_test.go @@ -33,7 +33,6 @@ func (s *TemplateRepositoryTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) diff --git a/internal/store/postgres/testdata/mock-notification-log.json b/internal/store/postgres/testdata/mock-notification-log.json deleted file mode 100644 index 41ee7c1b..00000000 --- a/internal/store/postgres/testdata/mock-notification-log.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "namespace_id": 1, - "notification_id": "receiver", - "subscription_id": 2, - "receiver_id": 3, - "alert_ids": [], - "silence_ids": [] - }, - { - "namespace_id": 1, - "type": "subscriber", - "data": { - "data-key": "data-value" - }, - "labels": { - "label-key": "label-value" - }, - "template": "" - } -] \ No newline at end of file diff --git a/internal/store/postgres/testdata/mock-notification.json b/internal/store/postgres/testdata/mock-notification.json deleted file mode 100644 index 1e18e99c..00000000 --- a/internal/store/postgres/testdata/mock-notification.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "namespace_id": 1, - "type": "receiver", - "data": { - "data-key": "data-value" - }, - "labels": {}, - "template": "" - }, - { - "namespace_id": 1, - "type": "subscriber", - "data": { - "data-key": "data-value" - }, - "labels": { - "label-key": "label-value" - }, - "template": "" - } -] \ No newline at end of file diff --git a/internal/store/postgres/testdata/mock-silence.json b/internal/store/postgres/testdata/mock-silence.json deleted file mode 100644 index e8d1a177..00000000 --- a/internal/store/postgres/testdata/mock-silence.json +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - "namespace_id": 1, - "type": "subscription", - "target_id": 2, - "target_expression": { - "rule": "" - } - }, - { - "namespace_id": 2, - "type": "labels", - "target_expression": { - "key1": "value1" - } - }, - { - "namespace_id": 3, - "type": "labels", - "target_expression": { - "key1": "value1", - "key2": "value2" - } - }, - { - "namespace_id": 2, - "type": "labels", - "target_expression": { - "key1": "value1", - "key2": "value2", - "key3": "value3" - } - } -] \ No newline at end of file diff --git a/pkg/pgc/type.go b/pkg/pgc/type.go index 1d769d8f..0dc1124a 100644 --- a/pkg/pgc/type.go +++ b/pkg/pgc/type.go @@ -4,12 +4,12 @@ import ( "database/sql/driver" "encoding/json" "errors" - "fmt" - "time" ) type StringInterfaceMap map[string]interface{} +type StringStringMap map[string]string + func (m *StringInterfaceMap) Scan(value interface{}) error { if value == nil { m = new(StringInterfaceMap) @@ -29,8 +29,6 @@ func (a StringInterfaceMap) Value() (driver.Value, error) { return json.Marshal(a) } -type StringStringMap map[string]string - func (m *StringStringMap) Scan(value interface{}) error { if value == nil { m = new(StringStringMap) @@ -49,29 +47,3 @@ func (a StringStringMap) Value() (driver.Value, error) { } return json.Marshal(a) } - -type TimeDuration time.Duration - -func (t *TimeDuration) Scan(value interface{}) error { - if value == nil { - return nil - } - - valueStr, ok := value.(string) - if !ok { - return errors.New("failed type assertion to string") - } - - dur, err := time.ParseDuration(valueStr) - if err != nil { - return fmt.Errorf("error parsing duration '%s': %w", valueStr, err) - } - - *t = TimeDuration(dur) - - return nil -} - -func (t TimeDuration) Value() (driver.Value, error) { - return time.Duration(t).String(), nil -} diff --git a/pkg/telemetry/application.go b/pkg/telemetry/application.go index 92ea49e1..5b62ba1a 100644 --- a/pkg/telemetry/application.go +++ b/pkg/telemetry/application.go @@ -14,10 +14,10 @@ const ( ) var ( - TagReceiverType = tag.MustNewKey("receiver_type") - TagNotificationType = tag.MustNewKey("notification_type") - TagMessageStatus = tag.MustNewKey("status") - TagHookCondition = tag.MustNewKey("hook_condition") + TagReceiverType = tag.MustNewKey("receiver_type") + TagRoutingMethod = tag.MustNewKey("routing_method") + TagMessageStatus = tag.MustNewKey("status") + TagHookCondition = tag.MustNewKey("hook_condition") MetricNotificationMessageQueueTime = stats.Int64("notification.message.queue.time", "time of message from enqueued to be picked up", stats.UnitMilliseconds) @@ -40,7 +40,7 @@ func setupApplicationViews() error { &view.View{ Name: MetricNotificationMessageCounter.Name(), Description: MetricNotificationMessageCounter.Description(), - TagKeys: []tag.Key{TagReceiverType, TagNotificationType, TagMessageStatus}, + TagKeys: []tag.Key{TagReceiverType, TagRoutingMethod, TagMessageStatus}, Measure: MetricNotificationMessageCounter, Aggregation: view.Count(), }, @@ -48,7 +48,7 @@ func setupApplicationViews() error { Name: MetricNotificationSubscriberNotFound.Name(), Description: MetricNotificationSubscriberNotFound.Description(), Measure: MetricNotificationSubscriberNotFound, - Aggregation: view.Count(), + Aggregation: view.Sum(), }, &view.View{ Name: MetricReceiverHookFailed.Name(), diff --git a/plugins/providers/cortex/alert.go b/plugins/providers/cortex/alert.go index 2383c857..18d89387 100644 --- a/plugins/providers/cortex/alert.go +++ b/plugins/providers/cortex/alert.go @@ -1,6 +1,8 @@ package cortex -import "errors" +import ( + "github.com/odpf/siren/core/alert" +) // GroupAlert contract is cortex/prometheus webhook_config contract // https://prometheus.io/docs/alerting/latest/configuration/#webhook_config @@ -21,26 +23,8 @@ type Alert struct { EndsAt string `mapstructure:"endsAt"` } -func (a Alert) Validate() error { - if _, ok := a.Labels["severity"]; !ok { - return errors.New("'severity' label is missing") - } - - if _, ok := a.Annotations["resource"]; !ok { - return errors.New("'resource' annotation is missing") - } - - if _, ok := a.Annotations["template"]; !ok { - return errors.New("'template' annotation is missing") - } - - if _, ok := a.Annotations["metric_value"]; !ok { - return errors.New("'metric_value' annotation is missing") - } - - if _, ok := a.Annotations["metric_name"]; !ok { - return errors.New("'metric_name' annotation is missing") - } - - return nil +func isValidCortexAlert(alrt alert.Alert) bool { + return !(alrt.ResourceName == "" || alrt.Rule == "" || + alrt.MetricValue == "" || alrt.MetricName == "" || + alrt.Severity == "") } diff --git a/plugins/providers/cortex/mocks/cortex_caller.go b/plugins/providers/cortex/mocks/cortex_caller.go index 588a9b4f..6527a7a3 100644 --- a/plugins/providers/cortex/mocks/cortex_caller.go +++ b/plugins/providers/cortex/mocks/cortex_caller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -43,9 +43,9 @@ type CortexCaller_CreateAlertmanagerConfig_Call struct { } // CreateAlertmanagerConfig is a helper method to define mock.On call -// - ctx context.Context -// - cfg string -// - templates map[string]string +// - ctx context.Context +// - cfg string +// - templates map[string]string func (_e *CortexCaller_Expecter) CreateAlertmanagerConfig(ctx interface{}, cfg interface{}, templates interface{}) *CortexCaller_CreateAlertmanagerConfig_Call { return &CortexCaller_CreateAlertmanagerConfig_Call{Call: _e.mock.On("CreateAlertmanagerConfig", ctx, cfg, templates)} } @@ -82,9 +82,9 @@ type CortexCaller_CreateRuleGroup_Call struct { } // CreateRuleGroup is a helper method to define mock.On call -// - ctx context.Context -// - namespace string -// - rg rwrulefmt.RuleGroup +// - ctx context.Context +// - namespace string +// - rg rwrulefmt.RuleGroup func (_e *CortexCaller_Expecter) CreateRuleGroup(ctx interface{}, namespace interface{}, rg interface{}) *CortexCaller_CreateRuleGroup_Call { return &CortexCaller_CreateRuleGroup_Call{Call: _e.mock.On("CreateRuleGroup", ctx, namespace, rg)} } @@ -121,9 +121,9 @@ type CortexCaller_DeleteRuleGroup_Call struct { } // DeleteRuleGroup is a helper method to define mock.On call -// - ctx context.Context -// - namespace string -// - groupName string +// - ctx context.Context +// - namespace string +// - groupName string func (_e *CortexCaller_Expecter) DeleteRuleGroup(ctx interface{}, namespace interface{}, groupName interface{}) *CortexCaller_DeleteRuleGroup_Call { return &CortexCaller_DeleteRuleGroup_Call{Call: _e.mock.On("DeleteRuleGroup", ctx, namespace, groupName)} } @@ -169,9 +169,9 @@ type CortexCaller_GetRuleGroup_Call struct { } // GetRuleGroup is a helper method to define mock.On call -// - ctx context.Context -// - namespace string -// - groupName string +// - ctx context.Context +// - namespace string +// - groupName string func (_e *CortexCaller_Expecter) GetRuleGroup(ctx interface{}, namespace interface{}, groupName interface{}) *CortexCaller_GetRuleGroup_Call { return &CortexCaller_GetRuleGroup_Call{Call: _e.mock.On("GetRuleGroup", ctx, namespace, groupName)} } @@ -217,8 +217,8 @@ type CortexCaller_ListRules_Call struct { } // ListRules is a helper method to define mock.On call -// - ctx context.Context -// - namespace string +// - ctx context.Context +// - namespace string func (_e *CortexCaller_Expecter) ListRules(ctx interface{}, namespace interface{}) *CortexCaller_ListRules_Call { return &CortexCaller_ListRules_Call{Call: _e.mock.On("ListRules", ctx, namespace)} } diff --git a/plugins/providers/cortex/service.go b/plugins/providers/cortex/service.go index 259fe2e7..2a2fa32a 100644 --- a/plugins/providers/cortex/service.go +++ b/plugins/providers/cortex/service.go @@ -76,12 +76,6 @@ func (s *PluginService) TransformToAlerts(ctx context.Context, providerID uint64 for _, item := range groupAlert.Alerts { - if err := item.Validate(); err != nil { - s.logger.Error(fmt.Sprintf("invalid alerts: %s", err.Error()), "group key", groupAlert.GroupKey, "alert detail", item) - badAlertCount++ - continue - } - if item.Status == "firing" { firingLen++ } @@ -114,7 +108,11 @@ func (s *PluginService) TransformToAlerts(ctx context.Context, providerID uint64 GeneratorURL: item.GeneratorURL, Fingerprint: item.Fingerprint, } - + if !isValidCortexAlert(alrt) { + s.logger.Error("invalid alerts", "group key", groupAlert.GroupKey, "alert detail", alrt) + badAlertCount++ + continue + } alerts = append(alerts, alrt) } diff --git a/plugins/queues/postgresq/queue_test.go b/plugins/queues/postgresq/queue_test.go index 96dfcf25..c0e01dd6 100644 --- a/plugins/queues/postgresq/queue_test.go +++ b/plugins/queues/postgresq/queue_test.go @@ -43,7 +43,6 @@ func (s *QueueTestSuite) SetupSuite() { dockertestx.PostgresWithDetail( pgUser, pgPass, pgDBName, ), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { s.T().Fatal(err) @@ -98,43 +97,41 @@ func (s *QueueTestSuite) TestSimpleEnqueueDequeue() { timeNow := time.Now() messagesGenerator := func() []notification.Message { - return []notification.Message{ + ns := []notification.Notification{ { - ID: uuid.NewString(), - ReceiverType: receiver.TypeSlack, - Status: notification.MessageStatusEnqueued, - CreatedAt: timeNow, - UpdatedAt: timeNow, + ID: uuid.NewString(), + Data: map[string]interface{}{}, + Labels: map[string]string{}, + CreatedAt: timeNow, }, { - ID: uuid.NewString(), - ReceiverType: receiver.TypeSlack, - Status: notification.MessageStatusEnqueued, - CreatedAt: timeNow, - UpdatedAt: timeNow, + ID: uuid.NewString(), + Data: map[string]interface{}{}, + Labels: map[string]string{}, + CreatedAt: timeNow, }, { - ID: uuid.NewString(), - ReceiverType: receiver.TypeSlack, - Status: notification.MessageStatusEnqueued, - CreatedAt: timeNow, - UpdatedAt: timeNow, + ID: uuid.NewString(), + Data: map[string]interface{}{}, + Labels: map[string]string{}, + CreatedAt: timeNow, }, { - ID: uuid.NewString(), - ReceiverType: receiver.TypeSlack, - Status: notification.MessageStatusEnqueued, - CreatedAt: timeNow, - UpdatedAt: timeNow, - }, - { - ID: uuid.NewString(), - ReceiverType: receiver.TypeSlack, - Status: notification.MessageStatusEnqueued, - CreatedAt: timeNow, - UpdatedAt: timeNow, + ID: uuid.NewString(), + Data: map[string]interface{}{}, + Labels: map[string]string{}, + CreatedAt: timeNow, }, } + + messages := []notification.Message{} + for _, n := range ns { + msg, err := n.ToMessage(receiver.TypeSlack, map[string]interface{}{}) + s.Require().NoError(err) + messages = append(messages, *msg) + } + + return messages } s.Run("should return no error if all messages are successfully processed", func() { @@ -196,10 +193,7 @@ func (s *QueueTestSuite) TestEnqueueDequeueWithCallback() { messages := make([]notification.Message, 5) for i := 0; i < len(messages); i++ { - messages[i].ID = fmt.Sprintf("%d", i+1) - messages[i].ReceiverType = receiver.TypeSlack - messages[i].Status = notification.MessageStatusEnqueued - messages[i].MaxTries = 3 + messages[i].Initialize(notification.Notification{}, receiver.TypeSlack, map[string]interface{}{}, notification.InitWithID(fmt.Sprintf("%d", i+1))) } s.Run("should update row with error for id \"5\"", func() { @@ -254,41 +248,40 @@ func (s *QueueTestSuite) TestEnqueueDequeueDLQ() { messages := make([]notification.Message, 5) for i := 0; i < len(messages); i++ { - messages[i].ID = fmt.Sprintf("%d", i+1) - messages[i].ReceiverType = receiver.TypeSlack - messages[i].Status = notification.MessageStatusEnqueued - messages[i].MaxTries = 3 + messages[i].Initialize(notification.Notification{}, receiver.TypeSlack, map[string]interface{}{}, notification.InitWithID(fmt.Sprintf("%d", i+1))) } - s.Run("failed messages should be re-processed by dlq and ignored by main queue", func() { + s.Run("failed messages should be re-processed by dlq an ignored by main queue", func() { var anError = errors.New("some error") - s.Require().NoError(s.q.Enqueue(s.ctx, messages...)) + err := s.q.Enqueue(s.ctx, messages...) + s.Require().NoError(err) // mark failed all for _, m := range messages { m.MarkFailed(time.Now(), true, anError) - s.Assert().NoError(s.q.ErrorCallback(s.ctx, m)) + err = s.q.ErrorCallback(s.ctx, m) + s.Assert().NoError(err) } - s.Assert().EqualError( - s.q.Dequeue(s.ctx, nil, 5, func(ctx context.Context, m []notification.Message) error { s.Assert().Empty(m); return nil }), - notification.ErrNoMessage.Error(), - ) + _ = s.q.Dequeue(s.ctx, nil, 5, func(ctx context.Context, m []notification.Message) error { s.Assert().Empty(m); return nil }) + s.Assert().NoError(err) - s.Assert().NoError(s.dlq.Dequeue(s.ctx, nil, 5, func(ctx context.Context, m []notification.Message) error { + _ = s.dlq.Dequeue(s.ctx, nil, 5, func(ctx context.Context, m []notification.Message) error { s.Assert().Len(m, 5) return nil - })) + }) tempMessage := &postgresq.NotificationMessage{} - s.Require().NoError(s.dbc.Get(tempMessage, fmt.Sprintf("SELECT * FROM %s LIMIT 1", postgresq.MessageQueueTableFullName))) + err = s.dbc.Get(tempMessage, fmt.Sprintf("SELECT * FROM %s LIMIT 1", postgresq.MessageQueueTableFullName)) + s.Require().NoError(err) s.Assert().Equal(string(notification.MessageStatusPending), tempMessage.Status) s.Assert().Equal(anError.Error(), tempMessage.LastError.String) s.Assert().Equal(1, tempMessage.TryCount) - s.Require().NoError(s.cleanup()) + err = s.cleanup() + s.Require().NoError(err) }) } diff --git a/plugins/receivers/pagerduty/config/default_alert_template_body_v1.goyaml b/plugins/receivers/pagerduty/config/default_alert_template_body_v1.goyaml index 87a3b278..7a97dd8b 100644 --- a/plugins/receivers/pagerduty/config/default_alert_template_body_v1.goyaml +++ b/plugins/receivers/pagerduty/config/default_alert_template_body_v1.goyaml @@ -9,7 +9,7 @@ [[- end]] event_type: "[[template "pagerduty.event_type" . ]]" [[if .Data.id]]incident_key: "[[.Data.id]]"[[ end ]] -description: ([[ .Data.status | toUpper ]][[ if eq .Data.status "firing" ]]:[[ .Data.num_alerts_firing ]][[ end ]]) +description: ([[ .Data.status | toUpper ]][[ if eq .Data.status "firing" ]]:[[ .Data.numAlertsFiring ]][[ end ]]) [[- if eq .Data.status "resolved" ]] ~([[ .Labels.severity | toUpper ]])~ [[- else ]] *([[ .Labels.severity | toUpper ]])* [[- end]] [[ .Labels.alertname ]] diff --git a/plugins/receivers/pagerduty/mocks/pagerduty_caller.go b/plugins/receivers/pagerduty/mocks/pagerduty_caller.go index 8301842a..8be35ba8 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.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -42,8 +42,8 @@ type PagerDutyCaller_NotifyV1_Call struct { } // NotifyV1 is a helper method to define mock.On call -// - ctx context.Context -// - message pagerduty.MessageV1 +// - ctx context.Context +// - message pagerduty.MessageV1 func (_e *PagerDutyCaller_Expecter) NotifyV1(ctx interface{}, message interface{}) *PagerDutyCaller_NotifyV1_Call { return &PagerDutyCaller_NotifyV1_Call{Call: _e.mock.On("NotifyV1", ctx, message)} } diff --git a/plugins/receivers/slack/config/default_alert_template_body.goyaml b/plugins/receivers/slack/config/default_alert_template_body.goyaml index 0efd709f..6162b0cb 100644 --- a/plugins/receivers/slack/config/default_alert_template_body.goyaml +++ b/plugins/receivers/slack/config/default_alert_template_body.goyaml @@ -10,7 +10,7 @@ [[- end]] [[- end]] [[- define "slack.pretext" -]] - [[- template "__alert_severity_prefix_emoji" . ]] ([[ .Data.status | toUpper ]][[ if eq .Data.status "firing" ]]:[[ .Data.num_alerts_firing ]][[ end ]]) + [[- template "__alert_severity_prefix_emoji" . ]] ([[ .Data.status | toUpper ]][[ if eq .Data.status "firing" ]]:[[ .Data.numAlertsFiring ]][[ end ]]) [[- if eq .Data.status "resolved" ]] ~([[ .Labels.severity | toUpper ]])~ [[- else ]] *([[ .Labels.severity | toUpper ]])* [[- end]] [[ .Labels.alertname ]] @@ -39,11 +39,12 @@ [[- end -]] username: "Siren" icon_emoji: ":eagle:" +link_names: false attachments: - title: "" pretext: "[[template "slack.pretext" . ]]" text: | -[[.Data.summary | indent 6]] +[[.Data.summary | indent 5]] color: "[[template "slack.color" . ]]" actions: - type: button diff --git a/plugins/receivers/slack/mocks/encryptor.go b/plugins/receivers/slack/mocks/encryptor.go index 7f70f3e3..c893a084 100644 --- a/plugins/receivers/slack/mocks/encryptor.go +++ b/plugins/receivers/slack/mocks/encryptor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -47,7 +47,7 @@ type Encryptor_Decrypt_Call struct { } // Decrypt is a helper method to define mock.On call -// - str secret.MaskableString +// - str secret.MaskableString func (_e *Encryptor_Expecter) Decrypt(str interface{}) *Encryptor_Decrypt_Call { return &Encryptor_Decrypt_Call{Call: _e.mock.On("Decrypt", str)} } @@ -91,7 +91,7 @@ type Encryptor_Encrypt_Call struct { } // Encrypt is a helper method to define mock.On call -// - str secret.MaskableString +// - str secret.MaskableString func (_e *Encryptor_Expecter) Encrypt(str interface{}) *Encryptor_Encrypt_Call { return &Encryptor_Encrypt_Call{Call: _e.mock.On("Encrypt", str)} } diff --git a/plugins/receivers/slack/mocks/goslack_caller.go b/plugins/receivers/slack/mocks/goslack_caller.go index 80123adb..741daf97 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.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -59,8 +59,8 @@ type GoSlackCaller_GetConversationsForUserContext_Call struct { } // GetConversationsForUserContext is a helper method to define mock.On call -// - ctx context.Context -// - params *slack.GetConversationsForUserParameters +// - ctx context.Context +// - params *slack.GetConversationsForUserParameters func (_e *GoSlackCaller_Expecter) GetConversationsForUserContext(ctx interface{}, params interface{}) *GoSlackCaller_GetConversationsForUserContext_Call { return &GoSlackCaller_GetConversationsForUserContext_Call{Call: _e.mock.On("GetConversationsForUserContext", ctx, params)} } @@ -106,8 +106,8 @@ type GoSlackCaller_GetUserByEmailContext_Call struct { } // GetUserByEmailContext is a helper method to define mock.On call -// - ctx context.Context -// - email string +// - ctx context.Context +// - email string func (_e *GoSlackCaller_Expecter) GetUserByEmailContext(ctx interface{}, email interface{}) *GoSlackCaller_GetUserByEmailContext_Call { return &GoSlackCaller_GetUserByEmailContext_Call{Call: _e.mock.On("GetUserByEmailContext", ctx, email)} } @@ -172,9 +172,9 @@ type GoSlackCaller_SendMessageContext_Call struct { } // SendMessageContext is a helper method to define mock.On call -// - ctx context.Context -// - channel string -// - options ...slack.MsgOption +// - ctx context.Context +// - channel string +// - options ...slack.MsgOption func (_e *GoSlackCaller_Expecter) SendMessageContext(ctx interface{}, channel interface{}, options ...interface{}) *GoSlackCaller_SendMessageContext_Call { return &GoSlackCaller_SendMessageContext_Call{Call: _e.mock.On("SendMessageContext", append([]interface{}{ctx, channel}, options...)...)} diff --git a/plugins/receivers/slack/mocks/slack_caller.go b/plugins/receivers/slack/mocks/slack_caller.go index a9097104..f8371aef 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.16.0. DO NOT EDIT. +// Code generated by mockery v2.14.0. DO NOT EDIT. package mocks @@ -51,10 +51,10 @@ type SlackCaller_ExchangeAuth_Call struct { } // ExchangeAuth is a helper method to define mock.On call -// - ctx context.Context -// - authCode string -// - clientID string -// - clientSecret string +// - ctx context.Context +// - authCode string +// - clientID string +// - clientSecret string func (_e *SlackCaller_Expecter) ExchangeAuth(ctx interface{}, authCode interface{}, clientID interface{}, clientSecret interface{}) *SlackCaller_ExchangeAuth_Call { return &SlackCaller_ExchangeAuth_Call{Call: _e.mock.On("ExchangeAuth", ctx, authCode, clientID, clientSecret)} } @@ -100,8 +100,8 @@ type SlackCaller_GetWorkspaceChannels_Call struct { } // GetWorkspaceChannels is a helper method to define mock.On call -// - ctx context.Context -// - token secret.MaskableString +// - ctx context.Context +// - token secret.MaskableString func (_e *SlackCaller_Expecter) GetWorkspaceChannels(ctx interface{}, token interface{}) *SlackCaller_GetWorkspaceChannels_Call { return &SlackCaller_GetWorkspaceChannels_Call{Call: _e.mock.On("GetWorkspaceChannels", ctx, token)} } @@ -138,9 +138,9 @@ type SlackCaller_Notify_Call struct { } // Notify is a helper method to define mock.On call -// - ctx context.Context -// - conf slack.NotificationConfig -// - message slack.Message +// - ctx context.Context +// - conf slack.NotificationConfig +// - message slack.Message func (_e *SlackCaller_Expecter) Notify(ctx interface{}, conf interface{}, message interface{}) *SlackCaller_Notify_Call { return &SlackCaller_Notify_Call{Call: _e.mock.On("Notify", ctx, conf, message)} } diff --git a/proto/odpf/siren/v1beta1/siren.pb.go b/proto/odpf/siren/v1beta1/siren.pb.go index 399922e3..899633bf 100644 --- a/proto/odpf/siren/v1beta1/siren.pb.go +++ b/proto/odpf/siren/v1beta1/siren.pb.go @@ -3723,7 +3723,7 @@ type UpdateRuleResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Rule *Rule `protobuf:"bytes,1,opt,name=rule,proto3" json:"rule,omitempty"` } func (x *UpdateRuleResponse) Reset() { @@ -3758,11 +3758,11 @@ func (*UpdateRuleResponse) Descriptor() ([]byte, []int) { return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{61} } -func (x *UpdateRuleResponse) GetId() uint64 { +func (x *UpdateRuleResponse) GetRule() *Rule { if x != nil { - return x.Id + return x.Rule } - return 0 + return nil } type TemplateVariables struct { @@ -5549,581 +5549,583 @@ var file_odpf_siren_v1beta1_siren_proto_rawDesc = []byte{ 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x22, 0x24, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0xa9, 0x01, 0x0a, 0x11, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, - 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, - 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, - 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0xd7, 0x02, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x2b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, - 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, - 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, - 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x1c, 0x0a, 0x04, 0x74, 0x61, 0x67, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, - 0x01, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x22, 0x42, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, + 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, + 0x72, 0x75, 0x6c, 0x65, 0x22, 0xa9, 0x01, 0x0a, 0x11, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, + 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, + 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, + 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0xd7, 0x02, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, + 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, + 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x62, 0x6f, + 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, + 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, + 0x24, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x1c, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, + 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x4d, 0x0a, 0x09, 0x76, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, + 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, + 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x22, 0x28, 0x0a, 0x14, 0x4c, 0x69, + 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x74, 0x61, 0x67, 0x22, 0x53, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, + 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x09, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x22, 0xd5, 0x01, 0x0a, 0x15, 0x55, 0x70, + 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x62, 0x6f, 0x64, 0x79, 0x12, 0x1c, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x12, 0x4d, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, + 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x42, 0x08, 0xfa, 0x42, + 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x22, 0x28, 0x0a, 0x16, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x41, 0x0a, 0x12, 0x47, + 0x65, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, + 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x4f, + 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, + 0x2b, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x18, 0x0a, 0x16, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xda, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x2b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x17, + 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, + 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x56, 0x0a, + 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x16, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x22, 0xe4, 0x02, 0x0a, 0x07, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, + 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, + 0x64, 0x12, 0x44, 0x0a, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x45, 0x78, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x4d, 0x0a, - 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, - 0x01, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x22, 0x28, 0x0a, 0x14, - 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x53, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3a, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x52, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x22, 0xd5, 0x01, 0x0a, 0x15, - 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, - 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x1c, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x04, - 0x74, 0x61, 0x67, 0x73, 0x12, 0x4d, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, - 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x73, 0x22, 0x28, 0x0a, 0x16, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x41, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, - 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x22, 0x4f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x22, 0x2b, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x18, - 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xda, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x17, 0xfa, 0x42, 0x14, 0x72, 0x12, 0x32, 0x10, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, - 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x56, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x54, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x76, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x16, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x54, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, - 0x6f, 0x64, 0x79, 0x22, 0xe4, 0x02, 0x0a, 0x07, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x49, 0x64, 0x12, 0x44, 0x0a, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x65, 0x78, - 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x45, - 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x39, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xb0, 0x01, 0x0a, 0x14, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x44, 0x0a, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x27, 0x0a, - 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x95, 0x05, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, - 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, - 0x0a, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0xb0, 0x01, 0x0a, 0x05, 0x6d, - 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x66, - 0x92, 0x41, 0x63, 0x32, 0x61, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x20, 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x73, 0x69, 0x6c, 0x65, 0x6e, - 0x63, 0x65, 0x73, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, - 0x72, 0x73, 0x2e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6b, 0x65, - 0x79, 0x20, 0x69, 0x73, 0x20, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, - 0x6d, 0x61, 0x70, 0x2e, 0x20, 0x65, 0x67, 0x2c, 0x20, 0x22, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5b, - 0x6b, 0x65, 0x79, 0x31, 0x5d, 0x22, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0xfe, 0x01, - 0x0a, 0x12, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, - 0x61, 0x74, 0x63, 0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x6f, 0x64, 0x70, + 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, + 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x64, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xb0, 0x01, 0x0a, 0x14, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x44, 0x0a, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, + 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x27, 0x0a, 0x15, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x95, 0x05, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, + 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0xb0, 0x01, 0x0a, 0x05, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x66, 0x92, 0x41, + 0x63, 0x32, 0x61, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, + 0x73, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, + 0x2e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6b, 0x65, 0x79, 0x20, + 0x69, 0x73, 0x20, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x6d, 0x61, + 0x70, 0x2e, 0x20, 0x65, 0x67, 0x2c, 0x20, 0x22, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5b, 0x6b, 0x65, + 0x79, 0x31, 0x5d, 0x22, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0xfe, 0x01, 0x0a, 0x12, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, + 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x8e, 0x01, 0x92, 0x41, 0x8a, 0x01, 0x32, + 0x87, 0x01, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x62, + 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x20, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x77, 0x72, 0x69, 0x74, 0x74, + 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x6d, 0x61, 0x70, 0x2e, 0x20, 0x65, 0x67, 0x2c, 0x20, 0x22, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x5b, 0x6b, 0x65, 0x79, 0x31, 0x5d, 0x22, 0x52, 0x11, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x38, 0x0a, 0x0a, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x44, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4f, 0x0a, 0x14, + 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, + 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x6c, 0x65, + 0x6e, 0x63, 0x65, 0x52, 0x08, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x23, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x22, 0x4b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x07, 0x73, 0x69, 0x6c, 0x65, + 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, + 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x22, + 0x26, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x17, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x69, 0x72, + 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x32, 0xb9, 0x2f, 0x0a, 0x0c, 0x53, 0x69, 0x72, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x9d, 0x01, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, + 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x92, 0x41, 0x1a, 0x0a, 0x08, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x73, 0x12, 0xa6, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, + 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, 0x41, 0x1d, + 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x11, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x0b, 0x47, + 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x8e, 0x01, 0x92, 0x41, 0x8a, - 0x01, 0x32, 0x87, 0x01, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x20, 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, - 0x64, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x77, 0x72, 0x69, - 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x6d, 0x61, 0x70, 0x2e, 0x20, 0x65, 0x67, 0x2c, - 0x20, 0x22, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, - 0x61, 0x74, 0x63, 0x68, 0x5b, 0x6b, 0x65, 0x79, 0x31, 0x5d, 0x22, 0x52, 0x11, 0x73, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x38, - 0x0a, 0x0a, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x44, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4f, - 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x69, - 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x22, - 0x23, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x22, 0x4b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x07, 0x73, 0x69, - 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, - 0x65, 0x22, 0x26, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x17, 0x0a, 0x15, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x32, 0xb9, 0x2f, 0x0a, 0x0c, 0x53, 0x69, 0x72, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x9d, 0x01, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, - 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x92, 0x41, 0x1a, 0x0a, - 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x20, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, - 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x73, 0x12, 0xa6, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, - 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, - 0x41, 0x1d, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x11, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x9c, 0x01, 0x0a, - 0x0b, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, - 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x92, - 0x41, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x67, 0x65, - 0x74, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xab, 0x01, 0x0a, 0x0e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x29, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x12, 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x01, 0x2a, - 0x1a, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xa8, 0x01, 0x0a, 0x0e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x92, 0x41, 0x1a, + 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x67, 0x65, 0x74, 0x20, + 0x61, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, + 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xab, 0x01, 0x0a, 0x0e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, - 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x12, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xbc, 0x01, 0x0a, 0x0e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, - 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, - 0x92, 0x41, 0x29, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x1d, 0x73, - 0x65, 0x6e, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, - 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x73, - 0x65, 0x6e, 0x64, 0x12, 0xa3, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, - 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x92, - 0x41, 0x1c, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x0f, 0x6c, - 0x69, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0xac, 0x01, 0x0a, 0x0f, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2a, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92, 0x41, 0x1f, 0x0a, 0x09, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, - 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, - 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0xa2, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x92, 0x41, - 0x1c, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x0f, 0x67, 0x65, - 0x74, 0x20, 0x61, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xb1, 0x01, - 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x92, 0x41, 0x1f, 0x0a, - 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x1a, 0x18, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, - 0x7d, 0x12, 0xae, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x42, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x12, 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x01, 0x2a, 0x1a, 0x17, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xa8, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, + 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, - 0x92, 0x41, 0x1f, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x2a, 0x18, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, - 0x64, 0x7d, 0x12, 0xb5, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, - 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x92, 0x41, 0x22, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x73, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xbe, 0x01, 0x0a, 0x12, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x49, 0x92, 0x41, 0x25, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, - 0x3a, 0x01, 0x2a, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xb4, 0x01, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x3f, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x12, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x12, 0xbc, 0x01, 0x0a, 0x0e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, + 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x92, 0x41, + 0x29, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x1d, 0x73, 0x65, 0x6e, + 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x6f, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, + 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x65, 0x6e, + 0x64, 0x12, 0xa3, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, + 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6f, 0x64, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x92, 0x41, 0x1c, + 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x0f, 0x6c, 0x69, 0x73, + 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0xac, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2a, 0x2e, 0x6f, 0x64, + 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92, 0x41, 0x1f, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x3a, 0x01, + 0x2a, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0xa2, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x92, 0x41, 0x1c, 0x0a, + 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x0f, 0x67, 0x65, 0x74, 0x20, + 0x61, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xb1, 0x01, 0x0a, 0x0f, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x92, 0x41, 0x22, 0x0a, 0x0c, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x47, 0x65, 0x74, - 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, - 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, - 0x64, 0x7d, 0x12, 0xc3, 0x01, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4e, 0x92, 0x41, 0x25, 0x0a, 0x0c, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x1a, 0x1b, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xc0, 0x01, 0x0a, 0x12, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x92, 0x41, 0x1f, 0x0a, 0x09, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x20, 0x61, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x1a, 0x18, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, + 0xae, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x92, 0x41, + 0x1f, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x2a, 0x18, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x12, 0xb5, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2c, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, + 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, + 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x43, 0x92, 0x41, 0x22, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, + 0x12, 0x16, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xbe, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4b, + 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x49, 0x92, 0x41, 0x25, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x2a, 0x1b, - 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x9d, 0x01, 0x0a, 0x0d, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x28, 0x2e, + 0x6e, 0x12, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x3a, 0x01, + 0x2a, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xb4, 0x01, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, - 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x37, 0x92, 0x41, 0x1a, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x72, 0x12, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, - 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0xa6, 0x01, 0x0a, 0x0e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x29, - 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x72, 0x12, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x72, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, - 0x22, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x72, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, - 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, - 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x92, 0x41, 0x1a, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x67, 0x65, 0x74, 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x12, 0xab, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x92, 0x41, 0x22, 0x0a, 0x0c, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x47, 0x65, 0x74, 0x20, 0x61, + 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x12, 0xc3, 0x01, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, - 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x92, - 0x41, 0x1d, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x11, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x01, 0x2a, 0x1a, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4e, 0x92, 0x41, 0x25, 0x0a, 0x0c, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x1a, 0x1b, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xc0, 0x01, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x2e, + 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, + 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4b, 0x92, 0x41, + 0x25, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x2a, 0x1b, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x9d, 0x01, 0x0a, 0x0d, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x28, 0x2e, 0x6f, 0x64, + 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, + 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x37, 0x92, 0x41, 0x1a, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, + 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0xa6, 0x01, 0x0a, 0x0e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, + 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x12, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x12, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6f, 0x64, 0x70, + 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x92, 0x41, 0x1a, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x12, 0x0e, 0x67, 0x65, 0x74, 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, - 0x7d, 0x12, 0xa8, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, + 0x7d, 0x12, 0xab, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, - 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x92, 0x41, 0x1d, - 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x11, 0x64, 0x65, 0x6c, 0x65, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x92, 0x41, 0x1d, + 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xa9, 0x01, 0x0a, - 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x6f, 0x64, - 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x65, 0x72, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4c, 0x92, 0x41, 0x14, 0x0a, - 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x0b, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x61, 0x6c, 0x65, - 0x72, 0x74, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x7d, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xb7, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, - 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6c, - 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x54, 0x92, 0x41, - 0x16, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x20, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x3a, 0x04, 0x62, - 0x6f, 0x64, 0x79, 0x22, 0x2d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x6c, - 0x65, 0x72, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x7d, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x7d, 0x12, 0xfc, 0x01, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, - 0x72, 0x74, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x34, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x93, 0x02, 0x1c, 0x3a, 0x01, 0x2a, 0x1a, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, + 0xa8, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, + 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x92, 0x41, 0x1d, 0x0a, 0x08, + 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x2a, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xa9, 0x01, 0x0a, 0x0a, 0x4c, + 0x69, 0x73, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4c, 0x92, 0x41, 0x14, 0x0a, 0x05, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x12, 0x0b, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x61, 0x6c, 0x65, 0x72, 0x74, + 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x7d, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xb7, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, - 0x74, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, - 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x72, 0x92, - 0x41, 0x25, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x1c, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x20, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x3a, 0x04, 0x62, - 0x6f, 0x64, 0x79, 0x22, 0x3c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x6c, - 0x65, 0x72, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x7d, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x7d, 0x12, 0x85, 0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, - 0x24, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, - 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x92, 0x41, - 0x12, 0x0a, 0x04, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x72, 0x75, - 0x6c, 0x65, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x92, 0x01, 0x0a, 0x0a, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x92, 0x41, 0x19, 0x0a, 0x04, 0x52, 0x75, - 0x6c, 0x65, 0x12, 0x11, 0x61, 0x64, 0x64, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, - 0x20, 0x72, 0x75, 0x6c, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x1a, 0x0e, - 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x9d, - 0x01, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, - 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x92, 0x41, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x9e, - 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x26, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x54, 0x92, 0x41, 0x16, 0x0a, + 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, + 0x6c, 0x65, 0x72, 0x74, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x3a, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x22, 0x2d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x6c, 0x65, 0x72, + 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x7d, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, + 0x12, 0xfc, 0x01, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, + 0x73, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x34, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, - 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x3e, 0x92, 0x41, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x0e, - 0x67, 0x65, 0x74, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, - 0xaa, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, + 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, + 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, + 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x72, 0x92, 0x41, 0x25, + 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x1c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, + 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x3a, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x22, 0x3c, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x6c, 0x65, 0x72, + 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x7d, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, + 0x85, 0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x24, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x41, 0x92, 0x41, 0x21, 0x0a, 0x08, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x15, 0x61, 0x64, 0x64, 0x2f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x1a, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0xaa, 0x01, 0x0a, - 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, - 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, - 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x41, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, - 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xb4, 0x01, 0x0a, 0x0e, 0x52, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, - 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x4b, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x12, 0x11, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, - 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, - 0x12, 0xa0, 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, - 0x63, 0x65, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, - 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, + 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x92, 0x41, 0x12, 0x0a, + 0x04, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x72, 0x75, 0x6c, 0x65, + 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x92, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, + 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, + 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x92, 0x41, 0x19, 0x0a, 0x04, 0x52, 0x75, 0x6c, 0x65, + 0x12, 0x11, 0x61, 0x64, 0x64, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x72, + 0x75, 0x6c, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x1a, 0x0e, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, + 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x28, + 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x37, 0x92, 0x41, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x12, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x9e, 0x01, 0x0a, + 0x0b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x92, 0x41, 0x1b, 0x0a, 0x07, 0x53, 0x69, - 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, - 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, - 0x22, 0x11, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x69, 0x6c, 0x65, 0x6e, - 0x63, 0x65, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, - 0x6e, 0x63, 0x65, 0x73, 0x12, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, - 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, - 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, + 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x92, + 0x41, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x67, 0x65, + 0x74, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xaa, 0x01, + 0x0a, 0x0e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, + 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x41, 0x92, 0x41, 0x21, 0x0a, 0x08, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x15, 0x61, 0x64, 0x64, 0x2f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x1a, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0xaa, 0x01, 0x0a, 0x0e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x92, 0x41, 0x1b, 0x0a, 0x07, 0x53, 0x69, - 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x67, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x73, - 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, - 0x12, 0x96, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, - 0x25, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, - 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, - 0x92, 0x41, 0x18, 0x0a, 0x07, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x0d, 0x67, 0x65, - 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x69, 0x6c, 0x65, - 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xa2, 0x01, 0x0a, 0x0d, 0x45, 0x78, - 0x70, 0x69, 0x72, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x28, 0x2e, 0x6f, 0x64, + 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, + 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x41, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x12, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, + 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0xb4, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x6f, 0x64, 0x70, + 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, + 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x4b, 0x92, 0x41, 0x1d, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x12, 0x11, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, + 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0xa0, + 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, + 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, + 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, + 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x92, 0x41, 0x1b, 0x0a, 0x07, 0x53, 0x69, 0x6c, 0x65, + 0x6e, 0x63, 0x65, 0x12, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x69, + 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, + 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, + 0x65, 0x73, 0x12, 0x27, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, - 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, - 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x3c, 0x92, 0x41, 0x1b, 0x0a, 0x07, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x10, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2f, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0xb0, - 0x01, 0x0a, 0x14, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x6e, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x42, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x6e, 0x2f, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, - 0x73, 0x69, 0x72, 0x65, 0x6e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x92, 0x41, 0x52, 0x12, - 0x4d, 0x0a, 0x0a, 0x53, 0x69, 0x72, 0x65, 0x6e, 0x20, 0x41, 0x50, 0x49, 0x73, 0x12, 0x3a, 0x44, - 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, - 0x6f, 0x75, 0x72, 0x20, 0x53, 0x69, 0x72, 0x65, 0x6e, 0x20, 0x41, 0x50, 0x49, 0x20, 0x77, 0x69, - 0x74, 0x68, 0x20, 0x67, 0x52, 0x50, 0x43, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x67, 0x52, 0x50, 0x43, - 0x2d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x32, 0x03, 0x30, 0x2e, 0x35, 0x2a, 0x01, - 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x92, 0x41, 0x1b, 0x0a, 0x07, 0x53, 0x69, 0x6c, 0x65, + 0x6e, 0x63, 0x65, 0x12, 0x10, 0x67, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x73, 0x69, 0x6c, + 0x65, 0x6e, 0x63, 0x65, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x96, + 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x25, 0x2e, + 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, + 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x69, 0x6c, + 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x92, 0x41, + 0x18, 0x0a, 0x07, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x0d, 0x67, 0x65, 0x74, 0x20, + 0x61, 0x20, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, + 0x16, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, + 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xa2, 0x01, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x53, + 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, + 0x92, 0x41, 0x1b, 0x0a, 0x07, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x65, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, + 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0xb0, 0x01, 0x0a, + 0x14, 0x69, 0x6f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, + 0x73, 0x69, 0x72, 0x65, 0x6e, 0x42, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x64, 0x70, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, + 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x73, 0x69, + 0x72, 0x65, 0x6e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x92, 0x41, 0x52, 0x12, 0x4d, 0x0a, + 0x0a, 0x53, 0x69, 0x72, 0x65, 0x6e, 0x20, 0x41, 0x50, 0x49, 0x73, 0x12, 0x3a, 0x44, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x6f, 0x75, + 0x72, 0x20, 0x53, 0x69, 0x72, 0x65, 0x6e, 0x20, 0x41, 0x50, 0x49, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x67, 0x52, 0x50, 0x43, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x67, 0x52, 0x50, 0x43, 0x2d, 0x47, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x32, 0x03, 0x30, 0x2e, 0x35, 0x2a, 0x01, 0x01, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -6302,97 +6304,98 @@ var file_odpf_siren_v1beta1_siren_proto_depIdxs = []int32{ 101, // 55: odpf.siren.v1beta1.Rule.updated_at:type_name -> google.protobuf.Timestamp 56, // 56: odpf.siren.v1beta1.ListRulesResponse.rules:type_name -> odpf.siren.v1beta1.Rule 57, // 57: odpf.siren.v1beta1.UpdateRuleRequest.variables:type_name -> odpf.siren.v1beta1.Variables - 101, // 58: odpf.siren.v1beta1.Template.created_at:type_name -> google.protobuf.Timestamp - 101, // 59: odpf.siren.v1beta1.Template.updated_at:type_name -> google.protobuf.Timestamp - 62, // 60: odpf.siren.v1beta1.Template.variables:type_name -> odpf.siren.v1beta1.TemplateVariables - 63, // 61: odpf.siren.v1beta1.ListTemplatesResponse.templates:type_name -> odpf.siren.v1beta1.Template - 62, // 62: odpf.siren.v1beta1.UpsertTemplateRequest.variables:type_name -> odpf.siren.v1beta1.TemplateVariables - 63, // 63: odpf.siren.v1beta1.GetTemplateResponse.template:type_name -> odpf.siren.v1beta1.Template - 97, // 64: odpf.siren.v1beta1.RenderTemplateRequest.variables:type_name -> odpf.siren.v1beta1.RenderTemplateRequest.VariablesEntry - 100, // 65: odpf.siren.v1beta1.Silence.target_expression:type_name -> google.protobuf.Struct - 101, // 66: odpf.siren.v1beta1.Silence.created_at:type_name -> google.protobuf.Timestamp - 101, // 67: odpf.siren.v1beta1.Silence.updated_at:type_name -> google.protobuf.Timestamp - 101, // 68: odpf.siren.v1beta1.Silence.deleted_at:type_name -> google.protobuf.Timestamp - 100, // 69: odpf.siren.v1beta1.CreateSilenceRequest.target_expression:type_name -> google.protobuf.Struct - 98, // 70: odpf.siren.v1beta1.ListSilencesRequest.match:type_name -> odpf.siren.v1beta1.ListSilencesRequest.MatchEntry - 99, // 71: odpf.siren.v1beta1.ListSilencesRequest.subscription_match:type_name -> odpf.siren.v1beta1.ListSilencesRequest.SubscriptionMatchEntry - 74, // 72: odpf.siren.v1beta1.ListSilencesResponse.silences:type_name -> odpf.siren.v1beta1.Silence - 74, // 73: odpf.siren.v1beta1.GetSilenceResponse.silence:type_name -> odpf.siren.v1beta1.Silence - 1, // 74: odpf.siren.v1beta1.SirenService.ListProviders:input_type -> odpf.siren.v1beta1.ListProvidersRequest - 3, // 75: odpf.siren.v1beta1.SirenService.CreateProvider:input_type -> odpf.siren.v1beta1.CreateProviderRequest - 5, // 76: odpf.siren.v1beta1.SirenService.GetProvider:input_type -> odpf.siren.v1beta1.GetProviderRequest - 7, // 77: odpf.siren.v1beta1.SirenService.UpdateProvider:input_type -> odpf.siren.v1beta1.UpdateProviderRequest - 9, // 78: odpf.siren.v1beta1.SirenService.DeleteProvider:input_type -> odpf.siren.v1beta1.DeleteProviderRequest - 45, // 79: odpf.siren.v1beta1.SirenService.NotifyReceiver:input_type -> odpf.siren.v1beta1.NotifyReceiverRequest - 12, // 80: odpf.siren.v1beta1.SirenService.ListNamespaces:input_type -> odpf.siren.v1beta1.ListNamespacesRequest - 14, // 81: odpf.siren.v1beta1.SirenService.CreateNamespace:input_type -> odpf.siren.v1beta1.CreateNamespaceRequest - 16, // 82: odpf.siren.v1beta1.SirenService.GetNamespace:input_type -> odpf.siren.v1beta1.GetNamespaceRequest - 18, // 83: odpf.siren.v1beta1.SirenService.UpdateNamespace:input_type -> odpf.siren.v1beta1.UpdateNamespaceRequest - 20, // 84: odpf.siren.v1beta1.SirenService.DeleteNamespace:input_type -> odpf.siren.v1beta1.DeleteNamespaceRequest - 24, // 85: odpf.siren.v1beta1.SirenService.ListSubscriptions:input_type -> odpf.siren.v1beta1.ListSubscriptionsRequest - 26, // 86: odpf.siren.v1beta1.SirenService.CreateSubscription:input_type -> odpf.siren.v1beta1.CreateSubscriptionRequest - 28, // 87: odpf.siren.v1beta1.SirenService.GetSubscription:input_type -> odpf.siren.v1beta1.GetSubscriptionRequest - 30, // 88: odpf.siren.v1beta1.SirenService.UpdateSubscription:input_type -> odpf.siren.v1beta1.UpdateSubscriptionRequest - 32, // 89: odpf.siren.v1beta1.SirenService.DeleteSubscription:input_type -> odpf.siren.v1beta1.DeleteSubscriptionRequest - 35, // 90: odpf.siren.v1beta1.SirenService.ListReceivers:input_type -> odpf.siren.v1beta1.ListReceiversRequest - 37, // 91: odpf.siren.v1beta1.SirenService.CreateReceiver:input_type -> odpf.siren.v1beta1.CreateReceiverRequest - 39, // 92: odpf.siren.v1beta1.SirenService.GetReceiver:input_type -> odpf.siren.v1beta1.GetReceiverRequest - 41, // 93: odpf.siren.v1beta1.SirenService.UpdateReceiver:input_type -> odpf.siren.v1beta1.UpdateReceiverRequest - 43, // 94: odpf.siren.v1beta1.SirenService.DeleteReceiver:input_type -> odpf.siren.v1beta1.DeleteReceiverRequest - 48, // 95: odpf.siren.v1beta1.SirenService.ListAlerts:input_type -> odpf.siren.v1beta1.ListAlertsRequest - 50, // 96: odpf.siren.v1beta1.SirenService.CreateAlerts:input_type -> odpf.siren.v1beta1.CreateAlertsRequest - 52, // 97: odpf.siren.v1beta1.SirenService.CreateAlertsWithNamespace:input_type -> odpf.siren.v1beta1.CreateAlertsWithNamespaceRequest - 58, // 98: odpf.siren.v1beta1.SirenService.ListRules:input_type -> odpf.siren.v1beta1.ListRulesRequest - 60, // 99: odpf.siren.v1beta1.SirenService.UpdateRule:input_type -> odpf.siren.v1beta1.UpdateRuleRequest - 64, // 100: odpf.siren.v1beta1.SirenService.ListTemplates:input_type -> odpf.siren.v1beta1.ListTemplatesRequest - 68, // 101: odpf.siren.v1beta1.SirenService.GetTemplate:input_type -> odpf.siren.v1beta1.GetTemplateRequest - 66, // 102: odpf.siren.v1beta1.SirenService.UpsertTemplate:input_type -> odpf.siren.v1beta1.UpsertTemplateRequest - 70, // 103: odpf.siren.v1beta1.SirenService.DeleteTemplate:input_type -> odpf.siren.v1beta1.DeleteTemplateRequest - 72, // 104: odpf.siren.v1beta1.SirenService.RenderTemplate:input_type -> odpf.siren.v1beta1.RenderTemplateRequest - 75, // 105: odpf.siren.v1beta1.SirenService.CreateSilence:input_type -> odpf.siren.v1beta1.CreateSilenceRequest - 77, // 106: odpf.siren.v1beta1.SirenService.ListSilences:input_type -> odpf.siren.v1beta1.ListSilencesRequest - 79, // 107: odpf.siren.v1beta1.SirenService.GetSilence:input_type -> odpf.siren.v1beta1.GetSilenceRequest - 81, // 108: odpf.siren.v1beta1.SirenService.ExpireSilence:input_type -> odpf.siren.v1beta1.ExpireSilenceRequest - 2, // 109: odpf.siren.v1beta1.SirenService.ListProviders:output_type -> odpf.siren.v1beta1.ListProvidersResponse - 4, // 110: odpf.siren.v1beta1.SirenService.CreateProvider:output_type -> odpf.siren.v1beta1.CreateProviderResponse - 6, // 111: odpf.siren.v1beta1.SirenService.GetProvider:output_type -> odpf.siren.v1beta1.GetProviderResponse - 8, // 112: odpf.siren.v1beta1.SirenService.UpdateProvider:output_type -> odpf.siren.v1beta1.UpdateProviderResponse - 10, // 113: odpf.siren.v1beta1.SirenService.DeleteProvider:output_type -> odpf.siren.v1beta1.DeleteProviderResponse - 46, // 114: odpf.siren.v1beta1.SirenService.NotifyReceiver:output_type -> odpf.siren.v1beta1.NotifyReceiverResponse - 13, // 115: odpf.siren.v1beta1.SirenService.ListNamespaces:output_type -> odpf.siren.v1beta1.ListNamespacesResponse - 15, // 116: odpf.siren.v1beta1.SirenService.CreateNamespace:output_type -> odpf.siren.v1beta1.CreateNamespaceResponse - 17, // 117: odpf.siren.v1beta1.SirenService.GetNamespace:output_type -> odpf.siren.v1beta1.GetNamespaceResponse - 19, // 118: odpf.siren.v1beta1.SirenService.UpdateNamespace:output_type -> odpf.siren.v1beta1.UpdateNamespaceResponse - 21, // 119: odpf.siren.v1beta1.SirenService.DeleteNamespace:output_type -> odpf.siren.v1beta1.DeleteNamespaceResponse - 25, // 120: odpf.siren.v1beta1.SirenService.ListSubscriptions:output_type -> odpf.siren.v1beta1.ListSubscriptionsResponse - 27, // 121: odpf.siren.v1beta1.SirenService.CreateSubscription:output_type -> odpf.siren.v1beta1.CreateSubscriptionResponse - 29, // 122: odpf.siren.v1beta1.SirenService.GetSubscription:output_type -> odpf.siren.v1beta1.GetSubscriptionResponse - 31, // 123: odpf.siren.v1beta1.SirenService.UpdateSubscription:output_type -> odpf.siren.v1beta1.UpdateSubscriptionResponse - 33, // 124: odpf.siren.v1beta1.SirenService.DeleteSubscription:output_type -> odpf.siren.v1beta1.DeleteSubscriptionResponse - 36, // 125: odpf.siren.v1beta1.SirenService.ListReceivers:output_type -> odpf.siren.v1beta1.ListReceiversResponse - 38, // 126: odpf.siren.v1beta1.SirenService.CreateReceiver:output_type -> odpf.siren.v1beta1.CreateReceiverResponse - 40, // 127: odpf.siren.v1beta1.SirenService.GetReceiver:output_type -> odpf.siren.v1beta1.GetReceiverResponse - 42, // 128: odpf.siren.v1beta1.SirenService.UpdateReceiver:output_type -> odpf.siren.v1beta1.UpdateReceiverResponse - 44, // 129: odpf.siren.v1beta1.SirenService.DeleteReceiver:output_type -> odpf.siren.v1beta1.DeleteReceiverResponse - 49, // 130: odpf.siren.v1beta1.SirenService.ListAlerts:output_type -> odpf.siren.v1beta1.ListAlertsResponse - 51, // 131: odpf.siren.v1beta1.SirenService.CreateAlerts:output_type -> odpf.siren.v1beta1.CreateAlertsResponse - 53, // 132: odpf.siren.v1beta1.SirenService.CreateAlertsWithNamespace:output_type -> odpf.siren.v1beta1.CreateAlertsWithNamespaceResponse - 59, // 133: odpf.siren.v1beta1.SirenService.ListRules:output_type -> odpf.siren.v1beta1.ListRulesResponse - 61, // 134: odpf.siren.v1beta1.SirenService.UpdateRule:output_type -> odpf.siren.v1beta1.UpdateRuleResponse - 65, // 135: odpf.siren.v1beta1.SirenService.ListTemplates:output_type -> odpf.siren.v1beta1.ListTemplatesResponse - 69, // 136: odpf.siren.v1beta1.SirenService.GetTemplate:output_type -> odpf.siren.v1beta1.GetTemplateResponse - 67, // 137: odpf.siren.v1beta1.SirenService.UpsertTemplate:output_type -> odpf.siren.v1beta1.UpsertTemplateResponse - 71, // 138: odpf.siren.v1beta1.SirenService.DeleteTemplate:output_type -> odpf.siren.v1beta1.DeleteTemplateResponse - 73, // 139: odpf.siren.v1beta1.SirenService.RenderTemplate:output_type -> odpf.siren.v1beta1.RenderTemplateResponse - 76, // 140: odpf.siren.v1beta1.SirenService.CreateSilence:output_type -> odpf.siren.v1beta1.CreateSilenceResponse - 78, // 141: odpf.siren.v1beta1.SirenService.ListSilences:output_type -> odpf.siren.v1beta1.ListSilencesResponse - 80, // 142: odpf.siren.v1beta1.SirenService.GetSilence:output_type -> odpf.siren.v1beta1.GetSilenceResponse - 82, // 143: odpf.siren.v1beta1.SirenService.ExpireSilence:output_type -> odpf.siren.v1beta1.ExpireSilenceResponse - 109, // [109:144] is the sub-list for method output_type - 74, // [74:109] is the sub-list for method input_type - 74, // [74:74] is the sub-list for extension type_name - 74, // [74:74] is the sub-list for extension extendee - 0, // [0:74] is the sub-list for field type_name + 56, // 58: odpf.siren.v1beta1.UpdateRuleResponse.rule:type_name -> odpf.siren.v1beta1.Rule + 101, // 59: odpf.siren.v1beta1.Template.created_at:type_name -> google.protobuf.Timestamp + 101, // 60: odpf.siren.v1beta1.Template.updated_at:type_name -> google.protobuf.Timestamp + 62, // 61: odpf.siren.v1beta1.Template.variables:type_name -> odpf.siren.v1beta1.TemplateVariables + 63, // 62: odpf.siren.v1beta1.ListTemplatesResponse.templates:type_name -> odpf.siren.v1beta1.Template + 62, // 63: odpf.siren.v1beta1.UpsertTemplateRequest.variables:type_name -> odpf.siren.v1beta1.TemplateVariables + 63, // 64: odpf.siren.v1beta1.GetTemplateResponse.template:type_name -> odpf.siren.v1beta1.Template + 97, // 65: odpf.siren.v1beta1.RenderTemplateRequest.variables:type_name -> odpf.siren.v1beta1.RenderTemplateRequest.VariablesEntry + 100, // 66: odpf.siren.v1beta1.Silence.target_expression:type_name -> google.protobuf.Struct + 101, // 67: odpf.siren.v1beta1.Silence.created_at:type_name -> google.protobuf.Timestamp + 101, // 68: odpf.siren.v1beta1.Silence.updated_at:type_name -> google.protobuf.Timestamp + 101, // 69: odpf.siren.v1beta1.Silence.deleted_at:type_name -> google.protobuf.Timestamp + 100, // 70: odpf.siren.v1beta1.CreateSilenceRequest.target_expression:type_name -> google.protobuf.Struct + 98, // 71: odpf.siren.v1beta1.ListSilencesRequest.match:type_name -> odpf.siren.v1beta1.ListSilencesRequest.MatchEntry + 99, // 72: odpf.siren.v1beta1.ListSilencesRequest.subscription_match:type_name -> odpf.siren.v1beta1.ListSilencesRequest.SubscriptionMatchEntry + 74, // 73: odpf.siren.v1beta1.ListSilencesResponse.silences:type_name -> odpf.siren.v1beta1.Silence + 74, // 74: odpf.siren.v1beta1.GetSilenceResponse.silence:type_name -> odpf.siren.v1beta1.Silence + 1, // 75: odpf.siren.v1beta1.SirenService.ListProviders:input_type -> odpf.siren.v1beta1.ListProvidersRequest + 3, // 76: odpf.siren.v1beta1.SirenService.CreateProvider:input_type -> odpf.siren.v1beta1.CreateProviderRequest + 5, // 77: odpf.siren.v1beta1.SirenService.GetProvider:input_type -> odpf.siren.v1beta1.GetProviderRequest + 7, // 78: odpf.siren.v1beta1.SirenService.UpdateProvider:input_type -> odpf.siren.v1beta1.UpdateProviderRequest + 9, // 79: odpf.siren.v1beta1.SirenService.DeleteProvider:input_type -> odpf.siren.v1beta1.DeleteProviderRequest + 45, // 80: odpf.siren.v1beta1.SirenService.NotifyReceiver:input_type -> odpf.siren.v1beta1.NotifyReceiverRequest + 12, // 81: odpf.siren.v1beta1.SirenService.ListNamespaces:input_type -> odpf.siren.v1beta1.ListNamespacesRequest + 14, // 82: odpf.siren.v1beta1.SirenService.CreateNamespace:input_type -> odpf.siren.v1beta1.CreateNamespaceRequest + 16, // 83: odpf.siren.v1beta1.SirenService.GetNamespace:input_type -> odpf.siren.v1beta1.GetNamespaceRequest + 18, // 84: odpf.siren.v1beta1.SirenService.UpdateNamespace:input_type -> odpf.siren.v1beta1.UpdateNamespaceRequest + 20, // 85: odpf.siren.v1beta1.SirenService.DeleteNamespace:input_type -> odpf.siren.v1beta1.DeleteNamespaceRequest + 24, // 86: odpf.siren.v1beta1.SirenService.ListSubscriptions:input_type -> odpf.siren.v1beta1.ListSubscriptionsRequest + 26, // 87: odpf.siren.v1beta1.SirenService.CreateSubscription:input_type -> odpf.siren.v1beta1.CreateSubscriptionRequest + 28, // 88: odpf.siren.v1beta1.SirenService.GetSubscription:input_type -> odpf.siren.v1beta1.GetSubscriptionRequest + 30, // 89: odpf.siren.v1beta1.SirenService.UpdateSubscription:input_type -> odpf.siren.v1beta1.UpdateSubscriptionRequest + 32, // 90: odpf.siren.v1beta1.SirenService.DeleteSubscription:input_type -> odpf.siren.v1beta1.DeleteSubscriptionRequest + 35, // 91: odpf.siren.v1beta1.SirenService.ListReceivers:input_type -> odpf.siren.v1beta1.ListReceiversRequest + 37, // 92: odpf.siren.v1beta1.SirenService.CreateReceiver:input_type -> odpf.siren.v1beta1.CreateReceiverRequest + 39, // 93: odpf.siren.v1beta1.SirenService.GetReceiver:input_type -> odpf.siren.v1beta1.GetReceiverRequest + 41, // 94: odpf.siren.v1beta1.SirenService.UpdateReceiver:input_type -> odpf.siren.v1beta1.UpdateReceiverRequest + 43, // 95: odpf.siren.v1beta1.SirenService.DeleteReceiver:input_type -> odpf.siren.v1beta1.DeleteReceiverRequest + 48, // 96: odpf.siren.v1beta1.SirenService.ListAlerts:input_type -> odpf.siren.v1beta1.ListAlertsRequest + 50, // 97: odpf.siren.v1beta1.SirenService.CreateAlerts:input_type -> odpf.siren.v1beta1.CreateAlertsRequest + 52, // 98: odpf.siren.v1beta1.SirenService.CreateAlertsWithNamespace:input_type -> odpf.siren.v1beta1.CreateAlertsWithNamespaceRequest + 58, // 99: odpf.siren.v1beta1.SirenService.ListRules:input_type -> odpf.siren.v1beta1.ListRulesRequest + 60, // 100: odpf.siren.v1beta1.SirenService.UpdateRule:input_type -> odpf.siren.v1beta1.UpdateRuleRequest + 64, // 101: odpf.siren.v1beta1.SirenService.ListTemplates:input_type -> odpf.siren.v1beta1.ListTemplatesRequest + 68, // 102: odpf.siren.v1beta1.SirenService.GetTemplate:input_type -> odpf.siren.v1beta1.GetTemplateRequest + 66, // 103: odpf.siren.v1beta1.SirenService.UpsertTemplate:input_type -> odpf.siren.v1beta1.UpsertTemplateRequest + 70, // 104: odpf.siren.v1beta1.SirenService.DeleteTemplate:input_type -> odpf.siren.v1beta1.DeleteTemplateRequest + 72, // 105: odpf.siren.v1beta1.SirenService.RenderTemplate:input_type -> odpf.siren.v1beta1.RenderTemplateRequest + 75, // 106: odpf.siren.v1beta1.SirenService.CreateSilence:input_type -> odpf.siren.v1beta1.CreateSilenceRequest + 77, // 107: odpf.siren.v1beta1.SirenService.ListSilences:input_type -> odpf.siren.v1beta1.ListSilencesRequest + 79, // 108: odpf.siren.v1beta1.SirenService.GetSilence:input_type -> odpf.siren.v1beta1.GetSilenceRequest + 81, // 109: odpf.siren.v1beta1.SirenService.ExpireSilence:input_type -> odpf.siren.v1beta1.ExpireSilenceRequest + 2, // 110: odpf.siren.v1beta1.SirenService.ListProviders:output_type -> odpf.siren.v1beta1.ListProvidersResponse + 4, // 111: odpf.siren.v1beta1.SirenService.CreateProvider:output_type -> odpf.siren.v1beta1.CreateProviderResponse + 6, // 112: odpf.siren.v1beta1.SirenService.GetProvider:output_type -> odpf.siren.v1beta1.GetProviderResponse + 8, // 113: odpf.siren.v1beta1.SirenService.UpdateProvider:output_type -> odpf.siren.v1beta1.UpdateProviderResponse + 10, // 114: odpf.siren.v1beta1.SirenService.DeleteProvider:output_type -> odpf.siren.v1beta1.DeleteProviderResponse + 46, // 115: odpf.siren.v1beta1.SirenService.NotifyReceiver:output_type -> odpf.siren.v1beta1.NotifyReceiverResponse + 13, // 116: odpf.siren.v1beta1.SirenService.ListNamespaces:output_type -> odpf.siren.v1beta1.ListNamespacesResponse + 15, // 117: odpf.siren.v1beta1.SirenService.CreateNamespace:output_type -> odpf.siren.v1beta1.CreateNamespaceResponse + 17, // 118: odpf.siren.v1beta1.SirenService.GetNamespace:output_type -> odpf.siren.v1beta1.GetNamespaceResponse + 19, // 119: odpf.siren.v1beta1.SirenService.UpdateNamespace:output_type -> odpf.siren.v1beta1.UpdateNamespaceResponse + 21, // 120: odpf.siren.v1beta1.SirenService.DeleteNamespace:output_type -> odpf.siren.v1beta1.DeleteNamespaceResponse + 25, // 121: odpf.siren.v1beta1.SirenService.ListSubscriptions:output_type -> odpf.siren.v1beta1.ListSubscriptionsResponse + 27, // 122: odpf.siren.v1beta1.SirenService.CreateSubscription:output_type -> odpf.siren.v1beta1.CreateSubscriptionResponse + 29, // 123: odpf.siren.v1beta1.SirenService.GetSubscription:output_type -> odpf.siren.v1beta1.GetSubscriptionResponse + 31, // 124: odpf.siren.v1beta1.SirenService.UpdateSubscription:output_type -> odpf.siren.v1beta1.UpdateSubscriptionResponse + 33, // 125: odpf.siren.v1beta1.SirenService.DeleteSubscription:output_type -> odpf.siren.v1beta1.DeleteSubscriptionResponse + 36, // 126: odpf.siren.v1beta1.SirenService.ListReceivers:output_type -> odpf.siren.v1beta1.ListReceiversResponse + 38, // 127: odpf.siren.v1beta1.SirenService.CreateReceiver:output_type -> odpf.siren.v1beta1.CreateReceiverResponse + 40, // 128: odpf.siren.v1beta1.SirenService.GetReceiver:output_type -> odpf.siren.v1beta1.GetReceiverResponse + 42, // 129: odpf.siren.v1beta1.SirenService.UpdateReceiver:output_type -> odpf.siren.v1beta1.UpdateReceiverResponse + 44, // 130: odpf.siren.v1beta1.SirenService.DeleteReceiver:output_type -> odpf.siren.v1beta1.DeleteReceiverResponse + 49, // 131: odpf.siren.v1beta1.SirenService.ListAlerts:output_type -> odpf.siren.v1beta1.ListAlertsResponse + 51, // 132: odpf.siren.v1beta1.SirenService.CreateAlerts:output_type -> odpf.siren.v1beta1.CreateAlertsResponse + 53, // 133: odpf.siren.v1beta1.SirenService.CreateAlertsWithNamespace:output_type -> odpf.siren.v1beta1.CreateAlertsWithNamespaceResponse + 59, // 134: odpf.siren.v1beta1.SirenService.ListRules:output_type -> odpf.siren.v1beta1.ListRulesResponse + 61, // 135: odpf.siren.v1beta1.SirenService.UpdateRule:output_type -> odpf.siren.v1beta1.UpdateRuleResponse + 65, // 136: odpf.siren.v1beta1.SirenService.ListTemplates:output_type -> odpf.siren.v1beta1.ListTemplatesResponse + 69, // 137: odpf.siren.v1beta1.SirenService.GetTemplate:output_type -> odpf.siren.v1beta1.GetTemplateResponse + 67, // 138: odpf.siren.v1beta1.SirenService.UpsertTemplate:output_type -> odpf.siren.v1beta1.UpsertTemplateResponse + 71, // 139: odpf.siren.v1beta1.SirenService.DeleteTemplate:output_type -> odpf.siren.v1beta1.DeleteTemplateResponse + 73, // 140: odpf.siren.v1beta1.SirenService.RenderTemplate:output_type -> odpf.siren.v1beta1.RenderTemplateResponse + 76, // 141: odpf.siren.v1beta1.SirenService.CreateSilence:output_type -> odpf.siren.v1beta1.CreateSilenceResponse + 78, // 142: odpf.siren.v1beta1.SirenService.ListSilences:output_type -> odpf.siren.v1beta1.ListSilencesResponse + 80, // 143: odpf.siren.v1beta1.SirenService.GetSilence:output_type -> odpf.siren.v1beta1.GetSilenceResponse + 82, // 144: odpf.siren.v1beta1.SirenService.ExpireSilence:output_type -> odpf.siren.v1beta1.ExpireSilenceResponse + 110, // [110:145] is the sub-list for method output_type + 75, // [75:110] is the sub-list for method input_type + 75, // [75:75] is the sub-list for extension type_name + 75, // [75:75] is the sub-list for extension extendee + 0, // [0:75] is the sub-list for field type_name } func init() { file_odpf_siren_v1beta1_siren_proto_init() } diff --git a/proto/odpf/siren/v1beta1/siren.pb.validate.go b/proto/odpf/siren/v1beta1/siren.pb.validate.go index c828f88f..eb6e5ca6 100644 --- a/proto/odpf/siren/v1beta1/siren.pb.validate.go +++ b/proto/odpf/siren/v1beta1/siren.pb.validate.go @@ -7996,7 +7996,34 @@ func (m *UpdateRuleResponse) validate(all bool) error { var errors []error - // no validation rules for Id + if all { + switch v := interface{}(m.GetRule()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, UpdateRuleResponseValidationError{ + field: "Rule", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, UpdateRuleResponseValidationError{ + field: "Rule", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetRule()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return UpdateRuleResponseValidationError{ + field: "Rule", + reason: "embedded message failed validation", + cause: err, + } + } + } if len(errors) > 0 { return UpdateRuleResponseMultiError(errors) diff --git a/proto/siren.swagger.yaml b/proto/siren.swagger.yaml index 426826f1..2be0c7a4 100644 --- a/proto/siren.swagger.yaml +++ b/proto/siren.swagger.yaml @@ -1399,9 +1399,8 @@ definitions: UpdateRuleResponse: type: object properties: - id: - type: string - format: uint64 + rule: + $ref: '#/definitions/Rule' UpdateSubscriptionResponse: type: object properties: diff --git a/test/e2e_test/cortex_alerting_test.go b/test/e2e_test/cortex_alerting_test.go index d99b90b0..64be75c1 100644 --- a/test/e2e_test/cortex_alerting_test.go +++ b/test/e2e_test/cortex_alerting_test.go @@ -3,25 +3,16 @@ package e2e_test import ( "bytes" "context" - "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "testing" - "time" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "github.com/mcuadros/go-defaults" - "github.com/odpf/salt/db" "github.com/odpf/siren/config" - "github.com/odpf/siren/core/alert" - "github.com/odpf/siren/core/log" "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/silence" "github.com/odpf/siren/internal/server" - "github.com/odpf/siren/internal/store/model" sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" "github.com/stretchr/testify/suite" "google.golang.org/protobuf/types/known/structpb" @@ -30,7 +21,6 @@ import ( type CortexAlertingTestSuite struct { suite.Suite client sirenv1beta1.SirenServiceClient - dbClient *db.Client cancelClient func() appConfig *config.Config testBench *CortexTest @@ -68,8 +58,6 @@ func (s *CortexAlertingTestSuite) SetupTest() { // TODO host.docker.internal only works for docker-desktop to call a service in host (siren) s.appConfig.Providers.Cortex.WebhookBaseAPI = fmt.Sprintf("http://test:%d/v1beta1/alerts/cortex", apiPort) s.appConfig.Providers.Cortex.GroupWaitDuration = "1s" - s.appConfig.Providers.Cortex.GroupIntervalDuration = "1s" - s.appConfig.Providers.Cortex.RepeatIntervalDuration = "1s" // enable worker s.appConfig.Notification.MessageHandler.Enabled = true @@ -88,11 +76,6 @@ func (s *CortexAlertingTestSuite) SetupTest() { Type: "cortex", }) s.Require().NoError(err) - - s.dbClient, err = db.New(s.testBench.PGConfig) - if err != nil { - s.T().Fatal(err) - } } func (s *CortexAlertingTestSuite) TearDownTest() { @@ -105,25 +88,6 @@ func (s *CortexAlertingTestSuite) TearDownTest() { func (s *CortexAlertingTestSuite) TestAlerting() { ctx := context.Background() - triggerAlertBody := ` - [ - { - "state": "firing", - "value": 1, - "labels": { - "severity": "WARNING", - "team": "odpf", - "service": "some-service", - "environment": "integration" - }, - "annotations": { - "resource": "test_alert", - "metric_name": "test_alert", - "metric_value": "1", - "template": "alert_test" - } - } - ]` _, err := s.client.CreateNamespace(ctx, &sirenv1beta1.CreateNamespaceRequest{ Name: "new-odpf-1", @@ -168,6 +132,26 @@ func (s *CortexAlertingTestSuite) TestAlerting() { }) s.Require().NoError(err) + triggerAlertBody := ` + [ + { + "state": "firing", + "value": 1, + "labels": { + "severity": "WARNING", + "team": "odpf", + "service": "some-service", + "environment": "integration" + }, + "annotations": { + "resource": "test_alert", + "metric_name": "test_alert", + "metric_value": "1", + "template": "alert_test" + } + } + ]` + for { bodyBytes, err := triggerCortexAlert(s.testBench.NginxHost, "new-odpf-1", triggerAlertBody) s.Assert().NoError(err) @@ -181,59 +165,10 @@ func (s *CortexAlertingTestSuite) TestAlerting() { } }) - } func (s *CortexAlertingTestSuite) TestIncomingHookAPI() { - var ( - ctx = context.Background() - triggerAlertBody = ` - { - "receiver": "http_subscribe-http-receiver-notification_receiverId_2_idx_0", - "status": "firing", - "alerts": [ - { - "status": "firing", - "labels": { - "key1": "value1", - "key2": "value2", - "severity": "WARNING", - "alertname": "some alert name", - "summary": "this is test alert", - "service": "some-service", - "environment": "integration", - "team": "odpf" - }, - "annotations": { - "metric_name": "test_alert", - "metric_value": "1", - "resource": "test_alert", - "template": "alert_test", - "summary": "this is test alert" - }, - "startsAt": "2022-10-06T03:39:19.2964655Z", - "endsAt": "0001-01-01T00:00:00Z", - "generatorURL": "", - "fingerprint": "684c979dcb5ffb96" - } - ], - "groupLabels": {}, - "commonLabels": { - "environment": "integration", - "team": "odpf" - }, - "commonAnnotations": { - "metric_name": "test_alert", - "metric_value": "1", - "resource": "test_alert", - "template": "alert_test" - }, - "externalURL": "/api/prom/alertmanager", - "version": "4", - "groupKey": "{}/{environment=\"integration\",team=\"odpf\"}:{}", - "truncatedAlerts": 0 - }` - ) + ctx := context.Background() _, err := s.client.CreateNamespace(ctx, &sirenv1beta1.CreateNamespaceRequest{ Name: "new-odpf-1", @@ -246,7 +181,7 @@ func (s *CortexAlertingTestSuite) TestIncomingHookAPI() { }) s.Require().NoError(err) - s.Run("incoming alert in alerts hook API with matching subscription labels should trigger notification", func() { + s.Run("Incoming alert in alerts hook API with matching subscription labels should trigger notification", func() { waitChan := make(chan struct{}, 1) // add receiver odpf-http @@ -255,40 +190,8 @@ func (s *CortexAlertingTestSuite) TestIncomingHookAPI() { defer r.Body.Close() s.Assert().NoError(err) - type sampleStruct struct { - ID string `json:"id"` - Alertname string `json:"alertname"` - Environment string `json:"environment"` - GeneratorURL string `json:"generator_url"` - Key1 string `json:"key1"` - Key2 string `json:"key2"` - MetricName string `json:"metric_name"` - MetricValue string `json:"metric_value"` - NotificationType string `json:"notification_type"` - NumAlertsFiring int `json:"num_alerts_firing"` - Resource string `json:"resource"` - Service string `json:"service"` - Severity string `json:"severity"` - Status string `json:"status"` - Firing string `json:"firing"` - Summary string `json:"summary"` - Team string `json:"team"` - Template string `json:"template"` - } - - expectedBody := `{"alertname":"some alert name","environment":"integration","generator_url":"","id":"0998ab88-3f5d-4d4a-a66f-40960b105f37","key1":"value1","key2":"value2","metric_name":"test_alert","metric_value":"1","notification_type":"subscriber","num_alerts_firing":1,"resource":"test_alert","service":"some-service","severity":"WARNING","status":"firing","summary":"this is test alert","team":"odpf","template":"alert_test"}` - - var ( - expectedStruct sampleStruct - resultStruct sampleStruct - ) - - s.Require().NoError(json.Unmarshal([]byte(expectedBody), &expectedStruct)) - s.Require().NoError(json.Unmarshal([]byte(body), &resultStruct)) - - if diff := cmp.Diff(expectedStruct, resultStruct, cmpopts.IgnoreFields(sampleStruct{}, "ID")); diff != "" { - s.T().Errorf("got diff: %v", diff) - } + expectedBody := `{"alertname":"some alert name","environment":"integration","generatorUrl":"","id":"cortex-684c979dcb5ffb96","key1":"value1","key2":"value2","metric_name":"test_alert","metric_value":"1","numAlertsFiring":1,"resource":"test_alert","routing_method":"subscribers","service":"some-service","severity":"WARNING","status":"firing","summary":"this is test alert","team":"odpf","template":"alert_test"}` + s.Assert().Equal(expectedBody, string(body)) close(waitChan) })) s.Require().Nil(err) @@ -325,33 +228,52 @@ func (s *CortexAlertingTestSuite) TestIncomingHookAPI() { }) s.Require().NoError(err) - res, err := http.DefaultClient.Post(fmt.Sprintf("http://localhost:%d/v1beta1/alerts/cortex/1/1", s.appConfig.Service.Port), "application/json", bytes.NewBufferString(triggerAlertBody)) - s.Require().NoError(err) - - bodyJSon, _ := io.ReadAll(res.Body) - fmt.Println(string(bodyJSon)) - - _, err = io.Copy(io.Discard, res.Body) - s.Require().NoError(err) - defer res.Body.Close() - - <-waitChan - }) - - s.Run("triggering cortex alert with matching subscription labels and silence by labels should not trigger notification", func() { - targetExpression, err := structpb.NewStruct(map[string]interface{}{ - "team": "odpf", - "service": "some-service", - "environment": "integration", - }) - s.Require().NoError(err) - - _, err = s.client.CreateSilence(ctx, &sirenv1beta1.CreateSilenceRequest{ - NamespaceId: 1, - Type: silence.TypeMatchers, - TargetExpression: targetExpression, - }) - s.Require().NoError(err) + triggerAlertBody := ` + { + "receiver": "http_subscribe-http-receiver-notification_receiverId_2_idx_0", + "status": "firing", + "alerts": [ + { + "status": "firing", + "labels": { + "key1": "value1", + "key2": "value2", + "severity": "WARNING", + "alertname": "some alert name", + "summary": "this is test alert", + "service": "some-service", + "environment": "integration", + "team": "odpf" + }, + "annotations": { + "metric_name": "test_alert", + "metric_value": "1", + "resource": "test_alert", + "template": "alert_test", + "summary": "this is test alert" + }, + "startsAt": "2022-10-06T03:39:19.2964655Z", + "endsAt": "0001-01-01T00:00:00Z", + "generatorURL": "", + "fingerprint": "684c979dcb5ffb96" + } + ], + "groupLabels": {}, + "commonLabels": { + "environment": "integration", + "team": "odpf" + }, + "commonAnnotations": { + "metric_name": "test_alert", + "metric_value": "1", + "resource": "test_alert", + "template": "alert_test" + }, + "externalURL": "/api/prom/alertmanager", + "version": "4", + "groupKey": "{}/{environment=\"integration\",team=\"odpf\"}:{}", + "truncatedAlerts": 0 + }` res, err := http.DefaultClient.Post(fmt.Sprintf("http://localhost:%d/v1beta1/alerts/cortex/1/1", s.appConfig.Service.Port), "application/json", bytes.NewBufferString(triggerAlertBody)) s.Require().NoError(err) @@ -363,83 +285,7 @@ func (s *CortexAlertingTestSuite) TestIncomingHookAPI() { s.Require().NoError(err) defer res.Body.Close() - time.Sleep(5 * time.Second) - - rows, err := s.dbClient.QueryxContext(context.Background(), `select * from notification_log`) - s.Require().NoError(err) - - var notificationLogs []log.Notification - for rows.Next() { - var nlModel model.NotificationLog - s.Require().NoError(rows.StructScan(&nlModel)) - notificationLogs = append(notificationLogs, nlModel.ToDomain()) - } - - // check alert ids of notification logs - if diff := cmp.Diff(notificationLogs, - []log.Notification{ - { - NamespaceID: 1, - ReceiverID: 1, - AlertIDs: []int64{1}, - SubscriptionID: 1, - }, - { - NamespaceID: 1, - SubscriptionID: 1, - AlertIDs: []int64{2}, - }, - }, - cmpopts.IgnoreFields(log.Notification{}, "ID", "NotificationID", "SilenceIDs", "CreatedAt")); diff != "" { - s.T().Fatalf("found diff %v", diff) - } - - var silenceExist bool - for _, nl := range notificationLogs { - if len(nl.SilenceIDs) != 0 { - silenceExist = true - } - } - s.Assert().True(silenceExist) - - rows, err = s.dbClient.QueryxContext(context.Background(), `select * from alerts`) - s.Require().NoError(err) - - var alerts []alert.Alert - for rows.Next() { - var alrtModel model.Alert - s.Require().NoError(rows.StructScan(&alrtModel)) - alerts = append(alerts, *alrtModel.ToDomain()) - } - - if diff := cmp.Diff(alerts, - []alert.Alert{ - { - ID: 1, - ProviderID: 1, - NamespaceID: 1, - ResourceName: "test_alert", - MetricName: "test_alert", - MetricValue: "1", - Severity: "WARNING", - Rule: "alert_test", - }, - { - ID: 2, - ProviderID: 1, - NamespaceID: 1, - ResourceName: "test_alert", - MetricName: "test_alert", - MetricValue: "1", - Severity: "WARNING", - Rule: "alert_test", - SilenceStatus: alert.SilenceStatusTotal, - }, - }, - cmpopts.IgnoreFields(alert.Alert{}, "ID", "TriggeredAt", "CreatedAt", "UpdatedAt")); diff != "" { - s.T().Fatalf("found diff %v", diff) - } - + <-waitChan }) } @@ -457,35 +303,15 @@ func (s *CortexAlertingTestSuite) TestSendNotification() { }) s.Require().NoError(err) - s.Run("triggering alert with matching subscription labels should trigger notification", func() { + s.Run("3. Triggering alert with matching subscription labels should trigger notification", func() { waitChan := make(chan struct{}, 1) testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) s.Assert().NoError(err) - type sampleStruct struct { - ID string `json:"id"` - Key1 string `json:"key1"` - Key2 string `json:"key2"` - Key3 string `json:"key3"` - NotificationType string `json:"notification_type"` - ReceiverID string `json:"receiver_id"` - } - - expectedBody := `{"key1":"value1","key2":"value2","key3":"value3","notification_type":"receiver","receiver_id":"1"}` - var ( - expectedStruct sampleStruct - resultStruct sampleStruct - ) - - s.Require().NoError(json.Unmarshal([]byte(expectedBody), &expectedStruct)) - s.Require().NoError(json.Unmarshal([]byte(body), &resultStruct)) - - if diff := cmp.Diff(expectedStruct, resultStruct, cmpopts.IgnoreFields(sampleStruct{}, "ID")); diff != "" { - s.T().Errorf("got diff: %v", diff) - } - + expectedBody := `{"key1":"value1","key2":"value2","key3":"value3","routing_method":"receiver"}` + s.Assert().Equal(expectedBody, string(body)) close(waitChan) })) s.Require().Nil(err) diff --git a/test/e2e_test/cortex_helper_test.go b/test/e2e_test/cortex_helper_test.go index fdc52cd4..9f20f99a 100644 --- a/test/e2e_test/cortex_helper_test.go +++ b/test/e2e_test/cortex_helper_test.go @@ -188,7 +188,6 @@ func InitCortexEnvironment(appConfig *config.Config) (*CortexTest, error) { dockertestx.PostgresWithLogger(logger), dockertestx.PostgresWithDockertestNetwork(ct.network), dockertestx.PostgresWithDockerPool(ct.pool), - dockertestx.PostgresWithVersionTag("13"), ) if err != nil { return nil, err diff --git a/test/e2e_test/cortex_namespace_test.go b/test/e2e_test/cortex_namespace_test.go index 894915dc..be896809 100644 --- a/test/e2e_test/cortex_namespace_test.go +++ b/test/e2e_test/cortex_namespace_test.go @@ -78,7 +78,7 @@ func (s *CortexNamespaceTestSuite) TearDownTest() { func (s *CortexNamespaceTestSuite) TestNamespace() { ctx := context.Background() - s.Run("initial state alert config not set, add a namespace will set config for the provider tenant", func() { + s.Run("Initial state alert config not set, add a namespace will set config for the provider tenant", func() { _, err := s.client.CreateNamespace(ctx, &sirenv1beta1.CreateNamespaceRequest{ Name: "new-odpf-1", Urn: "new-odpf-1", diff --git a/test/e2e_test/cortex_rule_test.go b/test/e2e_test/cortex_rule_test.go index ef83fc0f..e85d2e56 100644 --- a/test/e2e_test/cortex_rule_test.go +++ b/test/e2e_test/cortex_rule_test.go @@ -75,7 +75,7 @@ func (s *CortexRuleTestSuite) TearDownTest() { func (s *CortexRuleTestSuite) TestRules() { ctx := context.Background() - s.Run("initial state has no rule groups, upload rules and templates should return `testdata/cortex/expected-cortexrule-scenario-1.yaml`", func() { + s.Run("1. initial state has no rule groups, upload rules and templates should return `testdata/cortex/expected-cortexrule-scenario-1.yaml`", func() { err := uploadTemplate(ctx, s.client, "testdata/cortex/template-rule-sample-1.yaml") s.Require().NoError(err) err = uploadTemplate(ctx, s.client, "testdata/cortex/template-rule-sample-2.yaml") diff --git a/test/e2e_test/helper_test.go b/test/e2e_test/helper_test.go index ea33c1e8..9b8f7b4f 100644 --- a/test/e2e_test/helper_test.go +++ b/test/e2e_test/helper_test.go @@ -17,7 +17,7 @@ import ( sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "gopkg.in/yaml.v3" + "gopkg.in/yaml.v2" ) func uploadTemplate(ctx context.Context, cl sirenv1beta1.SirenServiceClient, filePath string) error { diff --git a/test/e2e_test/notification_test.go b/test/e2e_test/notification_test.go index ddf55a25..e0839223 100644 --- a/test/e2e_test/notification_test.go +++ b/test/e2e_test/notification_test.go @@ -2,7 +2,6 @@ package e2e_test import ( "context" - "encoding/json" "fmt" "io" "net/http" @@ -10,8 +9,6 @@ import ( "testing" "time" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "github.com/mcuadros/go-defaults" "github.com/odpf/siren/config" "github.com/odpf/siren/core/notification" @@ -88,6 +85,8 @@ func (s *NotificationTestSuite) TearDownTest() { } func (s *NotificationTestSuite) TestSendNotification() { + expectedNotification := `{"icon_emoji":":smile:","routing_method":"receiver","text":"test send notification"}` + ctx := context.Background() controlChan := make(chan struct{}, 1) @@ -95,27 +94,8 @@ func (s *NotificationTestSuite) TestSendNotification() { testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { bodyBytes, err := io.ReadAll(r.Body) s.Assert().NoError(err) + s.Assert().Equal(expectedNotification, string(bodyBytes)) - type sampleStruct struct { - ID string `json:"id"` - IconEmoji string `json:"icon_emoji"` - NotificationType string `json:"notification_type"` - ReceiverID string `json:"receiver_id"` - Text string `json:"text"` - } - - expectedNotification := `{"icon_emoji":":smile:","notification_type":"receiver","receiver_id":"2","text":"test send notification"}` - - var ( - resultStruct sampleStruct - expectedStruct sampleStruct - ) - s.Assert().NoError(json.Unmarshal(bodyBytes, &resultStruct)) - s.Assert().NoError(json.Unmarshal([]byte(expectedNotification), &expectedStruct)) - - if diff := cmp.Diff(expectedStruct, resultStruct, cmpopts.IgnoreFields(sampleStruct{}, "ID")); diff != "" { - s.T().Errorf("got diff: %v", diff) - } controlChan <- struct{}{} }))