diff --git a/Makefile b/Makefile index d57d22f6..60ab7e34 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 := "d6ec8837cf20f75fbaa1e834789caa650ee4378d" +PROTON_COMMIT := "3e995d8f0898b07bd35576b00012718cd7682b2f" .PHONY: all build test clean dist vet proto install @@ -23,6 +23,7 @@ coverage: ## Print code coverage go test -race -coverprofile coverage.out -covermode=atomic ./... && go tool cover -html=coverage.out generate: ## run all go generate in the code base (including generating mock files) + find . -type d -name "mocks" | xargs rm -r go generate ./... lint: ## lint checker diff --git a/cli/deps.go b/cli/deps.go index e7c5f291..b540c239 100644 --- a/cli/deps.go +++ b/cli/deps.go @@ -1,8 +1,6 @@ package cli import ( - "fmt" - "github.com/odpf/salt/log" "github.com/odpf/siren/config" "github.com/odpf/siren/core/alert" @@ -15,8 +13,6 @@ import ( "github.com/odpf/siren/core/template" "github.com/odpf/siren/internal/api" "github.com/odpf/siren/internal/store/postgres" - "github.com/odpf/siren/pkg/httpclient" - "github.com/odpf/siren/pkg/retry" "github.com/odpf/siren/pkg/secret" "github.com/odpf/siren/plugins/providers/cortex" "github.com/odpf/siren/plugins/receivers/file" @@ -25,40 +21,31 @@ import ( "github.com/odpf/siren/plugins/receivers/slack" ) -type ReceiverClient struct { - SlackClient *slack.Client - PagerDutyClient *pagerduty.Client - HTTPReceiverClient *httpreceiver.Client -} - -type ProviderClient struct { - CortexClient *cortex.Client -} - func InitAPIDeps( logger log.Logger, cfg config.Config, pgClient *postgres.Client, encryptor *secret.Crypto, queue notification.Queuer, -) (*api.Deps, *ReceiverClient, *ProviderClient, map[string]notification.Notifier, error) { +) (*api.Deps, map[string]notification.Notifier, error) { templateRepository := postgres.NewTemplateRepository(pgClient) templateService := template.NewService(templateRepository) alertRepository := postgres.NewAlertRepository(pgClient) - alertHistoryService := alert.NewService(alertRepository) providerRepository := postgres.NewProviderRepository(pgClient) providerService := provider.NewService(providerRepository) namespaceRepository := postgres.NewNamespaceRepository(pgClient) - namespaceService := namespace.NewService(encryptor, namespaceRepository) - cortexClient, err := cortex.NewClient(cortex.Config{Address: cfg.Cortex.Address}) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to init cortex client: %w", err) - } - cortexProviderService := cortex.NewProviderService(cortexClient) + cortexPluginService := cortex.NewPluginService(logger, cfg.Providers.Cortex) + + alertHistoryService := alert.NewService(alertRepository, map[string]alert.AlertTransformer{ + provider.TypeCortex: cortexPluginService, + }) + namespaceService := namespace.NewService(encryptor, namespaceRepository, providerService, map[string]namespace.ConfigSyncer{ + provider.TypeCortex: cortexPluginService, + }) ruleRepository := postgres.NewRuleRepository(pgClient) ruleService := rule.NewService( @@ -66,47 +53,24 @@ func InitAPIDeps( templateService, namespaceService, map[string]rule.RuleUploader{ - provider.TypeCortex: cortexProviderService, + provider.TypeCortex: cortexPluginService, }, ) // plugin receiver services - slackHTTPClient := httpclient.New(cfg.Receivers.Slack.HTTPClient) - slackRetrier := retry.New(cfg.Receivers.Slack.Retry) - slackClient := slack.NewClient( - cfg.Receivers.Slack, - slack.ClientWithHTTPClient(slackHTTPClient), - slack.ClientWithRetrier(slackRetrier), - ) - pagerdutyHTTPClient := httpclient.New(cfg.Receivers.Pagerduty.HTTPClient) - pagerdutyRetrier := retry.New(cfg.Receivers.Slack.Retry) - pagerdutyClient := pagerduty.NewClient( - cfg.Receivers.Pagerduty, - pagerduty.ClientWithHTTPClient(pagerdutyHTTPClient), - pagerduty.ClientWithRetrier(pagerdutyRetrier), - ) - httpreceiverHTTPClient := httpclient.New(cfg.Receivers.HTTPReceiver.HTTPClient) - httpreceiverRetrier := retry.New(cfg.Receivers.Slack.Retry) - httpreceiverClient := httpreceiver.NewClient( - logger, - cfg.Receivers.HTTPReceiver, - httpreceiver.ClientWithHTTPClient(httpreceiverHTTPClient), - httpreceiver.ClientWithRetrier(httpreceiverRetrier), - ) - - slackReceiverService := slack.NewReceiverService(slackClient, encryptor) - httpReceiverService := httpreceiver.NewReceiverService() - pagerDutyReceiverService := pagerduty.NewReceiverService() - fileReceiverService := file.NewReceiverService() + slackPluginService := slack.NewPluginService(cfg.Receivers.Slack, encryptor) + pagerDutyPluginService := pagerduty.NewPluginService(cfg.Receivers.Pagerduty) + httpreceiverPluginService := httpreceiver.NewPluginService(logger, cfg.Receivers.HTTPReceiver) + filePluginService := file.NewPluginService() receiverRepository := postgres.NewReceiverRepository(pgClient) receiverService := receiver.NewService( receiverRepository, map[string]receiver.ConfigResolver{ - receiver.TypeSlack: slackReceiverService, - receiver.TypeHTTP: httpReceiverService, - receiver.TypePagerDuty: pagerDutyReceiverService, - receiver.TypeFile: fileReceiverService, + receiver.TypeSlack: slackPluginService, + receiver.TypeHTTP: httpreceiverPluginService, + receiver.TypePagerDuty: pagerDutyPluginService, + receiver.TypeFile: filePluginService, }, ) @@ -115,20 +79,14 @@ func InitAPIDeps( subscriptionRepository, namespaceService, receiverService, - subscription.RegisterProviderPlugin(provider.TypeCortex, cortexProviderService), ) // notification - slackNotificationService := slack.NewNotificationService(slackClient, encryptor) - pagerdutyNotificationService := pagerduty.NewNotificationService(pagerdutyClient) - httpreceiverNotificationService := httpreceiver.NewNotificationService(httpreceiverClient) - fileNotificationService := file.NewNotificationService() - notifierRegistry := map[string]notification.Notifier{ - receiver.TypeSlack: slackNotificationService, - receiver.TypePagerDuty: pagerdutyNotificationService, - receiver.TypeHTTP: httpreceiverNotificationService, - receiver.TypeFile: fileNotificationService, + receiver.TypeSlack: slackPluginService, + receiver.TypePagerDuty: pagerDutyPluginService, + receiver.TypeHTTP: httpreceiverPluginService, + receiver.TypeFile: filePluginService, } notificationService := notification.NewService(logger, queue, receiverService, subscriptionService, notifierRegistry) @@ -142,12 +100,6 @@ func InitAPIDeps( ReceiverService: receiverService, SubscriptionService: subscriptionService, NotificationService: notificationService, - }, &ReceiverClient{ - SlackClient: slackClient, - PagerDutyClient: pagerdutyClient, - HTTPReceiverClient: httpreceiverClient, - }, &ProviderClient{ - CortexClient: cortexClient, }, notifierRegistry, nil } diff --git a/cli/receiver.go b/cli/receiver.go index 51883d67..3f82ac60 100644 --- a/cli/receiver.go +++ b/cli/receiver.go @@ -249,6 +249,8 @@ func updateReceiverCmd(cmdxConfig *cmdx.Config) *cobra.Command { Short: "Edit a receiver", Long: heredoc.Doc(` Edit an existing receiver. + + Note: receiver type is immutable. `), Annotations: map[string]string{ "group:core": "true", @@ -283,7 +285,6 @@ func updateReceiverCmd(cmdxConfig *cmdx.Config) *cobra.Command { _, err = client.UpdateReceiver(ctx, &sirenv1beta1.UpdateReceiverRequest{ Id: id, Name: receiverConfig.Name, - Type: receiverConfig.Type, Configurations: grpcConfigurations, Labels: receiverConfig.Labels, }) diff --git a/cli/root.go b/cli/root.go index 2155949b..ce6a7c42 100644 --- a/cli/root.go +++ b/cli/root.go @@ -41,6 +41,7 @@ func New() *cobra.Command { rootCmd.AddCommand(receiversCmd(cmdxConfig)) rootCmd.AddCommand(templatesCmd(cmdxConfig)) rootCmd.AddCommand(rulesCmd(cmdxConfig)) + rootCmd.AddCommand(subscriptionsCmd(cmdxConfig)) rootCmd.AddCommand(alertsCmd(cmdxConfig)) rootCmd.AddCommand(jobCmd(cmdxConfig)) rootCmd.AddCommand(workerCmd()) diff --git a/cli/server.go b/cli/server.go index 02c0182c..30103516 100644 --- a/cli/server.go +++ b/cli/server.go @@ -165,7 +165,7 @@ func StartServer(ctx context.Context, cfg config.Config) error { dlq = inmemory.New(logger, 10) } - apiDeps, _, _, notifierRegistry, err := InitAPIDeps(logger, cfg, pgClient, encryptor, queue) + apiDeps, notifierRegistry, err := InitAPIDeps(logger, cfg, pgClient, encryptor, queue) if err != nil { return err } diff --git a/cli/subscription.go b/cli/subscription.go new file mode 100644 index 00000000..e1d9fb6c --- /dev/null +++ b/cli/subscription.go @@ -0,0 +1,414 @@ +package cli + +import ( + "encoding/json" + "fmt" + "os" + "strconv" + + "github.com/MakeNowJust/heredoc" + "github.com/odpf/salt/cmdx" + "github.com/odpf/salt/printer" + "github.com/odpf/siren/core/subscription" + "github.com/odpf/siren/pkg/errors" + sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" + "github.com/spf13/cobra" + "google.golang.org/protobuf/types/known/structpb" +) + +func subscriptionsCmd(cmdxConfig *cmdx.Config) *cobra.Command { + cmd := &cobra.Command{ + Use: "subscription", + Aliases: []string{"subscriptions"}, + Short: "Manage subscriptions", + Long: heredoc.Doc(` + Work with subscriptions. + + Subscribe to a notification with matching label. + `), + Annotations: map[string]string{ + "group:core": "true", + "client": "true", + }, + } + + cmd.AddCommand( + listSubscriptionsCmd(cmdxConfig), + viewSubscriptionCmd(cmdxConfig), + createSubscriptionCmd(cmdxConfig), + updateSubscriptionCmd(cmdxConfig), + deleteSubscriptionCmd(cmdxConfig), + ) + + return cmd +} + +func listSubscriptionsCmd(cmdxConfig *cmdx.Config) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List subscriptions", + Long: heredoc.Doc(` + List all registered subscriptions. + `), + Annotations: map[string]string{ + "group:core": "true", + }, + RunE: func(cmd *cobra.Command, args []string) error { + spinner := printer.Spin("") + defer spinner.Stop() + + ctx := cmd.Context() + + c, err := loadClientConfig(cmd, cmdxConfig) + if err != nil { + return err + } + + client, cancel, err := createClient(ctx, c.Host) + if err != nil { + return err + } + defer cancel() + + resNamespaces, err := client.ListNamespaces(ctx, &sirenv1beta1.ListNamespacesRequest{}) + if err != nil { + return err + } + + var namespaceMaps = make(map[uint64]string) + for _, n := range resNamespaces.GetNamespaces() { + namespaceMaps[n.GetId()] = n.GetUrn() + } + + res, err := client.ListSubscriptions(ctx, &sirenv1beta1.ListSubscriptionsRequest{}) + if err != nil { + return err + } + + if res.GetSubscriptions() == nil { + return errors.New("no response from server") + } + + spinner.Stop() + subscriptions := res.GetSubscriptions() + report := [][]string{} + + fmt.Printf(" \nShowing %d of %d subscriptions\n \n", len(subscriptions), len(subscriptions)) + report = append(report, []string{"ID", "URN", "NAMESPACE", "RECEIVERS", "MATCH LABELS"}) + + for _, p := range subscriptions { + var ( + ns string + ok bool + ) + + if ns, ok = namespaceMaps[p.GetNamespace()]; !ok { + return fmt.Errorf("unrecognized namespace %d", p.GetNamespace()) + } + + matchStr, err := json.Marshal(p.GetMatch()) + if err != nil { + return errors.New("cannot marshal match labels") + } + + receiversStr, err := json.Marshal(p.GetReceivers()) + if err != nil { + return errors.New("cannot marshal receivers metadata") + } + + report = append(report, []string{ + fmt.Sprintf("%v", p.GetId()), + p.GetUrn(), + ns, + string(receiversStr), + string(matchStr), + }) + } + printer.Table(os.Stdout, report) + + fmt.Println("\nFor details on a subscription, try: siren subscription view ") + return nil + }, + } + + return cmd +} + +func viewSubscriptionCmd(cmdxConfig *cmdx.Config) *cobra.Command { + var format string + cmd := &cobra.Command{ + Use: "view", + Short: "View a subscription details", + Long: heredoc.Doc(` + View a subscription. + + Display the id, urn, namespace, receivers, and label matchers of a subscription. + `), + Example: heredoc.Doc(` + $ siren subscription view 1 + `), + Annotations: map[string]string{ + "group:core": "true", + }, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + spinner := printer.Spin("") + defer spinner.Stop() + + ctx := cmd.Context() + + c, err := loadClientConfig(cmd, cmdxConfig) + if err != nil { + return err + } + + client, cancel, err := createClient(ctx, c.Host) + if err != nil { + return err + } + defer cancel() + + id, err := strconv.ParseUint(args[0], 10, 32) + if err != nil { + return fmt.Errorf("invalid receiver id: %v", err) + } + + res, err := client.GetSubscription(ctx, &sirenv1beta1.GetSubscriptionRequest{ + Id: uint64(id), + }) + if err != nil { + return err + } + + if res.GetSubscription() == nil { + return errors.New("no response from server") + } + + var subscriptionReceivers []subscription.Receiver + for _, sr := range res.GetSubscription().GetReceivers() { + subscriptionReceivers = append(subscriptionReceivers, subscription.Receiver{ + ID: sr.GetId(), + Configuration: sr.GetConfiguration().AsMap(), + }) + } + + sub := &subscription.Subscription{ + ID: res.GetSubscription().GetId(), + URN: res.GetSubscription().GetUrn(), + Namespace: res.GetSubscription().GetNamespace(), + Receivers: subscriptionReceivers, + Match: res.GetSubscription().GetMatch(), + CreatedAt: res.GetSubscription().GetCreatedAt().AsTime(), + UpdatedAt: res.GetSubscription().GetUpdatedAt().AsTime(), + } + + spinner.Stop() + if err := printer.File(sub, format); err != nil { + return fmt.Errorf("failed to format subscription: %v", err) + } + return nil + }, + } + + cmd.Flags().StringVar(&format, "format", "yaml", "Print output with the selected format") + + return cmd +} + +func createSubscriptionCmd(cmdxConfig *cmdx.Config) *cobra.Command { + var filePath string + cmd := &cobra.Command{ + Use: "create", + Short: "Create a new subscription", + Long: heredoc.Doc(` + Create a new subscription. + `), + Annotations: map[string]string{ + "group:core": "true", + }, + RunE: func(cmd *cobra.Command, args []string) error { + spinner := printer.Spin("") + defer spinner.Stop() + + ctx := cmd.Context() + + c, err := loadClientConfig(cmd, cmdxConfig) + if err != nil { + return err + } + + var subscriptionDetail subscription.Subscription + if err := parseFile(filePath, &subscriptionDetail); err != nil { + return err + } + + var receiverMetadatasPB []*sirenv1beta1.ReceiverMetadata + for _, rcv := range subscriptionDetail.Receivers { + grpcConfigurations, err := structpb.NewStruct(rcv.Configuration) + if err != nil { + return err + } + receiverMetadatasPB = append(receiverMetadatasPB, &sirenv1beta1.ReceiverMetadata{ + Id: rcv.ID, + Configuration: grpcConfigurations, + }) + } + + client, cancel, err := createClient(ctx, c.Host) + if err != nil { + return err + } + defer cancel() + + res, err := client.CreateSubscription(ctx, &sirenv1beta1.CreateSubscriptionRequest{ + Urn: subscriptionDetail.URN, + Namespace: subscriptionDetail.Namespace, + Match: subscriptionDetail.Match, + Receivers: receiverMetadatasPB, + }) + + if err != nil { + return err + } + + spinner.Stop() + printer.Successf("Subscription created with id: %v", res.GetId()) + printer.Space() + printer.SuccessIcon() + + return nil + }, + } + + cmd.Flags().StringVarP(&filePath, "file", "f", "", "path to the subscription config") + cmd.MarkFlagRequired("file") + + return cmd +} + +func updateSubscriptionCmd(cmdxConfig *cmdx.Config) *cobra.Command { + var id uint64 + var filePath string + cmd := &cobra.Command{ + Use: "edit", + Short: "Edit a subscription", + Long: heredoc.Doc(` + Edit an existing subscription. + + Note: receiver type is immutable. + `), + Annotations: map[string]string{ + "group:core": "true", + }, + RunE: func(cmd *cobra.Command, args []string) error { + spinner := printer.Spin("") + defer spinner.Stop() + + ctx := cmd.Context() + + c, err := loadClientConfig(cmd, cmdxConfig) + if err != nil { + return err + } + + var subscriptionDetail subscription.Subscription + if err := parseFile(filePath, &subscriptionDetail); err != nil { + return err + } + + var receiverMetadatasPB []*sirenv1beta1.ReceiverMetadata + for _, rcv := range subscriptionDetail.Receivers { + grpcConfigurations, err := structpb.NewStruct(rcv.Configuration) + if err != nil { + return err + } + receiverMetadatasPB = append(receiverMetadatasPB, &sirenv1beta1.ReceiverMetadata{ + Id: rcv.ID, + Configuration: grpcConfigurations, + }) + } + + client, cancel, err := createClient(ctx, c.Host) + if err != nil { + return err + } + defer cancel() + + _, err = client.UpdateSubscription(ctx, &sirenv1beta1.UpdateSubscriptionRequest{ + Urn: subscriptionDetail.URN, + Namespace: subscriptionDetail.Namespace, + Match: subscriptionDetail.Match, + Receivers: receiverMetadatasPB, + }) + if err != nil { + return err + } + + spinner.Stop() + printer.Successf("Successfully updated subscription with id %d", id) + printer.Space() + printer.SuccessIcon() + + return nil + }, + } + + cmd.Flags().Uint64Var(&id, "id", 0, "subscription id") + cmd.MarkFlagRequired("id") + cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the subscription config") + cmd.MarkFlagRequired("file") + + return cmd +} + +func deleteSubscriptionCmd(cmdxConfig *cmdx.Config) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete a subscription details", + Example: heredoc.Doc(` + $ siren subscription delete 1 + `), + Annotations: map[string]string{ + "group:core": "true", + }, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + spinner := printer.Spin("") + defer spinner.Stop() + + ctx := cmd.Context() + + c, err := loadClientConfig(cmd, cmdxConfig) + if err != nil { + return err + } + + client, cancel, err := createClient(ctx, c.Host) + if err != nil { + return err + } + defer cancel() + + id, err := strconv.ParseUint(args[0], 10, 32) + if err != nil { + return fmt.Errorf("invalid subscription id: %v", err) + } + + _, err = client.DeleteSubscription(ctx, &sirenv1beta1.DeleteSubscriptionRequest{ + Id: uint64(id), + }) + if err != nil { + return err + } + + spinner.Stop() + printer.Success("Successfully deleted subscription") + printer.Space() + printer.SuccessIcon() + + return nil + }, + } + + return cmd +} diff --git a/cli/worker.go b/cli/worker.go index 76b397b4..fd2fadd9 100644 --- a/cli/worker.go +++ b/cli/worker.go @@ -95,7 +95,7 @@ func workerStartNotificationHandlerCommand() *cobra.Command { return fmt.Errorf("cannot initialize encryptor: %w", err) } - _, _, _, notifierRegistry, err := InitAPIDeps(logger, cfg, pgClient, encryptor, nil) + _, notifierRegistry, err := InitAPIDeps(logger, cfg, pgClient, encryptor, nil) if err != nil { return err } @@ -171,7 +171,7 @@ func workerStartNotificationDLQHandlerCommand() *cobra.Command { return fmt.Errorf("cannot initialize encryptor: %w", err) } - _, _, _, notifierRegistry, err := InitAPIDeps(logger, cfg, pgClient, encryptor, nil) + _, notifierRegistry, err := InitAPIDeps(logger, cfg, pgClient, encryptor, nil) if err != nil { return err } diff --git a/config/config.go b/config/config.go index 4429ca24..661d23bb 100644 --- a/config/config.go +++ b/config/config.go @@ -10,7 +10,7 @@ import ( "github.com/odpf/siren/internal/server" "github.com/odpf/siren/pkg/errors" "github.com/odpf/siren/pkg/telemetry" - "github.com/odpf/siren/plugins/providers/cortex" + "github.com/odpf/siren/plugins/providers" "github.com/odpf/siren/plugins/receivers" ) @@ -38,10 +38,10 @@ type Log struct { // Config contains the application configuration type Config struct { DB db.Config `mapstructure:"db"` - Cortex cortex.Config `mapstructure:"cortex"` NewRelic telemetry.NewRelicConfig `mapstructure:"newrelic" yaml:"newrelic"` Service server.Config `mapstructure:"service" yaml:"service"` Log Log `mapstructure:"log" yaml:"log"` + Providers providers.Config `mapstructure:"providers" yaml:"providers"` Receivers receivers.Config `mapstructure:"receivers" yaml:"receivers"` Notification notification.Config `mapstructure:"notification" yaml:"notification"` } diff --git a/core/alert/alert.go b/core/alert/alert.go index 50cb44ec..21006960 100644 --- a/core/alert/alert.go +++ b/core/alert/alert.go @@ -7,7 +7,7 @@ import ( //go:generate mockery --name=Repository -r --case underscore --with-expecter --structname AlertRepository --filename alert_repository.go --output=./mocks type Repository interface { - Create(context.Context, *Alert) (*Alert, error) + Create(context.Context, *Alert) error List(context.Context, Filter) ([]Alert, error) } @@ -22,4 +22,13 @@ type Alert struct { 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 + GroupKey string + Status string + Annotations map[string]string + Labels map[string]string + GeneratorURL string + Fingerprint string } diff --git a/core/alert/mocks/alert_repository.go b/core/alert/mocks/alert_repository.go index 5c5cb2d4..18225c80 100644 --- a/core/alert/mocks/alert_repository.go +++ b/core/alert/mocks/alert_repository.go @@ -24,26 +24,17 @@ func (_m *AlertRepository) EXPECT() *AlertRepository_Expecter { } // Create provides a mock function with given fields: _a0, _a1 -func (_m *AlertRepository) Create(_a0 context.Context, _a1 *alert.Alert) (*alert.Alert, error) { +func (_m *AlertRepository) Create(_a0 context.Context, _a1 *alert.Alert) error { ret := _m.Called(_a0, _a1) - var r0 *alert.Alert - if rf, ok := ret.Get(0).(func(context.Context, *alert.Alert) *alert.Alert); ok { + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *alert.Alert) error); ok { r0 = rf(_a0, _a1) } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*alert.Alert) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *alert.Alert) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) + r0 = ret.Error(0) } - return r0, r1 + return r0 } // AlertRepository_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' @@ -65,8 +56,8 @@ func (_c *AlertRepository_Create_Call) Run(run func(_a0 context.Context, _a1 *al return _c } -func (_c *AlertRepository_Create_Call) Return(_a0 *alert.Alert, _a1 error) *AlertRepository_Create_Call { - _c.Call.Return(_a0, _a1) +func (_c *AlertRepository_Create_Call) Return(_a0 error) *AlertRepository_Create_Call { + _c.Call.Return(_a0) return _c } diff --git a/core/alert/mocks/alert_transformer.go b/core/alert/mocks/alert_transformer.go new file mode 100644 index 00000000..6b3964e2 --- /dev/null +++ b/core/alert/mocks/alert_transformer.go @@ -0,0 +1,94 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + alert "github.com/odpf/siren/core/alert" + + mock "github.com/stretchr/testify/mock" +) + +// AlertTransformer is an autogenerated mock type for the AlertTransformer type +type AlertTransformer struct { + mock.Mock +} + +type AlertTransformer_Expecter struct { + mock *mock.Mock +} + +func (_m *AlertTransformer) EXPECT() *AlertTransformer_Expecter { + return &AlertTransformer_Expecter{mock: &_m.Mock} +} + +// TransformToAlerts provides a mock function with given fields: ctx, providerID, body +func (_m *AlertTransformer) TransformToAlerts(ctx context.Context, providerID uint64, body map[string]interface{}) ([]*alert.Alert, int, error) { + ret := _m.Called(ctx, providerID, body) + + var r0 []*alert.Alert + if rf, ok := ret.Get(0).(func(context.Context, uint64, map[string]interface{}) []*alert.Alert); ok { + r0 = rf(ctx, providerID, body) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*alert.Alert) + } + } + + var r1 int + if rf, ok := ret.Get(1).(func(context.Context, uint64, map[string]interface{}) int); ok { + r1 = rf(ctx, providerID, body) + } else { + r1 = ret.Get(1).(int) + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, uint64, map[string]interface{}) error); ok { + r2 = rf(ctx, providerID, body) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// AlertTransformer_TransformToAlerts_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransformToAlerts' +type AlertTransformer_TransformToAlerts_Call struct { + *mock.Call +} + +// TransformToAlerts is a helper method to define mock.On call +// - ctx context.Context +// - providerID uint64 +// - body map[string]interface{} +func (_e *AlertTransformer_Expecter) TransformToAlerts(ctx interface{}, providerID interface{}, body interface{}) *AlertTransformer_TransformToAlerts_Call { + return &AlertTransformer_TransformToAlerts_Call{Call: _e.mock.On("TransformToAlerts", ctx, providerID, body)} +} + +func (_c *AlertTransformer_TransformToAlerts_Call) Run(run func(ctx context.Context, providerID uint64, body map[string]interface{})) *AlertTransformer_TransformToAlerts_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(map[string]interface{})) + }) + return _c +} + +func (_c *AlertTransformer_TransformToAlerts_Call) Return(_a0 []*alert.Alert, _a1 int, _a2 error) *AlertTransformer_TransformToAlerts_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +type mockConstructorTestingTNewAlertTransformer interface { + mock.TestingT + Cleanup(func()) +} + +// NewAlertTransformer creates a new instance of AlertTransformer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewAlertTransformer(t mockConstructorTestingTNewAlertTransformer) *AlertTransformer { + mock := &AlertTransformer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/alert/provider_plugin.go b/core/alert/provider_plugin.go new file mode 100644 index 00000000..42bf0d5c --- /dev/null +++ b/core/alert/provider_plugin.go @@ -0,0 +1,10 @@ +package alert + +import ( + "context" +) + +//go:generate mockery --name=AlertTransformer -r --case underscore --with-expecter --structname AlertTransformer --filename alert_transformer.go --output=./mocks +type AlertTransformer interface { + TransformToAlerts(ctx context.Context, providerID uint64, body map[string]interface{}) ([]*Alert, int, error) +} diff --git a/core/alert/service.go b/core/alert/service.go index 44585cbc..791a9971 100644 --- a/core/alert/service.go +++ b/core/alert/service.go @@ -10,27 +10,34 @@ import ( // Service handles business logic type Service struct { repository Repository + registry map[string]AlertTransformer } // NewService returns repository struct -func NewService(repository Repository) *Service { - return &Service{repository} +func NewService(repository Repository, registry map[string]AlertTransformer) *Service { + return &Service{repository, registry} } -func (s *Service) Create(ctx context.Context, alerts []*Alert) ([]Alert, error) { - result := make([]Alert, 0, len(alerts)) +func (s *Service) CreateAlerts(ctx context.Context, providerType string, providerID uint64, body map[string]interface{}) ([]*Alert, int, error) { + pluginService, err := s.getProviderPluginService(providerType) + if err != nil { + return nil, 0, err + } + + alerts, firingLen, err := pluginService.TransformToAlerts(ctx, providerID, body) + if err != nil { + return nil, 0, err + } for _, alrt := range alerts { - newAlert, err := s.repository.Create(ctx, alrt) - if err != nil { + if err := s.repository.Create(ctx, alrt); err != nil { if errors.Is(err, ErrRelation) { - return nil, errors.ErrNotFound.WithMsgf(err.Error()) + return nil, 0, errors.ErrNotFound.WithMsgf(err.Error()) } - return nil, err + return nil, 0, err } - result = append(result, *newAlert) } - return result, nil + return alerts, firingLen, nil } func (s *Service) List(ctx context.Context, flt Filter) ([]Alert, error) { @@ -40,3 +47,11 @@ func (s *Service) List(ctx context.Context, flt Filter) ([]Alert, error) { return s.repository.List(ctx, flt) } + +func (s *Service) getProviderPluginService(providerType string) (AlertTransformer, error) { + pluginService, exist := s.registry[providerType] + if !exist { + return nil, errors.ErrInvalid.WithMsgf("unsupported provider type: %q", providerType) + } + return pluginService, nil +} diff --git a/core/alert/service_test.go b/core/alert/service_test.go index 90720b3f..2b50febb 100644 --- a/core/alert/service_test.go +++ b/core/alert/service_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/odpf/siren/core/alert" "github.com/odpf/siren/core/alert/mocks" "github.com/odpf/siren/pkg/errors" @@ -17,7 +18,7 @@ func TestService_Get(t *testing.T) { 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) + dummyService := alert.NewService(repositoryMock, nil) timenow := time.Now() dummyAlerts := []alert.Alert{ {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "baz", MetricValue: "20", @@ -44,7 +45,7 @@ func TestService_Get(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) + dummyService := alert.NewService(repositoryMock, nil) timenow := time.Now() dummyAlerts := []alert.Alert{ {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "baz", MetricValue: "20", @@ -66,7 +67,7 @@ func TestService_Get(t *testing.T) { t.Run("should call repository List method and handle errors", func(t *testing.T) { repositoryMock := &mocks.AlertRepository{} - dummyService := alert.NewService(repositoryMock) + 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,47 +82,112 @@ func TestService_Get(t *testing.T) { } func TestService_Create(t *testing.T) { - ctx := context.TODO() - timenow := time.Now() - - t.Run("should call repository Create method with proper arguments ", func(t *testing.T) { - repositoryMock := &mocks.AlertRepository{} - dummyService := alert.NewService(repositoryMock) - alertsToBeCreated := []*alert.Alert{ - {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "lag", MetricValue: "20", - Rule: "lagHigh", TriggeredAt: timenow}, + var ( + ctx = context.TODO() + timenow = time.Now() + testType = "test" + alertsToBeCreated = map[string]interface{}{ + "alerts": []map[string]interface{}{ + { + "annotations": map[string]interface{}{ + "metricName": "bar", + "metricValue": "30", + "resource": "foo", + "template": "random", + }, + "labels": map[string]interface{}{ + "severity": "foo", + }, + "startsAt": timenow.String(), + "status": "foo", + }, + }, } + ) - repositoryMock.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).Return(alertsToBeCreated[0], nil) - actualAlerts, err := dummyService.Create(ctx, alertsToBeCreated) - assert.Nil(t, err) - assert.NotEmpty(t, actualAlerts) - repositoryMock.AssertNumberOfCalls(t, "Create", 1) - }) + var testCases = []struct { + name string + setup func(*mocks.AlertRepository, *mocks.AlertTransformer) + alertsToBeCreated map[string]interface{} + expectedAlerts []*alert.Alert + expectedFiringLen int + wantErr bool + }{ + { + name: "should return error if TransformToAlerts return error", + setup: func(ar *mocks.AlertRepository, at *mocks.AlertTransformer) { + at.EXPECT().TransformToAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]interface {}")).Return(nil, 0, errors.New("some error")) + }, + alertsToBeCreated: alertsToBeCreated, + wantErr: true, + }, + { + name: "should call repository Create method with proper arguments", + setup: func(ar *mocks.AlertRepository, at *mocks.AlertTransformer) { + at.EXPECT().TransformToAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]interface {}")).Return([]*alert.Alert{ + {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "lag", MetricValue: "20", + Rule: "lagHigh", TriggeredAt: timenow}, + }, 1, nil) + ar.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*alert.Alert")).Return(nil) + }, + alertsToBeCreated: alertsToBeCreated, + expectedFiringLen: 1, + expectedAlerts: []*alert.Alert{ + {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "lag", MetricValue: "20", + Rule: "lagHigh", TriggeredAt: timenow}, + }, + }, + { + name: "should return error not found if repository return err relation", + setup: func(ar *mocks.AlertRepository, at *mocks.AlertTransformer) { + at.EXPECT().TransformToAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]interface {}")).Return([]*alert.Alert{ + {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "lag", MetricValue: "20", + Rule: "lagHigh", TriggeredAt: timenow}, + }, 1, nil) + ar.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).Return(alert.ErrRelation) + }, + alertsToBeCreated: alertsToBeCreated, + wantErr: true, + }, + { + name: "should handle errors from repository", + setup: func(ar *mocks.AlertRepository, at *mocks.AlertTransformer) { + at.EXPECT().TransformToAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("map[string]interface {}")).Return([]*alert.Alert{ + {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "lag", MetricValue: "20", + Rule: "lagHigh", TriggeredAt: timenow}, + }, 1, nil) + ar.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).Return(errors.New("random error")) + }, + alertsToBeCreated: alertsToBeCreated, + wantErr: true, + }, + } - t.Run("should return error not found if repository return err relation", func(t *testing.T) { - repositoryMock := &mocks.AlertRepository{} - dummyService := alert.NewService(repositoryMock) - alertsToBeCreated := []*alert.Alert{ - {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "lag", MetricValue: "20", - Rule: "lagHigh", TriggeredAt: timenow}, - } - repositoryMock.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).Return(nil, alert.ErrRelation) - actualAlerts, err := dummyService.Create(ctx, alertsToBeCreated) - assert.EqualError(t, err, "provider id does not exist") - assert.Nil(t, actualAlerts) - }) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var ( + repositoryMock = &mocks.AlertRepository{} + alertTransformerMock = &mocks.AlertTransformer{} + ) - t.Run("should handle errors from repository", func(t *testing.T) { - repositoryMock := &mocks.AlertRepository{} - dummyService := alert.NewService(repositoryMock) - alertsToBeCreated := []*alert.Alert{ - {ID: 1, ProviderID: 1, ResourceName: "foo", Severity: "CRITICAL", MetricName: "lag", MetricValue: "20", - Rule: "lagHigh", TriggeredAt: timenow}, - } - repositoryMock.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).Return(nil, errors.New("random error")) - actualAlerts, err := dummyService.Create(ctx, alertsToBeCreated) - assert.EqualError(t, err, "random error") - assert.Nil(t, actualAlerts) - }) + if tc.setup != nil { + tc.setup(repositoryMock, alertTransformerMock) + } + + svc := alert.NewService(repositoryMock, map[string]alert.AlertTransformer{ + testType: alertTransformerMock, + }) + actualAlerts, firingLen, err := svc.CreateAlerts(ctx, testType, 1, tc.alertsToBeCreated) + if tc.wantErr { + if err == nil { + t.Error("error should not be nil") + } + } else { + if diff := cmp.Diff(actualAlerts, tc.expectedAlerts); diff != "" { + t.Errorf("result not equal, diff are %+v", diff) + } + assert.Equal(t, tc.expectedFiringLen, firingLen) + } + }) + } } diff --git a/core/namespace/mocks/config_syncer.go b/core/namespace/mocks/config_syncer.go new file mode 100644 index 00000000..00fffe7f --- /dev/null +++ b/core/namespace/mocks/config_syncer.go @@ -0,0 +1,78 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + provider "github.com/odpf/siren/core/provider" +) + +// ConfigSyncer is an autogenerated mock type for the ConfigSyncer type +type ConfigSyncer struct { + mock.Mock +} + +type ConfigSyncer_Expecter struct { + mock *mock.Mock +} + +func (_m *ConfigSyncer) EXPECT() *ConfigSyncer_Expecter { + return &ConfigSyncer_Expecter{mock: &_m.Mock} +} + +// SyncRuntimeConfig provides a mock function with given fields: ctx, namespaceURN, prov +func (_m *ConfigSyncer) SyncRuntimeConfig(ctx context.Context, namespaceURN string, prov provider.Provider) error { + ret := _m.Called(ctx, namespaceURN, prov) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, provider.Provider) error); ok { + r0 = rf(ctx, namespaceURN, prov) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConfigSyncer_SyncRuntimeConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SyncRuntimeConfig' +type ConfigSyncer_SyncRuntimeConfig_Call struct { + *mock.Call +} + +// SyncRuntimeConfig is a helper method to define mock.On call +// - ctx context.Context +// - namespaceURN string +// - prov provider.Provider +func (_e *ConfigSyncer_Expecter) SyncRuntimeConfig(ctx interface{}, namespaceURN interface{}, prov interface{}) *ConfigSyncer_SyncRuntimeConfig_Call { + return &ConfigSyncer_SyncRuntimeConfig_Call{Call: _e.mock.On("SyncRuntimeConfig", ctx, namespaceURN, prov)} +} + +func (_c *ConfigSyncer_SyncRuntimeConfig_Call) Run(run func(ctx context.Context, namespaceURN string, prov provider.Provider)) *ConfigSyncer_SyncRuntimeConfig_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(provider.Provider)) + }) + return _c +} + +func (_c *ConfigSyncer_SyncRuntimeConfig_Call) Return(_a0 error) *ConfigSyncer_SyncRuntimeConfig_Call { + _c.Call.Return(_a0) + return _c +} + +type mockConstructorTestingTNewConfigSyncer interface { + mock.TestingT + Cleanup(func()) +} + +// NewConfigSyncer creates a new instance of ConfigSyncer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewConfigSyncer(t mockConstructorTestingTNewConfigSyncer) *ConfigSyncer { + mock := &ConfigSyncer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/namespace/mocks/namespace_repository.go b/core/namespace/mocks/namespace_repository.go index 5da19d26..08b266b6 100644 --- a/core/namespace/mocks/namespace_repository.go +++ b/core/namespace/mocks/namespace_repository.go @@ -22,6 +22,43 @@ func (_m *NamespaceRepository) EXPECT() *NamespaceRepository_Expecter { return &NamespaceRepository_Expecter{mock: &_m.Mock} } +// Commit provides a mock function with given fields: ctx +func (_m *NamespaceRepository) Commit(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NamespaceRepository_Commit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Commit' +type NamespaceRepository_Commit_Call struct { + *mock.Call +} + +// Commit is a helper method to define mock.On call +// - ctx context.Context +func (_e *NamespaceRepository_Expecter) Commit(ctx interface{}) *NamespaceRepository_Commit_Call { + return &NamespaceRepository_Commit_Call{Call: _e.mock.On("Commit", ctx)} +} + +func (_c *NamespaceRepository_Commit_Call) Run(run func(ctx context.Context)) *NamespaceRepository_Commit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *NamespaceRepository_Commit_Call) Return(_a0 error) *NamespaceRepository_Commit_Call { + _c.Call.Return(_a0) + return _c +} + // Create provides a mock function with given fields: _a0, _a1 func (_m *NamespaceRepository) Create(_a0 context.Context, _a1 *namespace.EncryptedNamespace) error { ret := _m.Called(_a0, _a1) @@ -191,6 +228,44 @@ func (_c *NamespaceRepository_List_Call) Return(_a0 []namespace.EncryptedNamespa return _c } +// Rollback provides a mock function with given fields: ctx, err +func (_m *NamespaceRepository) Rollback(ctx context.Context, err error) error { + ret := _m.Called(ctx, err) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, error) error); ok { + r0 = rf(ctx, err) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NamespaceRepository_Rollback_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Rollback' +type NamespaceRepository_Rollback_Call struct { + *mock.Call +} + +// Rollback is a helper method to define mock.On call +// - ctx context.Context +// - err error +func (_e *NamespaceRepository_Expecter) Rollback(ctx interface{}, err interface{}) *NamespaceRepository_Rollback_Call { + return &NamespaceRepository_Rollback_Call{Call: _e.mock.On("Rollback", ctx, err)} +} + +func (_c *NamespaceRepository_Rollback_Call) Run(run func(ctx context.Context, err error)) *NamespaceRepository_Rollback_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(error)) + }) + return _c +} + +func (_c *NamespaceRepository_Rollback_Call) Return(_a0 error) *NamespaceRepository_Rollback_Call { + _c.Call.Return(_a0) + return _c +} + // Update provides a mock function with given fields: _a0, _a1 func (_m *NamespaceRepository) Update(_a0 context.Context, _a1 *namespace.EncryptedNamespace) error { ret := _m.Called(_a0, _a1) @@ -229,6 +304,45 @@ func (_c *NamespaceRepository_Update_Call) Return(_a0 error) *NamespaceRepositor return _c } +// WithTransaction provides a mock function with given fields: ctx +func (_m *NamespaceRepository) WithTransaction(ctx context.Context) context.Context { + ret := _m.Called(ctx) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context) context.Context); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// NamespaceRepository_WithTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WithTransaction' +type NamespaceRepository_WithTransaction_Call struct { + *mock.Call +} + +// WithTransaction is a helper method to define mock.On call +// - ctx context.Context +func (_e *NamespaceRepository_Expecter) WithTransaction(ctx interface{}) *NamespaceRepository_WithTransaction_Call { + return &NamespaceRepository_WithTransaction_Call{Call: _e.mock.On("WithTransaction", ctx)} +} + +func (_c *NamespaceRepository_WithTransaction_Call) Run(run func(ctx context.Context)) *NamespaceRepository_WithTransaction_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *NamespaceRepository_WithTransaction_Call) Return(_a0 context.Context) *NamespaceRepository_WithTransaction_Call { + _c.Call.Return(_a0) + return _c +} + type mockConstructorTestingTNewNamespaceRepository interface { mock.TestingT Cleanup(func()) diff --git a/core/namespace/mocks/provider_service.go b/core/namespace/mocks/provider_service.go new file mode 100644 index 00000000..01dc4187 --- /dev/null +++ b/core/namespace/mocks/provider_service.go @@ -0,0 +1,86 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + provider "github.com/odpf/siren/core/provider" +) + +// ProviderService is an autogenerated mock type for the ProviderService type +type ProviderService struct { + mock.Mock +} + +type ProviderService_Expecter struct { + mock *mock.Mock +} + +func (_m *ProviderService) EXPECT() *ProviderService_Expecter { + return &ProviderService_Expecter{mock: &_m.Mock} +} + +// Get provides a mock function with given fields: ctx, id +func (_m *ProviderService) Get(ctx context.Context, id uint64) (*provider.Provider, error) { + ret := _m.Called(ctx, id) + + var r0 *provider.Provider + if rf, ok := ret.Get(0).(func(context.Context, uint64) *provider.Provider); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*provider.Provider) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ProviderService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type ProviderService_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +// - id uint64 +func (_e *ProviderService_Expecter) Get(ctx interface{}, id interface{}) *ProviderService_Get_Call { + return &ProviderService_Get_Call{Call: _e.mock.On("Get", ctx, id)} +} + +func (_c *ProviderService_Get_Call) Run(run func(ctx context.Context, id uint64)) *ProviderService_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64)) + }) + return _c +} + +func (_c *ProviderService_Get_Call) Return(_a0 *provider.Provider, _a1 error) *ProviderService_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +type mockConstructorTestingTNewProviderService interface { + mock.TestingT + Cleanup(func()) +} + +// NewProviderService creates a new instance of ProviderService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewProviderService(t mockConstructorTestingTNewProviderService) *ProviderService { + mock := &ProviderService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/namespace/mocks/transactor.go b/core/namespace/mocks/transactor.go new file mode 100644 index 00000000..a1f343be --- /dev/null +++ b/core/namespace/mocks/transactor.go @@ -0,0 +1,151 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// Transactor is an autogenerated mock type for the Transactor type +type Transactor struct { + mock.Mock +} + +type Transactor_Expecter struct { + mock *mock.Mock +} + +func (_m *Transactor) EXPECT() *Transactor_Expecter { + return &Transactor_Expecter{mock: &_m.Mock} +} + +// Commit provides a mock function with given fields: ctx +func (_m *Transactor) Commit(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Transactor_Commit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Commit' +type Transactor_Commit_Call struct { + *mock.Call +} + +// Commit is a helper method to define mock.On call +// - ctx context.Context +func (_e *Transactor_Expecter) Commit(ctx interface{}) *Transactor_Commit_Call { + return &Transactor_Commit_Call{Call: _e.mock.On("Commit", ctx)} +} + +func (_c *Transactor_Commit_Call) Run(run func(ctx context.Context)) *Transactor_Commit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *Transactor_Commit_Call) Return(_a0 error) *Transactor_Commit_Call { + _c.Call.Return(_a0) + return _c +} + +// Rollback provides a mock function with given fields: ctx, err +func (_m *Transactor) Rollback(ctx context.Context, err error) error { + ret := _m.Called(ctx, err) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, error) error); ok { + r0 = rf(ctx, err) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Transactor_Rollback_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Rollback' +type Transactor_Rollback_Call struct { + *mock.Call +} + +// Rollback is a helper method to define mock.On call +// - ctx context.Context +// - err error +func (_e *Transactor_Expecter) Rollback(ctx interface{}, err interface{}) *Transactor_Rollback_Call { + return &Transactor_Rollback_Call{Call: _e.mock.On("Rollback", ctx, err)} +} + +func (_c *Transactor_Rollback_Call) Run(run func(ctx context.Context, err error)) *Transactor_Rollback_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(error)) + }) + return _c +} + +func (_c *Transactor_Rollback_Call) Return(_a0 error) *Transactor_Rollback_Call { + _c.Call.Return(_a0) + return _c +} + +// WithTransaction provides a mock function with given fields: ctx +func (_m *Transactor) WithTransaction(ctx context.Context) context.Context { + ret := _m.Called(ctx) + + var r0 context.Context + if rf, ok := ret.Get(0).(func(context.Context) context.Context); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// Transactor_WithTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WithTransaction' +type Transactor_WithTransaction_Call struct { + *mock.Call +} + +// WithTransaction is a helper method to define mock.On call +// - ctx context.Context +func (_e *Transactor_Expecter) WithTransaction(ctx interface{}) *Transactor_WithTransaction_Call { + return &Transactor_WithTransaction_Call{Call: _e.mock.On("WithTransaction", ctx)} +} + +func (_c *Transactor_WithTransaction_Call) Run(run func(ctx context.Context)) *Transactor_WithTransaction_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *Transactor_WithTransaction_Call) Return(_a0 context.Context) *Transactor_WithTransaction_Call { + _c.Call.Return(_a0) + return _c +} + +type mockConstructorTestingTNewTransactor interface { + mock.TestingT + Cleanup(func()) +} + +// NewTransactor creates a new instance of Transactor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewTransactor(t mockConstructorTestingTNewTransactor) *Transactor { + mock := &Transactor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/namespace/namespace.go b/core/namespace/namespace.go index b3e08cca..b77bae6a 100644 --- a/core/namespace/namespace.go +++ b/core/namespace/namespace.go @@ -9,6 +9,7 @@ import ( //go:generate mockery --name=Repository -r --case underscore --with-expecter --structname NamespaceRepository --filename namespace_repository.go --output=./mocks type Repository interface { + Transactor List(context.Context) ([]EncryptedNamespace, error) Create(context.Context, *EncryptedNamespace) error Get(context.Context, uint64) (*EncryptedNamespace, error) @@ -16,6 +17,13 @@ type Repository interface { Delete(context.Context, uint64) error } +//go:generate mockery --name=Transactor -r --case underscore --with-expecter --structname Transactor --filename transactor.go --output=./mocks +type Transactor interface { + WithTransaction(ctx context.Context) context.Context + Rollback(ctx context.Context, err error) error + Commit(ctx context.Context) error +} + type EncryptedNamespace struct { *Namespace CredentialString string diff --git a/core/namespace/provider_plugin.go b/core/namespace/provider_plugin.go new file mode 100644 index 00000000..9933e136 --- /dev/null +++ b/core/namespace/provider_plugin.go @@ -0,0 +1,12 @@ +package namespace + +import ( + "context" + + "github.com/odpf/siren/core/provider" +) + +//go:generate mockery --name=ConfigSyncer -r --case underscore --with-expecter --structname ConfigSyncer --filename config_syncer.go --output=./mocks +type ConfigSyncer interface { + SyncRuntimeConfig(ctx context.Context, namespaceURN string, prov provider.Provider) error +} diff --git a/core/namespace/service.go b/core/namespace/service.go index 06f8708d..1c8263e8 100644 --- a/core/namespace/service.go +++ b/core/namespace/service.go @@ -4,21 +4,31 @@ import ( "context" "encoding/json" + "github.com/odpf/siren/core/provider" "github.com/odpf/siren/pkg/errors" "github.com/odpf/siren/pkg/secret" ) +//go:generate mockery --name=ProviderService -r --case underscore --with-expecter --structname ProviderService --filename provider_service.go --output=./mocks +type ProviderService interface { + Get(ctx context.Context, id uint64) (*provider.Provider, error) +} + // Service handles business logic type Service struct { - repository Repository - cryptoClient Encryptor + repository Repository + cryptoClient Encryptor + providerService ProviderService + registry map[string]ConfigSyncer } // NewService returns secure service struct -func NewService(cryptoClient Encryptor, repository Repository) *Service { +func NewService(cryptoClient Encryptor, repository Repository, providerService ProviderService, registry map[string]ConfigSyncer) *Service { return &Service{ - repository: repository, - cryptoClient: cryptoClient, + repository: repository, + providerService: providerService, + cryptoClient: cryptoClient, + registry: registry, } } @@ -43,13 +53,28 @@ func (s *Service) Create(ctx context.Context, ns *Namespace) error { if ns == nil { return errors.ErrInvalid.WithCausef("namespace is nil").WithMsgf("incoming namespace is empty") } + + prov, err := s.providerService.Get(ctx, ns.Provider.ID) + if err != nil { + return err + } + + pluginService, err := s.getProviderPluginService(prov.Type) + if err != nil { + return err + } + encryptedNamespace, err := s.encrypt(ns) if err != nil { return err } + ctx = s.repository.WithTransaction(ctx) err = s.repository.Create(ctx, encryptedNamespace) if err != nil { + if err := s.repository.Rollback(ctx, err); err != nil { + return err + } if errors.Is(err, ErrDuplicate) { return errors.ErrConflict.WithMsgf(err.Error()) } @@ -59,6 +84,17 @@ func (s *Service) Create(ctx context.Context, ns *Namespace) error { return err } + if err := pluginService.SyncRuntimeConfig(ctx, ns.URN, *prov); err != nil { + if err := s.repository.Rollback(ctx, err); err != nil { + return err + } + return err + } + + if err := s.repository.Commit(ctx); err != nil { + return err + } + ns.ID = encryptedNamespace.ID return nil @@ -77,13 +113,31 @@ func (s *Service) Get(ctx context.Context, id uint64) (*Namespace, error) { } func (s *Service) Update(ctx context.Context, ns *Namespace) error { + if ns == nil { + return errors.ErrInvalid.WithCausef("namespace is nil").WithMsgf("incoming namespace is empty") + } + + prov, err := s.providerService.Get(ctx, ns.Provider.ID) + if err != nil { + return err + } + + pluginService, err := s.getProviderPluginService(prov.Type) + if err != nil { + return err + } + encryptedNamespace, err := s.encrypt(ns) if err != nil { return err } + ctx = s.repository.WithTransaction(ctx) err = s.repository.Update(ctx, encryptedNamespace) if err != nil { + if err := s.repository.Rollback(ctx, err); err != nil { + return err + } if errors.Is(err, ErrDuplicate) { return errors.ErrConflict.WithMsgf(err.Error()) } @@ -96,6 +150,17 @@ func (s *Service) Update(ctx context.Context, ns *Namespace) error { return err } + if err := pluginService.SyncRuntimeConfig(ctx, ns.URN, *prov); err != nil { + if err := s.repository.Rollback(ctx, err); err != nil { + return err + } + return err + } + + if err := s.repository.Commit(ctx); err != nil { + return err + } + ns.ID = encryptedNamespace.ID return nil @@ -137,3 +202,11 @@ func (s *Service) decrypt(ens *EncryptedNamespace) (*Namespace, error) { ens.Namespace.Credentials = decryptedCredentials return ens.Namespace, nil } + +func (s *Service) getProviderPluginService(providerType string) (ConfigSyncer, error) { + pluginService, exist := s.registry[providerType] + if !exist { + return nil, errors.ErrInvalid.WithMsgf("unsupported provider type: %q", providerType) + } + return pluginService, nil +} diff --git a/core/namespace/service_test.go b/core/namespace/service_test.go index f451d2b0..c3f987e8 100644 --- a/core/namespace/service_test.go +++ b/core/namespace/service_test.go @@ -15,6 +15,8 @@ import ( mock "github.com/stretchr/testify/mock" ) +const testProviderType = "test-type" + func TestService_ListNamespaces(t *testing.T) { type testCase struct { @@ -27,7 +29,6 @@ func TestService_ListNamespaces(t *testing.T) { ctx = context.TODO() timeNow = time.Now() testCases = []testCase{ - { Description: "should return error if List repository error", Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { @@ -177,7 +178,7 @@ func TestService_ListNamespaces(t *testing.T) { repositoryMock = new(mocks.NamespaceRepository) encryptorMock = new(mocks.Encryptor) ) - svc := namespace.NewService(encryptorMock, repositoryMock) + svc := namespace.NewService(encryptorMock, repositoryMock, nil, nil) tc.Setup(repositoryMock, encryptorMock, tc) @@ -197,11 +198,10 @@ func TestService_ListNamespaces(t *testing.T) { } func TestService_CreateNamespace(t *testing.T) { - type testCase struct { Description string NSpace *namespace.Namespace - Setup func(*mocks.NamespaceRepository, *mocks.Encryptor, testCase) + Setup func(*mocks.NamespaceRepository, *mocks.Encryptor, *mocks.ProviderService, *mocks.ConfigSyncer, testCase) Err error } var ( @@ -209,12 +209,23 @@ func TestService_CreateNamespace(t *testing.T) { testCases = []testCase{ { Description: "should return error if namespace is nil", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) {}, Err: errors.New("incoming namespace is empty"), }, + { + Description: "should return error if provider service return error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil, errors.New("some error")) + }, + NSpace: &namespace.Namespace{ + Credentials: map[string]interface{}{}, + }, + Err: errors.New("some error"), + }, { Description: "should return error if encrypt return error caused credential is not in json", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) {}, + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) + }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ "invalid": make(chan int), @@ -224,7 +235,8 @@ func TestService_CreateNamespace(t *testing.T) { }, { Description: "should return error if encrypt return error", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("", errors.New("some error")) }, NSpace: &namespace.Namespace{ @@ -235,13 +247,16 @@ func TestService_CreateNamespace(t *testing.T) { Err: errors.New("some error"), }, { - Description: "should return error if encrypt success and create repository error", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error if create repository error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(errors.New("some error")) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -251,13 +266,16 @@ func TestService_CreateNamespace(t *testing.T) { Err: errors.New("some error"), }, { - Description: "should return error conflict if encrypt success and create repository return duplicate error", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error conflict if create repository return duplicate error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(namespace.ErrDuplicate) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -267,13 +285,16 @@ func TestService_CreateNamespace(t *testing.T) { Err: errors.New("urn and provider pair already exist"), }, { - Description: "should return error not found if encrypt success and create repository return relation error", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error not found if create repository return relation error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(namespace.ErrRelation) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -283,13 +304,37 @@ func TestService_CreateNamespace(t *testing.T) { Err: errors.New("provider id does not exist"), }, { - Description: "should return nil error if encrypt success and create repository success", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error if create repository success & sync config return error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(nil) + cs.EXPECT().SyncRuntimeConfig(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("provider.Provider")).Return(errors.New("some error")) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) + }, + NSpace: &namespace.Namespace{ + Credentials: map[string]interface{}{ + "credential": "value", + }, + }, + Err: errors.New("some error"), + }, + { + Description: "should return nil error if create repository success & sync config success", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) + e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) + rr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ + Namespace: tc.NSpace, + CredentialString: "some-ciphertext", + }).Return(nil) + cs.EXPECT().SyncRuntimeConfig(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("provider.Provider")).Return(nil) + rr.EXPECT().Commit(mock.AnythingOfType("*context.emptyCtx")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -303,12 +348,20 @@ func TestService_CreateNamespace(t *testing.T) { for _, tc := range testCases { t.Run(tc.Description, func(t *testing.T) { var ( - repositoryMock = new(mocks.NamespaceRepository) - encryptorMock = new(mocks.Encryptor) + repositoryMock = new(mocks.NamespaceRepository) + encryptorMock = new(mocks.Encryptor) + providerServiceMock = new(mocks.ProviderService) + providerPluginMock = new(mocks.ConfigSyncer) + ) + svc := namespace.NewService(encryptorMock, repositoryMock, providerServiceMock, + map[string]namespace.ConfigSyncer{ + testProviderType: providerPluginMock, + }, ) - svc := namespace.NewService(encryptorMock, repositoryMock) - tc.Setup(repositoryMock, encryptorMock, tc) + if tc.Setup != nil { + tc.Setup(repositoryMock, encryptorMock, providerServiceMock, providerPluginMock, tc) + } err := svc.Create(ctx, tc.NSpace) if tc.Err != err { @@ -395,7 +448,7 @@ func TestService_GetNamespace(t *testing.T) { repositoryMock = new(mocks.NamespaceRepository) encryptorMock = new(mocks.Encryptor) ) - svc := namespace.NewService(encryptorMock, repositoryMock) + svc := namespace.NewService(encryptorMock, repositoryMock, nil, nil) tc.Setup(repositoryMock, encryptorMock, tc) @@ -418,15 +471,31 @@ func TestService_UpdateNamespace(t *testing.T) { type testCase struct { Description string NSpace *namespace.Namespace - Setup func(*mocks.NamespaceRepository, *mocks.Encryptor, testCase) + Setup func(*mocks.NamespaceRepository, *mocks.Encryptor, *mocks.ProviderService, *mocks.ConfigSyncer, testCase) Err error } var ( ctx = context.TODO() testCases = []testCase{ + { + Description: "should return error if namespace is nil", + Err: errors.New("incoming namespace is empty"), + }, + { + Description: "should return error if provider service return error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil, errors.New("some error")) + }, + NSpace: &namespace.Namespace{ + Credentials: map[string]interface{}{}, + }, + Err: errors.New("some error"), + }, { Description: "should return error if encrypt return error caused credential is not in json", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) {}, + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) + }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ "invalid": make(chan int), @@ -436,7 +505,8 @@ func TestService_UpdateNamespace(t *testing.T) { }, { Description: "should return error if encrypt return error", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("", errors.New("some error")) }, NSpace: &namespace.Namespace{ @@ -447,13 +517,16 @@ func TestService_UpdateNamespace(t *testing.T) { Err: errors.New("some error"), }, { - Description: "should return error if encrypt success and update repository error", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error if update repository error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(errors.New("some error")) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -463,13 +536,16 @@ func TestService_UpdateNamespace(t *testing.T) { Err: errors.New("some error"), }, { - Description: "should return error not found if encrypt success and update repository return not found error", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error not found if update repository return not found error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(namespace.NotFoundError{}) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("namespace.NotFoundError")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -479,13 +555,16 @@ func TestService_UpdateNamespace(t *testing.T) { Err: errors.New("namespace not found"), }, { - Description: "should return error not found if encrypt success and update repository return relation error", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error not found if update repository return relation error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(namespace.ErrRelation) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -495,13 +574,16 @@ func TestService_UpdateNamespace(t *testing.T) { Err: errors.New("provider id does not exist"), }, { - Description: "should return error conflict if encrypt success and update repository return error duplicate", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error conflict if update repository return error duplicate", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(namespace.ErrDuplicate) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -511,13 +593,37 @@ func TestService_UpdateNamespace(t *testing.T) { Err: errors.New("urn and provider pair already exist"), }, { - Description: "should return nil error if encrypt success and update repository success", - Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, tc testCase) { + Description: "should return error if sync config return error", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ Namespace: tc.NSpace, CredentialString: "some-ciphertext", }).Return(nil) + cs.EXPECT().SyncRuntimeConfig(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("provider.Provider")).Return(errors.New("some error")) + rr.EXPECT().Rollback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*errors.errorString")).Return(nil) + }, + NSpace: &namespace.Namespace{ + Credentials: map[string]interface{}{ + "credential": "value", + }, + }, + Err: errors.New("some error"), + }, + { + Description: "should return nil error if update repository success", + Setup: func(rr *mocks.NamespaceRepository, e *mocks.Encryptor, ps *mocks.ProviderService, cs *mocks.ConfigSyncer, tc testCase) { + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{Type: testProviderType}, nil) + e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("some-ciphertext", nil) + rr.EXPECT().WithTransaction(mock.AnythingOfType("*context.emptyCtx")).Return(ctx) + rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &namespace.EncryptedNamespace{ + Namespace: tc.NSpace, + CredentialString: "some-ciphertext", + }).Return(nil) + cs.EXPECT().SyncRuntimeConfig(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("provider.Provider")).Return(nil) + rr.EXPECT().Commit(mock.AnythingOfType("*context.emptyCtx")).Return(nil) }, NSpace: &namespace.Namespace{ Credentials: map[string]interface{}{ @@ -532,12 +638,20 @@ func TestService_UpdateNamespace(t *testing.T) { for _, tc := range testCases { t.Run(tc.Description, func(t *testing.T) { var ( - repositoryMock = new(mocks.NamespaceRepository) - encryptorMock = new(mocks.Encryptor) + repositoryMock = new(mocks.NamespaceRepository) + encryptorMock = new(mocks.Encryptor) + providerServiceMock = new(mocks.ProviderService) + providerPluginMock = new(mocks.ConfigSyncer) + ) + svc := namespace.NewService(encryptorMock, repositoryMock, providerServiceMock, + map[string]namespace.ConfigSyncer{ + testProviderType: providerPluginMock, + }, ) - svc := namespace.NewService(encryptorMock, repositoryMock) - tc.Setup(repositoryMock, encryptorMock, tc) + if tc.Setup != nil { + tc.Setup(repositoryMock, encryptorMock, providerServiceMock, providerPluginMock, tc) + } err := svc.Update(ctx, tc.NSpace) if tc.Err != err { @@ -558,7 +672,7 @@ func TestDeleteNamespace(t *testing.T) { t.Run("should call repository Delete method and return nil if no error", func(t *testing.T) { repositoryMock := &mocks.NamespaceRepository{} - dummyService := namespace.NewService(nil, repositoryMock) + dummyService := namespace.NewService(nil, repositoryMock, nil, nil) repositoryMock.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), namespaceID).Return(nil).Once() err := dummyService.Delete(ctx, namespaceID) assert.Nil(t, err) @@ -567,7 +681,7 @@ func TestDeleteNamespace(t *testing.T) { t.Run("should call repository Delete method and return error if any", func(t *testing.T) { repositoryMock := &mocks.NamespaceRepository{} - dummyService := namespace.NewService(nil, repositoryMock) + dummyService := namespace.NewService(nil, repositoryMock, nil, nil) repositoryMock.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), namespaceID).Return(errors.New("random error")).Once() err := dummyService.Delete(ctx, namespaceID) assert.EqualError(t, err, "random error") diff --git a/core/notification/handler.go b/core/notification/handler.go index 6952624f..079ffbc9 100644 --- a/core/notification/handler.go +++ b/core/notification/handler.go @@ -102,7 +102,7 @@ func (h *Handler) MessageHandler(ctx context.Context, messages []Message) error message.MarkPending(time.Now()) - newConfig, err := notifier.PostHookTransformConfigs(ctx, message.Configs) + newConfig, err := notifier.PostHookQueueTransformConfigs(ctx, message.Configs) if err != nil { message.MarkFailed(time.Now(), false, err) @@ -113,7 +113,7 @@ func (h *Handler) MessageHandler(ctx context.Context, messages []Message) error } message.Configs = newConfig - if retryable, err := notifier.Publish(ctx, message); err != nil { + if retryable, err := notifier.Send(ctx, message); err != nil { message.MarkFailed(time.Now(), retryable, err) diff --git a/core/notification/handler_test.go b/core/notification/handler_test.go index 97ba2567..7d7576a2 100644 --- a/core/notification/handler_test.go +++ b/core/notification/handler_test.go @@ -37,7 +37,7 @@ func TestHandler_MessageHandler(t *testing.T) { }, }, setup: func(q *mocks.Queuer, n *mocks.Notifier) { - n.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) + n.EXPECT().PostHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) q.EXPECT().ErrorCallback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(nil) }, wantErr: true, @@ -50,63 +50,63 @@ func TestHandler_MessageHandler(t *testing.T) { }, }, setup: func(q *mocks.Queuer, n *mocks.Notifier) { - n.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) + n.EXPECT().PostHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("some error")) q.EXPECT().ErrorCallback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error")) }, wantErr: true, }, { - name: "return error if publish message return error and error handler queue return error", + name: "return error if send message return error and error handler queue return error", messages: []notification.Message{ { ReceiverType: testPluginType, }, }, setup: func(q *mocks.Queuer, n *mocks.Notifier) { - n.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) - n.EXPECT().Publish(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(false, errors.New("some error")) + n.EXPECT().PostHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) + n.EXPECT().Send(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(false, errors.New("some error")) q.EXPECT().ErrorCallback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error")) }, wantErr: true, }, { - name: "return error if publish message return error and error handler queue return no error", + name: "return error if send message return error and error handler queue return no error", messages: []notification.Message{ { ReceiverType: testPluginType, }, }, setup: func(q *mocks.Queuer, n *mocks.Notifier) { - n.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) - n.EXPECT().Publish(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(false, errors.New("some error")) + n.EXPECT().PostHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) + n.EXPECT().Send(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(false, errors.New("some error")) q.EXPECT().ErrorCallback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(nil) }, wantErr: true, }, { - name: "return error if publish message success and success handler queue return error", + name: "return error if send message success and success handler queue return error", messages: []notification.Message{ { ReceiverType: testPluginType, }, }, setup: func(q *mocks.Queuer, n *mocks.Notifier) { - n.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) - n.EXPECT().Publish(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(false, nil) + n.EXPECT().PostHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) + n.EXPECT().Send(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(false, nil) q.EXPECT().SuccessCallback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error")) }, wantErr: true, }, { - name: "return no error if publish message success and success handler queue return no error", + name: "return no error if send message success and success handler queue return no error", messages: []notification.Message{ { ReceiverType: testPluginType, }, }, setup: func(q *mocks.Queuer, n *mocks.Notifier) { - n.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) - n.EXPECT().Publish(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(false, nil) + n.EXPECT().PostHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{}, nil) + n.EXPECT().Send(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(false, nil) q.EXPECT().SuccessCallback(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(nil) }, wantErr: false, diff --git a/core/notification/mocks/notifier.go b/core/notification/mocks/notifier.go index 786456f3..ce4c4487 100644 --- a/core/notification/mocks/notifier.go +++ b/core/notification/mocks/notifier.go @@ -22,13 +22,13 @@ func (_m *Notifier) EXPECT() *Notifier_Expecter { return &Notifier_Expecter{mock: &_m.Mock} } -// DefaultTemplateOfProvider provides a mock function with given fields: templateName -func (_m *Notifier) DefaultTemplateOfProvider(templateName string) string { - ret := _m.Called(templateName) +// GetSystemDefaultTemplate provides a mock function with given fields: +func (_m *Notifier) GetSystemDefaultTemplate() string { + ret := _m.Called() var r0 string - if rf, ok := ret.Get(0).(func(string) string); ok { - r0 = rf(templateName) + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() } else { r0 = ret.Get(0).(string) } @@ -36,31 +36,30 @@ func (_m *Notifier) DefaultTemplateOfProvider(templateName string) string { return r0 } -// Notifier_DefaultTemplateOfProvider_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DefaultTemplateOfProvider' -type Notifier_DefaultTemplateOfProvider_Call struct { +// Notifier_GetSystemDefaultTemplate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSystemDefaultTemplate' +type Notifier_GetSystemDefaultTemplate_Call struct { *mock.Call } -// DefaultTemplateOfProvider is a helper method to define mock.On call -// - templateName string -func (_e *Notifier_Expecter) DefaultTemplateOfProvider(templateName interface{}) *Notifier_DefaultTemplateOfProvider_Call { - return &Notifier_DefaultTemplateOfProvider_Call{Call: _e.mock.On("DefaultTemplateOfProvider", templateName)} +// GetSystemDefaultTemplate is a helper method to define mock.On call +func (_e *Notifier_Expecter) GetSystemDefaultTemplate() *Notifier_GetSystemDefaultTemplate_Call { + return &Notifier_GetSystemDefaultTemplate_Call{Call: _e.mock.On("GetSystemDefaultTemplate")} } -func (_c *Notifier_DefaultTemplateOfProvider_Call) Run(run func(templateName string)) *Notifier_DefaultTemplateOfProvider_Call { +func (_c *Notifier_GetSystemDefaultTemplate_Call) Run(run func()) *Notifier_GetSystemDefaultTemplate_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) + run() }) return _c } -func (_c *Notifier_DefaultTemplateOfProvider_Call) Return(_a0 string) *Notifier_DefaultTemplateOfProvider_Call { +func (_c *Notifier_GetSystemDefaultTemplate_Call) Return(_a0 string) *Notifier_GetSystemDefaultTemplate_Call { _c.Call.Return(_a0) return _c } -// PostHookTransformConfigs provides a mock function with given fields: ctx, notificationConfigMap -func (_m *Notifier) PostHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { +// PostHookQueueTransformConfigs provides a mock function with given fields: ctx, notificationConfigMap +func (_m *Notifier) PostHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { ret := _m.Called(ctx, notificationConfigMap) var r0 map[string]interface{} @@ -82,32 +81,32 @@ func (_m *Notifier) PostHookTransformConfigs(ctx context.Context, notificationCo return r0, r1 } -// Notifier_PostHookTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostHookTransformConfigs' -type Notifier_PostHookTransformConfigs_Call struct { +// Notifier_PostHookQueueTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostHookQueueTransformConfigs' +type Notifier_PostHookQueueTransformConfigs_Call struct { *mock.Call } -// PostHookTransformConfigs is a helper method to define mock.On call +// PostHookQueueTransformConfigs is a helper method to define mock.On call // - ctx context.Context // - notificationConfigMap map[string]interface{} -func (_e *Notifier_Expecter) PostHookTransformConfigs(ctx interface{}, notificationConfigMap interface{}) *Notifier_PostHookTransformConfigs_Call { - return &Notifier_PostHookTransformConfigs_Call{Call: _e.mock.On("PostHookTransformConfigs", ctx, notificationConfigMap)} +func (_e *Notifier_Expecter) PostHookQueueTransformConfigs(ctx interface{}, notificationConfigMap interface{}) *Notifier_PostHookQueueTransformConfigs_Call { + return &Notifier_PostHookQueueTransformConfigs_Call{Call: _e.mock.On("PostHookQueueTransformConfigs", ctx, notificationConfigMap)} } -func (_c *Notifier_PostHookTransformConfigs_Call) Run(run func(ctx context.Context, notificationConfigMap map[string]interface{})) *Notifier_PostHookTransformConfigs_Call { +func (_c *Notifier_PostHookQueueTransformConfigs_Call) Run(run func(ctx context.Context, notificationConfigMap map[string]interface{})) *Notifier_PostHookQueueTransformConfigs_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(map[string]interface{})) }) return _c } -func (_c *Notifier_PostHookTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *Notifier_PostHookTransformConfigs_Call { +func (_c *Notifier_PostHookQueueTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *Notifier_PostHookQueueTransformConfigs_Call { _c.Call.Return(_a0, _a1) return _c } -// PreHookTransformConfigs provides a mock function with given fields: ctx, notificationConfigMap -func (_m *Notifier) PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { +// PreHookQueueTransformConfigs provides a mock function with given fields: ctx, notificationConfigMap +func (_m *Notifier) PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { ret := _m.Called(ctx, notificationConfigMap) var r0 map[string]interface{} @@ -129,32 +128,32 @@ func (_m *Notifier) PreHookTransformConfigs(ctx context.Context, notificationCon return r0, r1 } -// Notifier_PreHookTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PreHookTransformConfigs' -type Notifier_PreHookTransformConfigs_Call struct { +// Notifier_PreHookQueueTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PreHookQueueTransformConfigs' +type Notifier_PreHookQueueTransformConfigs_Call struct { *mock.Call } -// PreHookTransformConfigs is a helper method to define mock.On call +// PreHookQueueTransformConfigs is a helper method to define mock.On call // - ctx context.Context // - notificationConfigMap map[string]interface{} -func (_e *Notifier_Expecter) PreHookTransformConfigs(ctx interface{}, notificationConfigMap interface{}) *Notifier_PreHookTransformConfigs_Call { - return &Notifier_PreHookTransformConfigs_Call{Call: _e.mock.On("PreHookTransformConfigs", ctx, notificationConfigMap)} +func (_e *Notifier_Expecter) PreHookQueueTransformConfigs(ctx interface{}, notificationConfigMap interface{}) *Notifier_PreHookQueueTransformConfigs_Call { + return &Notifier_PreHookQueueTransformConfigs_Call{Call: _e.mock.On("PreHookQueueTransformConfigs", ctx, notificationConfigMap)} } -func (_c *Notifier_PreHookTransformConfigs_Call) Run(run func(ctx context.Context, notificationConfigMap map[string]interface{})) *Notifier_PreHookTransformConfigs_Call { +func (_c *Notifier_PreHookQueueTransformConfigs_Call) Run(run func(ctx context.Context, notificationConfigMap map[string]interface{})) *Notifier_PreHookQueueTransformConfigs_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(map[string]interface{})) }) return _c } -func (_c *Notifier_PreHookTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *Notifier_PreHookTransformConfigs_Call { +func (_c *Notifier_PreHookQueueTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *Notifier_PreHookQueueTransformConfigs_Call { _c.Call.Return(_a0, _a1) return _c } -// Publish provides a mock function with given fields: ctx, message -func (_m *Notifier) Publish(ctx context.Context, message notification.Message) (bool, error) { +// Send provides a mock function with given fields: ctx, message +func (_m *Notifier) Send(ctx context.Context, message notification.Message) (bool, error) { ret := _m.Called(ctx, message) var r0 bool @@ -174,26 +173,26 @@ func (_m *Notifier) Publish(ctx context.Context, message notification.Message) ( return r0, r1 } -// Notifier_Publish_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Publish' -type Notifier_Publish_Call struct { +// Notifier_Send_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Send' +type Notifier_Send_Call struct { *mock.Call } -// Publish is a helper method to define mock.On call +// Send is a helper method to define mock.On call // - ctx context.Context // - message notification.Message -func (_e *Notifier_Expecter) Publish(ctx interface{}, message interface{}) *Notifier_Publish_Call { - return &Notifier_Publish_Call{Call: _e.mock.On("Publish", ctx, message)} +func (_e *Notifier_Expecter) Send(ctx interface{}, message interface{}) *Notifier_Send_Call { + return &Notifier_Send_Call{Call: _e.mock.On("Send", ctx, message)} } -func (_c *Notifier_Publish_Call) Run(run func(ctx context.Context, message notification.Message)) *Notifier_Publish_Call { +func (_c *Notifier_Send_Call) Run(run func(ctx context.Context, message notification.Message)) *Notifier_Send_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(notification.Message)) }) return _c } -func (_c *Notifier_Publish_Call) Return(_a0 bool, _a1 error) *Notifier_Publish_Call { +func (_c *Notifier_Send_Call) Return(_a0 bool, _a1 error) *Notifier_Send_Call { _c.Call.Return(_a0, _a1) return _c } diff --git a/core/notification/plugin.go b/core/notification/receiver_plugin.go similarity index 68% rename from core/notification/plugin.go rename to core/notification/receiver_plugin.go index a4d87ada..7645bb6b 100644 --- a/core/notification/plugin.go +++ b/core/notification/receiver_plugin.go @@ -8,10 +8,10 @@ import ( //go:generate mockery --name=Notifier -r --case underscore --with-expecter --structname Notifier --filename notifier.go --output=./mocks type Notifier interface { - PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) - PostHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) - DefaultTemplateOfProvider(templateName string) string - Publish(ctx context.Context, message Message) (bool, error) + PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) + PostHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) + GetSystemDefaultTemplate() string + Send(ctx context.Context, message Message) (bool, error) } //go:generate mockery --name=Queuer -r --case underscore --with-expecter --structname Queuer --filename queuer.go --output=./mocks diff --git a/core/notification/service.go b/core/notification/service.go index bfd98508..10e94423 100644 --- a/core/notification/service.go +++ b/core/notification/service.go @@ -61,17 +61,17 @@ func (ns *NotificationService) DispatchToReceiver(ctx context.Context, n Notific return err } - message, err := n.ToMessage(rcv.Type, rcv.Configurations) + notifierPlugin, err := ns.getNotifierPlugin(rcv.Type) if err != nil { return err } - notifierPlugin, err := ns.getNotifierPlugin(rcv.Type) + message, err := n.ToMessage(rcv.Type, rcv.Configurations) if err != nil { return err } - newConfigs, err := notifierPlugin.PreHookTransformConfigs(ctx, message.Configs) + newConfigs, err := notifierPlugin.PreHookQueueTransformConfigs(ctx, message.Configs) if err != nil { return err } @@ -102,17 +102,17 @@ func (ns *NotificationService) DispatchToSubscribers(ctx context.Context, n Noti for _, s := range subscriptions { for _, rcv := range s.Receivers { - message, err := n.ToMessage(rcv.Type, rcv.Configuration) + notifierPlugin, err := ns.getNotifierPlugin(rcv.Type) if err != nil { return err } - notifierPlugin, err := ns.getNotifierPlugin(rcv.Type) + message, err := n.ToMessage(rcv.Type, rcv.Configuration) if err != nil { return err } - newConfigs, err := notifierPlugin.PreHookTransformConfigs(ctx, message.Configs) + newConfigs, err := notifierPlugin.PreHookQueueTransformConfigs(ctx, message.Configs) if err != nil { return err } @@ -126,7 +126,7 @@ func (ns *NotificationService) DispatchToSubscribers(ctx context.Context, n Noti var templateBody string if template.IsReservedName(n.Template) { - templateBody = notifierPlugin.DefaultTemplateOfProvider(n.Template) + templateBody = notifierPlugin.GetSystemDefaultTemplate() } if templateBody != "" { diff --git a/core/notification/service_test.go b/core/notification/service_test.go index d9b187d9..f79316bf 100644 --- a/core/notification/service_test.go +++ b/core/notification/service_test.go @@ -49,7 +49,7 @@ func TestNotificationService_DispatchToReceiver(t *testing.T) { "key": "value", }, }, nil) - n.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("invalid config")) + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("invalid config")) }, wantErr: true, }, @@ -62,7 +62,7 @@ func TestNotificationService_DispatchToReceiver(t *testing.T) { "key": "value", }, }, nil) - n.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ "key": "value", }, nil) q.EXPECT().Enqueue(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error")) @@ -78,7 +78,7 @@ func TestNotificationService_DispatchToReceiver(t *testing.T) { "key": "value", }, }, nil) - n.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ "key": "value", }, nil) q.EXPECT().Enqueue(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(nil) @@ -129,14 +129,7 @@ func TestNotificationService_DispatchToSubscribers(t *testing.T) { wantErr: true, }, { - name: "should return error if there is no matching subscription", - setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, n *mocks.Notifier) { - ss.EXPECT().MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]string")).Return(nil, nil) - }, - wantErr: true, - }, - { - name: "should return error if failed to transform notification to messages", + name: "should return error if receiver type of a receiver is unknown", setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, n *mocks.Notifier) { ss.EXPECT(). MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]string")). @@ -144,19 +137,23 @@ func TestNotificationService_DispatchToSubscribers(t *testing.T) { { Receivers: []subscription.Receiver{ { - Type: testPluginType, + Type: "random", }, }, }, }, nil) }, - n: notification.Notification{ - ValidDurationString: "xxx", + wantErr: true, + }, + { + name: "should return error if there is no matching subscription", + setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, n *mocks.Notifier) { + ss.EXPECT().MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]string")).Return(nil, nil) }, wantErr: true, }, { - name: "should return error if receiver type of a receiver is unknown", + name: "should return error if failed to transform notification to messages", setup: func(ss *mocks.SubscriptionService, q *mocks.Queuer, n *mocks.Notifier) { ss.EXPECT(). MatchByLabels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]string")). @@ -164,12 +161,15 @@ func TestNotificationService_DispatchToSubscribers(t *testing.T) { { Receivers: []subscription.Receiver{ { - Type: "random", + Type: testPluginType, }, }, }, }, nil) }, + n: notification.Notification{ + ValidDurationString: "xxx", + }, wantErr: true, }, { @@ -189,7 +189,7 @@ func TestNotificationService_DispatchToSubscribers(t *testing.T) { }, }, }, nil) - n.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("invalid config")) + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(nil, errors.New("invalid config")) }, wantErr: true, }, @@ -210,7 +210,7 @@ func TestNotificationService_DispatchToSubscribers(t *testing.T) { }, }, }, nil) - n.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ "key": "value", }, nil) q.EXPECT().Enqueue(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(errors.New("some error")) @@ -234,7 +234,7 @@ func TestNotificationService_DispatchToSubscribers(t *testing.T) { }, }, }, nil) - n.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ + n.EXPECT().PreHookQueueTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("map[string]interface {}")).Return(map[string]interface{}{ "key": "value", }, nil) q.EXPECT().Enqueue(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Message")).Return(nil) diff --git a/core/provider/provider.go b/core/provider/provider.go index 3f80c9ad..7aef3fce 100644 --- a/core/provider/provider.go +++ b/core/provider/provider.go @@ -5,10 +5,6 @@ import ( "time" ) -const ( - TypeCortex string = "cortex" -) - //go:generate mockery --name=Repository -r --case underscore --with-expecter --structname ProviderRepository --filename provider_repository.go --output=./mocks type Repository interface { List(context.Context, Filter) ([]Provider, error) diff --git a/core/provider/service.go b/core/provider/service.go index 536221d0..c74f9cb9 100644 --- a/core/provider/service.go +++ b/core/provider/service.go @@ -13,7 +13,9 @@ type Service struct { // NewService returns repository struct func NewService(repository Repository) *Service { - return &Service{repository} + return &Service{ + repository: repository, + } } func (s *Service) List(ctx context.Context, flt Filter) ([]Provider, error) { @@ -21,7 +23,10 @@ func (s *Service) List(ctx context.Context, flt Filter) ([]Provider, error) { } func (s *Service) Create(ctx context.Context, prov *Provider) error { - //TODO check provider is nil + if prov == nil { + return errors.ErrInvalid.WithMsgf("provider is nil") + } + err := s.repository.Create(ctx, prov) if err != nil { if errors.Is(err, ErrDuplicate) { @@ -29,6 +34,7 @@ func (s *Service) Create(ctx context.Context, prov *Provider) error { } return err } + return nil } @@ -44,6 +50,10 @@ func (s *Service) Get(ctx context.Context, id uint64) (*Provider, error) { } func (s *Service) Update(ctx context.Context, prov *Provider) error { + if prov == nil { + return errors.ErrInvalid.WithMsgf("provider is nil") + } + err := s.repository.Update(ctx, prov) if err != nil { if errors.Is(err, ErrDuplicate) { @@ -54,6 +64,7 @@ func (s *Service) Update(ctx context.Context, prov *Provider) error { } return err } + return nil } diff --git a/core/provider/sync.go b/core/provider/sync.go deleted file mode 100644 index 59ad47c8..00000000 --- a/core/provider/sync.go +++ /dev/null @@ -1,14 +0,0 @@ -package provider - -type SyncSubscriptionMethod string - -const ( - TypeSyncBatch SyncSubscriptionMethod = "batch" - TypeSyncSingle SyncSubscriptionMethod = "single" -) - -func (sm SyncSubscriptionMethod) String() string { - return string(sm) -} - -const DefaultSyncSubscriptionMethod = TypeSyncBatch diff --git a/core/provider/type.go b/core/provider/type.go new file mode 100644 index 00000000..1513edc7 --- /dev/null +++ b/core/provider/type.go @@ -0,0 +1,18 @@ +package provider + +const ( + TypeCortex string = "cortex" +) + +var SupportedTypes = []string{ + TypeCortex, +} + +func IsTypeSupported(providerType string) bool { + for _, st := range SupportedTypes { + if st == providerType { + return true + } + } + return false +} diff --git a/core/receiver/mocks/config_resolver.go b/core/receiver/mocks/config_resolver.go index 494463b5..504358fb 100644 --- a/core/receiver/mocks/config_resolver.go +++ b/core/receiver/mocks/config_resolver.go @@ -68,55 +68,8 @@ func (_c *ConfigResolver_BuildData_Call) Return(_a0 map[string]interface{}, _a1 return _c } -// BuildNotificationConfig provides a mock function with given fields: subscriptionConfigMap, receiverConfigMap -func (_m *ConfigResolver) BuildNotificationConfig(subscriptionConfigMap map[string]interface{}, receiverConfigMap map[string]interface{}) (map[string]interface{}, error) { - ret := _m.Called(subscriptionConfigMap, receiverConfigMap) - - var r0 map[string]interface{} - if rf, ok := ret.Get(0).(func(map[string]interface{}, map[string]interface{}) map[string]interface{}); ok { - r0 = rf(subscriptionConfigMap, receiverConfigMap) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]interface{}) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(map[string]interface{}, map[string]interface{}) error); ok { - r1 = rf(subscriptionConfigMap, receiverConfigMap) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ConfigResolver_BuildNotificationConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildNotificationConfig' -type ConfigResolver_BuildNotificationConfig_Call struct { - *mock.Call -} - -// BuildNotificationConfig is a helper method to define mock.On call -// - subscriptionConfigMap map[string]interface{} -// - receiverConfigMap map[string]interface{} -func (_e *ConfigResolver_Expecter) BuildNotificationConfig(subscriptionConfigMap interface{}, receiverConfigMap interface{}) *ConfigResolver_BuildNotificationConfig_Call { - return &ConfigResolver_BuildNotificationConfig_Call{Call: _e.mock.On("BuildNotificationConfig", subscriptionConfigMap, receiverConfigMap)} -} - -func (_c *ConfigResolver_BuildNotificationConfig_Call) Run(run func(subscriptionConfigMap map[string]interface{}, receiverConfigMap map[string]interface{})) *ConfigResolver_BuildNotificationConfig_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]interface{}), args[1].(map[string]interface{})) - }) - return _c -} - -func (_c *ConfigResolver_BuildNotificationConfig_Call) Return(_a0 map[string]interface{}, _a1 error) *ConfigResolver_BuildNotificationConfig_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// PostHookTransformConfigs provides a mock function with given fields: ctx, configs -func (_m *ConfigResolver) PostHookTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) { +// PostHookDBTransformConfigs provides a mock function with given fields: ctx, configs +func (_m *ConfigResolver) PostHookDBTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) { ret := _m.Called(ctx, configs) var r0 map[string]interface{} @@ -138,32 +91,32 @@ func (_m *ConfigResolver) PostHookTransformConfigs(ctx context.Context, configs return r0, r1 } -// ConfigResolver_PostHookTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostHookTransformConfigs' -type ConfigResolver_PostHookTransformConfigs_Call struct { +// ConfigResolver_PostHookDBTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostHookDBTransformConfigs' +type ConfigResolver_PostHookDBTransformConfigs_Call struct { *mock.Call } -// PostHookTransformConfigs is a helper method to define mock.On call +// PostHookDBTransformConfigs is a helper method to define mock.On call // - ctx context.Context // - configs map[string]interface{} -func (_e *ConfigResolver_Expecter) PostHookTransformConfigs(ctx interface{}, configs interface{}) *ConfigResolver_PostHookTransformConfigs_Call { - return &ConfigResolver_PostHookTransformConfigs_Call{Call: _e.mock.On("PostHookTransformConfigs", ctx, configs)} +func (_e *ConfigResolver_Expecter) PostHookDBTransformConfigs(ctx interface{}, configs interface{}) *ConfigResolver_PostHookDBTransformConfigs_Call { + return &ConfigResolver_PostHookDBTransformConfigs_Call{Call: _e.mock.On("PostHookDBTransformConfigs", ctx, configs)} } -func (_c *ConfigResolver_PostHookTransformConfigs_Call) Run(run func(ctx context.Context, configs map[string]interface{})) *ConfigResolver_PostHookTransformConfigs_Call { +func (_c *ConfigResolver_PostHookDBTransformConfigs_Call) Run(run func(ctx context.Context, configs map[string]interface{})) *ConfigResolver_PostHookDBTransformConfigs_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(map[string]interface{})) }) return _c } -func (_c *ConfigResolver_PostHookTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *ConfigResolver_PostHookTransformConfigs_Call { +func (_c *ConfigResolver_PostHookDBTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *ConfigResolver_PostHookDBTransformConfigs_Call { _c.Call.Return(_a0, _a1) return _c } -// PreHookTransformConfigs provides a mock function with given fields: ctx, configs -func (_m *ConfigResolver) PreHookTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) { +// PreHookDBTransformConfigs provides a mock function with given fields: ctx, configs +func (_m *ConfigResolver) PreHookDBTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) { ret := _m.Called(ctx, configs) var r0 map[string]interface{} @@ -185,26 +138,26 @@ func (_m *ConfigResolver) PreHookTransformConfigs(ctx context.Context, configs m return r0, r1 } -// ConfigResolver_PreHookTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PreHookTransformConfigs' -type ConfigResolver_PreHookTransformConfigs_Call struct { +// ConfigResolver_PreHookDBTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PreHookDBTransformConfigs' +type ConfigResolver_PreHookDBTransformConfigs_Call struct { *mock.Call } -// PreHookTransformConfigs is a helper method to define mock.On call +// PreHookDBTransformConfigs is a helper method to define mock.On call // - ctx context.Context // - configs map[string]interface{} -func (_e *ConfigResolver_Expecter) PreHookTransformConfigs(ctx interface{}, configs interface{}) *ConfigResolver_PreHookTransformConfigs_Call { - return &ConfigResolver_PreHookTransformConfigs_Call{Call: _e.mock.On("PreHookTransformConfigs", ctx, configs)} +func (_e *ConfigResolver_Expecter) PreHookDBTransformConfigs(ctx interface{}, configs interface{}) *ConfigResolver_PreHookDBTransformConfigs_Call { + return &ConfigResolver_PreHookDBTransformConfigs_Call{Call: _e.mock.On("PreHookDBTransformConfigs", ctx, configs)} } -func (_c *ConfigResolver_PreHookTransformConfigs_Call) Run(run func(ctx context.Context, configs map[string]interface{})) *ConfigResolver_PreHookTransformConfigs_Call { +func (_c *ConfigResolver_PreHookDBTransformConfigs_Call) Run(run func(ctx context.Context, configs map[string]interface{})) *ConfigResolver_PreHookDBTransformConfigs_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(map[string]interface{})) }) return _c } -func (_c *ConfigResolver_PreHookTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *ConfigResolver_PreHookTransformConfigs_Call { +func (_c *ConfigResolver_PreHookDBTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *ConfigResolver_PreHookDBTransformConfigs_Call { _c.Call.Return(_a0, _a1) return _c } diff --git a/core/receiver/mocks/resolver.go b/core/receiver/mocks/resolver.go deleted file mode 100644 index f0071468..00000000 --- a/core/receiver/mocks/resolver.go +++ /dev/null @@ -1,264 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// Resolver is an autogenerated mock type for the Resolver type -type Resolver struct { - mock.Mock -} - -type Resolver_Expecter struct { - mock *mock.Mock -} - -func (_m *Resolver) EXPECT() *Resolver_Expecter { - return &Resolver_Expecter{mock: &_m.Mock} -} - -// BuildData provides a mock function with given fields: ctx, configs -func (_m *Resolver) BuildData(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) { - ret := _m.Called(ctx, configs) - - var r0 map[string]interface{} - if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}) map[string]interface{}); ok { - r0 = rf(ctx, configs) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]interface{}) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, map[string]interface{}) error); ok { - r1 = rf(ctx, configs) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Resolver_BuildData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildData' -type Resolver_BuildData_Call struct { - *mock.Call -} - -// BuildData is a helper method to define mock.On call -// - ctx context.Context -// - configs map[string]interface{} -func (_e *Resolver_Expecter) BuildData(ctx interface{}, configs interface{}) *Resolver_BuildData_Call { - return &Resolver_BuildData_Call{Call: _e.mock.On("BuildData", ctx, configs)} -} - -func (_c *Resolver_BuildData_Call) Run(run func(ctx context.Context, configs map[string]interface{})) *Resolver_BuildData_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(map[string]interface{})) - }) - return _c -} - -func (_c *Resolver_BuildData_Call) Return(_a0 map[string]interface{}, _a1 error) *Resolver_BuildData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// BuildNotificationConfig provides a mock function with given fields: subscriptionConfigMap, receiverConfigMap -func (_m *Resolver) BuildNotificationConfig(subscriptionConfigMap map[string]interface{}, receiverConfigMap map[string]interface{}) (map[string]interface{}, error) { - ret := _m.Called(subscriptionConfigMap, receiverConfigMap) - - var r0 map[string]interface{} - if rf, ok := ret.Get(0).(func(map[string]interface{}, map[string]interface{}) map[string]interface{}); ok { - r0 = rf(subscriptionConfigMap, receiverConfigMap) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]interface{}) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(map[string]interface{}, map[string]interface{}) error); ok { - r1 = rf(subscriptionConfigMap, receiverConfigMap) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Resolver_BuildNotificationConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildNotificationConfig' -type Resolver_BuildNotificationConfig_Call struct { - *mock.Call -} - -// BuildNotificationConfig is a helper method to define mock.On call -// - subscriptionConfigMap map[string]interface{} -// - receiverConfigMap map[string]interface{} -func (_e *Resolver_Expecter) BuildNotificationConfig(subscriptionConfigMap interface{}, receiverConfigMap interface{}) *Resolver_BuildNotificationConfig_Call { - return &Resolver_BuildNotificationConfig_Call{Call: _e.mock.On("BuildNotificationConfig", subscriptionConfigMap, receiverConfigMap)} -} - -func (_c *Resolver_BuildNotificationConfig_Call) Run(run func(subscriptionConfigMap map[string]interface{}, receiverConfigMap map[string]interface{})) *Resolver_BuildNotificationConfig_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]interface{}), args[1].(map[string]interface{})) - }) - return _c -} - -func (_c *Resolver_BuildNotificationConfig_Call) Return(_a0 map[string]interface{}, _a1 error) *Resolver_BuildNotificationConfig_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Notify provides a mock function with given fields: ctx, configs, payloadMessage -func (_m *Resolver) Notify(ctx context.Context, configs map[string]interface{}, payloadMessage map[string]interface{}) error { - ret := _m.Called(ctx, configs, payloadMessage) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}, map[string]interface{}) error); ok { - r0 = rf(ctx, configs, payloadMessage) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Resolver_Notify_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Notify' -type Resolver_Notify_Call struct { - *mock.Call -} - -// Notify is a helper method to define mock.On call -// - ctx context.Context -// - configs map[string]interface{} -// - payloadMessage map[string]interface{} -func (_e *Resolver_Expecter) Notify(ctx interface{}, configs interface{}, payloadMessage interface{}) *Resolver_Notify_Call { - return &Resolver_Notify_Call{Call: _e.mock.On("Notify", ctx, configs, payloadMessage)} -} - -func (_c *Resolver_Notify_Call) Run(run func(ctx context.Context, configs map[string]interface{}, payloadMessage map[string]interface{})) *Resolver_Notify_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(map[string]interface{}), args[2].(map[string]interface{})) - }) - return _c -} - -func (_c *Resolver_Notify_Call) Return(_a0 error) *Resolver_Notify_Call { - _c.Call.Return(_a0) - return _c -} - -// PostHookTransformConfigs provides a mock function with given fields: ctx, configs -func (_m *Resolver) PostHookTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) { - ret := _m.Called(ctx, configs) - - var r0 map[string]interface{} - if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}) map[string]interface{}); ok { - r0 = rf(ctx, configs) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]interface{}) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, map[string]interface{}) error); ok { - r1 = rf(ctx, configs) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Resolver_PostHookTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PostHookTransformConfigs' -type Resolver_PostHookTransformConfigs_Call struct { - *mock.Call -} - -// PostHookTransformConfigs is a helper method to define mock.On call -// - ctx context.Context -// - configs map[string]interface{} -func (_e *Resolver_Expecter) PostHookTransformConfigs(ctx interface{}, configs interface{}) *Resolver_PostHookTransformConfigs_Call { - return &Resolver_PostHookTransformConfigs_Call{Call: _e.mock.On("PostHookTransformConfigs", ctx, configs)} -} - -func (_c *Resolver_PostHookTransformConfigs_Call) Run(run func(ctx context.Context, configs map[string]interface{})) *Resolver_PostHookTransformConfigs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(map[string]interface{})) - }) - return _c -} - -func (_c *Resolver_PostHookTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *Resolver_PostHookTransformConfigs_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// PreHookTransformConfigs provides a mock function with given fields: ctx, configs -func (_m *Resolver) PreHookTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) { - ret := _m.Called(ctx, configs) - - var r0 map[string]interface{} - if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}) map[string]interface{}); ok { - r0 = rf(ctx, configs) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]interface{}) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, map[string]interface{}) error); ok { - r1 = rf(ctx, configs) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Resolver_PreHookTransformConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PreHookTransformConfigs' -type Resolver_PreHookTransformConfigs_Call struct { - *mock.Call -} - -// PreHookTransformConfigs is a helper method to define mock.On call -// - ctx context.Context -// - configs map[string]interface{} -func (_e *Resolver_Expecter) PreHookTransformConfigs(ctx interface{}, configs interface{}) *Resolver_PreHookTransformConfigs_Call { - return &Resolver_PreHookTransformConfigs_Call{Call: _e.mock.On("PreHookTransformConfigs", ctx, configs)} -} - -func (_c *Resolver_PreHookTransformConfigs_Call) Run(run func(ctx context.Context, configs map[string]interface{})) *Resolver_PreHookTransformConfigs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(map[string]interface{})) - }) - return _c -} - -func (_c *Resolver_PreHookTransformConfigs_Call) Return(_a0 map[string]interface{}, _a1 error) *Resolver_PreHookTransformConfigs_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -type mockConstructorTestingTNewResolver interface { - mock.TestingT - Cleanup(func()) -} - -// NewResolver creates a new instance of Resolver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewResolver(t mockConstructorTestingTNewResolver) *Resolver { - mock := &Resolver{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/receiver/receiver.go b/core/receiver/receiver.go index aac51aff..22b647ee 100644 --- a/core/receiver/receiver.go +++ b/core/receiver/receiver.go @@ -15,9 +15,12 @@ type Repository interface { } type Receiver struct { - ID uint64 `json:"id"` - Name string `json:"name"` - Type string `json:"type"` // TODO receiver type should be immutable + ID uint64 `json:"id"` + Name string `json:"name"` + + // Type should be immutable + Type string `json:"type"` + Labels map[string]string `json:"labels"` Configurations map[string]interface{} `json:"configurations"` Data map[string]interface{} `json:"data"` diff --git a/core/receiver/plugin.go b/core/receiver/receiver_plugin.go similarity index 60% rename from core/receiver/plugin.go rename to core/receiver/receiver_plugin.go index ab748dcf..4bf3c38a 100644 --- a/core/receiver/plugin.go +++ b/core/receiver/receiver_plugin.go @@ -9,7 +9,6 @@ import "context" //go:generate mockery --name=ConfigResolver -r --case underscore --with-expecter --structname ConfigResolver --filename config_resolver.go --output=./mocks type ConfigResolver interface { BuildData(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) - BuildNotificationConfig(subscriptionConfigMap map[string]interface{}, receiverConfigMap map[string]interface{}) (map[string]interface{}, error) - PreHookTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) - PostHookTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) + PreHookDBTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) + PostHookDBTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) } diff --git a/core/receiver/service.go b/core/receiver/service.go index 57a527f5..1538b919 100644 --- a/core/receiver/service.go +++ b/core/receiver/service.go @@ -41,7 +41,7 @@ func (s *Service) List(ctx context.Context, flt Filter) ([]Receiver, error) { if err != nil { return nil, err } - transformedConfigs, err := receiverPlugin.PostHookTransformConfigs(ctx, rcv.Configurations) + transformedConfigs, err := receiverPlugin.PostHookDBTransformConfigs(ctx, rcv.Configurations) if err != nil { return nil, err } @@ -58,7 +58,7 @@ func (s *Service) Create(ctx context.Context, rcv *Receiver) error { return err } - rcv.Configurations, err = receiverPlugin.PreHookTransformConfigs(ctx, rcv.Configurations) + rcv.Configurations, err = receiverPlugin.PreHookDBTransformConfigs(ctx, rcv.Configurations) if err != nil { return err } @@ -85,7 +85,7 @@ func (s *Service) Get(ctx context.Context, id uint64) (*Receiver, error) { return nil, err } - transformedConfigs, err := receiverPlugin.PostHookTransformConfigs(ctx, rcv.Configurations) + transformedConfigs, err := receiverPlugin.PostHookDBTransformConfigs(ctx, rcv.Configurations) if err != nil { return nil, err } @@ -107,7 +107,7 @@ func (s *Service) Update(ctx context.Context, rcv *Receiver) error { return err } - rcv.Configurations, err = receiverPlugin.PreHookTransformConfigs(ctx, rcv.Configurations) + rcv.Configurations, err = receiverPlugin.PreHookDBTransformConfigs(ctx, rcv.Configurations) if err != nil { return err } @@ -119,22 +119,10 @@ func (s *Service) Update(ctx context.Context, rcv *Receiver) error { } return err } + return nil } func (s *Service) Delete(ctx context.Context, id uint64) error { return s.repository.Delete(ctx, id) } - -func (s *Service) BuildNotificationConfig(subsConfs map[string]interface{}, rcv *Receiver) (map[string]interface{}, error) { - if rcv == nil { - return nil, errors.ErrInvalid.WithCausef("receiver is nil") - } - - receiverPlugin, err := s.getReceiverPlugin(rcv.Type) - if err != nil { - return nil, err - } - - return receiverPlugin.BuildNotificationConfig(subsConfs, rcv.Configurations) -} diff --git a/core/receiver/service_test.go b/core/receiver/service_test.go index 23f9e542..b22096b0 100644 --- a/core/receiver/service_test.go +++ b/core/receiver/service_test.go @@ -50,7 +50,7 @@ func TestService_ListReceivers(t *testing.T) { UpdatedAt: timeNow, }, }, nil) - ss.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{ + ss.EXPECT().PostHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{ "token": "key", }).Return(nil, errors.New("decrypt error")) }, @@ -85,7 +85,7 @@ func TestService_ListReceivers(t *testing.T) { UpdatedAt: timeNow, }, }, nil) - ss.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{ + ss.EXPECT().PostHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{ "token": "key", }).Return(map[string]interface{}{ "token": "decrypted_key", @@ -160,9 +160,9 @@ func TestService_CreateReceiver(t *testing.T) { Err: errors.New("unsupported receiver type: \"random\""), }, { - Description: "should return error if PreHookTransformConfigs return error", + Description: "should return error if PreHookDBTransformConfigs return error", Setup: func(rr *mocks.ReceiverRepository, ss *mocks.ConfigResolver) { - ss.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(nil, errors.New("some error")) + ss.EXPECT().PreHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(nil, errors.New("some error")) }, Rcv: &receiver.Receiver{ @@ -177,7 +177,7 @@ func TestService_CreateReceiver(t *testing.T) { { Description: "should return error if Create repository return error", Setup: func(rr *mocks.ReceiverRepository, ss *mocks.ConfigResolver) { - ss.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ + ss.EXPECT().PreHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ "token": "encrypted_key", }, nil) rr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), &receiver.Receiver{ @@ -200,7 +200,7 @@ func TestService_CreateReceiver(t *testing.T) { { Description: "should return nil error if no error returned", Setup: func(rr *mocks.ReceiverRepository, ss *mocks.ConfigResolver) { - ss.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ + ss.EXPECT().PreHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ "token": "encrypted_key", }, nil) rr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), &receiver.Receiver{ @@ -302,7 +302,7 @@ func TestService_GetReceiver(t *testing.T) { CreatedAt: timeNow, UpdatedAt: timeNow, }, nil) - ss.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{ + ss.EXPECT().PostHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{ "token": "key", }).Return(nil, errors.New("decrypt error")) }, @@ -324,7 +324,7 @@ func TestService_GetReceiver(t *testing.T) { CreatedAt: timeNow, UpdatedAt: timeNow, }, nil) - ss.EXPECT().PostHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{ + ss.EXPECT().PostHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{ "token": "key", }).Return(map[string]interface{}{ "token": "decrypted_key", @@ -405,9 +405,9 @@ func TestService_UpdateReceiver(t *testing.T) { Err: errors.New("unsupported receiver type: \"random\""), }, { - Description: "should return error if PreHookTransformConfigs return error", + Description: "should return error if PreHookDBTransformConfigs return error", Setup: func(rr *mocks.ReceiverRepository, ss *mocks.ConfigResolver) { - ss.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(nil, errors.New("some error")) + ss.EXPECT().PreHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(nil, errors.New("some error")) }, Rcv: &receiver.Receiver{ ID: 123, @@ -421,7 +421,7 @@ func TestService_UpdateReceiver(t *testing.T) { { Description: "should return error if Update repository return error", Setup: func(rr *mocks.ReceiverRepository, ss *mocks.ConfigResolver) { - ss.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ + ss.EXPECT().PreHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ "token": "encrypted_key", }, nil) rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &receiver.Receiver{ @@ -444,7 +444,7 @@ func TestService_UpdateReceiver(t *testing.T) { { Description: "should return nil error if no error returned", Setup: func(rr *mocks.ReceiverRepository, ss *mocks.ConfigResolver) { - ss.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ + ss.EXPECT().PreHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ "token": "encrypted_key", }, nil) rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &receiver.Receiver{ @@ -466,7 +466,7 @@ func TestService_UpdateReceiver(t *testing.T) { }, { Description: "should return error not found if repository return not found error", Setup: func(rr *mocks.ReceiverRepository, ss *mocks.ConfigResolver) { - ss.EXPECT().PreHookTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ + ss.EXPECT().PreHookDBTransformConfigs(mock.AnythingOfType("*context.emptyCtx"), map[string]interface{}{"token": "key"}).Return(map[string]interface{}{ "token": "encrypted_key", }, nil) rr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), &receiver.Receiver{ diff --git a/core/rule/mocks/rule_uploader.go b/core/rule/mocks/rule_uploader.go index 88d7a1fd..3f00dd47 100644 --- a/core/rule/mocks/rule_uploader.go +++ b/core/rule/mocks/rule_uploader.go @@ -5,9 +5,11 @@ package mocks import ( context "context" - rule "github.com/odpf/siren/core/rule" + provider "github.com/odpf/siren/core/provider" mock "github.com/stretchr/testify/mock" + rule "github.com/odpf/siren/core/rule" + template "github.com/odpf/siren/core/template" ) @@ -24,13 +26,13 @@ func (_m *RuleUploader) EXPECT() *RuleUploader_Expecter { return &RuleUploader_Expecter{mock: &_m.Mock} } -// UpsertRule provides a mock function with given fields: ctx, rl, templateToUpdate, namespaceURN -func (_m *RuleUploader) UpsertRule(ctx context.Context, rl *rule.Rule, templateToUpdate *template.Template, namespaceURN string) error { - ret := _m.Called(ctx, rl, templateToUpdate, namespaceURN) +// UpsertRule provides a mock function with given fields: ctx, namespaceURN, prov, rl, templateToUpdate +func (_m *RuleUploader) UpsertRule(ctx context.Context, namespaceURN string, prov provider.Provider, rl *rule.Rule, templateToUpdate *template.Template) error { + ret := _m.Called(ctx, namespaceURN, prov, rl, templateToUpdate) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *rule.Rule, *template.Template, string) error); ok { - r0 = rf(ctx, rl, templateToUpdate, namespaceURN) + if rf, ok := ret.Get(0).(func(context.Context, string, provider.Provider, *rule.Rule, *template.Template) error); ok { + r0 = rf(ctx, namespaceURN, prov, rl, templateToUpdate) } else { r0 = ret.Error(0) } @@ -45,16 +47,17 @@ 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 -// - namespaceURN string -func (_e *RuleUploader_Expecter) UpsertRule(ctx interface{}, rl interface{}, templateToUpdate interface{}, namespaceURN interface{}) *RuleUploader_UpsertRule_Call { - return &RuleUploader_UpsertRule_Call{Call: _e.mock.On("UpsertRule", ctx, rl, templateToUpdate, namespaceURN)} +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)} } -func (_c *RuleUploader_UpsertRule_Call) Run(run func(ctx context.Context, rl *rule.Rule, templateToUpdate *template.Template, namespaceURN string)) *RuleUploader_UpsertRule_Call { +func (_c *RuleUploader_UpsertRule_Call) Run(run func(ctx context.Context, namespaceURN string, prov provider.Provider, rl *rule.Rule, templateToUpdate *template.Template)) *RuleUploader_UpsertRule_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*rule.Rule), args[2].(*template.Template), args[3].(string)) + run(args[0].(context.Context), args[1].(string), args[2].(provider.Provider), args[3].(*rule.Rule), args[4].(*template.Template)) }) return _c } diff --git a/core/rule/provider_plugin.go b/core/rule/provider_plugin.go index b2fd4172..79b061fd 100644 --- a/core/rule/provider_plugin.go +++ b/core/rule/provider_plugin.go @@ -3,6 +3,7 @@ package rule import ( "context" + "github.com/odpf/siren/core/provider" "github.com/odpf/siren/core/template" ) @@ -12,5 +13,5 @@ import ( // //go:generate mockery --name=RuleUploader -r --case underscore --with-expecter --structname RuleUploader --filename rule_uploader.go --output=./mocks type RuleUploader interface { - UpsertRule(ctx context.Context, rl *Rule, templateToUpdate *template.Template, namespaceURN string) error + UpsertRule(ctx context.Context, namespaceURN string, prov provider.Provider, rl *Rule, templateToUpdate *template.Template) error } diff --git a/core/rule/service.go b/core/rule/service.go index c4617d96..e54bb787 100644 --- a/core/rule/service.go +++ b/core/rule/service.go @@ -98,7 +98,7 @@ func (s *Service) Upsert(ctx context.Context, rl *Rule) error { return err } - if err := pluginService.UpsertRule(ctx, rl, templateToUpdate, ns.URN); err != nil { + if err := pluginService.UpsertRule(ctx, ns.URN, ns.Provider, rl, templateToUpdate); err != nil { if err := s.repository.Rollback(ctx, err); err != nil { return err } diff --git a/core/rule/service_test.go b/core/rule/service_test.go index 6ea07fa3..ddf5f48a 100644 --- a/core/rule/service_test.go +++ b/core/rule/service_test.go @@ -138,7 +138,7 @@ func TestService_Upsert(t *testing.T) { rr.EXPECT().WithTransaction(ctx).Return(ctx) rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) - ru.EXPECT().UpsertRule(mock.Anything, mock.AnythingOfType("*rule.Rule"), mock.AnythingOfType("*template.Template"), mock.AnythingOfType("string")).Return(nil) + ru.EXPECT().UpsertRule(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("provider.Provider"), mock.AnythingOfType("*rule.Rule"), mock.AnythingOfType("*template.Template")).Return(nil) rr.EXPECT().Commit(ctx).Return(nil) }, }, @@ -175,7 +175,7 @@ func TestService_Upsert(t *testing.T) { rr.EXPECT().WithTransaction(ctx).Return(ctx) rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) - ru.EXPECT().UpsertRule(mock.Anything, mock.AnythingOfType("*rule.Rule"), mock.AnythingOfType("*template.Template"), mock.AnythingOfType("string")).Return(errors.New("some error")) + ru.EXPECT().UpsertRule(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("provider.Provider"), mock.AnythingOfType("*rule.Rule"), mock.AnythingOfType("*template.Template")).Return(errors.New("some error")) rr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) }, ErrString: "some error", @@ -213,7 +213,7 @@ func TestService_Upsert(t *testing.T) { rr.EXPECT().WithTransaction(ctx).Return(ctx) rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) - ru.EXPECT().UpsertRule(mock.Anything, mock.AnythingOfType("*rule.Rule"), mock.AnythingOfType("*template.Template"), mock.AnythingOfType("string")).Return(errors.New("some error")) + ru.EXPECT().UpsertRule(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("provider.Provider"), mock.AnythingOfType("*rule.Rule"), mock.AnythingOfType("*template.Template")).Return(errors.New("some error")) rr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("rollback error")) }, ErrString: "rollback error", @@ -251,7 +251,7 @@ func TestService_Upsert(t *testing.T) { rr.EXPECT().WithTransaction(ctx).Return(ctx) rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) - ru.EXPECT().UpsertRule(mock.Anything, mock.AnythingOfType("*rule.Rule"), mock.AnythingOfType("*template.Template"), mock.AnythingOfType("string")).Return(nil) + ru.EXPECT().UpsertRule(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("provider.Provider"), mock.AnythingOfType("*rule.Rule"), mock.AnythingOfType("*template.Template")).Return(nil) rr.EXPECT().Commit(ctx).Return(errors.New("some commit error")) }, ErrString: "some commit error", diff --git a/core/subscription/mocks/receiver_service.go b/core/subscription/mocks/receiver_service.go index 6f40f7d1..76b8fa58 100644 --- a/core/subscription/mocks/receiver_service.go +++ b/core/subscription/mocks/receiver_service.go @@ -22,176 +22,6 @@ func (_m *ReceiverService) EXPECT() *ReceiverService_Expecter { return &ReceiverService_Expecter{mock: &_m.Mock} } -// BuildNotificationConfig provides a mock function with given fields: subsConfig, rcv -func (_m *ReceiverService) BuildNotificationConfig(subsConfig map[string]interface{}, rcv *receiver.Receiver) (map[string]interface{}, error) { - ret := _m.Called(subsConfig, rcv) - - var r0 map[string]interface{} - if rf, ok := ret.Get(0).(func(map[string]interface{}, *receiver.Receiver) map[string]interface{}); ok { - r0 = rf(subsConfig, rcv) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]interface{}) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(map[string]interface{}, *receiver.Receiver) error); ok { - r1 = rf(subsConfig, rcv) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReceiverService_BuildNotificationConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildNotificationConfig' -type ReceiverService_BuildNotificationConfig_Call struct { - *mock.Call -} - -// BuildNotificationConfig is a helper method to define mock.On call -// - subsConfig map[string]interface{} -// - rcv *receiver.Receiver -func (_e *ReceiverService_Expecter) BuildNotificationConfig(subsConfig interface{}, rcv interface{}) *ReceiverService_BuildNotificationConfig_Call { - return &ReceiverService_BuildNotificationConfig_Call{Call: _e.mock.On("BuildNotificationConfig", subsConfig, rcv)} -} - -func (_c *ReceiverService_BuildNotificationConfig_Call) Run(run func(subsConfig map[string]interface{}, rcv *receiver.Receiver)) *ReceiverService_BuildNotificationConfig_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]interface{}), args[1].(*receiver.Receiver)) - }) - return _c -} - -func (_c *ReceiverService_BuildNotificationConfig_Call) Return(_a0 map[string]interface{}, _a1 error) *ReceiverService_BuildNotificationConfig_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Create provides a mock function with given fields: ctx, rcv -func (_m *ReceiverService) Create(ctx context.Context, rcv *receiver.Receiver) error { - ret := _m.Called(ctx, rcv) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *receiver.Receiver) error); ok { - r0 = rf(ctx, rcv) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ReceiverService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type ReceiverService_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - ctx context.Context -// - rcv *receiver.Receiver -func (_e *ReceiverService_Expecter) Create(ctx interface{}, rcv interface{}) *ReceiverService_Create_Call { - return &ReceiverService_Create_Call{Call: _e.mock.On("Create", ctx, rcv)} -} - -func (_c *ReceiverService_Create_Call) Run(run func(ctx context.Context, rcv *receiver.Receiver)) *ReceiverService_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*receiver.Receiver)) - }) - return _c -} - -func (_c *ReceiverService_Create_Call) Return(_a0 error) *ReceiverService_Create_Call { - _c.Call.Return(_a0) - return _c -} - -// Delete provides a mock function with given fields: ctx, id -func (_m *ReceiverService) Delete(ctx context.Context, id uint64) error { - ret := _m.Called(ctx, id) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64) error); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ReceiverService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type ReceiverService_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -// - ctx context.Context -// - id uint64 -func (_e *ReceiverService_Expecter) Delete(ctx interface{}, id interface{}) *ReceiverService_Delete_Call { - return &ReceiverService_Delete_Call{Call: _e.mock.On("Delete", ctx, id)} -} - -func (_c *ReceiverService_Delete_Call) Run(run func(ctx context.Context, id uint64)) *ReceiverService_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) - }) - return _c -} - -func (_c *ReceiverService_Delete_Call) Return(_a0 error) *ReceiverService_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -// Get provides a mock function with given fields: ctx, id -func (_m *ReceiverService) Get(ctx context.Context, id uint64) (*receiver.Receiver, error) { - ret := _m.Called(ctx, id) - - var r0 *receiver.Receiver - if rf, ok := ret.Get(0).(func(context.Context, uint64) *receiver.Receiver); ok { - r0 = rf(ctx, id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*receiver.Receiver) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok { - r1 = rf(ctx, id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReceiverService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' -type ReceiverService_Get_Call struct { - *mock.Call -} - -// Get is a helper method to define mock.On call -// - ctx context.Context -// - id uint64 -func (_e *ReceiverService_Expecter) Get(ctx interface{}, id interface{}) *ReceiverService_Get_Call { - return &ReceiverService_Get_Call{Call: _e.mock.On("Get", ctx, id)} -} - -func (_c *ReceiverService_Get_Call) Run(run func(ctx context.Context, id uint64)) *ReceiverService_Get_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) - }) - return _c -} - -func (_c *ReceiverService_Get_Call) Return(_a0 *receiver.Receiver, _a1 error) *ReceiverService_Get_Call { - _c.Call.Return(_a0, _a1) - return _c -} - // List provides a mock function with given fields: ctx, flt func (_m *ReceiverService) List(ctx context.Context, flt receiver.Filter) ([]receiver.Receiver, error) { ret := _m.Called(ctx, flt) @@ -239,44 +69,6 @@ func (_c *ReceiverService_List_Call) Return(_a0 []receiver.Receiver, _a1 error) return _c } -// Update provides a mock function with given fields: ctx, rcv -func (_m *ReceiverService) Update(ctx context.Context, rcv *receiver.Receiver) error { - ret := _m.Called(ctx, rcv) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *receiver.Receiver) error); ok { - r0 = rf(ctx, rcv) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ReceiverService_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' -type ReceiverService_Update_Call struct { - *mock.Call -} - -// Update is a helper method to define mock.On call -// - ctx context.Context -// - rcv *receiver.Receiver -func (_e *ReceiverService_Expecter) Update(ctx interface{}, rcv interface{}) *ReceiverService_Update_Call { - return &ReceiverService_Update_Call{Call: _e.mock.On("Update", ctx, rcv)} -} - -func (_c *ReceiverService_Update_Call) Run(run func(ctx context.Context, rcv *receiver.Receiver)) *ReceiverService_Update_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*receiver.Receiver)) - }) - return _c -} - -func (_c *ReceiverService_Update_Call) Return(_a0 error) *ReceiverService_Update_Call { - _c.Call.Return(_a0) - return _c -} - type mockConstructorTestingTNewReceiverService interface { mock.TestingT Cleanup(func()) diff --git a/core/subscription/mocks/subscription_repository.go b/core/subscription/mocks/subscription_repository.go index 2d80d199..fa1fd0c2 100644 --- a/core/subscription/mocks/subscription_repository.go +++ b/core/subscription/mocks/subscription_repository.go @@ -22,43 +22,6 @@ func (_m *SubscriptionRepository) EXPECT() *SubscriptionRepository_Expecter { return &SubscriptionRepository_Expecter{mock: &_m.Mock} } -// Commit provides a mock function with given fields: ctx -func (_m *SubscriptionRepository) Commit(ctx context.Context) error { - ret := _m.Called(ctx) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionRepository_Commit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Commit' -type SubscriptionRepository_Commit_Call struct { - *mock.Call -} - -// Commit is a helper method to define mock.On call -// - ctx context.Context -func (_e *SubscriptionRepository_Expecter) Commit(ctx interface{}) *SubscriptionRepository_Commit_Call { - return &SubscriptionRepository_Commit_Call{Call: _e.mock.On("Commit", ctx)} -} - -func (_c *SubscriptionRepository_Commit_Call) Run(run func(ctx context.Context)) *SubscriptionRepository_Commit_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *SubscriptionRepository_Commit_Call) Return(_a0 error) *SubscriptionRepository_Commit_Call { - _c.Call.Return(_a0) - return _c -} - // Create provides a mock function with given fields: _a0, _a1 func (_m *SubscriptionRepository) Create(_a0 context.Context, _a1 *subscription.Subscription) error { ret := _m.Called(_a0, _a1) @@ -229,44 +192,6 @@ func (_c *SubscriptionRepository_List_Call) Return(_a0 []subscription.Subscripti return _c } -// Rollback provides a mock function with given fields: ctx, err -func (_m *SubscriptionRepository) Rollback(ctx context.Context, err error) error { - ret := _m.Called(ctx, err) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, error) error); ok { - r0 = rf(ctx, err) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionRepository_Rollback_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Rollback' -type SubscriptionRepository_Rollback_Call struct { - *mock.Call -} - -// Rollback is a helper method to define mock.On call -// - ctx context.Context -// - err error -func (_e *SubscriptionRepository_Expecter) Rollback(ctx interface{}, err interface{}) *SubscriptionRepository_Rollback_Call { - return &SubscriptionRepository_Rollback_Call{Call: _e.mock.On("Rollback", ctx, err)} -} - -func (_c *SubscriptionRepository_Rollback_Call) Run(run func(ctx context.Context, err error)) *SubscriptionRepository_Rollback_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(error)) - }) - return _c -} - -func (_c *SubscriptionRepository_Rollback_Call) Return(_a0 error) *SubscriptionRepository_Rollback_Call { - _c.Call.Return(_a0) - return _c -} - // Update provides a mock function with given fields: _a0, _a1 func (_m *SubscriptionRepository) Update(_a0 context.Context, _a1 *subscription.Subscription) error { ret := _m.Called(_a0, _a1) @@ -305,45 +230,6 @@ func (_c *SubscriptionRepository_Update_Call) Return(_a0 error) *SubscriptionRep return _c } -// WithTransaction provides a mock function with given fields: ctx -func (_m *SubscriptionRepository) WithTransaction(ctx context.Context) context.Context { - ret := _m.Called(ctx) - - var r0 context.Context - if rf, ok := ret.Get(0).(func(context.Context) context.Context); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(context.Context) - } - } - - return r0 -} - -// SubscriptionRepository_WithTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WithTransaction' -type SubscriptionRepository_WithTransaction_Call struct { - *mock.Call -} - -// WithTransaction is a helper method to define mock.On call -// - ctx context.Context -func (_e *SubscriptionRepository_Expecter) WithTransaction(ctx interface{}) *SubscriptionRepository_WithTransaction_Call { - return &SubscriptionRepository_WithTransaction_Call{Call: _e.mock.On("WithTransaction", ctx)} -} - -func (_c *SubscriptionRepository_WithTransaction_Call) Run(run func(ctx context.Context)) *SubscriptionRepository_WithTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *SubscriptionRepository_WithTransaction_Call) Return(_a0 context.Context) *SubscriptionRepository_WithTransaction_Call { - _c.Call.Return(_a0) - return _c -} - type mockConstructorTestingTNewSubscriptionRepository interface { mock.TestingT Cleanup(func()) diff --git a/core/subscription/mocks/subscription_syncer.go b/core/subscription/mocks/subscription_syncer.go deleted file mode 100644 index a0c08756..00000000 --- a/core/subscription/mocks/subscription_syncer.go +++ /dev/null @@ -1,158 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - subscription "github.com/odpf/siren/core/subscription" - mock "github.com/stretchr/testify/mock" -) - -// SubscriptionSyncer is an autogenerated mock type for the SubscriptionSyncer type -type SubscriptionSyncer struct { - mock.Mock -} - -type SubscriptionSyncer_Expecter struct { - mock *mock.Mock -} - -func (_m *SubscriptionSyncer) EXPECT() *SubscriptionSyncer_Expecter { - return &SubscriptionSyncer_Expecter{mock: &_m.Mock} -} - -// CreateSubscription provides a mock function with given fields: ctx, sub, subscriptionsInNamespace, namespaceURN -func (_m *SubscriptionSyncer) CreateSubscription(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string) error { - ret := _m.Called(ctx, sub, subscriptionsInNamespace, namespaceURN) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *subscription.Subscription, []subscription.Subscription, string) error); ok { - r0 = rf(ctx, sub, subscriptionsInNamespace, namespaceURN) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionSyncer_CreateSubscription_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateSubscription' -type SubscriptionSyncer_CreateSubscription_Call struct { - *mock.Call -} - -// CreateSubscription is a helper method to define mock.On call -// - ctx context.Context -// - sub *subscription.Subscription -// - subscriptionsInNamespace []subscription.Subscription -// - namespaceURN string -func (_e *SubscriptionSyncer_Expecter) CreateSubscription(ctx interface{}, sub interface{}, subscriptionsInNamespace interface{}, namespaceURN interface{}) *SubscriptionSyncer_CreateSubscription_Call { - return &SubscriptionSyncer_CreateSubscription_Call{Call: _e.mock.On("CreateSubscription", ctx, sub, subscriptionsInNamespace, namespaceURN)} -} - -func (_c *SubscriptionSyncer_CreateSubscription_Call) Run(run func(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string)) *SubscriptionSyncer_CreateSubscription_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*subscription.Subscription), args[2].([]subscription.Subscription), args[3].(string)) - }) - return _c -} - -func (_c *SubscriptionSyncer_CreateSubscription_Call) Return(_a0 error) *SubscriptionSyncer_CreateSubscription_Call { - _c.Call.Return(_a0) - return _c -} - -// DeleteSubscription provides a mock function with given fields: ctx, sub, subscriptionsInNamespace, namespaceURN -func (_m *SubscriptionSyncer) DeleteSubscription(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string) error { - ret := _m.Called(ctx, sub, subscriptionsInNamespace, namespaceURN) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *subscription.Subscription, []subscription.Subscription, string) error); ok { - r0 = rf(ctx, sub, subscriptionsInNamespace, namespaceURN) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionSyncer_DeleteSubscription_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteSubscription' -type SubscriptionSyncer_DeleteSubscription_Call struct { - *mock.Call -} - -// DeleteSubscription is a helper method to define mock.On call -// - ctx context.Context -// - sub *subscription.Subscription -// - subscriptionsInNamespace []subscription.Subscription -// - namespaceURN string -func (_e *SubscriptionSyncer_Expecter) DeleteSubscription(ctx interface{}, sub interface{}, subscriptionsInNamespace interface{}, namespaceURN interface{}) *SubscriptionSyncer_DeleteSubscription_Call { - return &SubscriptionSyncer_DeleteSubscription_Call{Call: _e.mock.On("DeleteSubscription", ctx, sub, subscriptionsInNamespace, namespaceURN)} -} - -func (_c *SubscriptionSyncer_DeleteSubscription_Call) Run(run func(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string)) *SubscriptionSyncer_DeleteSubscription_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*subscription.Subscription), args[2].([]subscription.Subscription), args[3].(string)) - }) - return _c -} - -func (_c *SubscriptionSyncer_DeleteSubscription_Call) Return(_a0 error) *SubscriptionSyncer_DeleteSubscription_Call { - _c.Call.Return(_a0) - return _c -} - -// UpdateSubscription provides a mock function with given fields: ctx, sub, subscriptionsInNamespace, namespaceURN -func (_m *SubscriptionSyncer) UpdateSubscription(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string) error { - ret := _m.Called(ctx, sub, subscriptionsInNamespace, namespaceURN) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *subscription.Subscription, []subscription.Subscription, string) error); ok { - r0 = rf(ctx, sub, subscriptionsInNamespace, namespaceURN) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionSyncer_UpdateSubscription_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateSubscription' -type SubscriptionSyncer_UpdateSubscription_Call struct { - *mock.Call -} - -// UpdateSubscription is a helper method to define mock.On call -// - ctx context.Context -// - sub *subscription.Subscription -// - subscriptionsInNamespace []subscription.Subscription -// - namespaceURN string -func (_e *SubscriptionSyncer_Expecter) UpdateSubscription(ctx interface{}, sub interface{}, subscriptionsInNamespace interface{}, namespaceURN interface{}) *SubscriptionSyncer_UpdateSubscription_Call { - return &SubscriptionSyncer_UpdateSubscription_Call{Call: _e.mock.On("UpdateSubscription", ctx, sub, subscriptionsInNamespace, namespaceURN)} -} - -func (_c *SubscriptionSyncer_UpdateSubscription_Call) Run(run func(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string)) *SubscriptionSyncer_UpdateSubscription_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*subscription.Subscription), args[2].([]subscription.Subscription), args[3].(string)) - }) - return _c -} - -func (_c *SubscriptionSyncer_UpdateSubscription_Call) Return(_a0 error) *SubscriptionSyncer_UpdateSubscription_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewSubscriptionSyncer interface { - mock.TestingT - Cleanup(func()) -} - -// NewSubscriptionSyncer creates a new instance of SubscriptionSyncer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSubscriptionSyncer(t mockConstructorTestingTNewSubscriptionSyncer) *SubscriptionSyncer { - mock := &SubscriptionSyncer{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/subscription/option.go b/core/subscription/option.go deleted file mode 100644 index 3dfa4676..00000000 --- a/core/subscription/option.go +++ /dev/null @@ -1,9 +0,0 @@ -package subscription - -type ServiceOption func(*Service) - -func RegisterProviderPlugin(typeName string, service SubscriptionSyncer) ServiceOption { - return func(s *Service) { - s.subscriptionProviderRegistry[typeName] = service - } -} diff --git a/core/subscription/provider_plugin.go b/core/subscription/provider_plugin.go deleted file mode 100644 index 817671de..00000000 --- a/core/subscription/provider_plugin.go +++ /dev/null @@ -1,18 +0,0 @@ -package subscription - -import ( - "context" -) - -// SubscriptionSyncer is an interface for the provider to upload subscription(s). -// Provider plugin needs to implement this interface in order to support subscription -// synchronization from siren to provider. All methods in the interface pass -// 2 kind of data: a subscription and all subscriptions in the tenant. -// This is to accomodate some provider (e.g. cortex) that only support bulk upload. -// -//go:generate mockery --name=SubscriptionSyncer -r --case underscore --with-expecter --structname SubscriptionSyncer --filename subscription_syncer.go --output=./mocks -type SubscriptionSyncer interface { - CreateSubscription(ctx context.Context, sub *Subscription, subscriptionsInNamespace []Subscription, namespaceURN string) error - UpdateSubscription(ctx context.Context, sub *Subscription, subscriptionsInNamespace []Subscription, namespaceURN string) error - DeleteSubscription(ctx context.Context, sub *Subscription, subscriptionsInNamespace []Subscription, namespaceURN string) error -} diff --git a/core/subscription/service.go b/core/subscription/service.go index 8129cd6c..15e14b24 100644 --- a/core/subscription/service.go +++ b/core/subscription/service.go @@ -2,7 +2,6 @@ package subscription import ( "context" - "sort" "github.com/odpf/siren/core/namespace" "github.com/odpf/siren/core/receiver" @@ -21,32 +20,21 @@ type NamespaceService interface { //go:generate mockery --name=ReceiverService -r --case underscore --with-expecter --structname ReceiverService --filename receiver_service.go --output=./mocks type ReceiverService interface { List(ctx context.Context, flt receiver.Filter) ([]receiver.Receiver, error) - Create(ctx context.Context, rcv *receiver.Receiver) error - Get(ctx context.Context, id uint64) (*receiver.Receiver, error) - Update(ctx context.Context, rcv *receiver.Receiver) error - Delete(ctx context.Context, id uint64) error - BuildNotificationConfig(subsConfig map[string]interface{}, rcv *receiver.Receiver) (map[string]interface{}, error) } // Service handles business logic type Service struct { - repository Repository - namespaceService NamespaceService - receiverService ReceiverService - subscriptionProviderRegistry map[string]SubscriptionSyncer + repository Repository + namespaceService NamespaceService + receiverService ReceiverService } // NewService returns service struct -func NewService(repository Repository, namespaceService NamespaceService, receiverService ReceiverService, sopts ...ServiceOption) *Service { +func NewService(repository Repository, namespaceService NamespaceService, receiverService ReceiverService) *Service { svc := &Service{ - repository: repository, - namespaceService: namespaceService, - receiverService: receiverService, - subscriptionProviderRegistry: map[string]SubscriptionSyncer{}, - } - - for _, opt := range sopts { - opt(svc) + repository: repository, + namespaceService: namespaceService, + receiverService: receiverService, } return svc @@ -62,24 +50,7 @@ func (s *Service) List(ctx context.Context, flt Filter) ([]Subscription, error) } func (s *Service) Create(ctx context.Context, sub *Subscription) error { - // check provider type of the namespace - ns, err := s.namespaceService.Get(ctx, sub.Namespace) - if err != nil { - return err - } - - sortReceivers(sub) - - pluginService, err := s.getProviderPluginService(ns.Provider.Type) - if err != nil { - return err - } - - ctx = s.repository.WithTransaction(ctx) - if err = s.repository.Create(ctx, sub); err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } + if err := s.repository.Create(ctx, sub); err != nil { if errors.Is(err, ErrDuplicate) { return errors.ErrConflict.WithMsgf(err.Error()) } @@ -89,24 +60,6 @@ func (s *Service) Create(ctx context.Context, sub *Subscription) error { return err } - subscriptionsInNamespace, err := s.FetchEnrichedSubscriptionsByNamespace(ctx, ns) - if err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } - return err - } - - if err := pluginService.CreateSubscription(ctx, sub, subscriptionsInNamespace, ns.URN); err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } - return err - } - - if err := s.repository.Commit(ctx); err != nil { - return err - } return nil } @@ -123,24 +76,7 @@ func (s *Service) Get(ctx context.Context, id uint64) (*Subscription, error) { } func (s *Service) Update(ctx context.Context, sub *Subscription) error { - // check provider type of the namespace - ns, err := s.namespaceService.Get(ctx, sub.Namespace) - if err != nil { - return err - } - - sortReceivers(sub) - - pluginService, err := s.getProviderPluginService(ns.Provider.Type) - if err != nil { - return err - } - - ctx = s.repository.WithTransaction(ctx) - if err = s.repository.Update(ctx, sub); err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } + if err := s.repository.Update(ctx, sub); err != nil { if errors.Is(err, ErrDuplicate) { return errors.ErrConflict.WithMsgf(err.Error()) } @@ -153,68 +89,11 @@ func (s *Service) Update(ctx context.Context, sub *Subscription) error { return err } - subscriptionsInNamespace, err := s.FetchEnrichedSubscriptionsByNamespace(ctx, ns) - if err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } - return err - } - - if err := pluginService.UpdateSubscription(ctx, sub, subscriptionsInNamespace, ns.URN); err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } - return err - } - - if err := s.repository.Commit(ctx); err != nil { - return err - } return nil } func (s *Service) Delete(ctx context.Context, id uint64) error { - sub, err := s.repository.Get(ctx, id) - if err != nil { - return err - } - - // check provider type of the namespace - ns, err := s.namespaceService.Get(ctx, sub.Namespace) - if err != nil { - return err - } - - pluginService, err := s.getProviderPluginService(ns.Provider.Type) - if err != nil { - return err - } - - ctx = s.repository.WithTransaction(ctx) if err := s.repository.Delete(ctx, id); err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } - return err - } - - subscriptionsInNamespace, err := s.FetchEnrichedSubscriptionsByNamespace(ctx, ns) - if err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } - return err - } - - if err := pluginService.DeleteSubscription(ctx, sub, subscriptionsInNamespace, ns.URN); err != nil { - if err := s.repository.Rollback(ctx, err); err != nil { - return err - } - return err - } - - if err := s.repository.Commit(ctx); err != nil { return err } @@ -241,7 +120,7 @@ func (s *Service) MatchByLabels(ctx context.Context, labels map[string]string) ( return nil, err } - subscriptionsByLabels, err = AssignReceivers(s.receiverService, receiversMap, subscriptionsByLabels) + subscriptionsByLabels, err = AssignReceivers(receiversMap, subscriptionsByLabels) if err != nil { return nil, err } @@ -249,43 +128,6 @@ func (s *Service) MatchByLabels(ctx context.Context, labels map[string]string) ( return subscriptionsByLabels, nil } -func (s *Service) getProviderPluginService(providerType string) (SubscriptionSyncer, error) { - pluginService, exist := s.subscriptionProviderRegistry[providerType] - if !exist { - return nil, errors.ErrInvalid.WithMsgf("unsupported provider type: %q", providerType) - } - return pluginService, nil -} - -func (s *Service) FetchEnrichedSubscriptionsByNamespace( - ctx context.Context, - ns *namespace.Namespace) ([]Subscription, error) { - - // fetch all subscriptions in this namespace. - subscriptionsInNamespace, err := s.repository.List(ctx, Filter{ - NamespaceID: ns.ID, - }) - if err != nil { - return nil, err - } - - if len(subscriptionsInNamespace) == 0 { - return subscriptionsInNamespace, nil - } - - receiversMap, err := CreateReceiversMap(ctx, s.receiverService, subscriptionsInNamespace) - if err != nil { - return nil, err - } - - subscriptionsInNamespace, err = AssignReceivers(s.receiverService, receiversMap, subscriptionsInNamespace) - if err != nil { - return nil, err - } - - return subscriptionsInNamespace, nil -} - func CreateReceiversMap(ctx context.Context, receiverService ReceiverService, subscriptions []Subscription) (map[uint64]*receiver.Receiver, error) { receiversMap := map[uint64]*receiver.Receiver{} for _, subs := range subscriptions { @@ -332,27 +174,29 @@ func CreateReceiversMap(ctx context.Context, receiverService ReceiverService, su return receiversMap, nil } -func AssignReceivers(receiverService ReceiverService, receiversMap map[uint64]*receiver.Receiver, subscriptions []Subscription) ([]Subscription, error) { +func AssignReceivers(receiversMap map[uint64]*receiver.Receiver, subscriptions []Subscription) ([]Subscription, error) { for is := range subscriptions { for ir, subsRcv := range subscriptions[is].Receivers { if mappedRcv := receiversMap[subsRcv.ID]; mappedRcv == nil { return nil, errors.ErrInvalid.WithMsgf("receiver id %d not found", subsRcv.ID) } - subsConfig, err := receiverService.BuildNotificationConfig(subsRcv.Configuration, receiversMap[subsRcv.ID]) - if err != nil { - return nil, errors.ErrInvalid.WithMsgf(err.Error()) - } + mergedConfigMap := MergeConfigsMap(subsRcv.Configuration, receiversMap[subsRcv.ID].Configurations) subscriptions[is].Receivers[ir].ID = receiversMap[subsRcv.ID].ID subscriptions[is].Receivers[ir].Type = receiversMap[subsRcv.ID].Type - subscriptions[is].Receivers[ir].Configuration = subsConfig + subscriptions[is].Receivers[ir].Configuration = mergedConfigMap } } return subscriptions, nil } -func sortReceivers(sub *Subscription) { - sort.Slice(sub.Receivers, func(i, j int) bool { - return sub.Receivers[i].ID < sub.Receivers[j].ID - }) +func MergeConfigsMap(subscriptionConfigMap map[string]interface{}, receiverConfigsMap map[string]interface{}) map[string]interface{} { + var newConfigMap = make(map[string]interface{}) + for k, v := range subscriptionConfigMap { + newConfigMap[k] = v + } + for k, v := range receiverConfigsMap { + newConfigMap[k] = v + } + return newConfigMap } diff --git a/core/subscription/service_test.go b/core/subscription/service_test.go index e76e0893..7e847e3d 100644 --- a/core/subscription/service_test.go +++ b/core/subscription/service_test.go @@ -6,16 +6,12 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/odpf/siren/core/namespace" - "github.com/odpf/siren/core/provider" "github.com/odpf/siren/core/receiver" "github.com/odpf/siren/core/subscription" "github.com/odpf/siren/core/subscription/mocks" "github.com/stretchr/testify/mock" ) -const testProviderType = "test-type" - func TestService_List(t *testing.T) { type testCase struct { Description string @@ -119,222 +115,40 @@ func TestService_Create(t *testing.T) { type testCase struct { Description string Subscription *subscription.Subscription - Setup func(*mocks.SubscriptionRepository, *mocks.NamespaceService, *mocks.ReceiverService, *mocks.SubscriptionSyncer) + Setup func(*mocks.SubscriptionRepository, *mocks.NamespaceService, *mocks.ReceiverService) ErrString string } var ( - ctx = context.TODO() - mockFetchSubscriptionsByNamespaceSuccess = func(sr *mocks.SubscriptionRepository, rs *mocks.ReceiverService) { - - sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return([]subscription.Subscription{ - { - URN: "alert-history-odpf", - Namespace: 2, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - { - URN: "odpf-data-warning", - Namespace: 1, - Receivers: []subscription.Receiver{ - { - ID: 1, - Configuration: map[string]interface{}{ - "channel_name": "odpf-data", - }, - }, - }, - Match: map[string]string{ - "environment": "integration", - "team": "odpf-data", - }, - }, - }, nil) - sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return([]subscription.Subscription{ - { - URN: "alert-history-odpf", - Namespace: 2, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - { - URN: "odpf-data-warning", - Namespace: 1, - Receivers: []subscription.Receiver{ - { - ID: 1, - Configuration: map[string]interface{}{ - "channel_name": "odpf-data", - }, - }, - }, - Match: map[string]string{ - "environment": "integration", - "team": "odpf-data", - }, - }, - }, nil) - rs.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ - { - ID: 1, - }, - }, nil) - rs.EXPECT().BuildNotificationConfig(mock.AnythingOfType("map[string]interface {}"), mock.AnythingOfType("*receiver.Receiver")).Return(map[string]interface{}{}, nil) - } - + ctx = context.TODO() testCases = []testCase{ - { - Description: "should return error if namespace service get return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil, errors.New("some error")) - }, - ErrString: "some error", - }, - { - Description: "should return error if provider type not exist", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: "random", - }, - }, nil) - }, - ErrString: "unsupported provider type: \"random\"", - }, { Description: "should return error conflict if create subscription return error duplicate", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(subscription.ErrDuplicate) - sr.EXPECT().Rollback(ctx, subscription.ErrDuplicate).Return(nil) }, ErrString: "urn already exist", }, { Description: "should return error not found if create subscription return error relation", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(subscription.ErrRelation) - sr.EXPECT().Rollback(ctx, subscription.ErrRelation).Return(nil) }, ErrString: "namespace id does not exist", }, { Description: "should return error if create subscription return some error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) }, ErrString: "some error", }, { Description: "should return no error if create subscription return no error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) - mockFetchSubscriptionsByNamespaceSuccess(sr, rs) - pp.EXPECT().CreateSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(nil) - sr.EXPECT().Commit(ctx).Return(nil) }, }, - { - Description: "should return error if transaction commit return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) - mockFetchSubscriptionsByNamespaceSuccess(sr, rs) - pp.EXPECT().CreateSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(nil) - sr.EXPECT().Commit(ctx).Return(errors.New("commit error")) - }, - ErrString: "commit error", - }, - { - Description: "should return error if create repository return error and rollback return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) - }, - ErrString: "some rollback error", - }, - { - Description: "should return error if provider plugin create return error and rollback success", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) - mockFetchSubscriptionsByNamespaceSuccess(sr, rs) - pp.EXPECT().CreateSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) - }, - ErrString: "some error", - }, - { - Description: "should return error if provider plugin create return error and rollback failed", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) - mockFetchSubscriptionsByNamespaceSuccess(sr, rs) - pp.EXPECT().CreateSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) - }, - ErrString: "some rollback error", - }, } ) @@ -344,15 +158,13 @@ func TestService_Create(t *testing.T) { repositoryMock = new(mocks.SubscriptionRepository) namespaceServiceMock = new(mocks.NamespaceService) receiverServiceMock = new(mocks.ReceiverService) - providerPluginMock = new(mocks.SubscriptionSyncer) ) svc := subscription.NewService( repositoryMock, namespaceServiceMock, receiverServiceMock, - subscription.RegisterProviderPlugin(testProviderType, providerPluginMock), ) - tc.Setup(repositoryMock, namespaceServiceMock, receiverServiceMock, providerPluginMock) + tc.Setup(repositoryMock, namespaceServiceMock, receiverServiceMock) err := svc.Create(ctx, &subscription.Subscription{}) if tc.ErrString != "" { @@ -364,7 +176,6 @@ func TestService_Create(t *testing.T) { repositoryMock.AssertExpectations(t) namespaceServiceMock.AssertExpectations(t) receiverServiceMock.AssertExpectations(t) - providerPluginMock.AssertExpectations(t) }) } } @@ -373,234 +184,46 @@ func TestService_Update(t *testing.T) { type testCase struct { Description string Subscription *subscription.Subscription - Setup func(*mocks.SubscriptionRepository, *mocks.NamespaceService, *mocks.ReceiverService, *mocks.SubscriptionSyncer) + Setup func(*mocks.SubscriptionRepository, *mocks.NamespaceService, *mocks.ReceiverService) ErrString string } var ( - ctx = context.TODO() - mockSyncToUpstreamSuccess = func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { - sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return([]subscription.Subscription{ - { - URN: "alert-history-odpf", - Namespace: 2, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - { - URN: "odpf-data-warning", - Namespace: 1, - Receivers: []subscription.Receiver{ - { - ID: 1, - Configuration: map[string]interface{}{ - "channel_name": "odpf-data", - }, - }, - }, - Match: map[string]string{ - "environment": "integration", - "team": "odpf-data", - }, - }, - }, nil) - sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return([]subscription.Subscription{ - { - URN: "alert-history-odpf", - Namespace: 2, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - { - URN: "odpf-data-warning", - Namespace: 1, - Receivers: []subscription.Receiver{ - { - ID: 1, - Configuration: map[string]interface{}{ - "channel_name": "odpf-data", - }, - }, - }, - Match: map[string]string{ - "environment": "integration", - "team": "odpf-data", - }, - }, - }, nil) - rs.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ - { - ID: 1, - }, - }, nil) - rs.EXPECT().BuildNotificationConfig(mock.AnythingOfType("map[string]interface {}"), mock.AnythingOfType("*receiver.Receiver")).Return(map[string]interface{}{}, nil) - } + ctx = context.TODO() testCases = []testCase{ - { - Description: "should return error if namespace service get return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, cc *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil, errors.New("some error")) - }, - ErrString: "some error", - }, - { - Description: "should return error if provider type not exist", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: "random", - }, - }, nil) - }, - ErrString: "unsupported provider type: \"random\"", - }, { Description: "should return error conflict if update subscription return error duplicate", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(subscription.ErrDuplicate) - sr.EXPECT().Rollback(ctx, subscription.ErrDuplicate).Return(nil) }, ErrString: "urn already exist", }, { Description: "should return error not found if update subscription return error relation", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(subscription.ErrRelation) - sr.EXPECT().Rollback(ctx, subscription.ErrRelation).Return(nil) }, ErrString: "namespace id does not exist", }, { Description: "should return error not found if update subscription return not found error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(subscription.NotFoundError{}) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) }, ErrString: "subscription not found", }, { Description: "should return error if update subscription return some error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) }, ErrString: "some error", }, - { - Description: "should return error if update subscription return error and rollback return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) - }, - ErrString: "some rollback error", - }, - { - Description: "should return error if provider plugin update return error and rollback success", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) - mockSyncToUpstreamSuccess(sr, ns, rs) - pp.EXPECT().UpdateSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) - }, - ErrString: "some error", - }, - { - Description: "should return error if provider plugin update return error and rollback failed", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) - mockSyncToUpstreamSuccess(sr, ns, rs) - pp.EXPECT().UpdateSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) - }, - ErrString: "some rollback error", - }, { Description: "should return no error if update subscription return no error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) - mockSyncToUpstreamSuccess(sr, ns, rs) - pp.EXPECT().UpdateSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(nil) - sr.EXPECT().Commit(ctx).Return(nil) }, }, - { - Description: "should return error if transaction commit return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) - mockSyncToUpstreamSuccess(sr, ns, rs) - pp.EXPECT().UpdateSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(nil) - sr.EXPECT().Commit(ctx).Return(errors.New("commit error")) - }, - ErrString: "commit error", - }, } ) @@ -610,15 +233,13 @@ func TestService_Update(t *testing.T) { repositoryMock = new(mocks.SubscriptionRepository) namespaceServiceMock = new(mocks.NamespaceService) receiverServiceMock = new(mocks.ReceiverService) - providerPluginMock = new(mocks.SubscriptionSyncer) ) svc := subscription.NewService( repositoryMock, namespaceServiceMock, receiverServiceMock, - subscription.RegisterProviderPlugin(testProviderType, providerPluginMock), ) - tc.Setup(repositoryMock, namespaceServiceMock, receiverServiceMock, providerPluginMock) + tc.Setup(repositoryMock, namespaceServiceMock, receiverServiceMock) err := svc.Update(ctx, &subscription.Subscription{}) if tc.ErrString != "" { @@ -630,7 +251,6 @@ func TestService_Update(t *testing.T) { repositoryMock.AssertExpectations(t) namespaceServiceMock.AssertExpectations(t) receiverServiceMock.AssertExpectations(t) - providerPluginMock.AssertExpectations(t) }) } } @@ -639,204 +259,32 @@ func TestService_Delete(t *testing.T) { type testCase struct { Description string Subscription *subscription.Subscription - Setup func(*mocks.SubscriptionRepository, *mocks.NamespaceService, *mocks.ReceiverService, *mocks.SubscriptionSyncer) + Setup func(*mocks.SubscriptionRepository, *mocks.NamespaceService, *mocks.ReceiverService) ErrString string } var ( - ctx = context.TODO() - mockSyncToUpstreamSuccess = func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { - sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return([]subscription.Subscription{ - { - URN: "alert-history-odpf", - Namespace: 2, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - { - URN: "odpf-data-warning", - Namespace: 1, - Receivers: []subscription.Receiver{ - { - ID: 1, - Configuration: map[string]interface{}{ - "channel_name": "odpf-data", - }, - }, - }, - Match: map[string]string{ - "environment": "integration", - "team": "odpf-data", - }, - }, - }, nil) - sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return([]subscription.Subscription{ - { - URN: "alert-history-odpf", - Namespace: 2, - Receivers: []subscription.Receiver{ - { - ID: 1, - }, - }, - }, - { - URN: "odpf-data-warning", - Namespace: 1, - Receivers: []subscription.Receiver{ - { - ID: 1, - Configuration: map[string]interface{}{ - "channel_name": "odpf-data", - }, - }, - }, - Match: map[string]string{ - "environment": "integration", - "team": "odpf-data", - }, - }, - }, nil) - rs.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("receiver.Filter")).Return([]receiver.Receiver{ - { - ID: 1, - }, - }, nil) - rs.EXPECT().BuildNotificationConfig(mock.AnythingOfType("map[string]interface {}"), mock.AnythingOfType("*receiver.Receiver")).Return(map[string]interface{}{}, nil) - } + ctx = context.TODO() testCases = []testCase{ - { - Description: "should return error if get subscription repository return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil, errors.New("some error")) - }, - ErrString: "some error", - }, - { - Description: "should return error if namespace service get return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil, errors.New("some error")) - }, - ErrString: "some error", - }, - { - Description: "should return error if provider type not exist", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: "random", - }, - }, nil) - }, - ErrString: "unsupported provider type: \"random\"", - }, { Description: "should return error if delete subscription return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) }, ErrString: "some error", }, { - Description: "should return error if delete subscription return error and rollback return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Description: "should return error if delete subscription return error", + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) - }, - ErrString: "some rollback error", - }, - { - Description: "should return error if provider plugin delete return error and rollback success", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil) - mockSyncToUpstreamSuccess(sr, ns, rs) - pp.EXPECT().DeleteSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) }, ErrString: "some error", }, - { - Description: "should return error if provider plugin delete return error and rollback failed", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil) - mockSyncToUpstreamSuccess(sr, ns, rs) - pp.EXPECT().DeleteSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(errors.New("some error")) - sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) - }, - ErrString: "some rollback error", - }, { Description: "should return no error if delete subscription return no error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService) { sr.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil) - mockSyncToUpstreamSuccess(sr, ns, rs) - pp.EXPECT().DeleteSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(nil) - sr.EXPECT().Commit(ctx).Return(nil) }, }, - { - Description: "should return error if transaction commit return error", - Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, rs *mocks.ReceiverService, pp *mocks.SubscriptionSyncer) { - sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) - ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{ - Provider: provider.Provider{ - Type: testProviderType, - }, - }, nil) - - sr.EXPECT().WithTransaction(ctx).Return(ctx) - sr.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil) - mockSyncToUpstreamSuccess(sr, ns, rs) - pp.EXPECT().DeleteSubscription(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription"), mock.AnythingOfType("[]subscription.Subscription"), mock.AnythingOfType("string")).Return(nil) - sr.EXPECT().Commit(ctx).Return(errors.New("commit error")) - }, - ErrString: "commit error", - }, } ) @@ -846,15 +294,13 @@ func TestService_Delete(t *testing.T) { repositoryMock = new(mocks.SubscriptionRepository) namespaceServiceMock = new(mocks.NamespaceService) receiverServiceMock = new(mocks.ReceiverService) - providerPluginMock = new(mocks.SubscriptionSyncer) ) svc := subscription.NewService( repositoryMock, namespaceServiceMock, receiverServiceMock, - subscription.RegisterProviderPlugin(testProviderType, providerPluginMock), ) - tc.Setup(repositoryMock, namespaceServiceMock, receiverServiceMock, providerPluginMock) + tc.Setup(repositoryMock, namespaceServiceMock, receiverServiceMock) err := svc.Delete(ctx, 100) if tc.ErrString != "" { @@ -866,7 +312,6 @@ func TestService_Delete(t *testing.T) { repositoryMock.AssertExpectations(t) namespaceServiceMock.AssertExpectations(t) receiverServiceMock.AssertExpectations(t) - providerPluginMock.AssertExpectations(t) }) } } @@ -972,7 +417,6 @@ func TestAssignReceivers(t *testing.T) { Description string Subscriptions []subscription.Subscription ReceiversMap map[uint64]*receiver.Receiver - Setup func(*mocks.ReceiverService) ExpectedSubscriptions []subscription.Subscription ErrString string } @@ -1022,32 +466,16 @@ func TestAssignReceivers(t *testing.T) { 3: nil, 4: nil, }, - Setup: func(rs *mocks.ReceiverService) { - }, ErrString: "receiver id 1 not found", }, - { - Description: "should return error if get subscription config return error", - Subscriptions: inputSubscriptions, - ReceiversMap: map[uint64]*receiver.Receiver{ - 1: {ID: 1, Type: receiver.TypeHTTP}, - 2: {ID: 2, Type: receiver.TypePagerDuty}, - 3: {ID: 3, Type: receiver.TypeSlack}, - 4: {ID: 4, Type: receiver.TypeSlack}, - }, - Setup: func(rs *mocks.ReceiverService) { - rs.EXPECT().BuildNotificationConfig(mock.AnythingOfType("map[string]interface {}"), mock.AnythingOfType("*receiver.Receiver")).Return(nil, errors.New("some error")) - }, - ErrString: "some error", - }, { Description: "should assign receivers to subscription if assigning receivers return no error", Subscriptions: inputSubscriptions, ReceiversMap: map[uint64]*receiver.Receiver{ - 1: {ID: 1, Type: receiver.TypeHTTP}, - 2: {ID: 2, Type: receiver.TypePagerDuty}, - 3: {ID: 3, Type: receiver.TypeSlack}, - 4: {ID: 4, Type: receiver.TypeSlack}, + 1: {ID: 1, Type: receiver.TypeHTTP, Configurations: map[string]interface{}{"newkey": "newvalue"}}, + 2: {ID: 2, Type: receiver.TypePagerDuty, Configurations: map[string]interface{}{"newkey": "newvalue"}}, + 3: {ID: 3, Type: receiver.TypeSlack, Configurations: map[string]interface{}{"newkey": "newvalue"}}, + 4: {ID: 4, Type: receiver.TypeSlack, Configurations: map[string]interface{}{"newkey": "newvalue"}}, }, ExpectedSubscriptions: []subscription.Subscription{ { @@ -1091,31 +519,19 @@ func TestAssignReceivers(t *testing.T) { }, }, }, - Setup: func(rs *mocks.ReceiverService) { - rs.EXPECT().BuildNotificationConfig(mock.AnythingOfType("map[string]interface {}"), mock.AnythingOfType("*receiver.Receiver")).Return(map[string]interface{}{ - "token": "abcabc", - "newkey": "newvalue", - }, nil) - }, }, } for _, tc := range testCases { t.Run(tc.Description, func(t *testing.T) { - var ( - receiverServiceMock = new(mocks.ReceiverService) - ) - - tc.Setup(receiverServiceMock) - - got, err := subscription.AssignReceivers(receiverServiceMock, tc.ReceiversMap, tc.Subscriptions) + got, err := subscription.AssignReceivers(tc.ReceiversMap, tc.Subscriptions) if tc.ErrString != "" { if tc.ErrString != err.Error() { t.Fatalf("got error %s, expected was %s", err.Error(), tc.ErrString) } } - if !cmp.Equal(got, tc.ExpectedSubscriptions) { - t.Fatalf("got result %+v, expected was %+v", got, tc.ExpectedSubscriptions) + if diff := cmp.Diff(got, tc.ExpectedSubscriptions); diff != "" { + t.Fatalf("got diff: %s", diff) } }) } diff --git a/core/subscription/subscription.go b/core/subscription/subscription.go index 0a4e622b..b32d0b06 100644 --- a/core/subscription/subscription.go +++ b/core/subscription/subscription.go @@ -7,7 +7,7 @@ import ( //go:generate mockery --name=Repository -r --case underscore --with-expecter --structname SubscriptionRepository --filename subscription_repository.go --output=./mocks type Repository interface { - Transactor + // Transactor List(context.Context, Filter) ([]Subscription, error) Create(context.Context, *Subscription) error Get(context.Context, uint64) (*Subscription, error) @@ -15,16 +15,12 @@ type Repository interface { Delete(context.Context, uint64) error } -type Transactor interface { - WithTransaction(ctx context.Context) context.Context - Rollback(ctx context.Context, err error) error - Commit(ctx context.Context) error -} - type Receiver struct { ID uint64 `json:"id"` - Type string `json:"type"` Configuration map[string]interface{} `json:"configuration"` + + // Type won't be exposed to the end-user, this is used to add more details for notification purposes + Type string } type Subscription struct { diff --git a/core/template/template.go b/core/template/template.go index f37f50f4..d8607575 100644 --- a/core/template/template.go +++ b/core/template/template.go @@ -6,7 +6,7 @@ import ( ) const ( - ReservedName_DefaultCortex = "system-default-cortex" + ReservedName_SystemDefault = "system-default" ) //go:generate mockery --name=Repository -r --case underscore --with-expecter --structname TemplateRepository --filename template_repository.go --output=./mocks @@ -35,5 +35,5 @@ type Template struct { } func IsReservedName(templateName string) bool { - return (templateName == ReservedName_DefaultCortex) + return (templateName == ReservedName_SystemDefault) } diff --git a/docs/docs/concepts/plugin.md b/docs/docs/concepts/plugin.md index 94ffc7c2..774830e8 100644 --- a/docs/docs/concepts/plugin.md +++ b/docs/docs/concepts/plugin.md @@ -1,13 +1,37 @@ # Plugin -Siren decouples various `provider`, `receiver`, and `queue` as a plugin. The purpose is to ease the extension of new plugin. We welcome all contributions to add new plugin. +Siren decouples `provider` and `receiver` as a plugin. The purpose is to ease the extension of new plugin. We welcome all contributions to add new plugin. ## Provider -Provider responsibility is to accept incoming rules configuration from Siren and send alerts to the designated Siren Hook API. Provider plugin needs to fulfill some interfaces. More detail about interfaces can be found in [contribution](../contribute/provider.md) page. Supported providers are: +Provider responsibility is to accept incoming rules configuration from Siren and send alerts to the designated Siren's Hook API. Provider plugin needs to fulfill some interfaces. More detail about interfaces can be found in [contribution](../contribute/provider.md) page. Supported providers are: - [Cortexmetrics](https://cortexmetrics.io/) +### Configurations + +Siren provider plugin could have an `AppConfig`: +- A config of provider plugin that is being loaded when the Siren app is started. `AppConfig` can be set up via environment variable or config file. Usually this is a generic config of a specific receiver regardless which namespace the provider is attached to. If your plugin requires `AppConfig`, you can set the config inside `plugins/providers/config.go`. +### Interface + +```go +type ProviderPlugin interface { + // AlertTransformer + TransformToAlerts(ctx context.Context, providerID uint64, body map[string]interface{}) ([]*alert.Alert, int, error) + + // ConfigSyncer + SyncRuntimeConfig(ctx context.Context, namespaceURN string, prov provider.Provider) error + + // RuleUploader + UpsertRule(ctx context.Context, namespaceURN string, prov provider.Provider, rl *rule.Rule, templateToUpdate *template.Template) error +} +``` +**AlertTransformer** interface is being used by alert service to transform incoming alert body in Siren's Hook API to list of *alert.Alert + +**ConfigSyncer** interface is being used by namespace service to synchronize runtime-provider configs for a specific namespace. In cortex, it is being used to sync alertmanager config. + +**RuleUploader** interface is being used to upsert rules to the provider. It support templatization of rules. + ## Receiver Receiver defines where the notification Siren sends to. Receiver plugin needs to fulfill some interfaces. More detail about interfaces can be found in [contribution](../contribute/receiver.md) page. Supported providers are: @@ -21,62 +45,72 @@ Receiver plugin is being used by two different services: receiver service and no ### Configurations -Siren receiver plugins have several configs: `ReceiverConfig`, `SubscriptionConfig` (if needed), and `NotificationConfig`. +Siren receiver plugin could have several configs: +1. `ReceiverConfig` + - A config that will be part of `receiver.Receiver` struct and will be stored inside the DB's receivers table. +2. `SubscriptionConfig` (optional) + - Only if needed + - Defined and used if the receivers inside subscription requires another additional configs rather than `ReceiverConfig`. For example, Slack stores encrypted `token` when storing receiver information inside the DB but has another config `channel_name` on subscription level. +3. `NotificationConfig` + - Embeds `ReceiverConfig` and `SubscriptionConfig` (if exists). +4. `AppConfig` + - A config of receiver plugin that is being loaded when the Siren app is started. `AppConfig` can be set up via environment variable or config file. Usually this is a generic config of a specific receiver regardless where the notification is being sent to (e.g. http config, receiver host, etc...). If your plugin requires `AppConfig`, you can set the config inside `plugins/receivers/config.go`. -- **ReceiverConfig** is a config that will be part of `receiver.Receiver` struct and will be stored inside the DB's receivers table. -- **SubscriptionConfig** is optional. Subscription config is defined and used if the receivers inside subscription requires another additional configs rather than `ReceiverConfig`. For example, Slack stores encrypted `token` when storing receiver information inside the DB but has another config `channel_name` on subscription level. -- **NotificationConfig** embeds `ReceiverConfig` and `SubscriptionConfig` (if needed). -- **AppConfig** is a config of receiver plugins that is being loaded when the Siren app is started. `AppConfig` can be set up via environment variable or config file. Usually this is a generic config of a specific receiver regardless where the notification is being sent to (e.g. http config, receiver host, etc...). If your plugin requires `AppConfig`, you can set the config inside `plugins/receivers/config.go`. - -In Siren receiver plugins, all configs will be transform back and forth from `map[string]interface{}` to struct using [mitchellh/mapstructure](https://github.com/mitchellh/mapstructure). You might also need to add more functions to validate and transform configs to `map[string]interface{}`. +> In Siren receiver plugins, all configs will be transform back and forth from `map[string]interface{}` to struct using [mitchellh/mapstructure](https://github.com/mitchellh/mapstructure). You might also need to add more functions to validate and transform configs to `map[string]interface{}`. ### Interface -#### ConfigResolver - -ConfigResolver is being used by receiver service to manage receivers. It is an interface for the receiver to resolve all configs and functions. - ```go -type ConfigResolver interface { - // TODO might be removed - BuildData(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) - // TODO might be removed - BuildNotificationConfig(subscriptionConfigMap map[string]interface{}, receiverConfigMap map[string]interface{}) (map[string]interface{}, error) - PreHookTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) - PostHookTransformConfigs(ctx context.Context, configs map[string]interface{}) (map[string]interface{}, error) +type ReceiverPlugin interface { + // Config Resolver + PreHookDBTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) + PostHookDBTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) + BuildData(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) + + // Notifier + PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) + PostHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) + GetSystemDefaultTemplate() string + Send(ctx context.Context, notificationMessage notification.Message) (bool, error) } ``` -- **BuildData** is being used in GetReceiver where `data` field in Receiver is being populated. This might not relevant anymore for our current use case and might be deprecated later. -- **BuildNotificationConfig** is being used for subscription. This might not relevant anymore for our current use case and might be deprecated later. -- **PreHookTransformConfigs** is being used to transform configs (e.g. encryption) before the config is being stored in the DB. -- **PostHookTransformConfigs** is being used to transform configs (e.g. decryption) after the config is being fetched from the DB. +ConfigResolver interface is being used by receiver service to manage receivers. It is an interface for the receiver to resolve all configs and functions. -#### Notifier +- **BuildData** is optional. It is being used in Get and List Receiver where `data` field in Receiver is being populated to send back custom information to the users. Slack receiver uses this to show what channels does the slack receiver support in a workspace. +- **PreHookDBTransformConfigs** is being used to transform configs (e.g. encryption) before the config is being stored in the DB. +- **PostHookDBTransformConfigs** is being used to transform configs (e.g. decryption) after the config is being fetched from the DB. Notifier interface is being used by notification service and consists of all functionalities to publish notifications. -```go -type Notifier interface { - PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) - PostHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) - DefaultTemplateOfProvider(templateName string) string - Publish(ctx context.Context, message Message) (bool, error) -} -``` - -- **PreHookTransformConfigs** is being used to transform configs (e.g. encryption) before the config is being enqueued. -- **PostHookTransformConfigs** is being used to transform configs (e.g. decryption) after the config is being dequeued. -- **DefaultTemplateOfProvider** assigns default provider template for alert notifications of as specific provider. Each provider might send alerts with different format, the template needs to build notification specific message out of the alerts for each provider. Each provider has to have a reserved template name (e.g. `template.ReservedName_xxx`) and all alerts coming from the provider needs to use the template with the reserved name. -- **Publish** handles how message is being sent. The first return argument is `retryable` boolean to indicate whether an error is a `retryable` error or not. If it is API call, usually response status code 429 or 5xx is retriable. You can use `pkg/retrier` to retry the call. +- **PreHookQueueTransformConfigs** is being used to transform configs (e.g. encryption) before the config is being enqueued. +- **PostHookQueueTransformConfigs** is being used to transform configs (e.g. decryption) after the config is being dequeued. +- **GetSystemDefaultTemplate** assigns default template for alert notifications. It is expected for hook API to transform the data into the [system-default template variables](#notification-system-default-template). +- **Send** handles how message is being sent. The first return argument is `retryable` boolean to indicate whether an error is a `retryable` error or not. If it is API call, usually response status code 429 or 5xx is retriable. You can use `pkg/retrier` to retry the call. -### Base Plugin +## Base Plugin -Siren provide base plugin in `plugins/receivers/base` which can be embedded in all plugins service struct. By doing so, you just need to implement all interfaces' method that you only need. The unimplemented methods one will already be handled by the `base` plugin. +Siren provide base plugin in `plugins/receivers/base` and `plugins/providers/base` which needs to be embedded in all plugins service struct. By doing so, you just need to implement all interface's methods that you only need. The unimplemented methods one will already be handled by the `base` plugin. -## Queue +## Notification System Default Template -Queue is used as a buffer for the outbound notifications. Siren has a pluggable queue where user could choose which Queue to use in the [config](../reference/server_configuration.md). Supported Queues are: - -- In-Memory -- PostgreSQl +Each receiver might want to have a specific template to transform alerts into a receiver's contract. In that case one needs to define a template in a `.goyaml` file inside `plugins/receivers/{type}/config/*.goyaml`. It is expected for each Hook API to [populate the Data and Labels of notification](#hook-api) with the same variable keys. Below are the supported variables that could be used inside the template. +``` +Data +- id +- status ("firing"/"resolved") +- resource +- template +- metricValue +- metricName +- generatorUrl +- numAlertsFiring +- dashboard +- playbook +- summary + +Labels +- severity ("WARNING"/"CRITICAL") +- alertname +- others specific rules +``` \ No newline at end of file diff --git a/docs/docs/contribute/provider.md b/docs/docs/contribute/provider.md index 40b99168..855f1c83 100644 --- a/docs/docs/contribute/provider.md +++ b/docs/docs/contribute/provider.md @@ -1,5 +1,22 @@ # Add a New Provider Plugin -Provider plugin is being used to update rule configurations to the provider and setup some provider configuration config if any. +Provider plugin is being used to update rule configurations to the provider, setup some runtime provider configuration config if any, and transform incoming alert data in Siren's Hook API to list of *alert.Alert model. -TODO: there is a need to deprecate the old subscription flow. This will be added once the new subscription flow has been implemented. \ No newline at end of file +## Requirements + +1. You need to find a way for the provider to send alert to Siren's hook API `{version}/alerts/{provider_type}/{provider_id}`. You need to implement a `TransformToAlerts` method to parse and transform the body of incoming request received by Siren's hook API to the list of Siren's *alert.Alert model. + +2. You need to implement `UpsertRules` method to push all rules changes to the provider everytime a rule is upserted. + +3. If provider supports setting the configuration dynamically and if for each provider's namespace there is a possibility to have different config (e.g. for multitenancy purpose like CortexMetrics), you could implement `SyncRuntimeConfig` function to push all configurations changes to the provider everytime a new namespace is created or an existing namespace is updated. + + +## Defining Configs + +If there is a need to have a generic config for the provider that is being loaded during start-up, you could add a new `AppConfig` and assign the config to `plugins/providers/config.go` to expose it to the app-level config. Siren will recognize and read the config when starting up. + +## Integrate New Plugin with Siren + +1. Define and add your new type of plugin inside `core/providers/type.go`. +2. Initialize your plugin receiver service and notification service and add to the `ConfigResolver` and `Notifier` registries map in `cli/deps`. +3. To make sure notification handler and dlq handler process your new type, don't forget to add your new receiver type in notification message & dlq handler config or make it default to support all receiver types. diff --git a/docs/docs/contribute/receiver.md b/docs/docs/contribute/receiver.md index 9da73363..2387f2ce 100644 --- a/docs/docs/contribute/receiver.md +++ b/docs/docs/contribute/receiver.md @@ -1,6 +1,32 @@ # Add a New Receiver Plugin -More details about the concept of receiver plugin can be found [here](../concepts/plugin.md#receiver). In this part, we will show hot to add a new receiver plugin to write notifications to a `file`. +More details about the concept of receiver plugin can be found [here](../concepts/plugin.md#receiver). + +## Requirements + +1. You need to figure out whether there is a need to do pre-processing of receiver configuration before storing to the database or enqueueing to the queue. For some receivers, there is a need to do encryption or validation in pre-processing step, in this case you could implement `PreHookDBTransformConfigs` to transform and validate configurations before storing it to the DB and `PreHookQueueTransformConfigs` to transform and validate configurations before storing it to the queue. + +2. If there is a need to transform back receiver's configurations (e.g. decrypting config), you need to implement `PostHookDBTransformConfigs` or `PostHookQueueTransformConfigs` to transform the config back for processing. + +3. You need to implement `Send` method to send notification message to the external notification vendor. + +## Defining Configs + +- If there is a need to have a generic config for the receiver that is being loaded during start-up, you could add a new `AppConfig` and assign the config to `plugins/receivers/config.go` to expose it to the app-level config. Siren will recognize and read the config when starting up. +- It is also possible for a receiver to have different config in the receiver and subscription. For example, slack has a dedicated config called `channel_name` in subscription to send notification only to a specific channel. In this case you need to define separate configurations: `ReceiverConfig` and `SubscriptionConfig`. +- You need to implement `NotificationConfig` which is a placeholder to combine `ReceiverConfig` and `SubscriptionConfig` (if any). Therefore `NotificationConfig` should just embed `ReceiverConfig` and `SubscriptionConfig` (if needed). You might also want to add more function to validate and transform the config to `map[string]interface{}`. + + +## Integrate New Plugin with Siren + +1. Define and add your new type of plugin inside `core/providers/type.go`. +2. Initialize your plugin receiver service and notification service and add to the `ConfigResolver` and `Notifier` registries map in `cli/deps`. +3. To make sure notification handler and dlq handler process your new type, don't forget to add your new receiver type in notification message & dlq handler config or make it default to support all receiver types. + + +# Sample: Add a new `file` receiver + +In this part, we will show how to add a new receiver plugin to send notifications to a local `file`. ## Defining Configs @@ -19,24 +45,27 @@ type NotificationConfig struct { For `file` type, we don't need an `AppConfig` as for now. So we don't need to add one in `plugins/receivers/config.go`. -Now that we already have defined all configs needed, we needs to implement all interfaces needed by defining a new `ReceiverService` and `NotificationService`. +Now that we already have defined all configs needed, we needs to implement all methods of interfaces needed by defining a new `PluginService`. -## Implement ConfigResolver +## Implement interfaces -We need to create a new `ReceiverService` and implement `ConfigResolver`. For `file` receiver, we don't need to do transformation of configs before and after writing and reading from the DB. Therefore, we only needs to implement two `ConfigResolver` methods: `PreHookTransformConfigs` to validate the config before storing it to the DB and `BuildNotificationConfig` to merge `ReceiverConfig` into a `NotificationConfig`. +We need to create a new `Plugin` and implement `ConfigResolver` and `Notifier`. For `file` receiver, we don't need to do encryption of configs before and after writing and reading from the DB as well as Queue. Therefore, we only needs to implement `PreHookDBTransformConfigs` to validate the config before storing it to the DB and `PreHookDBTransformConfigs` to validate the config before enqueueing it. ```go -type ReceiverService struct { - base.UnimplementedReceiverService +// highlight-start + +type PluginService struct { + base.UnimplementedService } -func NewReceiverService() *ReceiverService { - return &ReceiverService{} +// NewPluginService returns file receiver service struct. This service implement [receiver.Resolver] and [notification.Notifier] interface. +func NewPluginService() *PluginService { + return &PluginService{} } -func (s *ReceiverService) PreHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { +func (s *PluginService) PreHookDBTransformConfigs(ctx context.Context, receiverConfigMap map[string]interface{}) (map[string]interface{}, error) { receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(configurations, receiverConfig); err != nil { + if err := mapstructure.Decode(receiverConfigMap, receiverConfig); err != nil { return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) } @@ -44,37 +73,63 @@ func (s *ReceiverService) PreHookTransformConfigs(ctx context.Context, configura return nil, errors.ErrInvalid.WithMsgf(err.Error()) } - return configurations, nil + return receiverConfig.AsMap(), nil } -func (s *ReceiverService) BuildNotificationConfig(subsConfs map[string]interface{}, receiverConfs map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(receiverConfs, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) +func (s *PluginService) PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) } - notificationConfig := NotificationConfig{ - ReceiverConfig: *receiverConfig, + if err := notificationConfig.Validate(); err != nil { + return nil, err } return notificationConfig.AsMap(), nil } +// highlight-end ``` -## Implement Notifier - -We need to create a new `NotificationService` and implement `Notifier`. For `file` receiver, we don't need to do transformation of configs before and after enqueue and dequeue. Therefore, we only needs to implement two `Notifier` methods: `PreHookTransformConfigs` to validate the config before enqueuing notification message and `Publish` to send notifications (to write notifications to a file under `url`). +Beside those 2 functions, we also need to add a function to send notifications (to write notifications to a file under `url`). ```go -type NotificationService struct { - base.UnimplementedNotificationService +type PluginService struct { + base.UnimplementedService } -func NewNotificationService() *NotificationService { - return &NotificationService{} +func NewPluginService() *PluginService { + return &PluginService{} } -func (s *NotificationService) Publish(ctx context.Context, notificationMessage notification.Message) (bool, error) { +func (s *PluginService) PreHookDBTransformConfigs(ctx context.Context, receiverConfigMap map[string]interface{}) (map[string]interface{}, error) { + receiverConfig := &ReceiverConfig{} + if err := mapstructure.Decode(receiverConfigMap, receiverConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) + } + + if err := receiverConfig.Validate(); err != nil { + return nil, errors.ErrInvalid.WithMsgf(err.Error()) + } + + return receiverConfig.AsMap(), nil +} + +func (s *PluginService) PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) + } + + if err := notificationConfig.Validate(); err != nil { + return nil, err + } + + return notificationConfig.AsMap(), nil +} + +// highlight-start +func (s *PluginService) Send(ctx context.Context, notificationMessage notification.Message) (bool, error) { notificationConfig := &NotificationConfig{} if err := mapstructure.Decode(notificationMessage.Configs, notificationConfig); err != nil { return false, err @@ -84,9 +139,6 @@ func (s *NotificationService) Publish(ctx context.Context, notificationMessage n if err != nil { return false, err } - if err := s.validateFilePath(notificationConfig.URL); err != nil { - return false, err - } fileInstance, err := os.OpenFile(notificationConfig.URL, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777) if err != nil { @@ -102,24 +154,11 @@ func (s *NotificationService) Publish(ctx context.Context, notificationMessage n return false, nil } - - -func (s *NotificationService) PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { - return nil, err - } - - if err := notificationConfig.Validate(); err != nil { - return nil, err - } - - return notificationConfig.AsMap(), nil -} +// highlight-end ``` ## Integrate New Plugin with Siren 1. Define and add your new type of plugin called `file` inside `core/receivers/type.go`. -2. Initialize your plugin receiver service and notification service and add to the `ConfigResolver` and `Notifier` registries map. +2. Initialize your plugin receiver service and notification service and add to the `ConfigResolver` and `Notifier` registries map in `cli/deps`. 3. To make sure notification handler and dlq handler process your new type, don't forget to add your new receiver type in notification message & dlq handler config or make it default to support all receiver types. diff --git a/docs/docs/guides/notification.md b/docs/docs/guides/notification.md index ed2c8275..d966bfff 100644 --- a/docs/docs/guides/notification.md +++ b/docs/docs/guides/notification.md @@ -10,6 +10,15 @@ export const defaultHost = siteConfig.customFields.defaultHost Notification is one of main features in Siren. Siren capables to send notification to various receivers (e.g. Slack, PagerDuty). Notification in Siren could be sent directly to a receiver or user could subscribe notifications by providing key-value label matchers. For the latter, Siren routes notification to specific receivers by matching notification key-value labels with the provided label matchers. + +## Queue + +Queue is used as a buffer for the outbound notifications. Siren has a pluggable queue where user could choose which Queue to use in the [config](../reference/server_configuration.md). Supported Queues are: + +- In-Memory +- PostgreSQl + + ## Sending a message/notification We could send a notification to a specific receiver by passing a `receiver_id` in the path params and correct payload format in the body. The payload format needs to follow receiver type contract. diff --git a/docs/docs/guides/template.md b/docs/docs/guides/template.md index 4bf0201d..e7559c14 100644 --- a/docs/docs/guides/template.md +++ b/docs/docs/guides/template.md @@ -251,3 +251,4 @@ siren template upload cpu_template.yaml 1. It's suggested to always provide default value for the templated variables. 2. Updating a template used by rules via CLI will update all associated rules. + diff --git a/docs/docs/reference/api.md b/docs/docs/reference/api.md index 06d4d73c..35f6990f 100644 --- a/docs/docs/reference/api.md +++ b/docs/docs/reference/api.md @@ -28,17 +28,16 @@ list alerts | 200 | A successful response. | [ListAlertsResponse](#listalertsresponse) | | default | An unexpected error response. | [Status](#status) | -### /v1beta1/alerts/cortex/{provider_id} - #### POST ##### Summary -create cortex alerts +create alerts ##### Parameters | Name | Located in | Description | Required | Schema | | ---- | ---------- | ----------- | -------- | ---- | +| provider_type | path | | Yes | string | | provider_id | path | | Yes | string (uint64) | | body | body | | Yes | object | @@ -46,7 +45,7 @@ create cortex alerts | Code | Description | Schema | | ---- | ----------- | ------ | -| 200 | A successful response. | [CreateCortexAlertsResponse](#createcortexalertsresponse) | +| 200 | A successful response. | [CreateAlertsResponse](#createalertsresponse) | | default | An unexpected error response. | [Status](#status) | ### /v1beta1/namespaces @@ -593,19 +592,7 @@ render a template | ---- | ---- | ----------- | -------- | | @type | string | | No | -#### CortexAlert - -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| annotations | object | | No | -| ends_at | dateTime | | No | -| fingerprint | string | | No | -| generator_url | string | | No | -| labels | object | | No | -| starts_at | dateTime | | No | -| status | string | | No | - -#### CreateCortexAlertsResponse +#### CreateAlertsResponse | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | diff --git a/docs/docs/reference/cli.md b/docs/docs/reference/cli.md index a110acbb..6cebe47c 100644 --- a/docs/docs/reference/cli.md +++ b/docs/docs/reference/cli.md @@ -229,6 +229,43 @@ Start server on default port 8080 -c, --config string Config file path (default "config.yaml") ```` +## `siren subscription` + +Manage subscriptions + +### `siren subscription create [flags]` + +Create a new subscription + +``` +-f, --file string path to the subscription config +```` + +### `siren subscription delete` + +Delete a subscription details + +### `siren subscription edit [flags]` + +Edit a subscription + +``` +-f, --file string Path to the subscription config + --id uint subscription id +```` + +### `siren subscription list` + +List subscriptions + +### `siren subscription view [flags]` + +View a subscription details + +``` +--format string Print output with the selected format (default "yaml") +```` + ## `siren template` Manage templates diff --git a/docs/docs/tour/1_start_server.md b/docs/docs/tour/1_start_server.md index 3b866ae6..3b989080 100644 --- a/docs/docs/tour/1_start_server.md +++ b/docs/docs/tour/1_start_server.md @@ -26,6 +26,16 @@ $ ./siren server init You will have a new config file `./config.yaml` with the default values. The default values would be the minimum config to get Siren up and running. +### Update Config of Cortex Webhook URL + +When we register a cortex provider, we need to set the alertmanager config `webhook_config` and set Siren API to send alerts to Siren from Cortex. You need to update `webhook_url` of `cortex` in Siren to be like this. + +```yaml +providers: + cortex: + webhook_url: http://host.docker.internal:8080 +``` + ## 3. Start-up Siren dependencies We are using `docker-compose` to start-up all dependencies of Siren. See [introduction](./introduction.md) to see what components are defined in `docker-compose`. @@ -39,16 +49,15 @@ docker-compose up -d If you haven't got dependencies (postgresql, cortex, etc) images in your local, this might take a while to fetch all images. If all dependencies are successfully started, you will get this message in the terminal. ```shell -[+] Running 9/9 +[+] Running 8/8 â ¿ Network siren_siren Created 0.0s â ¿ Network siren_default Created 0.0s - â ¿ Volume "siren_data1-1" Created 0.0s - â ¿ Volume "siren_siren_dbdata" Created 0.0s - â ¿ Container siren_postgres Started 0.7s - â ¿ Container siren-minio1-1 Started 0.7s - â ¿ Container siren_cortex_am Started 1.3s - â ¿ Container siren-createbuckets-1 Started 1.1s - â ¿ Container siren_cortex_all Started 1.6s + â ¿ Container siren-minio1-1 Started 0.6s + â ¿ Container siren_postgres Started 0.6s + â ¿ Container siren-createbuckets-1 Started 0.8s + â ¿ Container siren_cortex_am Started 0.9s + â ¿ Container siren_cortex_all Started 1.1s + â ¿ Container siren_nginx Started 1.3s ``` ## 4. Run Siren server diff --git a/go.mod b/go.mod index 251f52b2..dec350c2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.16 require ( github.com/MakeNowJust/heredoc v1.0.0 github.com/Masterminds/squirrel v1.5.3 - github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/briandowns/spinner v1.19.0 // indirect github.com/charmbracelet/glamour v0.5.0 // indirect @@ -13,16 +12,12 @@ require ( github.com/go-openapi/loads v0.20.1 // indirect github.com/go-openapi/runtime v0.19.26 github.com/go-openapi/spec v0.20.2 // indirect - github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/google/go-cmp v0.5.8 github.com/google/uuid v1.3.0 github.com/grafana/cortex-tools v0.7.2 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 - github.com/jackc/pgconn v1.12.1 - github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa - github.com/jackc/pgx/v4 v4.16.1 github.com/jmoiron/sqlx v1.3.5 github.com/lib/pq v1.10.4 github.com/mcuadros/go-defaults v1.2.0 diff --git a/go.sum b/go.sum index 35ece55d..2c0cbfea 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,6 @@ github.com/HdrHistogram/hdrhistogram-go v0.9.0/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qE github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/squirrel v0.0.0-20161115235646-20f192218cf5/go.mod h1:xnKTFzjGUiZtiOagBsfnvomW+nJg2usB1ZpordQWqNM= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= @@ -197,8 +195,6 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -394,7 +390,6 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3Kb23bWV10IRV1TyeSpwM= @@ -887,8 +882,6 @@ github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= -github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= @@ -1213,10 +1206,8 @@ github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1: github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= @@ -1227,22 +1218,10 @@ github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA= github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8= -github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= -github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw= -github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -1252,11 +1231,7 @@ github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= -github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= @@ -1266,10 +1241,6 @@ github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkAL github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= github.com/jackc/pgtype v1.5.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= -github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= @@ -1279,16 +1250,12 @@ github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6 github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= github.com/jackc/pgx/v4 v4.9.0/go.mod h1:MNGWmViCgqbZck9ujOOBN63gK9XVGILXWCvKLGKmnms= github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y= -github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs= github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ= github.com/jessevdk/go-flags v0.0.0-20180331124232-1c38ed7ad0cc/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -1400,7 +1367,6 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -1592,20 +1558,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/odpf/salt v0.2.5-0.20221018125345-42198ed8fdf8 h1:lBo/FynTzWL2ieqMFnOOKl9Lsv2r61mDdHNkA0tQK30= -github.com/odpf/salt v0.2.5-0.20221018125345-42198ed8fdf8/go.mod h1:SVfb9FtFA22A5AUvXF9QfwkT3KSCkkKw53P0DmtrSXw= -github.com/odpf/salt v0.2.5-0.20221018125824-7acb752959f2 h1:kNktNFwIt3Z/nAy8JRE8+0G93rXaOOEUPZiS7i/pYQg= -github.com/odpf/salt v0.2.5-0.20221018125824-7acb752959f2/go.mod h1:SVfb9FtFA22A5AUvXF9QfwkT3KSCkkKw53P0DmtrSXw= -github.com/odpf/salt v0.2.5-0.20221018130129-f291aca63e97 h1:cL8xflcX2/JY+v3mEQl55t+cr7OBrt1k1FyISDUgMWo= -github.com/odpf/salt v0.2.5-0.20221018130129-f291aca63e97/go.mod h1:SVfb9FtFA22A5AUvXF9QfwkT3KSCkkKw53P0DmtrSXw= -github.com/odpf/salt v0.2.5-0.20221019083531-b3cdaee093f7 h1:9gvoHTpmAbVG0s5PAxSd/GcnY640fhFs8WgsT1p96EA= -github.com/odpf/salt v0.2.5-0.20221019083531-b3cdaee093f7/go.mod h1:SVfb9FtFA22A5AUvXF9QfwkT3KSCkkKw53P0DmtrSXw= -github.com/odpf/salt v0.2.5-0.20221019084514-a8f1775a4da1 h1:5BmTANkuarv/fMVh1N3BZqIFTtoBzb78h1U3J2b/vz4= -github.com/odpf/salt v0.2.5-0.20221019084514-a8f1775a4da1/go.mod h1:SVfb9FtFA22A5AUvXF9QfwkT3KSCkkKw53P0DmtrSXw= -github.com/odpf/salt v0.2.5-0.20221019085044-cfbe18da8de5 h1:ymGSi/riSucTIAhspBGZSgOElvmIMd66QeRv9HHHnLE= -github.com/odpf/salt v0.2.5-0.20221019085044-cfbe18da8de5/go.mod h1:SVfb9FtFA22A5AUvXF9QfwkT3KSCkkKw53P0DmtrSXw= -github.com/odpf/salt v0.2.5-0.20221019090012-e4d6eb61b861 h1:sr7ToSTdOVN7cg+yyG1q0Xcto77+k+/5Q54HGFW1xv0= -github.com/odpf/salt v0.2.5-0.20221019090012-e4d6eb61b861/go.mod h1:SVfb9FtFA22A5AUvXF9QfwkT3KSCkkKw53P0DmtrSXw= github.com/odpf/salt v0.2.5-0.20221019092709-83b91cba3daf h1:Ehn6ltBEIowDKtfnkptzZ+Y+32PSMalx2N3Td1jnbD4= github.com/odpf/salt v0.2.5-0.20221019092709-83b91cba3daf/go.mod h1:SVfb9FtFA22A5AUvXF9QfwkT3KSCkkKw53P0DmtrSXw= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= @@ -1882,7 +1834,6 @@ github.com/sercand/kuberesolver v2.4.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJP github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= @@ -2206,12 +2157,9 @@ golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/internal/api/api.go b/internal/api/api.go index 648f817f..639ea5fb 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -15,7 +15,7 @@ import ( //go:generate mockery --name=AlertService -r --case underscore --with-expecter --structname AlertService --filename alert_service.go --output=./mocks type AlertService interface { - Create(context.Context, []*alert.Alert) ([]alert.Alert, error) + CreateAlerts(ctx context.Context, providerType string, providerID uint64, body map[string]interface{}) ([]*alert.Alert, int, error) List(context.Context, alert.Filter) ([]alert.Alert, error) } diff --git a/internal/api/mocks/alert_service.go b/internal/api/mocks/alert_service.go index 5b7d1098..9a6a7913 100644 --- a/internal/api/mocks/alert_service.go +++ b/internal/api/mocks/alert_service.go @@ -23,50 +23,59 @@ func (_m *AlertService) EXPECT() *AlertService_Expecter { return &AlertService_Expecter{mock: &_m.Mock} } -// Create provides a mock function with given fields: _a0, _a1 -func (_m *AlertService) Create(_a0 context.Context, _a1 []*alert.Alert) ([]alert.Alert, error) { - ret := _m.Called(_a0, _a1) +// CreateAlerts provides a mock function with given fields: ctx, providerType, providerID, body +func (_m *AlertService) CreateAlerts(ctx context.Context, providerType string, providerID uint64, body map[string]interface{}) ([]*alert.Alert, int, error) { + ret := _m.Called(ctx, providerType, providerID, body) - var r0 []alert.Alert - if rf, ok := ret.Get(0).(func(context.Context, []*alert.Alert) []alert.Alert); ok { - r0 = rf(_a0, _a1) + var r0 []*alert.Alert + if rf, ok := ret.Get(0).(func(context.Context, string, uint64, map[string]interface{}) []*alert.Alert); ok { + r0 = rf(ctx, providerType, providerID, body) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]alert.Alert) + r0 = ret.Get(0).([]*alert.Alert) } } - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, []*alert.Alert) error); ok { - r1 = rf(_a0, _a1) + var r1 int + if rf, ok := ret.Get(1).(func(context.Context, string, uint64, map[string]interface{}) int); ok { + r1 = rf(ctx, providerType, providerID, body) } else { - r1 = ret.Error(1) + r1 = ret.Get(1).(int) } - return r0, r1 + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, uint64, map[string]interface{}) error); ok { + r2 = rf(ctx, providerType, providerID, body) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } -// AlertService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type AlertService_Create_Call struct { +// AlertService_CreateAlerts_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateAlerts' +type AlertService_CreateAlerts_Call struct { *mock.Call } -// Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 []*alert.Alert -func (_e *AlertService_Expecter) Create(_a0 interface{}, _a1 interface{}) *AlertService_Create_Call { - return &AlertService_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} +// CreateAlerts is a helper method to define mock.On call +// - ctx context.Context +// - providerType string +// - providerID uint64 +// - body map[string]interface{} +func (_e *AlertService_Expecter) CreateAlerts(ctx interface{}, providerType interface{}, providerID interface{}, body interface{}) *AlertService_CreateAlerts_Call { + return &AlertService_CreateAlerts_Call{Call: _e.mock.On("CreateAlerts", ctx, providerType, providerID, body)} } -func (_c *AlertService_Create_Call) Run(run func(_a0 context.Context, _a1 []*alert.Alert)) *AlertService_Create_Call { +func (_c *AlertService_CreateAlerts_Call) Run(run func(ctx context.Context, providerType string, providerID uint64, body map[string]interface{})) *AlertService_CreateAlerts_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]*alert.Alert)) + run(args[0].(context.Context), args[1].(string), args[2].(uint64), args[3].(map[string]interface{})) }) return _c } -func (_c *AlertService_Create_Call) Return(_a0 []alert.Alert, _a1 error) *AlertService_Create_Call { - _c.Call.Return(_a0, _a1) +func (_c *AlertService_CreateAlerts_Call) Return(_a0 []*alert.Alert, _a1 int, _a2 error) *AlertService_CreateAlerts_Call { + _c.Call.Return(_a0, _a1, _a2) return _c } diff --git a/internal/api/v1beta1/alert.go b/internal/api/v1beta1/alert.go index d5feeb5a..bb09413c 100644 --- a/internal/api/v1beta1/alert.go +++ b/internal/api/v1beta1/alert.go @@ -2,7 +2,6 @@ package v1beta1 import ( "context" - "fmt" "time" "github.com/odpf/siren/core/alert" @@ -42,40 +41,8 @@ func (s *GRPCServer) ListAlerts(ctx context.Context, req *sirenv1beta1.ListAlert }, nil } -func (s *GRPCServer) CreateCortexAlerts(ctx context.Context, req *sirenv1beta1.CreateCortexAlertsRequest) (*sirenv1beta1.CreateCortexAlertsResponse, error) { - alerts := make([]*alert.Alert, 0) - - badAlertCount := 0 - firingLen := 0 - // Alert model follows alertmanager webhook contract - // https://github.com/prometheus/alertmanager/blob/main/notify/webhook/webhook.go#L64 - for _, item := range req.GetAlerts() { - - if item.GetStatus() == "firing" { - firingLen++ - } - - severity := item.GetLabels()["severity"] - if item.GetStatus() == "resolved" { - severity = item.GetStatus() - } - - alrt := &alert.Alert{ - ProviderID: req.GetProviderId(), - ResourceName: fmt.Sprintf("%v", item.GetAnnotations()["resource"]), - MetricName: fmt.Sprintf("%v", item.GetAnnotations()["metricName"]), - MetricValue: fmt.Sprintf("%v", item.GetAnnotations()["metricValue"]), - Severity: severity, - Rule: fmt.Sprintf("%v", item.GetAnnotations()["template"]), - TriggeredAt: item.GetStartsAt().AsTime(), - } - if !isValidCortexAlert(alrt) { - badAlertCount++ - continue - } - alerts = append(alerts, alrt) - } - createdAlerts, err := s.alertService.Create(ctx, alerts) +func (s *GRPCServer) CreateAlerts(ctx context.Context, req *sirenv1beta1.CreateAlertsRequest) (*sirenv1beta1.CreateAlertsResponse, error) { + createdAlerts, firingLen, err := s.alertService.CreateAlerts(ctx, req.GetProviderType(), req.GetProviderId(), req.GetBody().AsMap()) if err != nil { return nil, s.generateRPCErr(err) } @@ -95,58 +62,64 @@ func (s *GRPCServer) CreateCortexAlerts(ctx context.Context, req *sirenv1beta1.C items = append(items, alertHistoryItem) } - result := &sirenv1beta1.CreateCortexAlertsResponse{ + result := &sirenv1beta1.CreateAlertsResponse{ Alerts: items, } - notificationCreationTime := time.Now() - // Publish to notification service - for _, a := range req.GetAlerts() { - n := CortexAlertPBToNotification(a, firingLen, req.GetGroupKey(), notificationCreationTime) + for _, a := range createdAlerts { + n := AlertPBToNotification(a, firingLen, a.GroupKey, time.Now()) if err := s.notificationService.DispatchToSubscribers(ctx, n); err != nil { s.logger.Warn("failed to send alert as notification", "err", err, "notification", n) } } - if badAlertCount > 0 { - s.logger.Error("parameters are missing for alert", "alert count", badAlertCount) - return result, nil - } - return result, nil } -func isValidCortexAlert(alrt *alert.Alert) bool { - return alrt != nil && !(alrt.ResourceName == "" || alrt.Rule == "" || - alrt.MetricValue == "" || alrt.MetricName == "" || - alrt.Severity == "") -} - -// TODO test -func CortexAlertPBToNotification( - a *sirenv1beta1.CortexAlert, +// Transform alerts and populate Data and Labels to be interpolated to the system-default template +// .Data +// - id +// - status "FIRING"/"RESOLVED" +// - resource +// - template +// - metricValue +// - metricName +// - generatorUrl +// - numAlertsFiring +// - dashboard +// - playbook +// - summary +// .Labels +// - severity "WARNING"/"CRITICAL" +// - alertname +// - (others labels defined in rules) +func AlertPBToNotification( + a *alert.Alert, firingLen int, groupKey string, createdTime time.Time, ) notification.Notification { + id := "cortex-" + a.Fingerprint + data := map[string]interface{}{} - for k, v := range a.GetAnnotations() { + for k, v := range a.Annotations { data[k] = v } - data["status"] = a.GetStatus() - data["generatorUrl"] = a.GetGeneratorUrl() + data["status"] = a.Status + data["generatorUrl"] = a.GeneratorURL data["numAlertsFiring"] = firingLen data["groupKey"] = groupKey + data["id"] = id return notification.Notification{ - ID: "cortex-" + a.GetFingerprint(), + ID: id, Data: data, - Labels: a.GetLabels(), - Template: template.ReservedName_DefaultCortex, + Labels: a.Labels, + Template: template.ReservedName_SystemDefault, CreatedAt: createdTime, } } diff --git a/internal/api/v1beta1/alert_test.go b/internal/api/v1beta1/alert_test.go index 0527302e..17a31620 100644 --- a/internal/api/v1beta1/alert_test.go +++ b/internal/api/v1beta1/alert_test.go @@ -7,6 +7,7 @@ import ( "github.com/odpf/salt/log" "github.com/odpf/siren/core/alert" + "github.com/odpf/siren/core/provider" "github.com/odpf/siren/internal/api" "github.com/odpf/siren/internal/api/mocks" "github.com/odpf/siren/internal/api/v1beta1" @@ -14,7 +15,7 @@ import ( sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/structpb" ) func TestGRPCServer_ListAlerts(t *testing.T) { @@ -76,33 +77,80 @@ func TestGRPCServer_ListAlerts(t *testing.T) { } func TestGRPCServer_CreateAlertHistory(t *testing.T) { - timenow := timestamppb.New(time.Now()) - payload := []*alert.Alert{ - { - ProviderID: 1, - ResourceName: "foo", - MetricName: "bar", - MetricValue: "30", - Severity: "CRITICAL", - Rule: "random", - TriggeredAt: timenow.AsTime(), + timenow := time.Now() + + payload := map[string]interface{}{ + "alerts": []interface{}{ + map[string]interface{}{ + "annotations": map[string]interface{}{ + "metricName": "bar", + "metricValue": "30", + "resource": "foo", + "template": "random", + }, + "labels": map[string]interface{}{ + "severity": "foo", + }, + "startsAt": timenow.String(), + "status": "foo", + }, }, } - dummyReq := &sirenv1beta1.CreateCortexAlertsRequest{ - ProviderId: 1, - Alerts: []*sirenv1beta1.CortexAlert{ - { - Status: "foo", - Labels: map[string]string{ - "severity": "CRITICAL", + + alertPB := &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "status": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + "labels": { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "severity": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + }, + }, }, - Annotations: map[string]string{ - "resource": "foo", - "template": "random", - "metricName": "bar", - "metricValue": "30", + }, + "annotations": { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "resource": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + "template": { + Kind: &structpb.Value_StringValue{StringValue: "random"}, + }, + "metricName": { + Kind: &structpb.Value_StringValue{StringValue: "bar"}, + }, + "metricValue": { + Kind: &structpb.Value_StringValue{StringValue: "30"}, + }, + }, + }, + }, + }, + "startsAt": { + Kind: &structpb.Value_StringValue{StringValue: timenow.String()}, + }, + }, + } + + dummyReq := &sirenv1beta1.CreateAlertsRequest{ + ProviderId: 1, + ProviderType: provider.TypeCortex, + Body: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "alerts": { + Kind: &structpb.Value_ListValue{ + ListValue: &structpb.ListValue{ + Values: []*structpb.Value{structpb.NewStructValue(alertPB)}, + }, + }, }, - StartsAt: timenow, }, }, } @@ -111,7 +159,7 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { mockedAlertService := &mocks.AlertService{} mockNotificationService := new(mocks.NotificationService) - dummyAlerts := []alert.Alert{{ + dummyAlerts := []*alert.Alert{{ ID: 1, ProviderID: 1, ResourceName: "foo", @@ -119,15 +167,15 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { MetricValue: "30", Severity: "CRITICAL", Rule: "random", - TriggeredAt: timenow.AsTime(), + TriggeredAt: timenow, }} - mockedAlertService.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), payload). - Return(dummyAlerts, nil).Once() + mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), payload). + Return(dummyAlerts, 1, nil).Once() mockNotificationService.EXPECT().DispatchToSubscribers(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) dummyGRPCServer := v1beta1.NewGRPCServer(nil, log.NewNoop(), &api.Deps{AlertService: mockedAlertService, NotificationService: mockNotificationService}) - res, err := dummyGRPCServer.CreateCortexAlerts(context.Background(), dummyReq) + res, err := dummyGRPCServer.CreateAlerts(context.Background(), dummyReq) assert.Equal(t, 1, len(res.GetAlerts())) assert.Equal(t, uint64(1), res.GetAlerts()[0].GetId()) assert.Equal(t, "foo", res.GetAlerts()[0].GetResourceName()) @@ -143,36 +191,82 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { mockedAlertService := &mocks.AlertService{} mockNotificationService := new(mocks.NotificationService) - dummyReq := &sirenv1beta1.CreateCortexAlertsRequest{ - ProviderId: 1, - Alerts: []*sirenv1beta1.CortexAlert{ - { - Status: "resolved", - Labels: map[string]string{ - "severity": "CRITICAL", + alertPB := &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "status": { + Kind: &structpb.Value_StringValue{StringValue: "resolved"}, + }, + "labels": { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "severity": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + }, + }, }, - Annotations: map[string]string{ - "resource": "foo", - "template": "random", - "metricName": "bar", - "metricValue": "30", + }, + "annotations": { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "resource": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + "template": { + Kind: &structpb.Value_StringValue{StringValue: "random"}, + }, + "metricName": { + Kind: &structpb.Value_StringValue{StringValue: "bar"}, + }, + "metricValue": { + Kind: &structpb.Value_StringValue{StringValue: "30"}, + }, + }, + }, }, - StartsAt: timenow, + }, + "startsAt": { + Kind: &structpb.Value_StringValue{StringValue: timenow.String()}, }, }, } - payload := []*alert.Alert{ - { - ProviderID: 1, - ResourceName: "foo", - MetricName: "bar", - MetricValue: "30", - Severity: "resolved", - Rule: "random", - TriggeredAt: timenow.AsTime(), + + dummyReq := &sirenv1beta1.CreateAlertsRequest{ + ProviderId: 1, + ProviderType: provider.TypeCortex, + Body: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "alerts": { + Kind: &structpb.Value_ListValue{ + ListValue: &structpb.ListValue{ + Values: []*structpb.Value{structpb.NewStructValue(alertPB)}, + }, + }, + }, + }, }, } - dummyAlerts := []alert.Alert{{ + + payload := map[string]interface{}{ + "alerts": []interface{}{ + map[string]interface{}{ + "annotations": map[string]interface{}{ + "metricName": "bar", + "metricValue": "30", + "resource": "foo", + "template": "random", + }, + "labels": map[string]interface{}{ + "severity": "foo", + }, + "startsAt": timenow.String(), + "status": "resolved", + }, + }, + } + dummyAlerts := []*alert.Alert{{ ID: 1, ProviderID: 1, ResourceName: "foo", @@ -180,15 +274,15 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { MetricValue: "30", Severity: "resolved", Rule: "random", - TriggeredAt: timenow.AsTime(), + TriggeredAt: timenow, }} - mockedAlertService.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), payload). - Return(dummyAlerts, nil).Once() + mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), payload). + Return(dummyAlerts, 1, nil).Once() mockNotificationService.EXPECT().DispatchToSubscribers(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) dummyGRPCServer := v1beta1.NewGRPCServer(nil, log.NewNoop(), &api.Deps{AlertService: mockedAlertService, NotificationService: mockNotificationService}) - res, err := dummyGRPCServer.CreateCortexAlerts(context.Background(), dummyReq) + res, err := dummyGRPCServer.CreateAlerts(context.Background(), dummyReq) assert.Equal(t, 1, len(res.GetAlerts())) assert.Equal(t, uint64(1), res.GetAlerts()[0].GetId()) assert.Equal(t, "foo", res.GetAlerts()[0].GetResourceName()) @@ -204,10 +298,10 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { mockedAlertService := &mocks.AlertService{} dummyGRPCServer := v1beta1.NewGRPCServer(nil, log.NewNoop(), &api.Deps{AlertService: mockedAlertService}) - mockedAlertService.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), payload). - Return(nil, errors.New("random error")).Once() + mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), payload). + Return(nil, 0, errors.New("random error")).Once() - res, err := dummyGRPCServer.CreateCortexAlerts(context.Background(), dummyReq) + res, err := dummyGRPCServer.CreateAlerts(context.Background(), dummyReq) assert.EqualError(t, err, "rpc error: code = Internal desc = some unexpected error occurred") assert.Nil(t, res) mockedAlertService.AssertExpectations(t) @@ -217,37 +311,140 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { mockedAlertService := &mocks.AlertService{} mockNotificationService := new(mocks.NotificationService) - dummyReq := &sirenv1beta1.CreateCortexAlertsRequest{ - ProviderId: 1, - Alerts: []*sirenv1beta1.CortexAlert{ - { - Status: "foo", - Labels: map[string]string{ - "severity": "CRITICAL", - }, - Annotations: map[string]string{ - "resource": "foo", + payload := map[string]interface{}{ + "alerts": []interface{}{ + map[string]interface{}{ + "annotations": map[string]interface{}{ "metricName": "bar", "metricValue": "30", - }, - StartsAt: timenow, - }, - { - Status: "foo", - Labels: map[string]string{ - "severity": "CRITICAL", - }, - Annotations: map[string]string{ "resource": "foo", "template": "random", + }, + "labels": map[string]interface{}{ + "severity": "foo", + }, + "startsAt": timenow.String(), + "status": "foo", + }, map[string]interface{}{ + "annotations": map[string]interface{}{ "metricName": "bar", "metricValue": "30", + "resource": "foo", + "template": "random", }, - StartsAt: timenow, + "labels": map[string]interface{}{ + "severity": "foo", + }, + "startsAt": timenow.String(), + "status": "resolved", }, }, } - dummyAlerts := []alert.Alert{{ + + alert1PB := &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "status": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + "labels": { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "severity": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + }, + }, + }, + }, + "annotations": { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "resource": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + "template": { + Kind: &structpb.Value_StringValue{StringValue: "random"}, + }, + "metricName": { + Kind: &structpb.Value_StringValue{StringValue: "bar"}, + }, + "metricValue": { + Kind: &structpb.Value_StringValue{StringValue: "30"}, + }, + }, + }, + }, + }, + "startsAt": { + Kind: &structpb.Value_StringValue{StringValue: timenow.String()}, + }, + }, + } + + alert2PB := &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "status": { + Kind: &structpb.Value_StringValue{StringValue: "resolved"}, + }, + "labels": { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "severity": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + }, + }, + }, + }, + "annotations": { + Kind: &structpb.Value_StructValue{ + StructValue: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "resource": { + Kind: &structpb.Value_StringValue{StringValue: "foo"}, + }, + "template": { + Kind: &structpb.Value_StringValue{StringValue: "random"}, + }, + "metricName": { + Kind: &structpb.Value_StringValue{StringValue: "bar"}, + }, + "metricValue": { + Kind: &structpb.Value_StringValue{StringValue: "30"}, + }, + }, + }, + }, + }, + "startsAt": { + Kind: &structpb.Value_StringValue{StringValue: timenow.String()}, + }, + }, + } + + dummyReq := &sirenv1beta1.CreateAlertsRequest{ + ProviderId: 1, + ProviderType: provider.TypeCortex, + Body: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "alerts": { + Kind: &structpb.Value_ListValue{ + ListValue: &structpb.ListValue{ + Values: []*structpb.Value{ + structpb.NewStructValue(alert1PB), + structpb.NewStructValue(alert2PB), + }, + }, + }, + }, + }, + }, + } + + dummyAlerts := []*alert.Alert{{ ProviderID: 1, ResourceName: "foo", MetricName: "bar", @@ -259,11 +456,11 @@ func TestGRPCServer_CreateAlertHistory(t *testing.T) { dummyGRPCServer := v1beta1.NewGRPCServer(nil, log.NewNoop(), &api.Deps{AlertService: mockedAlertService, NotificationService: mockNotificationService}) - mockedAlertService.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), payload). - Return(dummyAlerts, nil).Once() + mockedAlertService.EXPECT().CreateAlerts(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("uint64"), payload). + Return(dummyAlerts, 2, nil).Once() mockNotificationService.EXPECT().DispatchToSubscribers(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("notification.Notification")).Return(nil) - res, err := dummyGRPCServer.CreateCortexAlerts(context.Background(), dummyReq) + res, err := dummyGRPCServer.CreateAlerts(context.Background(), dummyReq) assert.Equal(t, 1, len(res.GetAlerts())) assert.Equal(t, uint64(1), res.GetAlerts()[0].GetProviderId()) assert.Equal(t, "foo", res.GetAlerts()[0].GetResourceName()) diff --git a/internal/api/v1beta1/mocks/alert_service.go b/internal/api/v1beta1/mocks/alert_service.go deleted file mode 100644 index 529a729d..00000000 --- a/internal/api/v1beta1/mocks/alert_service.go +++ /dev/null @@ -1,133 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - alert "github.com/odpf/siren/core/alert" - - 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} -} - -// Create provides a mock function with given fields: _a0, _a1 -func (_m *AlertService) Create(_a0 context.Context, _a1 []*alert.Alert) ([]alert.Alert, error) { - ret := _m.Called(_a0, _a1) - - var r0 []alert.Alert - if rf, ok := ret.Get(0).(func(context.Context, []*alert.Alert) []alert.Alert); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]alert.Alert) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, []*alert.Alert) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AlertService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type AlertService_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 []*alert.Alert -func (_e *AlertService_Expecter) Create(_a0 interface{}, _a1 interface{}) *AlertService_Create_Call { - return &AlertService_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} -} - -func (_c *AlertService_Create_Call) Run(run func(_a0 context.Context, _a1 []*alert.Alert)) *AlertService_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]*alert.Alert)) - }) - return _c -} - -func (_c *AlertService_Create_Call) Return(_a0 []alert.Alert, _a1 error) *AlertService_Create_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// List provides a mock function with given fields: _a0, _a1 -func (_m *AlertService) List(_a0 context.Context, _a1 alert.Filter) ([]alert.Alert, error) { - ret := _m.Called(_a0, _a1) - - var r0 []alert.Alert - if rf, ok := ret.Get(0).(func(context.Context, alert.Filter) []alert.Alert); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]alert.Alert) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, alert.Filter) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AlertService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type AlertService_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 alert.Filter -func (_e *AlertService_Expecter) List(_a0 interface{}, _a1 interface{}) *AlertService_List_Call { - return &AlertService_List_Call{Call: _e.mock.On("List", _a0, _a1)} -} - -func (_c *AlertService_List_Call) Run(run func(_a0 context.Context, _a1 alert.Filter)) *AlertService_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(alert.Filter)) - }) - return _c -} - -func (_c *AlertService_List_Call) Return(_a0 []alert.Alert, _a1 error) *AlertService_List_Call { - _c.Call.Return(_a0, _a1) - 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/internal/api/v1beta1/mocks/namespace_service.go b/internal/api/v1beta1/mocks/namespace_service.go deleted file mode 100644 index 57df151c..00000000 --- a/internal/api/v1beta1/mocks/namespace_service.go +++ /dev/null @@ -1,245 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - namespace "github.com/odpf/siren/core/namespace" - mock "github.com/stretchr/testify/mock" -) - -// NamespaceService is an autogenerated mock type for the NamespaceService type -type NamespaceService struct { - mock.Mock -} - -type NamespaceService_Expecter struct { - mock *mock.Mock -} - -func (_m *NamespaceService) EXPECT() *NamespaceService_Expecter { - return &NamespaceService_Expecter{mock: &_m.Mock} -} - -// Create provides a mock function with given fields: _a0, _a1 -func (_m *NamespaceService) Create(_a0 context.Context, _a1 *namespace.Namespace) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *namespace.Namespace) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// NamespaceService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type NamespaceService_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - _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)} -} - -func (_c *NamespaceService_Create_Call) Run(run func(_a0 context.Context, _a1 *namespace.Namespace)) *NamespaceService_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*namespace.Namespace)) - }) - return _c -} - -func (_c *NamespaceService_Create_Call) Return(_a0 error) *NamespaceService_Create_Call { - _c.Call.Return(_a0) - return _c -} - -// Delete provides a mock function with given fields: _a0, _a1 -func (_m *NamespaceService) Delete(_a0 context.Context, _a1 uint64) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// NamespaceService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type NamespaceService_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -// - _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)} -} - -func (_c *NamespaceService_Delete_Call) Run(run func(_a0 context.Context, _a1 uint64)) *NamespaceService_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) - }) - return _c -} - -func (_c *NamespaceService_Delete_Call) Return(_a0 error) *NamespaceService_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -// Get provides a mock function with given fields: _a0, _a1 -func (_m *NamespaceService) Get(_a0 context.Context, _a1 uint64) (*namespace.Namespace, error) { - ret := _m.Called(_a0, _a1) - - var r0 *namespace.Namespace - if rf, ok := ret.Get(0).(func(context.Context, uint64) *namespace.Namespace); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*namespace.Namespace) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NamespaceService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' -type NamespaceService_Get_Call struct { - *mock.Call -} - -// Get is a helper method to define mock.On call -// - _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)} -} - -func (_c *NamespaceService_Get_Call) Run(run func(_a0 context.Context, _a1 uint64)) *NamespaceService_Get_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) - }) - return _c -} - -func (_c *NamespaceService_Get_Call) Return(_a0 *namespace.Namespace, _a1 error) *NamespaceService_Get_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// List provides a mock function with given fields: _a0 -func (_m *NamespaceService) List(_a0 context.Context) ([]namespace.Namespace, error) { - ret := _m.Called(_a0) - - var r0 []namespace.Namespace - if rf, ok := ret.Get(0).(func(context.Context) []namespace.Namespace); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]namespace.Namespace) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NamespaceService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type NamespaceService_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - _a0 context.Context -func (_e *NamespaceService_Expecter) List(_a0 interface{}) *NamespaceService_List_Call { - return &NamespaceService_List_Call{Call: _e.mock.On("List", _a0)} -} - -func (_c *NamespaceService_List_Call) Run(run func(_a0 context.Context)) *NamespaceService_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *NamespaceService_List_Call) Return(_a0 []namespace.Namespace, _a1 error) *NamespaceService_List_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Update provides a mock function with given fields: _a0, _a1 -func (_m *NamespaceService) Update(_a0 context.Context, _a1 *namespace.Namespace) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *namespace.Namespace) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// NamespaceService_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' -type NamespaceService_Update_Call struct { - *mock.Call -} - -// Update is a helper method to define mock.On call -// - _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)} -} - -func (_c *NamespaceService_Update_Call) Run(run func(_a0 context.Context, _a1 *namespace.Namespace)) *NamespaceService_Update_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*namespace.Namespace)) - }) - return _c -} - -func (_c *NamespaceService_Update_Call) Return(_a0 error) *NamespaceService_Update_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewNamespaceService interface { - mock.TestingT - Cleanup(func()) -} - -// NewNamespaceService creates a new instance of NamespaceService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewNamespaceService(t mockConstructorTestingTNewNamespaceService) *NamespaceService { - mock := &NamespaceService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/api/v1beta1/mocks/receiver_service.go b/internal/api/v1beta1/mocks/receiver_service.go deleted file mode 100644 index 6091ae01..00000000 --- a/internal/api/v1beta1/mocks/receiver_service.go +++ /dev/null @@ -1,332 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - receiver "github.com/odpf/siren/core/receiver" - mock "github.com/stretchr/testify/mock" -) - -// ReceiverService is an autogenerated mock type for the ReceiverService type -type ReceiverService struct { - mock.Mock -} - -type ReceiverService_Expecter struct { - mock *mock.Mock -} - -func (_m *ReceiverService) EXPECT() *ReceiverService_Expecter { - return &ReceiverService_Expecter{mock: &_m.Mock} -} - -// Create provides a mock function with given fields: ctx, rcv -func (_m *ReceiverService) Create(ctx context.Context, rcv *receiver.Receiver) error { - ret := _m.Called(ctx, rcv) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *receiver.Receiver) error); ok { - r0 = rf(ctx, rcv) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ReceiverService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type ReceiverService_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - ctx context.Context -// - rcv *receiver.Receiver -func (_e *ReceiverService_Expecter) Create(ctx interface{}, rcv interface{}) *ReceiverService_Create_Call { - return &ReceiverService_Create_Call{Call: _e.mock.On("Create", ctx, rcv)} -} - -func (_c *ReceiverService_Create_Call) Run(run func(ctx context.Context, rcv *receiver.Receiver)) *ReceiverService_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*receiver.Receiver)) - }) - return _c -} - -func (_c *ReceiverService_Create_Call) Return(_a0 error) *ReceiverService_Create_Call { - _c.Call.Return(_a0) - return _c -} - -// Delete provides a mock function with given fields: ctx, id -func (_m *ReceiverService) Delete(ctx context.Context, id uint64) error { - ret := _m.Called(ctx, id) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64) error); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ReceiverService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type ReceiverService_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -// - ctx context.Context -// - id uint64 -func (_e *ReceiverService_Expecter) Delete(ctx interface{}, id interface{}) *ReceiverService_Delete_Call { - return &ReceiverService_Delete_Call{Call: _e.mock.On("Delete", ctx, id)} -} - -func (_c *ReceiverService_Delete_Call) Run(run func(ctx context.Context, id uint64)) *ReceiverService_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) - }) - return _c -} - -func (_c *ReceiverService_Delete_Call) Return(_a0 error) *ReceiverService_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -// EnrichSubscriptionConfig provides a mock function with given fields: subsConfs, rcv -func (_m *ReceiverService) EnrichSubscriptionConfig(subsConfs map[string]string, rcv *receiver.Receiver) (map[string]string, error) { - ret := _m.Called(subsConfs, rcv) - - var r0 map[string]string - if rf, ok := ret.Get(0).(func(map[string]string, *receiver.Receiver) map[string]string); ok { - r0 = rf(subsConfs, rcv) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]string) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(map[string]string, *receiver.Receiver) error); ok { - r1 = rf(subsConfs, rcv) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReceiverService_EnrichSubscriptionConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnrichSubscriptionConfig' -type ReceiverService_EnrichSubscriptionConfig_Call struct { - *mock.Call -} - -// EnrichSubscriptionConfig is a helper method to define mock.On call -// - subsConfs map[string]string -// - rcv *receiver.Receiver -func (_e *ReceiverService_Expecter) EnrichSubscriptionConfig(subsConfs interface{}, rcv interface{}) *ReceiverService_EnrichSubscriptionConfig_Call { - return &ReceiverService_EnrichSubscriptionConfig_Call{Call: _e.mock.On("EnrichSubscriptionConfig", subsConfs, rcv)} -} - -func (_c *ReceiverService_EnrichSubscriptionConfig_Call) Run(run func(subsConfs map[string]string, rcv *receiver.Receiver)) *ReceiverService_EnrichSubscriptionConfig_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]string), args[1].(*receiver.Receiver)) - }) - return _c -} - -func (_c *ReceiverService_EnrichSubscriptionConfig_Call) Return(_a0 map[string]string, _a1 error) *ReceiverService_EnrichSubscriptionConfig_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Get provides a mock function with given fields: ctx, id -func (_m *ReceiverService) Get(ctx context.Context, id uint64) (*receiver.Receiver, error) { - ret := _m.Called(ctx, id) - - var r0 *receiver.Receiver - if rf, ok := ret.Get(0).(func(context.Context, uint64) *receiver.Receiver); ok { - r0 = rf(ctx, id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*receiver.Receiver) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok { - r1 = rf(ctx, id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReceiverService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' -type ReceiverService_Get_Call struct { - *mock.Call -} - -// Get is a helper method to define mock.On call -// - ctx context.Context -// - id uint64 -func (_e *ReceiverService_Expecter) Get(ctx interface{}, id interface{}) *ReceiverService_Get_Call { - return &ReceiverService_Get_Call{Call: _e.mock.On("Get", ctx, id)} -} - -func (_c *ReceiverService_Get_Call) Run(run func(ctx context.Context, id uint64)) *ReceiverService_Get_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) - }) - return _c -} - -func (_c *ReceiverService_Get_Call) Return(_a0 *receiver.Receiver, _a1 error) *ReceiverService_Get_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// List provides a mock function with given fields: ctx, flt -func (_m *ReceiverService) List(ctx context.Context, flt receiver.Filter) ([]receiver.Receiver, error) { - ret := _m.Called(ctx, flt) - - var r0 []receiver.Receiver - if rf, ok := ret.Get(0).(func(context.Context, receiver.Filter) []receiver.Receiver); ok { - r0 = rf(ctx, flt) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]receiver.Receiver) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, receiver.Filter) error); ok { - r1 = rf(ctx, flt) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReceiverService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type ReceiverService_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - 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)} -} - -func (_c *ReceiverService_List_Call) Run(run func(ctx context.Context, flt receiver.Filter)) *ReceiverService_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(receiver.Filter)) - }) - return _c -} - -func (_c *ReceiverService_List_Call) Return(_a0 []receiver.Receiver, _a1 error) *ReceiverService_List_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Notify provides a mock function with given fields: ctx, id, payloadMessage -func (_m *ReceiverService) Notify(ctx context.Context, id uint64, payloadMessage map[string]interface{}) error { - ret := _m.Called(ctx, id, payloadMessage) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, map[string]interface{}) error); ok { - r0 = rf(ctx, id, payloadMessage) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ReceiverService_Notify_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Notify' -type ReceiverService_Notify_Call struct { - *mock.Call -} - -// Notify is a helper method to define mock.On call -// - ctx context.Context -// - id uint64 -// - payloadMessage map[string]interface{} -func (_e *ReceiverService_Expecter) Notify(ctx interface{}, id interface{}, payloadMessage interface{}) *ReceiverService_Notify_Call { - return &ReceiverService_Notify_Call{Call: _e.mock.On("Notify", ctx, id, payloadMessage)} -} - -func (_c *ReceiverService_Notify_Call) Run(run func(ctx context.Context, id uint64, payloadMessage map[string]interface{})) *ReceiverService_Notify_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(map[string]interface{})) - }) - return _c -} - -func (_c *ReceiverService_Notify_Call) Return(_a0 error) *ReceiverService_Notify_Call { - _c.Call.Return(_a0) - return _c -} - -// Update provides a mock function with given fields: ctx, rcv -func (_m *ReceiverService) Update(ctx context.Context, rcv *receiver.Receiver) error { - ret := _m.Called(ctx, rcv) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *receiver.Receiver) error); ok { - r0 = rf(ctx, rcv) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ReceiverService_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' -type ReceiverService_Update_Call struct { - *mock.Call -} - -// Update is a helper method to define mock.On call -// - ctx context.Context -// - rcv *receiver.Receiver -func (_e *ReceiverService_Expecter) Update(ctx interface{}, rcv interface{}) *ReceiverService_Update_Call { - return &ReceiverService_Update_Call{Call: _e.mock.On("Update", ctx, rcv)} -} - -func (_c *ReceiverService_Update_Call) Run(run func(ctx context.Context, rcv *receiver.Receiver)) *ReceiverService_Update_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*receiver.Receiver)) - }) - return _c -} - -func (_c *ReceiverService_Update_Call) Return(_a0 error) *ReceiverService_Update_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewReceiverService interface { - mock.TestingT - Cleanup(func()) -} - -// NewReceiverService creates a new instance of ReceiverService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewReceiverService(t mockConstructorTestingTNewReceiverService) *ReceiverService { - mock := &ReceiverService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/api/v1beta1/mocks/rule_service.go b/internal/api/v1beta1/mocks/rule_service.go deleted file mode 100644 index b09716e9..00000000 --- a/internal/api/v1beta1/mocks/rule_service.go +++ /dev/null @@ -1,123 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - rule "github.com/odpf/siren/core/rule" - mock "github.com/stretchr/testify/mock" -) - -// RuleService is an autogenerated mock type for the RuleService type -type RuleService struct { - mock.Mock -} - -type RuleService_Expecter struct { - mock *mock.Mock -} - -func (_m *RuleService) EXPECT() *RuleService_Expecter { - return &RuleService_Expecter{mock: &_m.Mock} -} - -// List provides a mock function with given fields: _a0, _a1 -func (_m *RuleService) List(_a0 context.Context, _a1 rule.Filter) ([]rule.Rule, error) { - ret := _m.Called(_a0, _a1) - - var r0 []rule.Rule - if rf, ok := ret.Get(0).(func(context.Context, rule.Filter) []rule.Rule); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]rule.Rule) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, rule.Filter) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RuleService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type RuleService_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 rule.Filter -func (_e *RuleService_Expecter) List(_a0 interface{}, _a1 interface{}) *RuleService_List_Call { - return &RuleService_List_Call{Call: _e.mock.On("List", _a0, _a1)} -} - -func (_c *RuleService_List_Call) Run(run func(_a0 context.Context, _a1 rule.Filter)) *RuleService_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(rule.Filter)) - }) - return _c -} - -func (_c *RuleService_List_Call) Return(_a0 []rule.Rule, _a1 error) *RuleService_List_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Upsert provides a mock function with given fields: _a0, _a1 -func (_m *RuleService) Upsert(_a0 context.Context, _a1 *rule.Rule) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *rule.Rule) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RuleService_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert' -type RuleService_Upsert_Call struct { - *mock.Call -} - -// Upsert is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *rule.Rule -func (_e *RuleService_Expecter) Upsert(_a0 interface{}, _a1 interface{}) *RuleService_Upsert_Call { - return &RuleService_Upsert_Call{Call: _e.mock.On("Upsert", _a0, _a1)} -} - -func (_c *RuleService_Upsert_Call) Run(run func(_a0 context.Context, _a1 *rule.Rule)) *RuleService_Upsert_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*rule.Rule)) - }) - return _c -} - -func (_c *RuleService_Upsert_Call) Return(_a0 error) *RuleService_Upsert_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewRuleService interface { - mock.TestingT - Cleanup(func()) -} - -// NewRuleService creates a new instance of RuleService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewRuleService(t mockConstructorTestingTNewRuleService) *RuleService { - mock := &RuleService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/api/v1beta1/mocks/subscription_service.go b/internal/api/v1beta1/mocks/subscription_service.go deleted file mode 100644 index 03c0206d..00000000 --- a/internal/api/v1beta1/mocks/subscription_service.go +++ /dev/null @@ -1,246 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - subscription "github.com/odpf/siren/core/subscription" - mock "github.com/stretchr/testify/mock" -) - -// SubscriptionService is an autogenerated mock type for the SubscriptionService type -type SubscriptionService struct { - mock.Mock -} - -type SubscriptionService_Expecter struct { - mock *mock.Mock -} - -func (_m *SubscriptionService) EXPECT() *SubscriptionService_Expecter { - return &SubscriptionService_Expecter{mock: &_m.Mock} -} - -// Create provides a mock function with given fields: _a0, _a1 -func (_m *SubscriptionService) Create(_a0 context.Context, _a1 *subscription.Subscription) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *subscription.Subscription) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type SubscriptionService_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *subscription.Subscription -func (_e *SubscriptionService_Expecter) Create(_a0 interface{}, _a1 interface{}) *SubscriptionService_Create_Call { - return &SubscriptionService_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} -} - -func (_c *SubscriptionService_Create_Call) Run(run func(_a0 context.Context, _a1 *subscription.Subscription)) *SubscriptionService_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*subscription.Subscription)) - }) - return _c -} - -func (_c *SubscriptionService_Create_Call) Return(_a0 error) *SubscriptionService_Create_Call { - _c.Call.Return(_a0) - return _c -} - -// Delete provides a mock function with given fields: _a0, _a1 -func (_m *SubscriptionService) Delete(_a0 context.Context, _a1 uint64) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type SubscriptionService_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -func (_e *SubscriptionService_Expecter) Delete(_a0 interface{}, _a1 interface{}) *SubscriptionService_Delete_Call { - return &SubscriptionService_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} -} - -func (_c *SubscriptionService_Delete_Call) Run(run func(_a0 context.Context, _a1 uint64)) *SubscriptionService_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) - }) - return _c -} - -func (_c *SubscriptionService_Delete_Call) Return(_a0 error) *SubscriptionService_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -// Get provides a mock function with given fields: _a0, _a1 -func (_m *SubscriptionService) Get(_a0 context.Context, _a1 uint64) (*subscription.Subscription, error) { - ret := _m.Called(_a0, _a1) - - var r0 *subscription.Subscription - if rf, ok := ret.Get(0).(func(context.Context, uint64) *subscription.Subscription); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*subscription.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SubscriptionService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' -type SubscriptionService_Get_Call struct { - *mock.Call -} - -// Get is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -func (_e *SubscriptionService_Expecter) Get(_a0 interface{}, _a1 interface{}) *SubscriptionService_Get_Call { - return &SubscriptionService_Get_Call{Call: _e.mock.On("Get", _a0, _a1)} -} - -func (_c *SubscriptionService_Get_Call) Run(run func(_a0 context.Context, _a1 uint64)) *SubscriptionService_Get_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) - }) - return _c -} - -func (_c *SubscriptionService_Get_Call) Return(_a0 *subscription.Subscription, _a1 error) *SubscriptionService_Get_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// List provides a mock function with given fields: _a0, _a1 -func (_m *SubscriptionService) List(_a0 context.Context, _a1 subscription.Filter) ([]subscription.Subscription, error) { - ret := _m.Called(_a0, _a1) - - var r0 []subscription.Subscription - if rf, ok := ret.Get(0).(func(context.Context, subscription.Filter) []subscription.Subscription); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]subscription.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, subscription.Filter) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SubscriptionService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type SubscriptionService_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 subscription.Filter -func (_e *SubscriptionService_Expecter) List(_a0 interface{}, _a1 interface{}) *SubscriptionService_List_Call { - return &SubscriptionService_List_Call{Call: _e.mock.On("List", _a0, _a1)} -} - -func (_c *SubscriptionService_List_Call) Run(run func(_a0 context.Context, _a1 subscription.Filter)) *SubscriptionService_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(subscription.Filter)) - }) - return _c -} - -func (_c *SubscriptionService_List_Call) Return(_a0 []subscription.Subscription, _a1 error) *SubscriptionService_List_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Update provides a mock function with given fields: _a0, _a1 -func (_m *SubscriptionService) Update(_a0 context.Context, _a1 *subscription.Subscription) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *subscription.Subscription) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SubscriptionService_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' -type SubscriptionService_Update_Call struct { - *mock.Call -} - -// Update is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *subscription.Subscription -func (_e *SubscriptionService_Expecter) Update(_a0 interface{}, _a1 interface{}) *SubscriptionService_Update_Call { - return &SubscriptionService_Update_Call{Call: _e.mock.On("Update", _a0, _a1)} -} - -func (_c *SubscriptionService_Update_Call) Run(run func(_a0 context.Context, _a1 *subscription.Subscription)) *SubscriptionService_Update_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*subscription.Subscription)) - }) - return _c -} - -func (_c *SubscriptionService_Update_Call) Return(_a0 error) *SubscriptionService_Update_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewSubscriptionService interface { - mock.TestingT - Cleanup(func()) -} - -// NewSubscriptionService creates a new instance of SubscriptionService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSubscriptionService(t mockConstructorTestingTNewSubscriptionService) *SubscriptionService { - mock := &SubscriptionService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/api/v1beta1/mocks/template_service.go b/internal/api/v1beta1/mocks/template_service.go deleted file mode 100644 index 5cc5a733..00000000 --- a/internal/api/v1beta1/mocks/template_service.go +++ /dev/null @@ -1,254 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - template "github.com/odpf/siren/core/template" - mock "github.com/stretchr/testify/mock" -) - -// TemplateService is an autogenerated mock type for the TemplateService type -type TemplateService struct { - mock.Mock -} - -type TemplateService_Expecter struct { - mock *mock.Mock -} - -func (_m *TemplateService) EXPECT() *TemplateService_Expecter { - return &TemplateService_Expecter{mock: &_m.Mock} -} - -// Delete provides a mock function with given fields: _a0, _a1 -func (_m *TemplateService) Delete(_a0 context.Context, _a1 string) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// TemplateService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type TemplateService_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -// - _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)} -} - -func (_c *TemplateService_Delete_Call) Run(run func(_a0 context.Context, _a1 string)) *TemplateService_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *TemplateService_Delete_Call) Return(_a0 error) *TemplateService_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -// GetByName provides a mock function with given fields: _a0, _a1 -func (_m *TemplateService) GetByName(_a0 context.Context, _a1 string) (*template.Template, error) { - ret := _m.Called(_a0, _a1) - - var r0 *template.Template - if rf, ok := ret.Get(0).(func(context.Context, string) *template.Template); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*template.Template) - } - } - - 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 -} - -// TemplateService_GetByName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByName' -type TemplateService_GetByName_Call struct { - *mock.Call -} - -// GetByName is a helper method to define mock.On call -// - _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)} -} - -func (_c *TemplateService_GetByName_Call) Run(run func(_a0 context.Context, _a1 string)) *TemplateService_GetByName_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *TemplateService_GetByName_Call) Return(_a0 *template.Template, _a1 error) *TemplateService_GetByName_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// List provides a mock function with given fields: _a0, _a1 -func (_m *TemplateService) List(_a0 context.Context, _a1 template.Filter) ([]template.Template, error) { - ret := _m.Called(_a0, _a1) - - var r0 []template.Template - if rf, ok := ret.Get(0).(func(context.Context, template.Filter) []template.Template); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]template.Template) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, template.Filter) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// TemplateService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type TemplateService_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - _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)} -} - -func (_c *TemplateService_List_Call) Run(run func(_a0 context.Context, _a1 template.Filter)) *TemplateService_List_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(template.Filter)) - }) - return _c -} - -func (_c *TemplateService_List_Call) Return(_a0 []template.Template, _a1 error) *TemplateService_List_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Render provides a mock function with given fields: _a0, _a1, _a2 -func (_m *TemplateService) Render(_a0 context.Context, _a1 string, _a2 map[string]string) (string, error) { - ret := _m.Called(_a0, _a1, _a2) - - var r0 string - if rf, ok := ret.Get(0).(func(context.Context, string, map[string]string) string); ok { - r0 = rf(_a0, _a1, _a2) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string, map[string]string) error); ok { - r1 = rf(_a0, _a1, _a2) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// TemplateService_Render_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Render' -type TemplateService_Render_Call struct { - *mock.Call -} - -// Render is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -// - _a2 map[string]string -func (_e *TemplateService_Expecter) Render(_a0 interface{}, _a1 interface{}, _a2 interface{}) *TemplateService_Render_Call { - return &TemplateService_Render_Call{Call: _e.mock.On("Render", _a0, _a1, _a2)} -} - -func (_c *TemplateService_Render_Call) Run(run func(_a0 context.Context, _a1 string, _a2 map[string]string)) *TemplateService_Render_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(map[string]string)) - }) - return _c -} - -func (_c *TemplateService_Render_Call) Return(_a0 string, _a1 error) *TemplateService_Render_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -// Upsert provides a mock function with given fields: _a0, _a1 -func (_m *TemplateService) Upsert(_a0 context.Context, _a1 *template.Template) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *template.Template) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// TemplateService_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert' -type TemplateService_Upsert_Call struct { - *mock.Call -} - -// Upsert is a helper method to define mock.On call -// - _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)} -} - -func (_c *TemplateService_Upsert_Call) Run(run func(_a0 context.Context, _a1 *template.Template)) *TemplateService_Upsert_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*template.Template)) - }) - return _c -} - -func (_c *TemplateService_Upsert_Call) Return(_a0 error) *TemplateService_Upsert_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewTemplateService interface { - mock.TestingT - Cleanup(func()) -} - -// NewTemplateService creates a new instance of TemplateService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewTemplateService(t mockConstructorTestingTNewTemplateService) *TemplateService { - mock := &TemplateService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/api/v1beta1/receiver.go b/internal/api/v1beta1/receiver.go index 3839b16d..be494bf3 100644 --- a/internal/api/v1beta1/receiver.go +++ b/internal/api/v1beta1/receiver.go @@ -2,11 +2,14 @@ package v1beta1 import ( "context" + "fmt" + "reflect" "github.com/mitchellh/mapstructure" "github.com/odpf/siren/core/notification" "github.com/odpf/siren/core/receiver" "github.com/odpf/siren/pkg/errors" + "github.com/odpf/siren/pkg/secret" sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -20,7 +23,7 @@ func (s *GRPCServer) ListReceivers(ctx context.Context, _ *sirenv1beta1.ListRece items := []*sirenv1beta1.Receiver{} for _, rcv := range receivers { - configurations, err := structpb.NewStruct(rcv.Configurations) + configurations, err := structpb.NewStruct(sanitizeConfigMap(rcv.Configurations)) if err != nil { return nil, s.generateRPCErr(err) } @@ -74,7 +77,7 @@ func (s *GRPCServer) GetReceiver(ctx context.Context, req *sirenv1beta1.GetRecei return nil, s.generateRPCErr(err) } - configuration, err := structpb.NewStruct(rcv.Configurations) + configuration, err := structpb.NewStruct(sanitizeConfigMap(rcv.Configurations)) if err != nil { return nil, s.generateRPCErr(err) } @@ -140,3 +143,17 @@ func (s *GRPCServer) NotifyReceiver(ctx context.Context, req *sirenv1beta1.Notif return &sirenv1beta1.NotifyReceiverResponse{}, nil } + +// sanitizeConfigMap does all sanitization to present receiver configurations to the user +func sanitizeConfigMap(receiverConfigMap map[string]interface{}) map[string]interface{} { + var newConfigMap = make(map[string]interface{}) + for k, v := range receiverConfigMap { + // sanitize maskable string. convert `secret.MaskableString` to string to be compatible with structpb + if reflect.TypeOf(v) == reflect.TypeOf(secret.MaskableString("")) { + newConfigMap[k] = fmt.Sprintf("%v", v) + } else { + newConfigMap[k] = v + } + } + return newConfigMap +} diff --git a/internal/api/v1beta1/subscription.go b/internal/api/v1beta1/subscription.go index 2f303a2d..c18ba457 100644 --- a/internal/api/v1beta1/subscription.go +++ b/internal/api/v1beta1/subscription.go @@ -2,10 +2,10 @@ package v1beta1 import ( "context" - "fmt" "github.com/odpf/siren/core/subscription" sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -18,12 +18,26 @@ func (s *GRPCServer) ListSubscriptions(ctx context.Context, _ *sirenv1beta1.List items := []*sirenv1beta1.Subscription{} for _, sub := range subscriptions { + + receiverMetadatasPB := make([]*sirenv1beta1.ReceiverMetadata, 0) + for _, item := range sub.Receivers { + configMapPB, err := structpb.NewStruct(item.Configuration) + if err != nil { + return nil, err + } + + receiverMetadatasPB = append(receiverMetadatasPB, &sirenv1beta1.ReceiverMetadata{ + Id: item.ID, + Configuration: configMapPB, + }) + } + item := &sirenv1beta1.Subscription{ Id: sub.ID, Urn: sub.URN, Namespace: sub.Namespace, Match: sub.Match, - Receivers: getReceiverMetadataListFromDomainObject(sub.Receivers), + Receivers: receiverMetadatasPB, CreatedAt: timestamppb.New(sub.CreatedAt), UpdatedAt: timestamppb.New(sub.UpdatedAt), } @@ -60,8 +74,15 @@ func (s *GRPCServer) GetSubscription(ctx context.Context, req *sirenv1beta1.GetS receivers := make([]*sirenv1beta1.ReceiverMetadata, 0) for _, receiverMetadataItem := range sub.Receivers { - item := getReceiverMetadataFromDomainObject(&receiverMetadataItem) - receivers = append(receivers, &item) + configMapPB, err := structpb.NewStruct(receiverMetadataItem.Configuration) + if err != nil { + return nil, err + } + + receivers = append(receivers, &sirenv1beta1.ReceiverMetadata{ + Id: receiverMetadataItem.ID, + Configuration: configMapPB, + }) } return &sirenv1beta1.GetSubscriptionResponse{ @@ -104,43 +125,13 @@ func (s *GRPCServer) DeleteSubscription(ctx context.Context, req *sirenv1beta1.D return &sirenv1beta1.DeleteSubscriptionResponse{}, nil } -func getReceiverMetadataFromDomainObject(item *subscription.Receiver) sirenv1beta1.ReceiverMetadata { - configMap := make(map[string]string) - for k, v := range item.Configuration { - configMap[k] = fmt.Sprintf("%v", v) - } - - return sirenv1beta1.ReceiverMetadata{ - Id: item.ID, - Configuration: configMap, - } -} - -func getReceiverMetadataInDomainObject(item *sirenv1beta1.ReceiverMetadata) subscription.Receiver { - configMapInterface := make(map[string]interface{}) - for k, v := range item.Configuration { - configMapInterface[k] = v - } - - return subscription.Receiver{ - ID: item.Id, - Configuration: configMapInterface, - } -} - func getReceiverMetadataListInDomainObject(domainReceivers []*sirenv1beta1.ReceiverMetadata) []subscription.Receiver { receivers := make([]subscription.Receiver, 0) - for _, receiverMetadataItem := range domainReceivers { - receivers = append(receivers, getReceiverMetadataInDomainObject(receiverMetadataItem)) - } - return receivers -} - -func getReceiverMetadataListFromDomainObject(domainReceivers []subscription.Receiver) []*sirenv1beta1.ReceiverMetadata { - receivers := make([]*sirenv1beta1.ReceiverMetadata, 0) - for _, receiverMetadataItem := range domainReceivers { - item := getReceiverMetadataFromDomainObject(&receiverMetadataItem) - receivers = append(receivers, &item) + for _, item := range domainReceivers { + receivers = append(receivers, subscription.Receiver{ + ID: item.Id, + Configuration: item.Configuration.AsMap(), + }) } return receivers } diff --git a/internal/api/v1beta1/subscription_test.go b/internal/api/v1beta1/subscription_test.go index 2b1a1acb..1282a7c3 100644 --- a/internal/api/v1beta1/subscription_test.go +++ b/internal/api/v1beta1/subscription_test.go @@ -14,6 +14,8 @@ import ( "github.com/odpf/siren/pkg/errors" sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/structpb" ) func TestGRPCServer_ListSubscriptions(t *testing.T) { @@ -111,11 +113,14 @@ func TestGRPCServer_CreateSubscription(t *testing.T) { match := make(map[string]string) match["foo"] = "baz" - configMapString := make(map[string]string) + configMapString := make(map[string]interface{}) for k, v := range configuration { configMapString[k] = fmt.Sprintf("%v", v) } + configMapPB, err := structpb.NewStruct(configMapString) + require.NoError(t, err) + payload := &subscription.Subscription{ Namespace: 1, URN: "foo", @@ -144,7 +149,7 @@ func TestGRPCServer_CreateSubscription(t *testing.T) { res, err := dummyGRPCServer.CreateSubscription(context.Background(), &sirenv1beta1.CreateSubscriptionRequest{ Namespace: 1, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, err) @@ -162,7 +167,7 @@ func TestGRPCServer_CreateSubscription(t *testing.T) { res, err := dummyGRPCServer.CreateSubscription(context.Background(), &sirenv1beta1.CreateSubscriptionRequest{ Namespace: 1, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, res) @@ -180,7 +185,7 @@ func TestGRPCServer_CreateSubscription(t *testing.T) { res, err := dummyGRPCServer.CreateSubscription(context.Background(), &sirenv1beta1.CreateSubscriptionRequest{ Namespace: 1, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, res) @@ -198,7 +203,7 @@ func TestGRPCServer_CreateSubscription(t *testing.T) { res, err := dummyGRPCServer.CreateSubscription(context.Background(), &sirenv1beta1.CreateSubscriptionRequest{ Namespace: 1, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, res) @@ -219,11 +224,14 @@ func TestGRPCServer_UpdateSubscription(t *testing.T) { Match: match, } - configMapString := make(map[string]string) + configMapString := make(map[string]interface{}) for k, v := range configuration { configMapString[k] = fmt.Sprintf("%v", v) } + configMapPB, err := structpb.NewStruct(configMapString) + require.NoError(t, err) + t.Run("should update a subscription", func(t *testing.T) { mockedSubscriptionService := &mocks.SubscriptionService{} dummyGRPCServer := v1beta1.NewGRPCServer(nil, log.NewNoop(), &api.Deps{SubscriptionService: mockedSubscriptionService}) @@ -236,7 +244,7 @@ func TestGRPCServer_UpdateSubscription(t *testing.T) { Id: 1, Namespace: 10, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, err) @@ -252,7 +260,7 @@ func TestGRPCServer_UpdateSubscription(t *testing.T) { Id: 1, Namespace: 10, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, res) @@ -268,7 +276,7 @@ func TestGRPCServer_UpdateSubscription(t *testing.T) { Id: 1, Namespace: 10, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, res) @@ -284,7 +292,7 @@ func TestGRPCServer_UpdateSubscription(t *testing.T) { Id: 1, Namespace: 10, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, res) @@ -300,7 +308,7 @@ func TestGRPCServer_UpdateSubscription(t *testing.T) { Id: 1, Namespace: 10, Urn: "foo", - Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapString}}, + Receivers: []*sirenv1beta1.ReceiverMetadata{{Id: 1, Configuration: configMapPB}}, Match: match, }) assert.Nil(t, res) diff --git a/internal/store/postgres/alerts.go b/internal/store/postgres/alerts.go index d847379a..3d191e26 100644 --- a/internal/store/postgres/alerts.go +++ b/internal/store/postgres/alerts.go @@ -39,9 +39,9 @@ func NewAlertRepository(client *Client) *AlertRepository { return &AlertRepository{client} } -func (r AlertRepository) Create(ctx context.Context, alrt *alert.Alert) (*alert.Alert, error) { +func (r AlertRepository) Create(ctx context.Context, alrt *alert.Alert) error { if alrt == nil { - return nil, errors.New("alert domain is nil") + return errors.New("alert domain is nil") } var alertModel model.Alert @@ -61,12 +61,12 @@ func (r AlertRepository) Create(ctx context.Context, alrt *alert.Alert) (*alert. ).StructScan(&newAlertModel); err != nil { err := checkPostgresError(err) if errors.Is(err, errForeignKeyViolation) { - return nil, alert.ErrRelation + return alert.ErrRelation } - return nil, err + return err } - return newAlertModel.ToDomain(), nil + return nil } func (r AlertRepository) List(ctx context.Context, flt alert.Filter) ([]alert.Alert, error) { diff --git a/internal/store/postgres/alerts_test.go b/internal/store/postgres/alerts_test.go index 22273b84..0f2f4d10 100644 --- a/internal/store/postgres/alerts_test.go +++ b/internal/store/postgres/alerts_test.go @@ -63,8 +63,7 @@ func (s *AlertsRepositoryTestSuite) SetupSuite() { func (s *AlertsRepositoryTestSuite) SetupTest() { var err error - _, err = bootstrapAlert(s.client) - if err != nil { + if err = bootstrapAlert(s.client); err != nil { s.T().Fatal(err) } } @@ -195,15 +194,12 @@ func (s *AlertsRepositoryTestSuite) TestCreate() { for _, tc := range testCases { s.Run(tc.Description, func() { - got, err := s.repository.Create(s.ctx, tc.AlertToCreate) + err := s.repository.Create(s.ctx, tc.AlertToCreate) if tc.ErrString != "" { if err.Error() != tc.ErrString { s.T().Fatalf("got error %s, expected was %s", err.Error(), tc.ErrString) } } - if tc.ExpectedID != 0 && (got.ID != tc.ExpectedID) { - s.T().Fatalf("got result %+v, expected was %+v", got.ID, tc.ExpectedID) - } }) } } diff --git a/internal/store/postgres/namespace.go b/internal/store/postgres/namespace.go index f43c98dd..d0f6f88f 100644 --- a/internal/store/postgres/namespace.go +++ b/internal/store/postgres/namespace.go @@ -3,6 +3,7 @@ package postgres import ( "context" "database/sql" + "fmt" sq "github.com/Masterminds/squirrel" "github.com/odpf/siren/core/namespace" @@ -89,7 +90,7 @@ func (r NamespaceRepository) Create(ctx context.Context, ns *namespace.Encrypted nsModel.FromDomain(*ns) var createdNamespace model.Namespace - if err := r.client.db.QueryRowxContext(ctx, namespaceInsertQuery, + if err := r.client.GetDB(ctx).QueryRowxContext(ctx, namespaceInsertQuery, nsModel.ProviderID, nsModel.URN, nsModel.Name, @@ -118,7 +119,7 @@ func (r NamespaceRepository) Get(ctx context.Context, id uint64) (*namespace.Enc } var nsDetailModel model.NamespaceDetail - if err := r.client.db.GetContext(ctx, &nsDetailModel, query, args...); err != nil { + if err := r.client.GetDB(ctx).QueryRowxContext(ctx, query, args...).StructScan(&nsDetailModel); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, namespace.NotFoundError{ID: id} } @@ -137,7 +138,7 @@ func (r NamespaceRepository) Update(ctx context.Context, ns *namespace.Encrypted namespaceModel.FromDomain(*ns) var updatedNamespace model.Namespace - if err := r.client.db.QueryRowxContext(ctx, namespaceUpdateQuery, + if err := r.client.GetDB(ctx).QueryRowxContext(ctx, namespaceUpdateQuery, namespaceModel.ID, namespaceModel.ProviderID, namespaceModel.URN, @@ -164,8 +165,25 @@ func (r NamespaceRepository) Update(ctx context.Context, ns *namespace.Encrypted } func (r NamespaceRepository) Delete(ctx context.Context, id uint64) error { - if _, err := r.client.db.ExecContext(ctx, namespaceDeleteQuery, id); err != nil { + rows, err := r.client.GetDB(ctx).QueryxContext(ctx, namespaceDeleteQuery, id) + if err != nil { return err } + rows.Close() return nil } + +func (r *NamespaceRepository) WithTransaction(ctx context.Context) context.Context { + return r.client.WithTransaction(ctx, nil) +} + +func (r *NamespaceRepository) Rollback(ctx context.Context, err error) error { + if txErr := r.client.Rollback(ctx); txErr != nil { + return fmt.Errorf("rollback error %s with error: %w", txErr.Error(), err) + } + return nil +} + +func (r *NamespaceRepository) Commit(ctx context.Context) error { + return r.client.Commit(ctx) +} diff --git a/internal/store/postgres/namespace_test.go b/internal/store/postgres/namespace_test.go index ec13e482..0a37b3e0 100644 --- a/internal/store/postgres/namespace_test.go +++ b/internal/store/postgres/namespace_test.go @@ -424,6 +424,60 @@ func (s *NamespaceRepositoryTestSuite) TestDelete() { } } +func (s *NamespaceRepositoryTestSuite) TestTransaction() { + s.Run("successfully commit transaction", func() { + fetchedNamespaces, err := s.repository.List(s.ctx) + s.NoError(err) + s.Len(fetchedNamespaces, 3) + + ctx := s.repository.WithTransaction(context.Background()) + err = s.repository.Create(ctx, &namespace.EncryptedNamespace{ + Namespace: &namespace.Namespace{ + Name: "instance-2-tx", + URN: "instance-2-tx", + Provider: provider.Provider{ + ID: 2, + }, + }, + CredentialString: "xxx", + }) + s.NoError(err) + + err = s.repository.Commit(ctx) + s.NoError(err) + + fetchedNamespaces, err = s.repository.List(s.ctx) + s.NoError(err) + s.Len(fetchedNamespaces, 4) + }) + + s.Run("successfully rollback transaction", func() { + fetchedNamespaces, err := s.repository.List(s.ctx) + s.NoError(err) + s.Len(fetchedNamespaces, 4) + + ctx := s.repository.WithTransaction(context.Background()) + err = s.repository.Create(ctx, &namespace.EncryptedNamespace{ + Namespace: &namespace.Namespace{ + Name: "instance-2-tx-rb", + URN: "instance-2-tx-rb", + Provider: provider.Provider{ + ID: 2, + }, + }, + CredentialString: "xxx", + }) + s.NoError(err) + + err = s.repository.Rollback(ctx, nil) + s.NoError(err) + + fetchedNamespaces, err = s.repository.List(s.ctx) + s.NoError(err) + s.Len(fetchedNamespaces, 4) + }) +} + func TestNamespaceRepository(t *testing.T) { suite.Run(t, new(NamespaceRepositoryTestSuite)) } diff --git a/internal/store/postgres/postgres.go b/internal/store/postgres/postgres.go index 6a97e8d5..a3097c1b 100644 --- a/internal/store/postgres/postgres.go +++ b/internal/store/postgres/postgres.go @@ -5,9 +5,8 @@ import ( "database/sql" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" "github.com/jmoiron/sqlx" + "github.com/lib/pq" "github.com/odpf/salt/db" "github.com/odpf/salt/log" "github.com/odpf/siren/internal/store/postgres/migrations" @@ -40,15 +39,15 @@ func NewClient(logger log.Logger, dbc *db.Client) (*Client, error) { } func checkPostgresError(err error) error { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { - switch pgErr.Code { - case pgerrcode.UniqueViolation: - return fmt.Errorf("%w [%s]", errDuplicateKey, pgErr.Detail) - case pgerrcode.CheckViolation: - return fmt.Errorf("%w [%s]", errCheckViolation, pgErr.Detail) - case pgerrcode.ForeignKeyViolation: - return fmt.Errorf("%w [%s]", errForeignKeyViolation, pgErr.Detail) + var pqErr *pq.Error + if errors.As(err, &pqErr) { + switch pqErr.Code.Name() { + case "unique_violation": + return fmt.Errorf("%w [%s]", errDuplicateKey, pqErr.Detail) + case "check_violation": + return fmt.Errorf("%w [%s]", errCheckViolation, pqErr.Detail) + case "foreign_key_violation": + return fmt.Errorf("%w [%s]", errForeignKeyViolation, pqErr.Detail) } } return err diff --git a/internal/store/postgres/postgres_test.go b/internal/store/postgres/postgres_test.go index d8786370..602f4c81 100644 --- a/internal/store/postgres/postgres_test.go +++ b/internal/store/postgres/postgres_test.go @@ -6,7 +6,6 @@ import ( "fmt" "os" - _ "github.com/jackc/pgx/v4/stdlib" "github.com/odpf/salt/db" "github.com/odpf/salt/dockertest" "github.com/odpf/salt/log" @@ -28,7 +27,7 @@ const ( var ( dbConfig = db.Config{ - Driver: "pgx", + Driver: "postgres", MaxIdleConns: 10, MaxOpenConns: 10, ConnMaxLifeTime: 1000, @@ -158,31 +157,28 @@ func bootstrapReceiver(client *postgres.Client) ([]receiver.Receiver, error) { return insertedData, nil } -func bootstrapAlert(client *postgres.Client) ([]alert.Alert, error) { +func bootstrapAlert(client *postgres.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 insertedData []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 } - - insertedData = append(insertedData, *alrt) } - return insertedData, nil + return nil } func bootstrapTemplate(client *postgres.Client) ([]template.Template, error) { diff --git a/internal/store/postgres/subscription.go b/internal/store/postgres/subscription.go index 3cb01d26..c3fc9d2d 100644 --- a/internal/store/postgres/subscription.go +++ b/internal/store/postgres/subscription.go @@ -108,7 +108,7 @@ func (r *SubscriptionRepository) Create(ctx context.Context, sub *subscription.S subscriptionModel.FromDomain(*sub) var newSubscriptionModel model.Subscription - if err := r.client.GetDB(ctx).QueryRowxContext(ctx, subscriptionInsertQuery, + if err := r.client.db.QueryRowxContext(ctx, subscriptionInsertQuery, subscriptionModel.NamespaceID, subscriptionModel.URN, subscriptionModel.Receiver, @@ -136,7 +136,7 @@ func (r *SubscriptionRepository) Get(ctx context.Context, id uint64) (*subscript } var subscriptionModel model.Subscription - if err := r.client.GetDB(ctx).QueryRowxContext(ctx, query, args...).StructScan(&subscriptionModel); err != nil { + if err := r.client.db.QueryRowxContext(ctx, query, args...).StructScan(&subscriptionModel); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, subscription.NotFoundError{ID: id} } @@ -155,7 +155,7 @@ func (r *SubscriptionRepository) Update(ctx context.Context, sub *subscription.S subscriptionModel.FromDomain(*sub) var newSubscriptionModel model.Subscription - if err := r.client.GetDB(ctx).QueryRowxContext(ctx, subscriptionUpdateQuery, + if err := r.client.db.QueryRowxContext(ctx, subscriptionUpdateQuery, subscriptionModel.ID, subscriptionModel.NamespaceID, subscriptionModel.URN, @@ -182,25 +182,10 @@ func (r *SubscriptionRepository) Update(ctx context.Context, sub *subscription.S // TODO this won't be synced to provider func (r *SubscriptionRepository) Delete(ctx context.Context, id uint64) error { - rows, err := r.client.GetDB(ctx).QueryxContext(ctx, subscriptionDeleteQuery, id) + rows, err := r.client.db.QueryxContext(ctx, subscriptionDeleteQuery, id) if err != nil { return err } rows.Close() return nil } - -func (r *SubscriptionRepository) WithTransaction(ctx context.Context) context.Context { - return r.client.WithTransaction(ctx, nil) -} - -func (r *SubscriptionRepository) Rollback(ctx context.Context, err error) error { - if txErr := r.client.Rollback(ctx); txErr != nil { - return fmt.Errorf("rollback error %s with error: %w", txErr.Error(), err) - } - return nil -} - -func (r *SubscriptionRepository) Commit(ctx context.Context) error { - return r.client.Commit(ctx) -} diff --git a/internal/store/postgres/subscription_test.go b/internal/store/postgres/subscription_test.go index 586c7843..d0c3d865 100644 --- a/internal/store/postgres/subscription_test.go +++ b/internal/store/postgres/subscription_test.go @@ -493,74 +493,6 @@ func (s *SubscriptionRepositoryTestSuite) TestDelete() { } } -func (s *SubscriptionRepositoryTestSuite) TestTransaction() { - s.Run("successfully commit transaction", func() { - ctx := s.repository.WithTransaction(context.Background()) - err := s.repository.Create(ctx, &subscription.Subscription{ - Namespace: 2, - URN: "foo-commit", - Match: map[string]string{ - "foo": "bar", - }, - Receivers: []subscription.Receiver{ - { - ID: 2, - Configuration: map[string]interface{}{}, - }, - { - ID: 1, - Configuration: map[string]interface{}{ - "channel_name": "test", - }, - }, - }, - }) - s.NoError(err) - - err = s.repository.Commit(ctx) - s.NoError(err) - - fetchedRules, err := s.repository.List(s.ctx, subscription.Filter{ - NamespaceID: 2, - }) - s.NoError(err) - s.Len(fetchedRules, 3) - }) - - s.Run("successfully rollback transaction", func() { - ctx := s.repository.WithTransaction(context.Background()) - err := s.repository.Create(ctx, &subscription.Subscription{ - Namespace: 2, - URN: "foo-rollback", - Match: map[string]string{ - "foo": "bar", - }, - Receivers: []subscription.Receiver{ - { - ID: 2, - Configuration: map[string]interface{}{}, - }, - { - ID: 1, - Configuration: map[string]interface{}{ - "channel_name": "test", - }, - }, - }, - }) - s.NoError(err) - - err = s.repository.Rollback(ctx, nil) - s.NoError(err) - - fetchedRules, err := s.repository.List(s.ctx, subscription.Filter{ - NamespaceID: 2, - }) - s.NoError(err) - s.Len(fetchedRules, 3) - }) -} - func TestSubscriptionRepository(t *testing.T) { suite.Run(t, new(SubscriptionRepositoryTestSuite)) } diff --git a/plugins/providers/base/service.go b/plugins/providers/base/service.go new file mode 100644 index 00000000..94897b96 --- /dev/null +++ b/plugins/providers/base/service.go @@ -0,0 +1,21 @@ +package base + +import ( + "context" + "html/template" + + "github.com/odpf/siren/core/provider" + "github.com/odpf/siren/core/rule" + "github.com/odpf/siren/plugins" +) + +// UnimplementedService is a base receiver plugin service layer +type UnimplementedService struct{} + +func (s *UnimplementedService) SyncRuntimeConfig(ctx context.Context, namespaceURN string, prov provider.Provider) error { + return nil +} + +func (s *UnimplementedService) UpsertRule(ctx context.Context, namespaceURN string, prov provider.Provider, rl *rule.Rule, templateToUpdate *template.Template) error { + return plugins.ErrNotImplemented +} diff --git a/plugins/providers/config.go b/plugins/providers/config.go new file mode 100644 index 00000000..9539e46c --- /dev/null +++ b/plugins/providers/config.go @@ -0,0 +1,9 @@ +package providers + +import ( + "github.com/odpf/siren/plugins/providers/cortex" +) + +type Config struct { + Cortex cortex.AppConfig `mapstructure:"cortex"` +} diff --git a/plugins/providers/cortex/alert.go b/plugins/providers/cortex/alert.go new file mode 100644 index 00000000..dacdd048 --- /dev/null +++ b/plugins/providers/cortex/alert.go @@ -0,0 +1,30 @@ +package cortex + +import ( + "github.com/odpf/siren/core/alert" +) + +// GroupAlert contract is cortex/prometheus webhook_config contract +// https://prometheus.io/docs/alerting/latest/configuration/#webhook_config +type GroupAlert struct { + GroupKey string `mapstructure:"groupKey"` + ExternalURL string `mapstructure:"externalUrl"` + Version string `mapstructure:"version"` + Alerts []Alert `mapstructure:"alerts"` +} + +type Alert struct { + Status string `mapstructure:"status"` + Annotations map[string]string `mapstructure:"annotations"` + Labels map[string]string `mapstructure:"labels"` + GeneratorURL string `mapstructure:"generatorURL"` + Fingerprint string `mapstructure:"fingerprint"` + StartsAt string `mapstructure:"startsAt"` + EndsAt string `mapstructure:"endsAt"` +} + +func isValidCortexAlert(alrt *alert.Alert) bool { + return alrt != nil && !(alrt.ResourceName == "" || alrt.Rule == "" || + alrt.MetricValue == "" || alrt.MetricName == "" || + alrt.Severity == "") +} diff --git a/plugins/providers/cortex/client.go b/plugins/providers/cortex/client.go deleted file mode 100644 index 94f81ba6..00000000 --- a/plugins/providers/cortex/client.go +++ /dev/null @@ -1,124 +0,0 @@ -package cortex - -import ( - "bytes" - "context" - "fmt" - "html/template" - - "github.com/grafana/cortex-tools/pkg/client" - "github.com/grafana/cortex-tools/pkg/rules/rwrulefmt" - promconfig "github.com/prometheus/alertmanager/config" -) - -//go:generate mockery --name=CortexCaller -r --case underscore --with-expecter --structname CortexCaller --filename cortex_caller.go --output=./mocks -type CortexCaller interface { - CreateAlertmanagerConfig(ctx context.Context, cfg string, templates map[string]string) error - CreateRuleGroup(ctx context.Context, namespace string, rg rwrulefmt.RuleGroup) error - DeleteRuleGroup(ctx context.Context, namespace, groupName string) error - GetRuleGroup(ctx context.Context, namespace, groupName string) (*rwrulefmt.RuleGroup, error) - ListRules(ctx context.Context, namespace string) (map[string][]rwrulefmt.RuleGroup, error) -} - -// Client is a wrapper of cortex-tools client -type Client struct { - cortexClient CortexCaller - helperTemplate string - configYaml string -} - -// NewClient creates a new Client -func NewClient(cfg Config, opts ...ClientOption) (*Client, error) { - c := &Client{ - helperTemplate: HelperTemplateString, - configYaml: ConfigYamlString, - } - - for _, opt := range opts { - opt(c) - } - - if c.cortexClient == nil { - cortexCfg := client.Config{ - Address: cfg.Address, - } - cortexClient, err := client.New(cortexCfg) - if err != nil { - return nil, err - } - c.cortexClient = cortexClient - } - - return c, nil -} - -// CreateAlertmanagerConfig uploads an alert manager config to cortex -// this function merges alertmanager template defined in config/helper.tmpl -// and a rendered alertmanager config template usually used in -// subscription flow -func (c *Client) CreateAlertmanagerConfig(ctx context.Context, amConfigs AlertManagerConfig, tenantID string) error { - cfg, err := c.generateAlertmanagerConfig(amConfigs) - if err != nil { - return err - } - templates := map[string]string{ - "helper.tmpl": c.helperTemplate, - } - - err = c.cortexClient.CreateAlertmanagerConfig(NewContextWithTenantID(ctx, tenantID), cfg, templates) - if err != nil { - return fmt.Errorf("cortex client: %w", err) - } - return nil -} - -func (c *Client) generateAlertmanagerConfig(amConfigs AlertManagerConfig) (string, error) { - delims := template.New("alertmanagerConfigTemplate").Delims("[[", "]]") - parse, err := delims.Parse(c.configYaml) - if err != nil { - return "", err - } - var tpl bytes.Buffer - err = parse.Execute(&tpl, amConfigs) - if err != nil { - // it is unlikely that the code returns error here - return "", err - } - configStr := tpl.String() - _, err = promconfig.Load(configStr) - if err != nil { - return "", err - } - return configStr, nil -} - -// CreateRuleGroup creates a rule group in a namespace in a tenant -// in cortex ruler. this will replace the existing rule group if exist -func (c *Client) CreateRuleGroup(ctx context.Context, tenantID string, namespace string, rg rwrulefmt.RuleGroup) error { - err := c.cortexClient.CreateRuleGroup(NewContextWithTenantID(ctx, tenantID), namespace, rg) - if err != nil { - return fmt.Errorf("cortex client: %w", err) - } - return nil -} - -// DeleteRuleGroup removes a rule group in a namespace in a tenant -// in cortex ruler -func (c *Client) DeleteRuleGroup(ctx context.Context, tenantID, namespace, groupName string) error { - err := c.cortexClient.DeleteRuleGroup(NewContextWithTenantID(ctx, tenantID), namespace, groupName) - if err != nil { - return fmt.Errorf("cortex client: %w", err) - } - return nil -} - -// GetRuleGroup fetchs a rule group in a namespace in a tenant -// in cortex ruler -func (c *Client) GetRuleGroup(ctx context.Context, tenantID, namespace, groupName string) (*rwrulefmt.RuleGroup, error) { - results, err := c.cortexClient.GetRuleGroup(ctx, namespace, groupName) - if err != nil { - return nil, fmt.Errorf("cortex client: %w", err) - } - - return results, nil -} diff --git a/plugins/providers/cortex/client_test.go b/plugins/providers/cortex/client_test.go deleted file mode 100644 index a0bebf50..00000000 --- a/plugins/providers/cortex/client_test.go +++ /dev/null @@ -1,300 +0,0 @@ -package cortex_test - -import ( - "context" - "os" - "testing" - - "github.com/grafana/cortex-tools/pkg/rules/rwrulefmt" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/plugins/providers/cortex" - "github.com/odpf/siren/plugins/providers/cortex/mocks" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -func TestClient_New(t *testing.T) { - t.Run("should initiate cortex client if not passed from option", func(t *testing.T) { - c, err := cortex.NewClient(cortex.Config{}) - if err != nil { - t.Fatalf("got error %v, expected was nil", err) - } - if c == nil { - t.Fatalf("got client %v, expected was not nil", c) - } - }) - - t.Run("should return error when cortex client client creation return error", func(t *testing.T) { - c, err := cortex.NewClient(cortex.Config{ - Address: ":::", - }) - expectedErrorString := "parse \":::\": missing protocol scheme" - if err.Error() != expectedErrorString { - t.Fatalf("got error %v, expected was %v", err, expectedErrorString) - } - if c != nil { - t.Fatalf("got client %v, expected was nil", c) - } - }) -} - -func TestClient_CreateAlertmanagerConfig(t *testing.T) { - type testCase struct { - Description string - Setup func(*mocks.CortexCaller) *cortex.Client - AMConfig cortex.AlertManagerConfig - Err error - } - - var ( - tenantID = "123123" - amConfig = cortex.AlertManagerConfig{ - Receivers: []cortex.ReceiverConfig{ - { - Name: "config1", - Type: "slack", - Match: map[string]string{ - "foo": "bar"}, - Configurations: map[string]string{ - "token": "xoxb", - "channel_name": "test", - }, - }, - { - Name: "config2", - Type: "pagerduty", - Match: map[string]string{ - "bar": "baz", - }, - Configurations: map[string]string{ - "service_key": "1234", - }, - }, - { - Name: "config3", - Type: "http", - Match: map[string]string{}, - Configurations: map[string]string{ - "url": "http://localhost:3000", - }, - }, - }, - } - testCases = []testCase{ - { - Description: "return error if error parsing config yaml", - Setup: func(cc *mocks.CortexCaller) *cortex.Client { - c, err := cortex.NewClient(cortex.Config{}, - cortex.WithCortexClient(cc), - cortex.WithHelperTemplate("[[$", "")) - require.NoError(t, err) - return c - }, - Err: errors.New("template: alertmanagerConfigTemplate:1: unclosed action"), - }, - { - Description: "return error if CreateAlertmanagerConfig returns error", - Setup: func(cc *mocks.CortexCaller) *cortex.Client { - configYaml, err := os.ReadFile("./testdata/config.goyaml") - require.NoError(t, err) - helperTemplate, err := os.ReadFile("./testdata/helper.tmpl") - require.NoError(t, err) - expectedConfigYaml, err := os.ReadFile("./testdata/expected_config.yaml") - require.NoError(t, err) - - cc.EXPECT().CreateAlertmanagerConfig(mock.AnythingOfType("*context.valueCtx"), string(expectedConfigYaml), map[string]string{ - "helper.tmpl": string(helperTemplate), - }).Return(errors.New("some error")) - - c, err := cortex.NewClient(cortex.Config{}, - cortex.WithCortexClient(cc), - cortex.WithHelperTemplate(string(configYaml), string(helperTemplate))) - require.NoError(t, err) - - return c - }, - AMConfig: amConfig, - Err: errors.New("cortex client: some error"), - }, - { - Description: "return nil error if succeed", - Setup: func(cc *mocks.CortexCaller) *cortex.Client { - configYaml, err := os.ReadFile("./testdata/config.goyaml") - require.NoError(t, err) - helperTemplate, err := os.ReadFile("./testdata/helper.tmpl") - require.NoError(t, err) - expectedConfigYaml, err := os.ReadFile("./testdata/expected_config.yaml") - require.NoError(t, err) - - cc.EXPECT().CreateAlertmanagerConfig(mock.AnythingOfType("*context.valueCtx"), string(expectedConfigYaml), map[string]string{ - "helper.tmpl": string(helperTemplate), - }).Return(nil) - c, err := cortex.NewClient(cortex.Config{}, - cortex.WithCortexClient(cc), - cortex.WithHelperTemplate(string(configYaml), string(helperTemplate))) - require.NoError(t, err) - return c - }, - AMConfig: amConfig, - Err: nil, - }, - } - ) - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - mockCortexClient := new(mocks.CortexCaller) - - c := tc.Setup(mockCortexClient) - - err := c.CreateAlertmanagerConfig(context.TODO(), tc.AMConfig, tenantID) - if err != tc.Err { - if tc.Err.Error() != err.Error() { - t.Fatalf("got error %s, expected was %s", err, tc.Err) - } - } - }) - } - -} - -func TestClient_CreateRuleGroup(t *testing.T) { - var testTenantID = "odpf" - - t.Run("should return error if cortex client return error", func(t *testing.T) { - cortexCallerMock := &mocks.CortexCaller{} - - c, err := cortex.NewClient(cortex.Config{}, cortex.WithCortexClient(cortexCallerMock)) - require.Nil(t, err) - require.NotNil(t, c) - - cortexCallerMock.EXPECT().CreateRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("rwrulefmt.RuleGroup")).Return(errors.New("some error")) - - err = c.CreateRuleGroup(context.TODO(), testTenantID, "namespace", rwrulefmt.RuleGroup{}) - assert.NotNil(t, err) - - cortexCallerMock.AssertExpectations(t) - }) - - t.Run("should return nil error if cortex client return nil error", func(t *testing.T) { - cortexCallerMock := &mocks.CortexCaller{} - - c, err := cortex.NewClient(cortex.Config{}, cortex.WithCortexClient(cortexCallerMock)) - require.Nil(t, err) - require.NotNil(t, c) - - cortexCallerMock.EXPECT().CreateRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("rwrulefmt.RuleGroup")).Return(nil) - - err = c.CreateRuleGroup(context.TODO(), testTenantID, "namespace", rwrulefmt.RuleGroup{}) - assert.Nil(t, err) - - cortexCallerMock.AssertExpectations(t) - }) -} - -func TestClient_DeleteRuleGroup(t *testing.T) { - var testTenantID = "odpf" - - t.Run("should return error if cortex client return error", func(t *testing.T) { - cortexCallerMock := &mocks.CortexCaller{} - - c, err := cortex.NewClient(cortex.Config{}, cortex.WithCortexClient(cortexCallerMock)) - require.Nil(t, err) - require.NotNil(t, c) - - cortexCallerMock.EXPECT().DeleteRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(errors.New("some error")) - - err = c.DeleteRuleGroup(context.TODO(), testTenantID, "namespace", "groupname") - assert.NotNil(t, err) - - cortexCallerMock.AssertExpectations(t) - }) - - t.Run("should return nil error if cortex client return nil error", func(t *testing.T) { - cortexCallerMock := &mocks.CortexCaller{} - - c, err := cortex.NewClient(cortex.Config{}, cortex.WithCortexClient(cortexCallerMock)) - require.Nil(t, err) - require.NotNil(t, c) - - cortexCallerMock.EXPECT().DeleteRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil) - - err = c.DeleteRuleGroup(context.TODO(), testTenantID, "namespace", "groupname") - assert.Nil(t, err) - - cortexCallerMock.AssertExpectations(t) - }) -} - -func TestClient_GetRuleGroup(t *testing.T) { - var testTenantID = "odpf" - - t.Run("should return error if cortex client return error", func(t *testing.T) { - cortexCallerMock := &mocks.CortexCaller{} - - c, err := cortex.NewClient(cortex.Config{}, cortex.WithCortexClient(cortexCallerMock)) - require.Nil(t, err) - require.NotNil(t, c) - - cortexCallerMock.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil, errors.New("some error")) - - rg, err := c.GetRuleGroup(context.TODO(), testTenantID, "namespace", "groupname") - assert.NotNil(t, err) - assert.Nil(t, rg) - - cortexCallerMock.AssertExpectations(t) - }) - - t.Run("should return nil error if cortex client return nil error", func(t *testing.T) { - cortexCallerMock := &mocks.CortexCaller{} - - c, err := cortex.NewClient(cortex.Config{}, cortex.WithCortexClient(cortexCallerMock)) - require.Nil(t, err) - require.NotNil(t, c) - - cortexCallerMock.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&rwrulefmt.RuleGroup{}, nil) - - rg, err := c.GetRuleGroup(context.TODO(), testTenantID, "namespace", "groupname") - assert.Nil(t, err) - assert.NotNil(t, rg) - - cortexCallerMock.AssertExpectations(t) - }) -} - -// func TestClient_ListRules(t *testing.T) { -// var testTenantID = "odpf" - -// t.Run("should return error if cortex client return error", func(t *testing.T) { -// cortexCallerMock := &mocks.CortexCaller{} - -// c, err := cortex.NewClient(cortex.Config{}, cortex.WithCortexClient(cortexCallerMock)) -// require.Nil(t, err) -// require.NotNil(t, c) - -// cortexCallerMock.EXPECT().ListRules(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string")).Return(nil, errors.New("some error")) - -// rg, err := c.ListRules(context.TODO(), testTenantID, "namespace") -// assert.NotNil(t, err) -// assert.Nil(t, rg) - -// cortexCallerMock.AssertExpectations(t) -// }) - -// t.Run("should return nil error if cortex client return nil error", func(t *testing.T) { -// cortexCallerMock := &mocks.CortexCaller{} - -// c, err := cortex.NewClient(cortex.Config{}, cortex.WithCortexClient(cortexCallerMock)) -// require.Nil(t, err) -// require.NotNil(t, c) - -// cortexCallerMock.EXPECT().ListRules(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string")).Return(map[string][]rwrulefmt.RuleGroup{}, nil) - -// rg, err := c.ListRules(context.TODO(), testTenantID, "namespace") -// assert.Nil(t, err) -// assert.NotNil(t, rg) - -// cortexCallerMock.AssertExpectations(t) -// }) -// } diff --git a/plugins/providers/cortex/config.go b/plugins/providers/cortex/config.go index abc47688..23e6b45d 100644 --- a/plugins/providers/cortex/config.go +++ b/plugins/providers/cortex/config.go @@ -9,7 +9,12 @@ var ( ConfigYamlString string ) -// Config is a cortex provider config -type Config struct { - Address string `mapstructure:"address" default:"http://localhost:9009"` +type AppConfig struct { + GroupWaitDuration string `mapstructure:"group_wait" yaml:"group_wait" default:"30s"` + WebhookBaseAPI string `mapstructure:"webhook_base_api" yaml:"webhook_base_api" default:"http://localhost:8080/v1beta1/alerts/cortex"` +} + +type TemplateConfig struct { + GroupWaitDuration string + WebhookURL string } diff --git a/plugins/providers/cortex/config/config.goyaml b/plugins/providers/cortex/config/config.goyaml index e922a720..884d9682 100644 --- a/plugins/providers/cortex/config/config.goyaml +++ b/plugins/providers/cortex/config/config.goyaml @@ -6,40 +6,10 @@ global: slack_api_url: https://slack.com/api/chat.postMessage receivers: - name: default -[[- range $key, $receiver := .Receivers -]] -[[if eq $receiver.Type "slack"]] - - name: [[$receiver.Type]]_[[$receiver.Name]] - slack_configs: - - channel: '[[index $receiver.Configurations "channel_name"]]' - http_config: - bearer_token: '[[index $receiver.Configurations "token"]]' - icon_emoji: ':eagle:' - link_names: false - send_resolved: true - color: '{{ template "slack.color" . }}' - title: '' - pretext: '{{template "slack.pretext" . }}' - text: '{{ template "slack.body" . }}' - actions: - - type: button - text: 'Runbook :books:' - url: '{{template "slack.runbook" . }}' - - type: button - text: 'Dashboard :bar_chart:' - url: '{{template "slack.dashboard" . }}' -[[- end -]] -[[- if eq $receiver.Type "pagerduty"]] - - name: [[$receiver.Type]]_[[$receiver.Name]] - pagerduty_configs: - - service_key: '[[index $receiver.Configurations "service_key"]]' -[[- end -]] -[[- if eq $receiver.Type "http"]] - - name: [[$receiver.Type]]_[[$receiver.Name]] webhook_configs: - - url: '[[index $receiver.Configurations "url"]]' -[[- end -]] -[[- end]] + - url: '[[.WebhookURL]]' route: + receiver: default group_by: - alertname - severity @@ -47,18 +17,6 @@ route: - service_name - time_stamp - identifier - group_wait: 30s + group_wait: [[.GroupWaitDuration]] group_interval: 30m - repeat_interval: 4h - receiver: default - routes: - [[- range $key, $receiver := .Receivers]] - - receiver: [[$receiver.Type]]_[[$receiver.Name]] - [[- if gt (len $receiver.Match) 0 ]] - match: - [[- range $key, $value := $receiver.Match ]] - [[ $key ]]: [[ $value ]] - [[- end]] - [[- end ]] - continue: true - [[- end -]] \ No newline at end of file + repeat_interval: 4h \ No newline at end of file diff --git a/plugins/providers/cortex/context.go b/plugins/providers/cortex/context.go index d92ef0c8..bb69f028 100644 --- a/plugins/providers/cortex/context.go +++ b/plugins/providers/cortex/context.go @@ -12,13 +12,13 @@ var ( // NewContextWithTenantID returns a new context.Context // that carries the provided tenant ID. -func NewContextWithTenantID(ctx context.Context, tenantId string) context.Context { +func newContextWithTenantID(ctx context.Context, tenantId string) context.Context { return context.WithValue(ctx, tenantContextKey, tenantId) } -// TenantIDFromContext returns the tenant ID from the context +// tenantIDFromContext returns the tenant ID from the context // if present, and empty otherwise. -func TenantIDFromContext(ctx context.Context) string { +func tenantIDFromContext(ctx context.Context) string { if t, ok := ctx.Value(tenantContextKey).(string); ok { return t } diff --git a/plugins/providers/cortex/context_test.go b/plugins/providers/cortex/context_test.go index c3a6aacb..c84c48e3 100644 --- a/plugins/providers/cortex/context_test.go +++ b/plugins/providers/cortex/context_test.go @@ -1,19 +1,18 @@ -package cortex_test +package cortex import ( "context" "testing" "github.com/google/go-cmp/cmp" - "github.com/odpf/siren/plugins/providers/cortex" ) func TestContext(t *testing.T) { t.Run("should return passed tenant id if exist in context", func(t *testing.T) { var ( passedTenantID = "some-tenant-id" - ctx = cortex.NewContextWithTenantID(context.Background(), passedTenantID) - actualTenantID = cortex.TenantIDFromContext(ctx) + ctx = newContextWithTenantID(context.Background(), passedTenantID) + actualTenantID = tenantIDFromContext(ctx) ) if !cmp.Equal(passedTenantID, actualTenantID) { t.Fatalf("actual is \"%+v\" but expected was \"%+v\"", actualTenantID, passedTenantID) @@ -21,14 +20,14 @@ func TestContext(t *testing.T) { }) t.Run("should return empty tenant id if not exist in context", func(t *testing.T) { - actual := cortex.TenantIDFromContext(context.Background()) + actual := tenantIDFromContext(context.Background()) if actual != "" { t.Fatalf("actual is \"%+v\" but expected was \"%+v\"", actual, "") } }) t.Run("should return empty tenant id if context is nil", func(t *testing.T) { - actual := cortex.TenantIDFromContext(context.TODO()) + actual := tenantIDFromContext(context.TODO()) if actual != "" { t.Fatalf("actual is \"%+v\" but expected was \"%+v\"", actual, "") } diff --git a/plugins/providers/cortex/cortex.go b/plugins/providers/cortex/cortex.go deleted file mode 100644 index a96a3aa5..00000000 --- a/plugins/providers/cortex/cortex.go +++ /dev/null @@ -1,14 +0,0 @@ -package cortex - -// ReceiverConfig is a receiver configuration in cortex alertmanager format -type ReceiverConfig struct { - Name string - Type string - Match map[string]string - Configurations map[string]string -} - -// AlertManagerConfig is a placeholder to store cortex alertmanager format -type AlertManagerConfig struct { - Receivers []ReceiverConfig -} diff --git a/plugins/providers/cortex/mocks/cortex_client.go b/plugins/providers/cortex/mocks/cortex_client.go deleted file mode 100644 index 52320179..00000000 --- a/plugins/providers/cortex/mocks/cortex_client.go +++ /dev/null @@ -1,208 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - cortex "github.com/odpf/siren/plugins/providers/cortex" - mock "github.com/stretchr/testify/mock" - - rwrulefmt "github.com/grafana/cortex-tools/pkg/rules/rwrulefmt" -) - -// CortexClient is an autogenerated mock type for the CortexClient type -type CortexClient struct { - mock.Mock -} - -type CortexClient_Expecter struct { - mock *mock.Mock -} - -func (_m *CortexClient) EXPECT() *CortexClient_Expecter { - return &CortexClient_Expecter{mock: &_m.Mock} -} - -// CreateAlertmanagerConfig provides a mock function with given fields: _a0, _a1, _a2 -func (_m *CortexClient) CreateAlertmanagerConfig(_a0 context.Context, _a1 cortex.AlertManagerConfig, _a2 string) error { - ret := _m.Called(_a0, _a1, _a2) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, cortex.AlertManagerConfig, string) error); ok { - r0 = rf(_a0, _a1, _a2) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CortexClient_CreateAlertmanagerConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateAlertmanagerConfig' -type CortexClient_CreateAlertmanagerConfig_Call struct { - *mock.Call -} - -// CreateAlertmanagerConfig is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 cortex.AlertManagerConfig -// - _a2 string -func (_e *CortexClient_Expecter) CreateAlertmanagerConfig(_a0 interface{}, _a1 interface{}, _a2 interface{}) *CortexClient_CreateAlertmanagerConfig_Call { - return &CortexClient_CreateAlertmanagerConfig_Call{Call: _e.mock.On("CreateAlertmanagerConfig", _a0, _a1, _a2)} -} - -func (_c *CortexClient_CreateAlertmanagerConfig_Call) Run(run func(_a0 context.Context, _a1 cortex.AlertManagerConfig, _a2 string)) *CortexClient_CreateAlertmanagerConfig_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(cortex.AlertManagerConfig), args[2].(string)) - }) - return _c -} - -func (_c *CortexClient_CreateAlertmanagerConfig_Call) Return(_a0 error) *CortexClient_CreateAlertmanagerConfig_Call { - _c.Call.Return(_a0) - return _c -} - -// CreateRuleGroup provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *CortexClient) CreateRuleGroup(_a0 context.Context, _a1 string, _a2 string, _a3 rwrulefmt.RuleGroup) error { - ret := _m.Called(_a0, _a1, _a2, _a3) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, rwrulefmt.RuleGroup) error); ok { - r0 = rf(_a0, _a1, _a2, _a3) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CortexClient_CreateRuleGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateRuleGroup' -type CortexClient_CreateRuleGroup_Call struct { - *mock.Call -} - -// CreateRuleGroup is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -// - _a2 string -// - _a3 rwrulefmt.RuleGroup -func (_e *CortexClient_Expecter) CreateRuleGroup(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *CortexClient_CreateRuleGroup_Call { - return &CortexClient_CreateRuleGroup_Call{Call: _e.mock.On("CreateRuleGroup", _a0, _a1, _a2, _a3)} -} - -func (_c *CortexClient_CreateRuleGroup_Call) Run(run func(_a0 context.Context, _a1 string, _a2 string, _a3 rwrulefmt.RuleGroup)) *CortexClient_CreateRuleGroup_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(rwrulefmt.RuleGroup)) - }) - return _c -} - -func (_c *CortexClient_CreateRuleGroup_Call) Return(_a0 error) *CortexClient_CreateRuleGroup_Call { - _c.Call.Return(_a0) - return _c -} - -// DeleteRuleGroup provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *CortexClient) DeleteRuleGroup(_a0 context.Context, _a1 string, _a2 string, _a3 string) error { - ret := _m.Called(_a0, _a1, _a2, _a3) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok { - r0 = rf(_a0, _a1, _a2, _a3) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CortexClient_DeleteRuleGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteRuleGroup' -type CortexClient_DeleteRuleGroup_Call struct { - *mock.Call -} - -// DeleteRuleGroup is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -// - _a2 string -// - _a3 string -func (_e *CortexClient_Expecter) DeleteRuleGroup(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *CortexClient_DeleteRuleGroup_Call { - return &CortexClient_DeleteRuleGroup_Call{Call: _e.mock.On("DeleteRuleGroup", _a0, _a1, _a2, _a3)} -} - -func (_c *CortexClient_DeleteRuleGroup_Call) Run(run func(_a0 context.Context, _a1 string, _a2 string, _a3 string)) *CortexClient_DeleteRuleGroup_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string)) - }) - return _c -} - -func (_c *CortexClient_DeleteRuleGroup_Call) Return(_a0 error) *CortexClient_DeleteRuleGroup_Call { - _c.Call.Return(_a0) - return _c -} - -// GetRuleGroup provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *CortexClient) GetRuleGroup(_a0 context.Context, _a1 string, _a2 string, _a3 string) (*rwrulefmt.RuleGroup, error) { - ret := _m.Called(_a0, _a1, _a2, _a3) - - var r0 *rwrulefmt.RuleGroup - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *rwrulefmt.RuleGroup); ok { - r0 = rf(_a0, _a1, _a2, _a3) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*rwrulefmt.RuleGroup) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { - r1 = rf(_a0, _a1, _a2, _a3) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CortexClient_GetRuleGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRuleGroup' -type CortexClient_GetRuleGroup_Call struct { - *mock.Call -} - -// GetRuleGroup is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -// - _a2 string -// - _a3 string -func (_e *CortexClient_Expecter) GetRuleGroup(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *CortexClient_GetRuleGroup_Call { - return &CortexClient_GetRuleGroup_Call{Call: _e.mock.On("GetRuleGroup", _a0, _a1, _a2, _a3)} -} - -func (_c *CortexClient_GetRuleGroup_Call) Run(run func(_a0 context.Context, _a1 string, _a2 string, _a3 string)) *CortexClient_GetRuleGroup_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string)) - }) - return _c -} - -func (_c *CortexClient_GetRuleGroup_Call) Return(_a0 *rwrulefmt.RuleGroup, _a1 error) *CortexClient_GetRuleGroup_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -type mockConstructorTestingTNewCortexClient interface { - mock.TestingT - Cleanup(func()) -} - -// NewCortexClient creates a new instance of CortexClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewCortexClient(t mockConstructorTestingTNewCortexClient) *CortexClient { - mock := &CortexClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/plugins/providers/cortex/option.go b/plugins/providers/cortex/option.go index 3c9c5c1a..2da25c59 100644 --- a/plugins/providers/cortex/option.go +++ b/plugins/providers/cortex/option.go @@ -1,18 +1,10 @@ package cortex -type ClientOption func(*Client) - -// WithHelperTemplate assigns helper template and config yaml string -func WithHelperTemplate(configYaml, helperTemplate string) ClientOption { - return func(c *Client) { - c.configYaml = configYaml - c.helperTemplate = helperTemplate - } -} +type ServiceOption func(*PluginService) // WithCortexClient uses cortex-tools client passed in the argument -func WithCortexClient(cc CortexCaller) ClientOption { - return func(c *Client) { - c.cortexClient = cc +func WithCortexClient(cc CortexCaller) ServiceOption { + return func(so *PluginService) { + so.cortexClient = cc } } diff --git a/plugins/providers/cortex/rule_service.go b/plugins/providers/cortex/rule_service.go deleted file mode 100644 index 47ed35dd..00000000 --- a/plugins/providers/cortex/rule_service.go +++ /dev/null @@ -1,101 +0,0 @@ -package cortex - -import ( - "context" - "fmt" - - "github.com/grafana/cortex-tools/pkg/rules/rwrulefmt" - "github.com/odpf/siren/core/rule" - "github.com/odpf/siren/core/template" - "github.com/odpf/siren/pkg/errors" - "github.com/prometheus/prometheus/pkg/rulefmt" - "gopkg.in/yaml.v3" -) - -// UpsertRule manages upsert logic to cortex ruler. Cortex client API granularity is on the rule-group. -// This function has a logic to work with rule-level granurality and adapt it to cortex logic. -func (s *CortexService) UpsertRule(ctx context.Context, rl *rule.Rule, templateToUpdate *template.Template, namespaceURN string) error { - inputValues := make(map[string]string) - for _, v := range rl.Variables { - inputValues[v.Name] = v.Value - } - - renderedRule, err := template.RenderWithEnrichedDefault(templateToUpdate.Body, templateToUpdate.Variables, inputValues) - if err != nil { - return err - } - - var upsertedRuleNodes []rulefmt.RuleNode - if err := yaml.Unmarshal([]byte(renderedRule), &upsertedRuleNodes); err != nil { - return errors.ErrInvalid.WithMsgf("cannot parse upserted rule").WithCausef(err.Error()) - } - - cortexRuleGroup, err := s.cortexClient.GetRuleGroup(ctx, namespaceURN, rl.Namespace, rl.GroupName) - if err != nil { - if err.Error() != "cortex client: requested resource not found" { - return errors.ErrInvalid.WithMsgf("cannot get rule group from cortex when upserting rules").WithCausef(err.Error()) - } - cortexRuleGroup = &rwrulefmt.RuleGroup{} - } - - newRuleNodes, err := mergeRuleNodes(cortexRuleGroup.Rules, upsertedRuleNodes, rl.Enabled) - if err != nil { - return err - } - - if len(newRuleNodes) == 0 { - if err := s.cortexClient.DeleteRuleGroup(ctx, namespaceURN, rl.Namespace, rl.GroupName); err != nil { - if err.Error() == "requested resource not found" { - return nil - } - return fmt.Errorf("error calling cortex: %w", err) - } - return nil - } - - cortexRuleGroup = &rwrulefmt.RuleGroup{ - RuleGroup: rulefmt.RuleGroup{ - Name: rl.GroupName, - Rules: newRuleNodes, - }, - } - if err := s.cortexClient.CreateRuleGroup(ctx, namespaceURN, rl.Namespace, *cortexRuleGroup); err != nil { - return fmt.Errorf("error calling cortex: %w", err) - } - return nil -} - -func mergeRuleNodes(ruleNodes []rulefmt.RuleNode, newRuleNodes []rulefmt.RuleNode, enabled bool) ([]rulefmt.RuleNode, error) { - for _, nrn := range newRuleNodes { - var action string = "insert" - var idxCount = 0 - for _, ruleNode := range ruleNodes { - if ruleNode.Alert.Value == nrn.Alert.Value { - if !enabled { - action = "delete" - break - } - action = "update" - break - } - idxCount++ - } - - switch action { - case "delete": - if idxCount >= len(ruleNodes) || idxCount < 0 { - return nil, errors.New("something wrong when comparing rule node") - } - ruleNodes = append(ruleNodes[:idxCount], ruleNodes[idxCount+1:]...) - case "update": - ruleNodes[idxCount] = nrn - default: - if !enabled { - return ruleNodes, nil - } - ruleNodes = append(ruleNodes, nrn) - } - } - - return ruleNodes, nil -} diff --git a/plugins/providers/cortex/rule_service_test.go b/plugins/providers/cortex/rule_service_test.go deleted file mode 100644 index abdb1f89..00000000 --- a/plugins/providers/cortex/rule_service_test.go +++ /dev/null @@ -1,209 +0,0 @@ -package cortex_test - -import ( - "context" - "testing" - - "github.com/MakeNowJust/heredoc" - "github.com/grafana/cortex-tools/pkg/rules/rwrulefmt" - "github.com/odpf/siren/core/rule" - "github.com/odpf/siren/core/template" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/plugins/providers/cortex" - "github.com/odpf/siren/plugins/providers/cortex/mocks" - "github.com/stretchr/testify/mock" -) - -func TestCortexService_UpsertRule(t *testing.T) { - var sampleTemplate = template.Template{ - Name: "my-template", - Body: heredoc.Doc(` -- alert: cpu high warning - expr: avg by (host, environment) (cpu_usage_user{cpu="cpu-total"}) > [[.warning]] - for: '[[.for]]' - labels: - alertname: CPU usage has been above [[.warning]] for last [[.for]] {{ $labels.host }} - environment: '{{ $labels.environment }}' - severity: WARNING - team: '[[.team]]' - annotations: - metric_name: cpu_usage_user - metric_value: '{{ printf "%0.2f" $value }}' - resource: '{{ $labels.host }}' - summary: CPU usage has been {{ printf "%0.2f" $value }} for last [[.for]] on host {{ $labels.host }} - template: cpu-usage -- alert: cpu high critical - expr: avg by (host, environment) (cpu_usage_user{cpu="cpu-total"}) > [[.critical]] - for: '[[.for]]' - labels: - alertname: CPU usage has been above [[.warning]] for last [[.for]] {{ $labels.host }} - environment: '{{ $labels.environment }}' - severity: CRITICAL - team: '[[.team]]' - annotations: - metric_name: cpu_usage_user - metric_value: '{{ printf "%0.2f" $value }}' - resource: '{{ $labels.host }}' - summary: CPU usage has been {{ printf "%0.2f" $value }} for last [[.for]] on host {{ $labels.host }} - template: cpu-usage`), - Variables: []template.Variable{ - { - Name: "for", - Type: "string", - Description: "For eg 5m, 2h; Golang duration format", - Default: "5m", - }, - { - Name: "warning", - Type: "int", - Default: "85", - }, - { - Name: "critical", - Type: "int", - Default: "90", - }, - { - Name: "team", - Type: "string", - Description: "For eg team name which the alert should go to", - Default: "odpf-infra", - }, - }, - Tags: []string{"system"}, - } - - var sampleRule = rule.Rule{ - Name: "siren_api_provider-urn_namespace-urn_system_cpu-usage_cpu-usage", - Namespace: "system", - GroupName: "cpu-usage", - Template: "cpu-usage", - Enabled: true, - Variables: []rule.RuleVariable{ - { - Name: "for", - Value: "5m", - }, - { - Name: "warning", - Value: "85", - }, - { - Name: "critical", - Value: "90", - }, - { - Name: "team", - Value: "odpf-infra", - }, - }, - ProviderNamespace: 1, - } - - type args struct { - rl *rule.Rule - templateToUpdate *template.Template - namespaceURN string - } - tests := []struct { - name string - setup func(*mocks.CortexClient) - args args - err error - }{ - { - name: "should return error if cannot render the rule and template", - setup: func(cc *mocks.CortexClient) {}, - args: args{ - rl: &rule.Rule{}, - templateToUpdate: func() *template.Template { - copiedTemplate := template.Template{} - copiedTemplate.Body = "[[x" - return &copiedTemplate - }(), - namespaceURN: "odpf", - }, - err: errors.New("template: parser:1: function \"x\" not defined"), - }, - { - name: "should return error if cannot cannot parse rendered rule to RuleNode", - setup: func(cc *mocks.CortexClient) {}, - args: args{ - rl: &sampleRule, - templateToUpdate: func() *template.Template { - copiedTemplate := sampleTemplate - copiedTemplate.Body = "name: a" - return &copiedTemplate - }(), - namespaceURN: "odpf", - }, - err: errors.New("cannot parse upserted rule"), - }, - { - name: "should return error if getting rule group from cortex return error", - setup: func(cc *mocks.CortexClient) { - cc.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil, errors.New("some error")) - }, - args: args{ - rl: &sampleRule, - templateToUpdate: &sampleTemplate, - namespaceURN: "odpf", - }, - err: errors.New("cannot get rule group from cortex when upserting rules"), - }, - { - name: "should return error if merge rule nodes return empty and delete rule group return error", - setup: func(cc *mocks.CortexClient) { - cc.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&rwrulefmt.RuleGroup{}, nil) - cc.EXPECT().DeleteRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(errors.New("some error")) - }, - args: args{ - rl: func() *rule.Rule { - copiedRule := sampleRule - copiedRule.Enabled = false - return &copiedRule - }(), - templateToUpdate: &sampleTemplate, - namespaceURN: "odpf", - }, - err: errors.New("error calling cortex: some error"), - }, - { - name: "should return nil if create rule group return error", - setup: func(cc *mocks.CortexClient) { - cc.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&rwrulefmt.RuleGroup{}, nil) - cc.EXPECT().CreateRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("rwrulefmt.RuleGroup")).Return(errors.New("some error")) - }, - args: args{ - rl: &sampleRule, - templateToUpdate: &sampleTemplate, - namespaceURN: "odpf", - }, - err: errors.New("error calling cortex: some error"), - }, - { - name: "should return nil if create rule group return no error", - setup: func(cc *mocks.CortexClient) { - cc.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&rwrulefmt.RuleGroup{}, nil) - cc.EXPECT().CreateRuleGroup(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("rwrulefmt.RuleGroup")).Return(nil) - }, - args: args{ - rl: &sampleRule, - templateToUpdate: &sampleTemplate, - namespaceURN: "odpf", - }, - err: nil, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - mockCortexClient := new(mocks.CortexClient) - tc.setup(mockCortexClient) - s := cortex.NewProviderService(mockCortexClient) - err := s.UpsertRule(context.Background(), tc.args.rl, tc.args.templateToUpdate, tc.args.namespaceURN) - if err != nil && tc.err.Error() != err.Error() { - t.Fatalf("got error %s, expected was %s", err.Error(), tc.err) - } - }) - } -} diff --git a/plugins/providers/cortex/service.go b/plugins/providers/cortex/service.go index 0e3a7125..57007847 100644 --- a/plugins/providers/cortex/service.go +++ b/plugins/providers/cortex/service.go @@ -1,71 +1,279 @@ package cortex import ( + "bytes" "context" "fmt" + texttemplate "text/template" + "time" + "github.com/grafana/cortex-tools/pkg/client" "github.com/grafana/cortex-tools/pkg/rules/rwrulefmt" - "github.com/odpf/siren/core/subscription" + "github.com/mitchellh/mapstructure" + "github.com/odpf/salt/log" + "github.com/odpf/siren/core/alert" + "github.com/odpf/siren/core/provider" + "github.com/odpf/siren/core/rule" + "github.com/odpf/siren/core/template" + "github.com/odpf/siren/pkg/errors" + "github.com/odpf/siren/plugins/receivers/base" + promconfig "github.com/prometheus/alertmanager/config" + "github.com/prometheus/prometheus/pkg/rulefmt" + "gopkg.in/yaml.v3" ) -//go:generate mockery --name=CortexClient -r --case underscore --with-expecter --structname CortexClient --filename cortex_client.go --output=./mocks -type CortexClient interface { - CreateAlertmanagerConfig(context.Context, AlertManagerConfig, string) error - CreateRuleGroup(context.Context, string, string, rwrulefmt.RuleGroup) error - DeleteRuleGroup(context.Context, string, string, string) error - GetRuleGroup(context.Context, string, string, string) (*rwrulefmt.RuleGroup, error) +//go:generate mockery --name=CortexCaller -r --case underscore --with-expecter --structname CortexCaller --filename cortex_caller.go --output=./mocks +type CortexCaller interface { + CreateAlertmanagerConfig(ctx context.Context, cfg string, templates map[string]string) error + CreateRuleGroup(ctx context.Context, namespace string, rg rwrulefmt.RuleGroup) error + DeleteRuleGroup(ctx context.Context, namespace, groupName string) error + GetRuleGroup(ctx context.Context, namespace, groupName string) (*rwrulefmt.RuleGroup, error) + ListRules(ctx context.Context, namespace string) (map[string][]rwrulefmt.RuleGroup, error) } -// CortexService is a service layer of cortex provider plugin -type CortexService struct { - cortexClient CortexClient +// Service is a service layer of cortex provider plugin +type PluginService struct { + base.UnimplementedService + + logger log.Logger + appConfig AppConfig + helperTemplate string + configYaml string + cortexClient CortexCaller } -// NewProviderService returns cortex service provider plugin struct -func NewProviderService(cortexClient CortexClient) *CortexService { - return &CortexService{ - cortexClient: cortexClient, +// NewPluginService returns cortex service provider plugin struct +func NewPluginService(logger log.Logger, appConfig AppConfig, opts ...ServiceOption) *PluginService { + s := &PluginService{ + logger: logger, + appConfig: appConfig, + helperTemplate: HelperTemplateString, + configYaml: ConfigYamlString, + } + + for _, opt := range opts { + opt(s) } + + return s } -// SyncSubscriptions dumps all subscriptions of a tenant as an alertmanager config to cortex -// namespaceURN is the tenant ID -func (s *CortexService) SyncSubscriptions(ctx context.Context, subscriptions []subscription.Subscription, namespaceURN string) error { - amConfig := make([]ReceiverConfig, 0) - for _, item := range subscriptions { - amConfig = append(amConfig, GetAlertManagerReceiverConfig(&item)...) +// TransformToAlerts is a function to transform alert body in hook API to []*alert.Alert +func (s *PluginService) TransformToAlerts(ctx context.Context, providerID uint64, body map[string]interface{}) ([]*alert.Alert, int, error) { + var groupAlert = &GroupAlert{} + if err := mapstructure.Decode(body, groupAlert); err != nil { + return nil, 0, err + } + + var ( + alerts = make([]*alert.Alert, 0) + badAlertCount = 0 + firingLen = 0 + ) + + for _, item := range groupAlert.Alerts { + + if item.Status == "firing" { + firingLen++ + } + + severity := item.Labels["severity"] + if item.Status == "resolved" { + severity = item.Status + } + + startsAt, err := time.Parse(time.RFC3339Nano, item.StartsAt) + if err != nil { + badAlertCount++ + break + } + + alrt := &alert.Alert{ + ProviderID: providerID, + ResourceName: item.Annotations["resource"], + MetricName: item.Annotations["metricName"], + MetricValue: item.Annotations["metricValue"], + Severity: severity, + Rule: item.Annotations["template"], + TriggeredAt: startsAt, + + GroupKey: groupAlert.GroupKey, + Status: item.Status, + Annotations: item.Annotations, + Labels: item.Labels, + GeneratorURL: item.GeneratorURL, + Fingerprint: item.Fingerprint, + } + if !isValidCortexAlert(alrt) { + badAlertCount++ + continue + } + alerts = append(alerts, alrt) } - if err := s.cortexClient.CreateAlertmanagerConfig(ctx, AlertManagerConfig{ - Receivers: amConfig, - }, namespaceURN); err != nil { + if badAlertCount > 0 { + s.logger.Error("parameters are missing for alert", "alert count", badAlertCount) + return alerts, firingLen, nil + } + + return alerts, firingLen, nil +} + +// SyncRuntimeConfig synchronizes runtime configuration of provider +func (s *PluginService) SyncRuntimeConfig(ctx context.Context, namespaceURN string, prov provider.Provider) error { + if s.appConfig.WebhookBaseAPI == "" { + return errors.New("Cortex webhook base api string in config cannot be empty") + } + + webhookURL := fmt.Sprintf("%s/%d", s.appConfig.WebhookBaseAPI, prov.ID) + + tmplConfig := TemplateConfig{ + GroupWaitDuration: s.appConfig.GroupWaitDuration, + WebhookURL: webhookURL, + } + + cfg, err := s.generateAlertmanagerConfig(tmplConfig) + if err != nil { + return err + } + templates := map[string]string{ + "helper.tmpl": s.helperTemplate, + } + + cortexClient, err := s.getCortexClient(prov.Host) + if err != nil { return err } + if err = cortexClient.CreateAlertmanagerConfig(newContextWithTenantID(ctx, namespaceURN), cfg, templates); err != nil { + return err + } return nil } -// GetAlertManagerReceiverConfig transforms subscription to list of receiver in cortex receiver config format -func GetAlertManagerReceiverConfig(subs *subscription.Subscription) []ReceiverConfig { - if subs == nil { +// UpsertRule manages upsert logic to cortex ruler. Cortex client API granularity is on the rule-group. +// This function has a logic to work with rule-level granurality and adapt it to cortex logic. +func (s *PluginService) UpsertRule(ctx context.Context, namespaceURN string, prov provider.Provider, rl *rule.Rule, templateToUpdate *template.Template) error { + inputValues := make(map[string]string) + for _, v := range rl.Variables { + inputValues[v.Name] = v.Value + } + + renderedRule, err := template.RenderWithEnrichedDefault(templateToUpdate.Body, templateToUpdate.Variables, inputValues) + if err != nil { + return err + } + + cortexClient, err := s.getCortexClient(prov.Host) + if err != nil { + return err + } + + var upsertedRuleNodes []rulefmt.RuleNode + if err := yaml.Unmarshal([]byte(renderedRule), &upsertedRuleNodes); err != nil { + return errors.ErrInvalid.WithMsgf("cannot parse upserted rule").WithCausef(err.Error()) + } + + cortexRuleGroup, err := cortexClient.GetRuleGroup(newContextWithTenantID(ctx, namespaceURN), rl.Namespace, rl.GroupName) + if err != nil { + if errors.Is(err, client.ErrResourceNotFound) { + cortexRuleGroup = &rwrulefmt.RuleGroup{} + } else { + return errors.ErrInvalid.WithMsgf("cannot get rule group from cortex when upserting rules").WithCausef(err.Error()) + } + } + + newRuleNodes, err := mergeRuleNodes(cortexRuleGroup.Rules, upsertedRuleNodes, rl.Enabled) + if err != nil { + return err + } + + if len(newRuleNodes) == 0 { + if err := cortexClient.DeleteRuleGroup(newContextWithTenantID(ctx, namespaceURN), rl.Namespace, rl.GroupName); err != nil { + if err.Error() == "requested resource not found" { + return nil + } + return fmt.Errorf("error calling cortex: %w", err) + } return nil } - amReceiverConfig := make([]ReceiverConfig, 0) - for idx, item := range subs.Receivers { - configMapString := make(map[string]string) - for key, value := range item.Configuration { - strKey := fmt.Sprintf("%v", key) - strValue := fmt.Sprintf("%v", value) - configMapString[strKey] = strValue + cortexRuleGroup = &rwrulefmt.RuleGroup{ + RuleGroup: rulefmt.RuleGroup{ + Name: rl.GroupName, + Rules: newRuleNodes, + }, + } + if err := cortexClient.CreateRuleGroup(newContextWithTenantID(ctx, namespaceURN), rl.Namespace, *cortexRuleGroup); err != nil { + return fmt.Errorf("error calling cortex: %w", err) + } + return nil +} + +func mergeRuleNodes(ruleNodes []rulefmt.RuleNode, newRuleNodes []rulefmt.RuleNode, enabled bool) ([]rulefmt.RuleNode, error) { + for _, nrn := range newRuleNodes { + var action string = "insert" + var idxCount = 0 + for _, ruleNode := range ruleNodes { + if ruleNode.Alert.Value == nrn.Alert.Value { + if !enabled { + action = "delete" + break + } + action = "update" + break + } + idxCount++ } - newAMReceiver := ReceiverConfig{ - Name: fmt.Sprintf("%s_receiverId_%d_idx_%d", subs.URN, item.ID, idx), - Match: subs.Match, - Configurations: configMapString, - Type: item.Type, + + switch action { + case "delete": + if idxCount >= len(ruleNodes) || idxCount < 0 { + return nil, errors.New("something wrong when comparing rule node") + } + ruleNodes = append(ruleNodes[:idxCount], ruleNodes[idxCount+1:]...) + case "update": + ruleNodes[idxCount] = nrn + default: + if !enabled { + return ruleNodes, nil + } + ruleNodes = append(ruleNodes, nrn) } - amReceiverConfig = append(amReceiverConfig, newAMReceiver) } - return amReceiverConfig + + return ruleNodes, nil +} + +func (s *PluginService) generateAlertmanagerConfig(tmplConfig TemplateConfig) (string, error) { + delims := texttemplate.New("alertmanagerConfigTemplate").Delims("[[", "]]") + parse, err := delims.Parse(s.configYaml) + if err != nil { + return "", err + } + var tpl bytes.Buffer + err = parse.Execute(&tpl, tmplConfig) + if err != nil { + // it is unlikely that the code returns error here + return "", err + } + configStr := tpl.String() + _, err = promconfig.Load(configStr) + if err != nil { + return "", err + } + return configStr, nil +} + +func (s *PluginService) getCortexClient(address string) (CortexCaller, error) { + if s.cortexClient != nil { + return s.cortexClient, nil + } + cortexClient, err := client.New(client.Config{ + Address: address, + }) + if err != nil { + return nil, err + } + return cortexClient, nil } diff --git a/plugins/providers/cortex/service_test.go b/plugins/providers/cortex/service_test.go index 9afdf29b..da4997d5 100644 --- a/plugins/providers/cortex/service_test.go +++ b/plugins/providers/cortex/service_test.go @@ -1,129 +1,211 @@ package cortex_test import ( + "context" "testing" - "github.com/google/go-cmp/cmp" - "github.com/odpf/siren/core/receiver" - "github.com/odpf/siren/core/subscription" + "github.com/MakeNowJust/heredoc" + "github.com/grafana/cortex-tools/pkg/rules/rwrulefmt" + "github.com/odpf/salt/log" + "github.com/odpf/siren/core/provider" + "github.com/odpf/siren/core/rule" + "github.com/odpf/siren/core/template" + "github.com/odpf/siren/pkg/errors" "github.com/odpf/siren/plugins/providers/cortex" + "github.com/odpf/siren/plugins/providers/cortex/mocks" + "github.com/stretchr/testify/mock" ) -func TestGetAlertManagerReceiverConfig(t *testing.T) { - type testCase struct { - Description string - Subscription *subscription.Subscription - ExpectedAMReceiverConfig []cortex.ReceiverConfig +func TestService_UpsertRule(t *testing.T) { + var sampleTemplate = template.Template{ + Name: "my-template", + Body: heredoc.Doc(` +- alert: cpu high warning + expr: avg by (host, environment) (cpu_usage_user{cpu="cpu-total"}) > [[.warning]] + for: '[[.for]]' + labels: + alertname: CPU usage has been above [[.warning]] for last [[.for]] {{ $labels.host }} + environment: '{{ $labels.environment }}' + severity: WARNING + team: '[[.team]]' + annotations: + metric_name: cpu_usage_user + metric_value: '{{ printf "%0.2f" $value }}' + resource: '{{ $labels.host }}' + summary: CPU usage has been {{ printf "%0.2f" $value }} for last [[.for]] on host {{ $labels.host }} + template: cpu-usage +- alert: cpu high critical + expr: avg by (host, environment) (cpu_usage_user{cpu="cpu-total"}) > [[.critical]] + for: '[[.for]]' + labels: + alertname: CPU usage has been above [[.warning]] for last [[.for]] {{ $labels.host }} + environment: '{{ $labels.environment }}' + severity: CRITICAL + team: '[[.team]]' + annotations: + metric_name: cpu_usage_user + metric_value: '{{ printf "%0.2f" $value }}' + resource: '{{ $labels.host }}' + summary: CPU usage has been {{ printf "%0.2f" $value }} for last [[.for]] on host {{ $labels.host }} + template: cpu-usage`), + Variables: []template.Variable{ + { + Name: "for", + Type: "string", + Description: "For eg 5m, 2h; Golang duration format", + Default: "5m", + }, + { + Name: "warning", + Type: "int", + Default: "85", + }, + { + Name: "critical", + Type: "int", + Default: "90", + }, + { + Name: "team", + Type: "string", + Description: "For eg team name which the alert should go to", + Default: "odpf-infra", + }, + }, + Tags: []string{"system"}, } - var testCases = []testCase{ + var sampleRule = rule.Rule{ + Name: "siren_api_provider-urn_namespace-urn_system_cpu-usage_cpu-usage", + Namespace: "system", + GroupName: "cpu-usage", + Template: "cpu-usage", + Enabled: true, + Variables: []rule.RuleVariable{ + { + Name: "for", + Value: "5m", + }, + { + Name: "warning", + Value: "85", + }, + { + Name: "critical", + Value: "90", + }, + { + Name: "team", + Value: "odpf-infra", + }, + }, + ProviderNamespace: 1, + } + + type args struct { + rl *rule.Rule + templateToUpdate *template.Template + namespaceURN string + } + tests := []struct { + name string + setup func(*mocks.CortexCaller) + args args + err error + }{ { - Description: "should return nil if subscription is nil", + name: "should return error if cannot render the rule and template", + setup: func(cc *mocks.CortexCaller) {}, + args: args{ + rl: &rule.Rule{}, + templateToUpdate: func() *template.Template { + copiedTemplate := template.Template{} + copiedTemplate.Body = "[[x" + return &copiedTemplate + }(), + namespaceURN: "odpf", + }, + err: errors.New("template: parser:1: function \"x\" not defined"), }, { - Description: "should build am receiver configs properly", - Subscription: &subscription.Subscription{ - Receivers: []subscription.Receiver{ - { - ID: 5, - Type: receiver.TypeHTTP, - Configuration: map[string]interface{}{ - "url": "http://webhook", - }, - }, - { - ID: 7, - Type: receiver.TypeSlack, - Configuration: map[string]interface{}{ - "channel_name": "odpf-channel", - "token": "123123123", - }, - }, - { - ID: 9, - Type: receiver.TypeSlack, - Configuration: map[string]interface{}{ - "channel_name": "odpf-channel", - "token": "123123123", - }, - }, - { - ID: 10, - Type: receiver.TypePagerDuty, - Configuration: map[string]interface{}{ - "service_key": "a-service-key", - }, - }, - }, - Match: map[string]string{ - "label1": "value1", - "label2": "value2", - "label3": "value3", - }, - }, - ExpectedAMReceiverConfig: []cortex.ReceiverConfig{ - { - Name: "_receiverId_5_idx_0", - Type: receiver.TypeHTTP, - Match: map[string]string{ - "label1": "value1", - "label2": "value2", - "label3": "value3", - }, - Configurations: map[string]string{ - "url": "http://webhook", - }, - }, - { - Name: "_receiverId_7_idx_1", - Type: receiver.TypeSlack, - - Match: map[string]string{ - "label1": "value1", - "label2": "value2", - "label3": "value3", - }, - Configurations: map[string]string{ - "channel_name": "odpf-channel", - "token": "123123123", - }, - }, - { - Name: "_receiverId_9_idx_2", - Type: receiver.TypeSlack, - Match: map[string]string{ - "label1": "value1", - "label2": "value2", - "label3": "value3", - }, - Configurations: map[string]string{ - "channel_name": "odpf-channel", - "token": "123123123", - }, - }, - { - Name: "_receiverId_10_idx_3", - Type: receiver.TypePagerDuty, - Match: map[string]string{ - "label1": "value1", - "label2": "value2", - "label3": "value3", - }, - Configurations: map[string]string{ - "service_key": "a-service-key", - }, - }, + name: "should return error if cannot cannot parse rendered rule to RuleNode", + setup: func(cc *mocks.CortexCaller) {}, + args: args{ + rl: &sampleRule, + templateToUpdate: func() *template.Template { + copiedTemplate := sampleTemplate + copiedTemplate.Body = "name: a" + return &copiedTemplate + }(), + namespaceURN: "odpf", }, + err: errors.New("cannot parse upserted rule"), + }, + { + name: "should return error if getting rule group from cortex return error", + setup: func(cc *mocks.CortexCaller) { + cc.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil, errors.New("some error")) + }, + args: args{ + rl: &sampleRule, + templateToUpdate: &sampleTemplate, + namespaceURN: "odpf", + }, + err: errors.New("cannot get rule group from cortex when upserting rules"), + }, + { + name: "should return error if merge rule nodes return empty and delete rule group return error", + setup: func(cc *mocks.CortexCaller) { + cc.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&rwrulefmt.RuleGroup{}, nil) + cc.EXPECT().DeleteRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(errors.New("some error")) + }, + args: args{ + rl: func() *rule.Rule { + copiedRule := sampleRule + copiedRule.Enabled = false + return &copiedRule + }(), + templateToUpdate: &sampleTemplate, + namespaceURN: "odpf", + }, + err: errors.New("error calling cortex: some error"), + }, + { + name: "should return nil if create rule group return error", + setup: func(cc *mocks.CortexCaller) { + cc.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&rwrulefmt.RuleGroup{}, nil) + cc.EXPECT().CreateRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("rwrulefmt.RuleGroup")).Return(errors.New("some error")) + }, + args: args{ + rl: &sampleRule, + templateToUpdate: &sampleTemplate, + namespaceURN: "odpf", + }, + err: errors.New("error calling cortex: some error"), + }, + { + name: "should return nil if create rule group return no error", + setup: func(cc *mocks.CortexCaller) { + cc.EXPECT().GetRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&rwrulefmt.RuleGroup{}, nil) + cc.EXPECT().CreateRuleGroup(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("rwrulefmt.RuleGroup")).Return(nil) + }, + args: args{ + rl: &sampleRule, + templateToUpdate: &sampleTemplate, + namespaceURN: "odpf", + }, + err: nil, }, } - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - got := cortex.GetAlertManagerReceiverConfig(tc.Subscription) - if !cmp.Equal(got, tc.ExpectedAMReceiverConfig) { - t.Fatalf("got result %+v, expected was %+v", got, tc.ExpectedAMReceiverConfig) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + mockCortexClient := new(mocks.CortexCaller) + tc.setup(mockCortexClient) + s := cortex.NewPluginService(log.NewNoop(), cortex.AppConfig{}, cortex.WithCortexClient(mockCortexClient)) + err := s.UpsertRule(context.Background(), tc.args.namespaceURN, provider.Provider{}, tc.args.rl, tc.args.templateToUpdate) + if err != nil && tc.err.Error() != err.Error() { + t.Fatalf("got error %s, expected was %s", err.Error(), tc.err) } }) } - } diff --git a/plugins/providers/cortex/subscription_service.go b/plugins/providers/cortex/subscription_service.go deleted file mode 100644 index 6cd31040..00000000 --- a/plugins/providers/cortex/subscription_service.go +++ /dev/null @@ -1,22 +0,0 @@ -package cortex - -import ( - "context" - - "github.com/odpf/siren/core/subscription" -) - -// CreateSubscription is the abstraction to create a subscription called in core service -func (s *CortexService) CreateSubscription(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string) error { - return s.SyncSubscriptions(ctx, subscriptionsInNamespace, namespaceURN) -} - -// UpdateSubscription is the abstraction to update a subscription called in core service -func (s *CortexService) UpdateSubscription(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string) error { - return s.SyncSubscriptions(ctx, subscriptionsInNamespace, namespaceURN) -} - -// DeleteSubscription is the abstraction to remove a subscription called in core service -func (s *CortexService) DeleteSubscription(ctx context.Context, sub *subscription.Subscription, subscriptionsInNamespace []subscription.Subscription, namespaceURN string) error { - return s.SyncSubscriptions(ctx, subscriptionsInNamespace, namespaceURN) -} diff --git a/plugins/receivers/base/notification_service.go b/plugins/receivers/base/notification_service.go deleted file mode 100644 index b0617ffd..00000000 --- a/plugins/receivers/base/notification_service.go +++ /dev/null @@ -1,37 +0,0 @@ -package base - -import ( - "context" - - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/provider" - "github.com/odpf/siren/plugins" -) - -// UnimplementedNotificationService is a base notification plugin service layer for File webhook -type UnimplementedNotificationService struct{} - -func (s *UnimplementedNotificationService) ValidateConfigMap(notificationConfigMap map[string]interface{}) error { - return plugins.ErrNotImplemented -} - -func (s *UnimplementedNotificationService) Publish(ctx context.Context, notificationMessage notification.Message) (bool, error) { - return false, plugins.ErrNotImplemented -} - -func (s *UnimplementedNotificationService) PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { - return notificationConfigMap, nil -} - -func (s *UnimplementedNotificationService) PostHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { - return notificationConfigMap, nil -} - -func (s *UnimplementedNotificationService) DefaultTemplateOfProvider(providerType string) string { - switch providerType { - case provider.TypeCortex: - return "" - default: - return "" - } -} diff --git a/plugins/receivers/base/receiver_service.go b/plugins/receivers/base/receiver_service.go deleted file mode 100644 index d2cf6131..00000000 --- a/plugins/receivers/base/receiver_service.go +++ /dev/null @@ -1,26 +0,0 @@ -package base - -import ( - "context" - - "github.com/odpf/siren/plugins" -) - -// UnimplementedReceiverService is a base receiver plugin service layer for file -type UnimplementedReceiverService struct{} - -func (s *UnimplementedReceiverService) PreHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - return configurations, nil -} - -func (s *UnimplementedReceiverService) PostHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - return configurations, nil -} - -func (s *UnimplementedReceiverService) BuildData(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - return map[string]interface{}{}, nil -} - -func (s *UnimplementedReceiverService) BuildNotificationConfig(subsConfs map[string]interface{}, receiverConfs map[string]interface{}) (map[string]interface{}, error) { - return nil, plugins.ErrNotImplemented -} diff --git a/plugins/receivers/base/service.go b/plugins/receivers/base/service.go new file mode 100644 index 00000000..1cd01e01 --- /dev/null +++ b/plugins/receivers/base/service.go @@ -0,0 +1,39 @@ +package base + +import ( + "context" + + "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/plugins" +) + +// UnimplementedService is a base receiver plugin service layer +type UnimplementedService struct{} + +func (s *UnimplementedService) PreHookDBTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { + return configurations, nil +} + +func (s *UnimplementedService) PostHookDBTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { + return configurations, nil +} + +func (s *UnimplementedService) BuildData(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { + return map[string]interface{}{}, nil +} + +func (s *UnimplementedService) PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + return notificationConfigMap, nil +} + +func (s *UnimplementedService) PostHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + return notificationConfigMap, nil +} + +func (s *UnimplementedService) GetSystemDefaultTemplate() string { + return "" +} + +func (s *UnimplementedService) Send(ctx context.Context, notificationMessage notification.Message) (bool, error) { + return false, plugins.ErrNotImplemented +} diff --git a/plugins/receivers/file/receiver_service.go b/plugins/receivers/file/receiver_service.go deleted file mode 100644 index 17b5f7cd..00000000 --- a/plugins/receivers/file/receiver_service.go +++ /dev/null @@ -1,46 +0,0 @@ -package file - -import ( - "context" - "fmt" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/plugins/receivers/base" -) - -// ReceiverService is a receiver plugin service layer for file -type ReceiverService struct { - base.UnimplementedReceiverService -} - -// NewReceiverService returns file receiver service struct. This service implement [receiver.Resolver] interface. -func NewReceiverService() *ReceiverService { - return &ReceiverService{} -} - -func (s *ReceiverService) PreHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(configurations, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) - } - - if err := receiverConfig.Validate(); err != nil { - return nil, errors.ErrInvalid.WithMsgf(err.Error()) - } - - return configurations, nil -} - -func (s *ReceiverService) BuildNotificationConfig(subsConfs map[string]interface{}, receiverConfs map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(receiverConfs, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) - } - - notificationConfig := NotificationConfig{ - ReceiverConfig: *receiverConfig, - } - - return notificationConfig.AsMap(), nil -} diff --git a/plugins/receivers/file/receiver_service_test.go b/plugins/receivers/file/receiver_service_test.go deleted file mode 100644 index aaa5ea60..00000000 --- a/plugins/receivers/file/receiver_service_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package file_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/odpf/siren/plugins/receivers/file" -) - -func TestFileService_Functions(t *testing.T) { - t.Run("should return as-is if populate receiver is called", func(t *testing.T) { - svc := file.NewReceiverService() - - got, err := svc.BuildData(context.TODO(), make(map[string]interface{})) - - if err != nil { - t.Fatalf("got error %s, expected was nil", err.Error()) - } - - if !cmp.Equal(got, map[string]interface{}{}) { - t.Fatalf("got result %v, expected was %v", got, map[string]interface{}{}) - } - }) -} - -func TestFileService_GetSubscriptionConfig(t *testing.T) { - type testCase struct { - Description string - SubscriptionConfigs map[string]interface{} - ReceiverConfigs map[string]interface{} - ExpectedConfigMap map[string]interface{} - wantErr bool - } - - var ( - testCases = []testCase{ - { - Description: "should return error if receiver 'url' exist but it is not string", - ReceiverConfigs: map[string]interface{}{ - "url": 123, - }, - wantErr: true, - }, - { - Description: "should return configs with token if receiver 'url' exist in string", - ReceiverConfigs: map[string]interface{}{ - "url": "url", - }, - ExpectedConfigMap: map[string]interface{}{ - "url": "url", - }, - }, - } - ) - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - svc := file.NewReceiverService() - - got, err := svc.BuildNotificationConfig(tc.SubscriptionConfigs, tc.ReceiverConfigs) - if (err != nil) != tc.wantErr { - t.Errorf("got error = %v, wantErr %v", err, tc.wantErr) - } - if err == nil { - if !cmp.Equal(got, tc.ExpectedConfigMap) { - t.Errorf("got result %+v, expected was %+v", got, tc.ExpectedConfigMap) - } - } - }) - } -} diff --git a/plugins/receivers/file/notification_service.go b/plugins/receivers/file/service.go similarity index 56% rename from plugins/receivers/file/notification_service.go rename to plugins/receivers/file/service.go index 24c76358..21c2e397 100644 --- a/plugins/receivers/file/notification_service.go +++ b/plugins/receivers/file/service.go @@ -9,20 +9,46 @@ import ( "github.com/mitchellh/mapstructure" "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/pkg/errors" "github.com/odpf/siren/plugins/receivers/base" ) -// NotificationService is a notification plugin service layer for File webhook -type NotificationService struct { - base.UnimplementedNotificationService +type PluginService struct { + base.UnimplementedService } -// NewNotificationService returns Filereceiver service struct. This service implement [receiver.Notifier] interface. -func NewNotificationService() *NotificationService { - return &NotificationService{} +// NewPluginService returns file receiver service struct. This service implement [receiver.Resolver] and [notification.Notifier] interface. +func NewPluginService() *PluginService { + return &PluginService{} } -func (s *NotificationService) Publish(ctx context.Context, notificationMessage notification.Message) (bool, error) { +func (s *PluginService) PreHookDBTransformConfigs(ctx context.Context, receiverConfigMap map[string]interface{}) (map[string]interface{}, error) { + receiverConfig := &ReceiverConfig{} + if err := mapstructure.Decode(receiverConfigMap, receiverConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) + } + + if err := receiverConfig.Validate(); err != nil { + return nil, errors.ErrInvalid.WithMsgf(err.Error()) + } + + return receiverConfig.AsMap(), nil +} + +func (s *PluginService) PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) + } + + if err := notificationConfig.Validate(); err != nil { + return nil, err + } + + return notificationConfig.AsMap(), nil +} + +func (s *PluginService) Send(ctx context.Context, notificationMessage notification.Message) (bool, error) { notificationConfig := &NotificationConfig{} if err := mapstructure.Decode(notificationMessage.Configs, notificationConfig); err != nil { return false, err @@ -51,20 +77,7 @@ func (s *NotificationService) Publish(ctx context.Context, notificationMessage n return false, nil } -func (s *NotificationService) PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) - } - - if err := notificationConfig.Validate(); err != nil { - return nil, err - } - - return notificationConfig.AsMap(), nil -} - -func (s *NotificationService) validateFilePath(path string) error { +func (s *PluginService) validateFilePath(path string) error { dirs := strings.Split(path, "/") filename := dirs[len(dirs)-1] format := strings.Split(filename, ".") diff --git a/plugins/receivers/file/notification_service_test.go b/plugins/receivers/file/service_test.go similarity index 89% rename from plugins/receivers/file/notification_service_test.go rename to plugins/receivers/file/service_test.go index 22870a36..73de1a91 100644 --- a/plugins/receivers/file/notification_service_test.go +++ b/plugins/receivers/file/service_test.go @@ -8,7 +8,7 @@ import ( "github.com/odpf/siren/plugins/receivers/file" ) -func TestNotificationService_Publish(t *testing.T) { +func TestService_Send(t *testing.T) { tests := []struct { name string notificationMessage notification.Message @@ -50,9 +50,9 @@ func TestNotificationService_Publish(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - fr := file.NewNotificationService() + fr := file.NewPluginService() - got, err := fr.Publish(context.Background(), tt.notificationMessage) + got, err := fr.Send(context.Background(), tt.notificationMessage) if (err != nil) != tt.wantErr { t.Errorf("NotificationService.Publish() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/plugins/receivers/httpreceiver/client.go b/plugins/receivers/httpreceiver/client.go deleted file mode 100644 index 7b6f893d..00000000 --- a/plugins/receivers/httpreceiver/client.go +++ /dev/null @@ -1,95 +0,0 @@ -package httpreceiver - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "net/http" - - "github.com/odpf/salt/log" - "github.com/odpf/siren/pkg/httpclient" - "github.com/odpf/siren/pkg/retry" -) - -type ClientOption func(*Client) - -// ClientWithHTTPClient assigns custom client when creating a httpreceiver client -func ClientWithHTTPClient(cli *httpclient.Client) ClientOption { - return func(c *Client) { - c.httpClient = cli - } -} - -// ClientWithRetrier wraps client call with retrier -func ClientWithRetrier(runner retry.Runner) ClientOption { - return func(c *Client) { - c.retrier = runner - } -} - -type Client struct { - cfg AppConfig - logger log.Logger - httpClient *httpclient.Client - retrier retry.Runner -} - -func NewClient(logger log.Logger, cfg AppConfig, opts ...ClientOption) *Client { - c := &Client{ - cfg: cfg, - logger: logger, - } - - for _, opt := range opts { - opt(c) - } - - if c.httpClient == nil { - c.httpClient = httpclient.New(httpclient.Config{}) - } - - return c -} - -func (c *Client) Notify(ctx context.Context, apiURL string, body []byte) error { - if c.retrier != nil { - if err := c.retrier.Run(ctx, func(ctx context.Context) error { - return c.notify(ctx, apiURL, body) - }); err != nil { - return err - } - } - return c.notify(ctx, apiURL, body) -} - -func (c *Client) notify(ctx context.Context, apiURL string, body []byte) error { - req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(body)) - if err != nil { - return fmt.Errorf("failed to create request body: %w", err) - } - - resp, err := c.httpClient.HTTP().Do(req) - if err != nil { - return fmt.Errorf("failure in http call: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode == 429 || resp.StatusCode >= 500 { - return retry.RetryableError{Err: errors.New(http.StatusText(resp.StatusCode))} - } - - if resp.StatusCode >= 300 { - return errors.New(http.StatusText(resp.StatusCode)) - } else { - // Status code 2xx only - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - c.logger.Info("httpreceiver call success", "url", apiURL, "response", string(bodyBytes)) - } - - return nil -} diff --git a/plugins/receivers/httpreceiver/httpreceiver.go b/plugins/receivers/httpreceiver/httpreceiver.go deleted file mode 100644 index f32684d4..00000000 --- a/plugins/receivers/httpreceiver/httpreceiver.go +++ /dev/null @@ -1,8 +0,0 @@ -package httpreceiver - -import "context" - -//go:generate mockery --name=HTTPCaller -r --case underscore --with-expecter --structname HTTPCaller --filename http_caller.go --output=./mocks -type HTTPCaller interface { - Notify(ctx context.Context, apiURL string, body []byte) error -} diff --git a/plugins/receivers/httpreceiver/mocks/http_caller.go b/plugins/receivers/httpreceiver/mocks/http_caller.go deleted file mode 100644 index 6f198f16..00000000 --- a/plugins/receivers/httpreceiver/mocks/http_caller.go +++ /dev/null @@ -1,76 +0,0 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// HTTPCaller is an autogenerated mock type for the HTTPCaller type -type HTTPCaller struct { - mock.Mock -} - -type HTTPCaller_Expecter struct { - mock *mock.Mock -} - -func (_m *HTTPCaller) EXPECT() *HTTPCaller_Expecter { - return &HTTPCaller_Expecter{mock: &_m.Mock} -} - -// Notify provides a mock function with given fields: ctx, apiURL, body -func (_m *HTTPCaller) Notify(ctx context.Context, apiURL string, body []byte) error { - ret := _m.Called(ctx, apiURL, body) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, []byte) error); ok { - r0 = rf(ctx, apiURL, body) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// HTTPCaller_Notify_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Notify' -type HTTPCaller_Notify_Call struct { - *mock.Call -} - -// Notify is a helper method to define mock.On call -// - ctx context.Context -// - apiURL string -// - body []byte -func (_e *HTTPCaller_Expecter) Notify(ctx interface{}, apiURL interface{}, body interface{}) *HTTPCaller_Notify_Call { - return &HTTPCaller_Notify_Call{Call: _e.mock.On("Notify", ctx, apiURL, body)} -} - -func (_c *HTTPCaller_Notify_Call) Run(run func(ctx context.Context, apiURL string, body []byte)) *HTTPCaller_Notify_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].([]byte)) - }) - return _c -} - -func (_c *HTTPCaller_Notify_Call) Return(_a0 error) *HTTPCaller_Notify_Call { - _c.Call.Return(_a0) - return _c -} - -type mockConstructorTestingTNewHTTPCaller interface { - mock.TestingT - Cleanup(func()) -} - -// NewHTTPCaller creates a new instance of HTTPCaller. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewHTTPCaller(t mockConstructorTestingTNewHTTPCaller) *HTTPCaller { - mock := &HTTPCaller{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/plugins/receivers/httpreceiver/notification_service.go b/plugins/receivers/httpreceiver/notification_service.go deleted file mode 100644 index 51ef20b8..00000000 --- a/plugins/receivers/httpreceiver/notification_service.go +++ /dev/null @@ -1,61 +0,0 @@ -package httpreceiver - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/pkg/retry" - "github.com/odpf/siren/plugins/receivers/base" -) - -// NotificationService is a notification plugin service layer for http webhook -type NotificationService struct { - base.UnimplementedNotificationService - client HTTPCaller -} - -// NewNotificationService returns httpreceiver service struct. This service implement [receiver.Notifier] interface. -func NewNotificationService(client HTTPCaller) *NotificationService { - return &NotificationService{ - client: client, - } -} - -func (h *NotificationService) Publish(ctx context.Context, notificationMessage notification.Message) (bool, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationMessage.Configs, notificationConfig); err != nil { - return false, err - } - - bodyBytes, err := json.Marshal(notificationMessage.Details) - if err != nil { - return false, err - } - - if err := h.client.Notify(ctx, notificationConfig.URL, bodyBytes); err != nil { - if errors.As(err, new(retry.RetryableError)) { - return true, err - } else { - return false, err - } - } - - return false, nil -} - -func (h *NotificationService) PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) - } - - if err := notificationConfig.Validate(); err != nil { - return nil, err - } - - return notificationConfig.AsMap(), nil -} diff --git a/plugins/receivers/httpreceiver/notification_service_test.go b/plugins/receivers/httpreceiver/notification_service_test.go deleted file mode 100644 index ce6fe2fc..00000000 --- a/plugins/receivers/httpreceiver/notification_service_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package httpreceiver_test - -import ( - "context" - "errors" - "testing" - - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/pkg/retry" - "github.com/odpf/siren/plugins/receivers/httpreceiver" - "github.com/odpf/siren/plugins/receivers/httpreceiver/mocks" - "github.com/stretchr/testify/mock" -) - -func TestNotificationService_Publish(t *testing.T) { - tests := []struct { - name string - setup func(*mocks.HTTPCaller) - notificationMessage notification.Message - wantRetryable bool - wantErr bool - }{ - { - name: "should return error if failed to decode notification config", - notificationMessage: notification.Message{ - Configs: map[string]interface{}{ - "url": true, - }, - }, - wantErr: true, - }, - { - name: "should return error if failed to decode notification detail", - notificationMessage: notification.Message{ - Details: map[string]interface{}{ - "description": make(chan bool), - }, - }, - wantErr: true, - }, - { - name: "should return error and not retryable if notify return error", - setup: func(pd *mocks.HTTPCaller) { - pd.EXPECT().Notify(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("[]uint8")).Return(errors.New("some error")) - }, - notificationMessage: notification.Message{ - Configs: map[string]interface{}{ - "url": "123123", - }, - Details: map[string]interface{}{ - "description": "hello", - }, - }, - wantRetryable: false, - wantErr: true, - }, - { - name: "should return error and retryable if notify return retryable error", - setup: func(sc *mocks.HTTPCaller) { - sc.EXPECT().Notify(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string"), mock.AnythingOfType("[]uint8")).Return(retry.RetryableError{Err: errors.New("some error")}) - }, - notificationMessage: notification.Message{ - Configs: map[string]interface{}{ - "url": "123123", - }, - Details: map[string]interface{}{ - "description": "hello", - }, - }, - wantRetryable: true, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockHTTPCaller := new(mocks.HTTPCaller) - - if tt.setup != nil { - tt.setup(mockHTTPCaller) - } - - hr := httpreceiver.NewNotificationService(mockHTTPCaller) - - got, err := hr.Publish(context.Background(), tt.notificationMessage) - if (err != nil) != tt.wantErr { - t.Errorf("NotificationService.Publish() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.wantRetryable { - t.Errorf("NotificationService.Publish() = %v, want %v", got, tt.wantRetryable) - } - }) - } -} diff --git a/plugins/receivers/httpreceiver/option.go b/plugins/receivers/httpreceiver/option.go new file mode 100644 index 00000000..5c031c1b --- /dev/null +++ b/plugins/receivers/httpreceiver/option.go @@ -0,0 +1,23 @@ +package httpreceiver + +import ( + "github.com/odpf/siren/pkg/httpclient" + "github.com/odpf/siren/pkg/retry" +) + +type ServiceOption func(*PluginService) + +// WithHTTPClient assigns custom http client when creating a service +func WithHTTPClient(httpClient *httpclient.Client) ServiceOption { + return func(s *PluginService) { + s.httpClient = httpClient + } +} + +// WithRetrier wraps client call with retrier +func WithRetrier(runner retry.Runner) ServiceOption { + return func(s *PluginService) { + // note: for now retry only happen in send message context method + s.retrier = runner + } +} diff --git a/plugins/receivers/httpreceiver/receiver_service.go b/plugins/receivers/httpreceiver/receiver_service.go deleted file mode 100644 index fbf16c8f..00000000 --- a/plugins/receivers/httpreceiver/receiver_service.go +++ /dev/null @@ -1,46 +0,0 @@ -package httpreceiver - -import ( - "context" - "fmt" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/plugins/receivers/base" -) - -// ReceiverService is a receiver plugin service layer for http -type ReceiverService struct { - base.UnimplementedReceiverService -} - -// NewReceiverService returns httpreceiver service struct. This service implement [receiver.Resolver] interface. -func NewReceiverService() *ReceiverService { - return &ReceiverService{} -} - -func (s *ReceiverService) PreHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(configurations, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) - } - - if err := receiverConfig.Validate(); err != nil { - return nil, errors.ErrInvalid.WithMsgf(err.Error()) - } - - return configurations, nil -} - -func (s *ReceiverService) BuildNotificationConfig(subsConfs map[string]interface{}, receiverConfs map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(receiverConfs, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) - } - - notificationConfig := NotificationConfig{ - ReceiverConfig: *receiverConfig, - } - - return notificationConfig.AsMap(), nil -} diff --git a/plugins/receivers/httpreceiver/receiver_service_test.go b/plugins/receivers/httpreceiver/receiver_service_test.go deleted file mode 100644 index ad1fb937..00000000 --- a/plugins/receivers/httpreceiver/receiver_service_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package httpreceiver_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/odpf/siren/plugins/receivers/httpreceiver" -) - -func TestHTTPService_Functions(t *testing.T) { - t.Run("should return as-is if populate receiver is called", func(t *testing.T) { - svc := httpreceiver.NewReceiverService() - - got, err := svc.BuildData(context.TODO(), make(map[string]interface{})) - - if err != nil { - t.Fatalf("got error %s, expected was nil", err.Error()) - } - - if !cmp.Equal(got, map[string]interface{}{}) { - t.Fatalf("got result %v, expected was %v", got, map[string]interface{}{}) - } - }) -} - -func TestReceiverService_GetSubscriptionConfig(t *testing.T) { - type testCase struct { - Description string - SubscriptionConfigs map[string]interface{} - ReceiverConfigs map[string]interface{} - ExpectedConfigMap map[string]interface{} - wantErr bool - } - - var ( - testCases = []testCase{ - { - Description: "should return error if receiver 'url' exist but it is not string", - ReceiverConfigs: map[string]interface{}{ - "url": 123, - }, - wantErr: true, - }, - { - Description: "should return configs with token if receiver 'url' exist in string", - ReceiverConfigs: map[string]interface{}{ - "url": "url", - }, - ExpectedConfigMap: map[string]interface{}{ - "url": "url", - }, - }, - } - ) - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - svc := httpreceiver.NewReceiverService() - - got, err := svc.BuildNotificationConfig(tc.SubscriptionConfigs, tc.ReceiverConfigs) - if (err != nil) != tc.wantErr { - t.Errorf("got error = %v, wantErr %v", err, tc.wantErr) - } - if err == nil { - if !cmp.Equal(got, tc.ExpectedConfigMap) { - t.Errorf("got result %+v, expected was %+v", got, tc.ExpectedConfigMap) - } - } - }) - } -} diff --git a/plugins/receivers/httpreceiver/service.go b/plugins/receivers/httpreceiver/service.go new file mode 100644 index 00000000..4f8e9f07 --- /dev/null +++ b/plugins/receivers/httpreceiver/service.go @@ -0,0 +1,130 @@ +package httpreceiver + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/mitchellh/mapstructure" + "github.com/odpf/salt/log" + "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/pkg/errors" + "github.com/odpf/siren/pkg/httpclient" + "github.com/odpf/siren/pkg/retry" + "github.com/odpf/siren/plugins/receivers/base" +) + +type PluginService struct { + base.UnimplementedService + httpClient *httpclient.Client + retrier retry.Runner + logger log.Logger +} + +func NewPluginService(logger log.Logger, cfg AppConfig, opts ...ServiceOption) *PluginService { + s := &PluginService{} + + for _, opt := range opts { + opt(s) + } + + s.logger = logger + + if s.httpClient == nil { + s.httpClient = httpclient.New(httpclient.Config{}) + } + + return s +} + +func (s *PluginService) PreHookDBTransformConfigs(ctx context.Context, receiverConfigMap map[string]interface{}) (map[string]interface{}, error) { + receiverConfig := &ReceiverConfig{} + if err := mapstructure.Decode(receiverConfigMap, receiverConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) + } + + if err := receiverConfig.Validate(); err != nil { + return nil, errors.ErrInvalid.WithMsgf(err.Error()) + } + + return receiverConfig.AsMap(), nil +} + +func (s *PluginService) PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) + } + + if err := notificationConfig.Validate(); err != nil { + return nil, err + } + + return notificationConfig.AsMap(), nil +} + +func (s *PluginService) Send(ctx context.Context, notificationMessage notification.Message) (bool, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationMessage.Configs, notificationConfig); err != nil { + return false, err + } + + bodyBytes, err := json.Marshal(notificationMessage.Details) + if err != nil { + return false, err + } + + if err := s.Notify(ctx, notificationConfig.URL, bodyBytes); err != nil { + if errors.As(err, new(retry.RetryableError)) { + return true, err + } else { + return false, err + } + } + + return false, nil +} + +func (s *PluginService) Notify(ctx context.Context, apiURL string, body []byte) error { + if s.retrier != nil { + if err := s.retrier.Run(ctx, func(ctx context.Context) error { + return s.notify(ctx, apiURL, body) + }); err != nil { + return err + } + } + return s.notify(ctx, apiURL, body) +} + +func (s *PluginService) notify(ctx context.Context, apiURL string, body []byte) error { + req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(body)) + if err != nil { + return fmt.Errorf("failed to create request body: %w", err) + } + + resp, err := s.httpClient.HTTP().Do(req) + if err != nil { + return fmt.Errorf("failure in http call: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == 429 || resp.StatusCode >= 500 { + return retry.RetryableError{Err: errors.New(http.StatusText(resp.StatusCode))} + } + + if resp.StatusCode >= 300 { + return errors.New(http.StatusText(resp.StatusCode)) + } else { + // Status code 2xx only + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %w", err) + } + s.logger.Info("httpreceiver call success", "url", apiURL, "response", string(bodyBytes)) + } + + return nil +} diff --git a/plugins/receivers/httpreceiver/client_test.go b/plugins/receivers/httpreceiver/service_test.go similarity index 80% rename from plugins/receivers/httpreceiver/client_test.go rename to plugins/receivers/httpreceiver/service_test.go index 22d40233..fcef1be8 100644 --- a/plugins/receivers/httpreceiver/client_test.go +++ b/plugins/receivers/httpreceiver/service_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestClient_Notify_WithoutRetrier(t *testing.T) { +func TestService_Notify_WithoutRetrier(t *testing.T) { testCases := []struct { name string ctx context.Context @@ -40,7 +40,7 @@ func TestClient_Notify_WithoutRetrier(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - c := httpreceiver.NewClient(log.NewNoop(), tc.cfg) + c := httpreceiver.NewPluginService(log.NewNoop(), tc.cfg) if err := c.Notify(tc.ctx, tc.apiURL, tc.message); (err != nil) != tc.wantErr { t.Errorf("Client.Notify() error = %v, wantErr %v", err, tc.wantErr) } @@ -48,13 +48,13 @@ func TestClient_Notify_WithoutRetrier(t *testing.T) { } } -func TestClient_Notify_HTTPCall(t *testing.T) { +func TestService_Notify_HTTPCall(t *testing.T) { t.Run("should return error if error response is retryable", func(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusTooManyRequests) })) - c := httpreceiver.NewClient(log.NewNoop(), httpreceiver.AppConfig{}) + c := httpreceiver.NewPluginService(log.NewNoop(), httpreceiver.AppConfig{}) err := c.Notify(context.Background(), testServer.URL, nil) assert.EqualError(t, err, "Too Many Requests") @@ -67,7 +67,7 @@ func TestClient_Notify_HTTPCall(t *testing.T) { w.WriteHeader(http.StatusBadRequest) })) - c := httpreceiver.NewClient(log.NewNoop(), httpreceiver.AppConfig{}) + c := httpreceiver.NewPluginService(log.NewNoop(), httpreceiver.AppConfig{}) err := c.Notify(context.Background(), testServer.URL, nil) assert.EqualError(t, err, "Bad Request") @@ -80,7 +80,7 @@ func TestClient_Notify_HTTPCall(t *testing.T) { w.Header().Set("Content-Length", "1") })) - c := httpreceiver.NewClient(log.NewNoop(), httpreceiver.AppConfig{}) + c := httpreceiver.NewPluginService(log.NewNoop(), httpreceiver.AppConfig{}) err := c.Notify(context.Background(), testServer.URL, nil) assert.EqualError(t, err, "failed to read response body: unexpected EOF") @@ -93,7 +93,7 @@ func TestClient_Notify_HTTPCall(t *testing.T) { w.Header().Set("Content-Length", "1") })) - c := httpreceiver.NewClient(log.NewNoop(), httpreceiver.AppConfig{}) + c := httpreceiver.NewPluginService(log.NewNoop(), httpreceiver.AppConfig{}) err := c.Notify(context.Background(), testServer.URL, nil) assert.EqualError(t, err, "failed to read response body: unexpected EOF") @@ -102,7 +102,7 @@ func TestClient_Notify_HTTPCall(t *testing.T) { }) } -func TestClient_Notify_WithRetrier(t *testing.T) { +func TestService_Notify_WithRetrier(t *testing.T) { var expectedCounter = 4 t.Run("when 429 is returned", func(t *testing.T) { @@ -113,8 +113,8 @@ func TestClient_Notify_WithRetrier(t *testing.T) { w.WriteHeader(http.StatusTooManyRequests) })) - c := httpreceiver.NewClient(log.NewNoop(), httpreceiver.AppConfig{}, - httpreceiver.ClientWithRetrier(retry.New(retry.Config{Enable: true}))) + c := httpreceiver.NewPluginService(log.NewNoop(), httpreceiver.AppConfig{}, + httpreceiver.WithRetrier(retry.New(retry.Config{Enable: true}))) _ = c.Notify(context.Background(), testServer.URL, nil) assert.Equal(t, expectedCounter, counter) @@ -130,8 +130,8 @@ func TestClient_Notify_WithRetrier(t *testing.T) { w.Write([]byte(`{"ok":false}`)) })) - c := httpreceiver.NewClient(log.NewNoop(), httpreceiver.AppConfig{}, - httpreceiver.ClientWithRetrier(retry.New(retry.Config{Enable: true}))) + c := httpreceiver.NewPluginService(log.NewNoop(), httpreceiver.AppConfig{}, + httpreceiver.WithRetrier(retry.New(retry.Config{Enable: true}))) _ = c.Notify(context.Background(), testServer.URL, nil) assert.Equal(t, expectedCounter, counter) diff --git a/plugins/receivers/pagerduty/config/default_cortex_alert_template_body_v1.goyaml b/plugins/receivers/pagerduty/config/default_alert_template_body_v1.goyaml similarity index 91% rename from plugins/receivers/pagerduty/config/default_cortex_alert_template_body_v1.goyaml rename to plugins/receivers/pagerduty/config/default_alert_template_body_v1.goyaml index 9b3ad61a..76151834 100644 --- a/plugins/receivers/pagerduty/config/default_cortex_alert_template_body_v1.goyaml +++ b/plugins/receivers/pagerduty/config/default_alert_template_body_v1.goyaml @@ -16,7 +16,7 @@ Annotations:\n [[ end ]][[if .Data.generatorUrl ]]Source: [[ .Data.generatorUrl ]][[ end ]] [[ end ]] event_type: "[[template "pagerduty.event_type" . ]]" -[[if .Data.incidentKey]]incident_key: "[[.Data.incidentKey]]"[[ end ]] +[[if .Data.id]]incident_key: "[[.Data.id]]"[[ end ]] description: ([[ .Data.status | toUpper ]][[ if .Data.numAlertsFiring ]][[ if eq .Data.status "firing" ]]:[[ .Data.numAlertsFiring ]][[ end ]][[ end ]]) [[ .Labels | joinStringValues " " ]] client: "Siren" details: diff --git a/plugins/receivers/pagerduty/notification_service.go b/plugins/receivers/pagerduty/notification_service.go deleted file mode 100644 index 2f927e27..00000000 --- a/plugins/receivers/pagerduty/notification_service.go +++ /dev/null @@ -1,71 +0,0 @@ -package pagerduty - -import ( - "context" - "errors" - "fmt" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/template" - "github.com/odpf/siren/pkg/retry" - "github.com/odpf/siren/plugins/receivers/base" -) - -// NotificationService is a notification plugin service layer for pagerduty -type NotificationService struct { - base.UnimplementedNotificationService - client PagerDutyCaller -} - -// NewNotificationService returns pagerduty service struct. This service implement [receiver.Notifier] interface. -func NewNotificationService(client PagerDutyCaller) *NotificationService { - return &NotificationService{ - client: client, - } -} - -func (pd *NotificationService) Publish(ctx context.Context, notificationMessage notification.Message) (bool, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationMessage.Configs, notificationConfig); err != nil { - return false, err - } - - pgMessageV1 := &MessageV1{} - if err := mapstructure.Decode(notificationMessage.Details, &pgMessageV1); err != nil { - return false, err - } - pgMessageV1.ServiceKey = notificationConfig.ServiceKey - - if err := pd.client.NotifyV1(ctx, *pgMessageV1); err != nil { - if errors.As(err, new(retry.RetryableError)) { - return true, err - } else { - return false, err - } - } - - return false, nil -} - -func (pd *NotificationService) PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) - } - - if err := notificationConfig.Validate(); err != nil { - return nil, err - } - - return notificationConfig.AsMap(), nil -} - -func (pd *NotificationService) DefaultTemplateOfProvider(templateName string) string { - switch templateName { - case template.ReservedName_DefaultCortex: - return defaultCortexAlertTemplateBodyV1 - default: - return "" - } -} diff --git a/plugins/receivers/pagerduty/option.go b/plugins/receivers/pagerduty/option.go new file mode 100644 index 00000000..5cbb1864 --- /dev/null +++ b/plugins/receivers/pagerduty/option.go @@ -0,0 +1,29 @@ +package pagerduty + +import ( + "github.com/odpf/siren/pkg/httpclient" + "github.com/odpf/siren/pkg/retry" +) + +type ServiceOption func(*PluginService) + +// WithHTTPClient assigns custom http client when creating a service +func WithHTTPClient(httpClient *httpclient.Client) ServiceOption { + return func(s *PluginService) { + s.httpClient = httpClient + } +} + +// WithRetrier wraps client call with retrier +func WithRetrier(runner retry.Runner) ServiceOption { + return func(s *PluginService) { + // note: for now retry only happen in send message context method + s.retrier = runner + } +} + +func WithPagerDutyClient(client PagerDutyCaller) ServiceOption { + return func(s *PluginService) { + s.client = client + } +} diff --git a/plugins/receivers/pagerduty/receiver_service.go b/plugins/receivers/pagerduty/receiver_service.go deleted file mode 100644 index 63ae4b60..00000000 --- a/plugins/receivers/pagerduty/receiver_service.go +++ /dev/null @@ -1,54 +0,0 @@ -package pagerduty - -import ( - "context" - "fmt" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/plugins/receivers/base" -) - -// ReceiverService is a receiver plugin service layer for pagerduty -type ReceiverService struct { - base.UnimplementedReceiverService -} - -// NewService returns pagerduty service struct. This service implement [receiver.Resolver] interface. -func NewReceiverService() *ReceiverService { - return &ReceiverService{} -} - -func (s *ReceiverService) PreHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(configurations, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) - } - - if err := receiverConfig.Validate(); err != nil { - return nil, errors.ErrInvalid.WithMsgf(err.Error()) - } - - return configurations, nil -} - -func (s *ReceiverService) PostHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - return configurations, nil -} - -func (s *ReceiverService) BuildData(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - return map[string]interface{}{}, nil -} - -func (s *ReceiverService) BuildNotificationConfig(subsConfs map[string]interface{}, receiverConfs map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(receiverConfs, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) - } - - notificationConfig := NotificationConfig{ - ReceiverConfig: *receiverConfig, - } - - return notificationConfig.AsMap(), nil -} diff --git a/plugins/receivers/pagerduty/receiver_service_test.go b/plugins/receivers/pagerduty/receiver_service_test.go deleted file mode 100644 index ef5d69a9..00000000 --- a/plugins/receivers/pagerduty/receiver_service_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package pagerduty_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/odpf/siren/pkg/secret" - "github.com/odpf/siren/plugins/receivers/pagerduty" -) - -func TestReceiverService_Functions(t *testing.T) { - t.Run("should return empty if get populated data is called", func(t *testing.T) { - svc := pagerduty.NewReceiverService() - - got, err := svc.BuildData(context.TODO(), map[string]interface{}{}) - if err != nil { - t.Fatalf("got error %s, expected was nil", err.Error()) - } - - if !cmp.Equal(got, map[string]interface{}{}) { - t.Fatalf("got result %v, expected was %v", got, map[string]interface{}{}) - } - }) -} - -func TestReceiverService_BuildNotificationConfig(t *testing.T) { - type testCase struct { - Description string - SubscriptionConfigs map[string]interface{} - ReceiverConfigs map[string]interface{} - ExpectedConfigMap map[string]interface{} - wantErr bool - } - - var ( - testCases = []testCase{ - { - Description: "should return error if receiver 'service_key' exist but it is not string", - ReceiverConfigs: map[string]interface{}{ - "service_key": 123, - }, - wantErr: true, - }, - { - Description: "should return configs with service_key if receiver 'service_key' exist in string", - ReceiverConfigs: map[string]interface{}{ - "service_key": secret.MaskableString("service_key"), - }, - ExpectedConfigMap: map[string]interface{}{ - "service_key": secret.MaskableString("service_key"), - }, - }, - } - ) - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - svc := pagerduty.NewReceiverService() - - got, err := svc.BuildNotificationConfig(tc.SubscriptionConfigs, tc.ReceiverConfigs) - if (err != nil) != tc.wantErr { - t.Errorf("got error = %v, wantErr %v", err, tc.wantErr) - } - if err == nil { - if !cmp.Equal(got, tc.ExpectedConfigMap) { - t.Errorf("got result %+v, expected was %+v", got, tc.ExpectedConfigMap) - } - } - }) - } -} diff --git a/plugins/receivers/pagerduty/service.go b/plugins/receivers/pagerduty/service.go new file mode 100644 index 00000000..e8b344e9 --- /dev/null +++ b/plugins/receivers/pagerduty/service.go @@ -0,0 +1,87 @@ +package pagerduty + +import ( + "context" + "fmt" + + "github.com/mitchellh/mapstructure" + "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/pkg/errors" + "github.com/odpf/siren/pkg/httpclient" + "github.com/odpf/siren/pkg/retry" + "github.com/odpf/siren/plugins/receivers/base" +) + +type PluginService struct { + base.UnimplementedService + client PagerDutyCaller + httpClient *httpclient.Client + retrier retry.Runner +} + +func NewPluginService(cfg AppConfig, opts ...ServiceOption) *PluginService { + s := &PluginService{} + + for _, opt := range opts { + opt(s) + } + + if s.client == nil { + s.client = NewClient(cfg, ClientWithHTTPClient(s.httpClient), ClientWithRetrier(s.retrier)) + } + + return s +} + +func (s *PluginService) PreHookDBTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { + receiverConfig := &ReceiverConfig{} + if err := mapstructure.Decode(configurations, receiverConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) + } + + if err := receiverConfig.Validate(); err != nil { + return nil, errors.ErrInvalid.WithMsgf(err.Error()) + } + + return configurations, nil +} + +func (s *PluginService) PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) + } + + if err := notificationConfig.Validate(); err != nil { + return nil, err + } + + return notificationConfig.AsMap(), nil +} + +func (s *PluginService) Send(ctx context.Context, notificationMessage notification.Message) (bool, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationMessage.Configs, notificationConfig); err != nil { + return false, err + } + + pgMessageV1 := &MessageV1{} + if err := mapstructure.Decode(notificationMessage.Details, &pgMessageV1); err != nil { + return false, err + } + pgMessageV1.ServiceKey = notificationConfig.ServiceKey + + if err := s.client.NotifyV1(ctx, *pgMessageV1); err != nil { + if errors.As(err, new(retry.RetryableError)) { + return true, err + } else { + return false, err + } + } + + return false, nil +} + +func (s *PluginService) GetSystemDefaultTemplate() string { + return defaultAlertTemplateBodyV1 +} diff --git a/plugins/receivers/pagerduty/notification_service_test.go b/plugins/receivers/pagerduty/service_test.go similarity index 85% rename from plugins/receivers/pagerduty/notification_service_test.go rename to plugins/receivers/pagerduty/service_test.go index 659cce33..d67b8026 100644 --- a/plugins/receivers/pagerduty/notification_service_test.go +++ b/plugins/receivers/pagerduty/service_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/mock" ) -func TestNotificationService_Publish_V1(t *testing.T) { +func TestService_Send_V1(t *testing.T) { tests := []struct { name string setup func(*mocks.PagerDutyCaller) @@ -79,15 +79,15 @@ func TestNotificationService_Publish_V1(t *testing.T) { tt.setup(mockPDClient) } - pd := pagerduty.NewNotificationService(mockPDClient) + pd := pagerduty.NewPluginService(pagerduty.AppConfig{}, pagerduty.WithPagerDutyClient(mockPDClient)) - got, err := pd.Publish(context.Background(), tt.notificationMessage) + got, err := pd.Send(context.Background(), tt.notificationMessage) if (err != nil) != tt.wantErr { - t.Errorf("NotificationService.Publish() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("NotificationService.Send() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.wantRetryable { - t.Errorf("NotificationService.Publish() = %v, want %v", got, tt.wantRetryable) + t.Errorf("NotificationService.Send() = %v, want %v", got, tt.wantRetryable) } }) } diff --git a/plugins/receivers/pagerduty/template.go b/plugins/receivers/pagerduty/template.go index ef5d7276..03493677 100644 --- a/plugins/receivers/pagerduty/template.go +++ b/plugins/receivers/pagerduty/template.go @@ -3,6 +3,6 @@ package pagerduty import _ "embed" var ( - //go:embed config/default_cortex_alert_template_body_v1.goyaml - defaultCortexAlertTemplateBodyV1 string + //go:embed config/default_alert_template_body_v1.goyaml + defaultAlertTemplateBodyV1 string ) diff --git a/plugins/receivers/slack/client.go b/plugins/receivers/slack/client.go index dd0d4940..40658463 100644 --- a/plugins/receivers/slack/client.go +++ b/plugins/receivers/slack/client.go @@ -46,11 +46,27 @@ type Credential struct { TeamName string } +type ClientOption func(*Client) + +// ClientWithHTTPClient assigns custom http client when creating a slack client +func ClientWithHTTPClient(httpClient *httpclient.Client) ClientOption { + return func(c *Client) { + c.httpClient = httpClient + } +} + +// ClientWithRetrier wraps client call with retrier +func ClientWithRetrier(runner retry.Runner) ClientOption { + return func(c *Client) { + // note: for now retry only happen in send message context method + c.retrier = runner + } +} + type Client struct { cfg AppConfig httpClient *httpclient.Client - // data *clientData - retrier retry.Runner + retrier retry.Runner } // NewClient is a constructor to create slack client. diff --git a/plugins/receivers/slack/config/default_cortex_alert_template_body.goyaml b/plugins/receivers/slack/config/default_alert_template_body.goyaml similarity index 100% rename from plugins/receivers/slack/config/default_cortex_alert_template_body.goyaml rename to plugins/receivers/slack/config/default_alert_template_body.goyaml diff --git a/plugins/receivers/slack/notification_service.go b/plugins/receivers/slack/notification_service.go deleted file mode 100644 index 38438190..00000000 --- a/plugins/receivers/slack/notification_service.go +++ /dev/null @@ -1,113 +0,0 @@ -package slack - -import ( - "context" - "fmt" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/core/notification" - "github.com/odpf/siren/core/template" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/pkg/retry" - "github.com/odpf/siren/plugins/receivers/base" -) - -const ( - TypeChannelChannel = "channel" - TypeChannelUser = "user" - - defaultChannelType = TypeChannelChannel -) - -// NotificationService is a notification plugin service layer for slack -type NotificationService struct { - base.UnimplementedNotificationService - cryptoClient Encryptor - client SlackCaller -} - -// NewNotificationService returns slack service struct. This service implement [receiver.Notifier] interface. -func NewNotificationService(client SlackCaller, cryptoClient Encryptor) *NotificationService { - return &NotificationService{ - client: client, - cryptoClient: cryptoClient, - } -} - -func (s *NotificationService) Publish(ctx context.Context, notificationMessage notification.Message) (bool, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationMessage.Configs, notificationConfig); err != nil { - return false, err - } - - slackMessage := &Message{} - if err := mapstructure.Decode(notificationMessage.Details, &slackMessage); err != nil { - return false, err - } - - if notificationConfig.ChannelType == "" { - notificationConfig.ChannelType = defaultChannelType - } - if notificationConfig.ChannelName != "" { - slackMessage.Channel = notificationConfig.ChannelName - } - - if err := s.client.Notify(ctx, *notificationConfig, *slackMessage); err != nil { - if errors.As(err, new(retry.RetryableError)) { - return true, err - } else { - return false, err - } - } - - return false, nil -} - -func (s *NotificationService) PreHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) - } - - if err := notificationConfig.Validate(); err != nil { - return nil, err - } - - cipher, err := s.cryptoClient.Encrypt(notificationConfig.Token) - if err != nil { - return nil, fmt.Errorf("slack token encryption failed: %w", err) - } - - notificationConfig.Token = cipher - - return notificationConfig.AsMap(), nil -} - -func (s *NotificationService) PostHookTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { - notificationConfig := &NotificationConfig{} - if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to notification config: %w", err) - } - - if err := notificationConfig.Validate(); err != nil { - return nil, err - } - - token, err := s.cryptoClient.Decrypt(notificationConfig.Token) - if err != nil { - return nil, fmt.Errorf("slack token decryption failed: %w", err) - } - - notificationConfig.Token = token - - return notificationConfig.AsMap(), nil -} - -func (s *NotificationService) DefaultTemplateOfProvider(templateName string) string { - switch templateName { - case template.ReservedName_DefaultCortex: - return defaultCortexAlertTemplateBody - default: - return "" - } -} diff --git a/plugins/receivers/slack/option.go b/plugins/receivers/slack/option.go index 44dec800..93f67ac6 100644 --- a/plugins/receivers/slack/option.go +++ b/plugins/receivers/slack/option.go @@ -5,51 +5,25 @@ import ( "github.com/odpf/siren/pkg/retry" ) -type ClientOption func(*Client) +type ServiceOption func(*PluginService) -// ClientWithHTTPClient assigns custom http client when creating a slack client -func ClientWithHTTPClient(httpClient *httpclient.Client) ClientOption { - return func(c *Client) { - c.httpClient = httpClient +// WithHTTPClient assigns custom http client when creating a slack service +func WithHTTPClient(httpClient *httpclient.Client) ServiceOption { + return func(s *PluginService) { + s.httpClient = httpClient } } -// ClientWithRetrier wraps client call with retrier -func ClientWithRetrier(runner retry.Runner) ClientOption { - return func(c *Client) { +// WithRetrier wraps client call with retrier +func WithRetrier(runner retry.Runner) ServiceOption { + return func(s *PluginService) { // note: for now retry only happen in send message context method - c.retrier = runner + s.retrier = runner } } -// type ClientCallOption func(*clientData) - -// // CallWithGoSlackClient uses a custom slack client when calling slack API -// func CallWithGoSlackClient(gsc GoSlackCaller) ClientCallOption { -// return func(c *clientData) { -// c.goslackClient = gsc -// } -// } - -// // CallWithClientSecret uses a new client with client ID, secret, and auth code to call slack API -// func CallWithClientSecret(authCode string, clientID, clientSecret string) ClientCallOption { -// return func(c *clientData) { -// c.authCode = authCode -// c.clientID = clientID -// c.clientSecret = clientSecret -// } -// } - -// // CallWithToken uses access token to call slack API -// func CallWithToken(token string) ClientCallOption { -// return func(c *clientData) { -// c.token = token -// } -// } - -// // CallWithCustomPath uses custom path endpoint to call -// func CallWithCustomPath(path string) ClientCallOption { -// return func(c *clientData) { -// c.customPath = path -// } -// } +func WithSlackClient(client SlackCaller) ServiceOption { + return func(s *PluginService) { + s.client = client + } +} diff --git a/plugins/receivers/slack/option_test.go b/plugins/receivers/slack/option_test.go deleted file mode 100644 index 6feb0b78..00000000 --- a/plugins/receivers/slack/option_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package slack_test - -import ( - "testing" - - "github.com/odpf/siren/pkg/httpclient" - "github.com/odpf/siren/plugins/receivers/slack" -) - -func TestClientOption(t *testing.T) { - t.Run("should be fine when using http client from external", func(t *testing.T) { - c := slack.NewClient(slack.AppConfig{}, slack.ClientWithHTTPClient(httpclient.New(httpclient.Config{}))) - if c == nil { - t.Fatal("client should not be nil") - } - }) -} - -// func TestClientCallOption(t *testing.T) { -// t.Run("should use token when token is passed", func(t *testing.T) { -// c := slack.NewClient() -// if c == nil { -// t.Fatal("client should not be nil") -// } - -// _, err := c.GetWorkspaceChannels( -// context.TODO(), -// slack.CallWithToken("1234"), -// ) - -// expectedErrorString := "failed to fetch joined channel list: invalid_auth" -// if err.Error() != expectedErrorString { -// t.Fatalf("got error %s, expected was %s", err, expectedErrorString) -// } -// }) - -// t.Run("should use client id and secret when passed", func(t *testing.T) { -// c := slack.NewClient() -// if c == nil { -// t.Fatal("client should not be nil") -// } - -// _, err := c.GetWorkspaceChannels( -// context.TODO(), -// slack.CallWithClientSecret("1234", "1234", "1234"), -// ) - -// expectedErrorString := "goslack client creation failure: slack oauth call failed" -// if err.Error() != expectedErrorString { -// t.Fatalf("got error %s, expected was %s", err, expectedErrorString) -// } -// }) - -// t.Run("should return error if use client id and secret but missing required params", func(t *testing.T) { -// c := slack.NewClient() -// if c == nil { -// t.Fatal("client should not be nil") -// } - -// _, err := c.GetWorkspaceChannels( -// context.TODO(), -// slack.CallWithClientSecret("1234", "1234", ""), -// ) - -// expectedErrorString := "goslack client creation failure: no client id/secret credential provided" -// if err.Error() != expectedErrorString { -// t.Fatalf("got error %s, expected was %s", err, expectedErrorString) -// } -// }) -// } diff --git a/plugins/receivers/slack/receiver_service.go b/plugins/receivers/slack/receiver_service.go deleted file mode 100644 index 7d7515f0..00000000 --- a/plugins/receivers/slack/receiver_service.go +++ /dev/null @@ -1,126 +0,0 @@ -package slack - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/mitchellh/mapstructure" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/plugins/receivers/base" -) - -// ReceiverService is a receiver plugin service layer for slack -type ReceiverService struct { - base.UnimplementedReceiverService - client SlackCaller - cryptoClient Encryptor -} - -// NewReceiverService returns slack service struct. This service implement [receiver.Resolver] interface. -func NewReceiverService(client SlackCaller, cryptoClient Encryptor) *ReceiverService { - return &ReceiverService{ - client: client, - cryptoClient: cryptoClient, - } -} - -func (s *ReceiverService) PreHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - slackCredentialConfig := &SlackCredentialConfig{} - if err := mapstructure.Decode(configurations, slackCredentialConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to pre transform config: %w", err) - } - - if err := slackCredentialConfig.Validate(); err != nil { - return nil, errors.ErrInvalid.WithMsgf(err.Error()) - } - - creds, err := s.client.ExchangeAuth(ctx, slackCredentialConfig.AuthCode, slackCredentialConfig.ClientID, slackCredentialConfig.ClientSecret) - if err != nil { - return nil, fmt.Errorf("failed to exchange code with slack OAuth server: %w", err) - } - - cipherText, err := s.cryptoClient.Encrypt(creds.AccessToken) - if err != nil { - return nil, fmt.Errorf("slack token encryption failed: %w", err) - } - - receiverConfig := ReceiverConfig{ - Workspace: creds.TeamName, - Token: cipherText, - } - - return receiverConfig.AsMap(), nil -} - -// PostHookTransformConfigs do transformation in post-hook service lifecycle -func (s *ReceiverService) PostHookTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(configurations, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) - } - - if err := receiverConfig.Validate(); err != nil { - return nil, err - } - - token, err := s.cryptoClient.Decrypt(receiverConfig.Token) - if err != nil { - return nil, fmt.Errorf("slack token decryption failed: %w", err) - } - - receiverConfig.Token = token - - return receiverConfig.AsMap(), nil -} - -// BuildData populates receiver data field based on config -func (s *ReceiverService) BuildData(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(configurations, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) - } - - if err := receiverConfig.Validate(); err != nil { - return nil, err - } - - channels, err := s.client.GetWorkspaceChannels( - ctx, - receiverConfig.Token, - ) - if err != nil { - return nil, fmt.Errorf("could not get channels: %w", err) - } - - data, err := json.Marshal(channels) - if err != nil { - // this is very unlikely to return error since we have an explicitly defined type of channels - return nil, fmt.Errorf("invalid channels: %w", err) - } - - receiverData := ReceiverData{ - Channels: string(data), - } - - return receiverData.AsMap(), nil -} - -func (s *ReceiverService) BuildNotificationConfig(subsConfs map[string]interface{}, receiverConfs map[string]interface{}) (map[string]interface{}, error) { - subscriptionConfig := &SubscriptionConfig{} - if err := mapstructure.Decode(subsConfs, subscriptionConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to subscription config: %w", err) - } - - receiverConfig := &ReceiverConfig{} - if err := mapstructure.Decode(receiverConfs, receiverConfig); err != nil { - return nil, fmt.Errorf("failed to transform configurations to subscription config: %w", err) - } - - notificationConfig := NotificationConfig{ - SubscriptionConfig: *subscriptionConfig, - ReceiverConfig: *receiverConfig, - } - - return notificationConfig.AsMap(), nil -} diff --git a/plugins/receivers/slack/receiver_service_test.go b/plugins/receivers/slack/receiver_service_test.go deleted file mode 100644 index fc460ddc..00000000 --- a/plugins/receivers/slack/receiver_service_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package slack_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/odpf/siren/pkg/errors" - "github.com/odpf/siren/pkg/secret" - "github.com/odpf/siren/plugins/receivers/slack" - "github.com/odpf/siren/plugins/receivers/slack/mocks" - "github.com/stretchr/testify/mock" -) - -func TestReceiverService_BuildData(t *testing.T) { - type testCase struct { - Description string - Setup func(sc *mocks.SlackCaller, e *mocks.Encryptor) - Confs map[string]interface{} - Err error - } - var ( - ctx = context.TODO() - testCases = []testCase{ - { - Description: "should return error if configuration is invalid", - Setup: func(sc *mocks.SlackCaller, e *mocks.Encryptor) {}, - Confs: make(map[string]interface{}), - Err: errors.New("invalid slack receiver config, workspace: , token: "), - }, - { - Description: "should return error if failed to get workspace channels with slack client", - Setup: func(sc *mocks.SlackCaller, e *mocks.Encryptor) { - sc.EXPECT().GetWorkspaceChannels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("secret.MaskableString")).Return(nil, errors.New("some error")) - }, - Confs: map[string]interface{}{ - "token": secret.MaskableString("key"), - "workspace": "odpf", - }, - Err: errors.New("could not get channels: some error"), - }, - { - Description: "should return nil error if success populating receiver.Receiver", - Setup: func(sc *mocks.SlackCaller, e *mocks.Encryptor) { - sc.EXPECT().GetWorkspaceChannels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("secret.MaskableString")).Return([]slack.Channel{ - { - ID: "id", - Name: "name", - }, - }, nil) - }, - Confs: map[string]interface{}{ - "token": secret.MaskableString("key"), - "workspace": "odpf", - }, - }, - } - ) - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - var ( - slackClientMock = new(mocks.SlackCaller) - encryptorMock = new(mocks.Encryptor) - ) - - svc := slack.NewReceiverService(slackClientMock, encryptorMock) - - tc.Setup(slackClientMock, encryptorMock) - - _, err := svc.BuildData(ctx, tc.Confs) - if tc.Err != err { - if tc.Err.Error() != err.Error() { - t.Fatalf("got error %s, expected was %s", err.Error(), tc.Err.Error()) - } - } - - slackClientMock.AssertExpectations(t) - encryptorMock.AssertExpectations(t) - }) - } -} - -func TestReceiverService_BuildNotificationConfig(t *testing.T) { - type testCase struct { - Description string - SubscriptionConfigs map[string]interface{} - ReceiverConfigs map[string]interface{} - ExpectedConfigMap map[string]interface{} - wantErr bool - } - - var ( - testCases = []testCase{ - { - Description: "should return error if receiver 'token' exist but it is not string", - SubscriptionConfigs: map[string]interface{}{ - "channel_name": "odpf_warning", - }, - ReceiverConfigs: map[string]interface{}{ - "token": 123, - }, - wantErr: true, - }, - { - Description: "should return configs without token if receiver 'token' does not exist", - SubscriptionConfigs: map[string]interface{}{ - "channel_name": "odpf_warning", - }, - ExpectedConfigMap: map[string]interface{}{ - "channel_name": "odpf_warning", - "token": secret.MaskableString(""), - "workspace": "", - }, - }, - { - Description: "should return configs with token if receiver 'token' exist in string", - SubscriptionConfigs: map[string]interface{}{ - "channel_name": "odpf_warning", - }, - ReceiverConfigs: map[string]interface{}{ - "token": "123", - }, - ExpectedConfigMap: map[string]interface{}{ - "channel_name": "odpf_warning", - "token": secret.MaskableString("123"), - "workspace": "", - }, - }, - } - ) - - for _, tc := range testCases { - t.Run(tc.Description, func(t *testing.T) { - svc := slack.NewReceiverService(nil, nil) - - got, err := svc.BuildNotificationConfig(tc.SubscriptionConfigs, tc.ReceiverConfigs) - if (err != nil) != tc.wantErr { - t.Errorf("got error = %v, wantErr %v", err, tc.wantErr) - } - if err == nil { - if !cmp.Equal(got, tc.ExpectedConfigMap) { - t.Errorf("got result %+v, expected was %+v", got, tc.ExpectedConfigMap) - } - } - }) - } -} diff --git a/plugins/receivers/slack/service.go b/plugins/receivers/slack/service.go new file mode 100644 index 00000000..0199fdfd --- /dev/null +++ b/plugins/receivers/slack/service.go @@ -0,0 +1,201 @@ +package slack + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/mitchellh/mapstructure" + "github.com/odpf/siren/core/notification" + "github.com/odpf/siren/pkg/errors" + "github.com/odpf/siren/pkg/httpclient" + "github.com/odpf/siren/pkg/retry" + "github.com/odpf/siren/plugins/receivers/base" +) + +const ( + TypeChannelChannel = "channel" + TypeChannelUser = "user" + + defaultChannelType = TypeChannelChannel +) + +// PluginService is a plugin service layer for slack +type PluginService struct { + base.UnimplementedService + client SlackCaller + cryptoClient Encryptor + httpClient *httpclient.Client + retrier retry.Runner +} + +// NewPluginService returns slack plugin service struct. This service implement [receiver.Resolver] and [notification.Notifier] interface. +func NewPluginService(cfg AppConfig, cryptoClient Encryptor, opts ...ServiceOption) *PluginService { + s := &PluginService{} + + for _, opt := range opts { + opt(s) + } + + s.cryptoClient = cryptoClient + + if s.client == nil { + s.client = NewClient(cfg, ClientWithHTTPClient(s.httpClient), ClientWithRetrier(s.retrier)) + } + + return s +} + +func (s *PluginService) PreHookDBTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { + slackCredentialConfig := &SlackCredentialConfig{} + if err := mapstructure.Decode(configurations, slackCredentialConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to pre transform config: %w", err) + } + + if err := slackCredentialConfig.Validate(); err != nil { + return nil, errors.ErrInvalid.WithMsgf(err.Error()) + } + + creds, err := s.client.ExchangeAuth(ctx, slackCredentialConfig.AuthCode, slackCredentialConfig.ClientID, slackCredentialConfig.ClientSecret) + if err != nil { + return nil, fmt.Errorf("failed to exchange code with slack OAuth server: %w", err) + } + + cipherText, err := s.cryptoClient.Encrypt(creds.AccessToken) + if err != nil { + return nil, fmt.Errorf("slack token encryption failed: %w", err) + } + + receiverConfig := ReceiverConfig{ + Workspace: creds.TeamName, + Token: cipherText, + } + + return receiverConfig.AsMap(), nil +} + +// PostHookTransformConfigs do transformation in post-hook service lifecycle +func (s *PluginService) PostHookDBTransformConfigs(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { + receiverConfig := &ReceiverConfig{} + if err := mapstructure.Decode(configurations, receiverConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) + } + + if err := receiverConfig.Validate(); err != nil { + return nil, err + } + + token, err := s.cryptoClient.Decrypt(receiverConfig.Token) + if err != nil { + return nil, fmt.Errorf("slack token decryption failed: %w", err) + } + + receiverConfig.Token = token + + return receiverConfig.AsMap(), nil +} + +// BuildData populates receiver data field based on config +func (s *PluginService) BuildData(ctx context.Context, configurations map[string]interface{}) (map[string]interface{}, error) { + receiverConfig := &ReceiverConfig{} + if err := mapstructure.Decode(configurations, receiverConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to receiver config: %w", err) + } + + if err := receiverConfig.Validate(); err != nil { + return nil, err + } + + channels, err := s.client.GetWorkspaceChannels( + ctx, + receiverConfig.Token, + ) + if err != nil { + return nil, fmt.Errorf("could not get channels: %w", err) + } + + data, err := json.Marshal(channels) + if err != nil { + // this is very unlikely to return error since we have an explicitly defined type of channels + return nil, fmt.Errorf("invalid channels: %w", err) + } + + receiverData := ReceiverData{ + Channels: string(data), + } + + return receiverData.AsMap(), nil +} + +func (s *PluginService) PreHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to slack notification config: %w", err) + } + + if err := notificationConfig.Validate(); err != nil { + return nil, err + } + + cipher, err := s.cryptoClient.Encrypt(notificationConfig.Token) + if err != nil { + return nil, fmt.Errorf("slack token encryption failed: %w", err) + } + + notificationConfig.Token = cipher + + return notificationConfig.AsMap(), nil +} + +func (s *PluginService) PostHookQueueTransformConfigs(ctx context.Context, notificationConfigMap map[string]interface{}) (map[string]interface{}, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationConfigMap, notificationConfig); err != nil { + return nil, fmt.Errorf("failed to transform configurations to notification config: %w", err) + } + + if err := notificationConfig.Validate(); err != nil { + return nil, err + } + + token, err := s.cryptoClient.Decrypt(notificationConfig.Token) + if err != nil { + return nil, fmt.Errorf("slack token decryption failed: %w", err) + } + + notificationConfig.Token = token + + return notificationConfig.AsMap(), nil +} + +func (s *PluginService) Send(ctx context.Context, notificationMessage notification.Message) (bool, error) { + notificationConfig := &NotificationConfig{} + if err := mapstructure.Decode(notificationMessage.Configs, notificationConfig); err != nil { + return false, err + } + + slackMessage := &Message{} + if err := mapstructure.Decode(notificationMessage.Details, &slackMessage); err != nil { + return false, err + } + + if notificationConfig.ChannelType == "" { + notificationConfig.ChannelType = defaultChannelType + } + if notificationConfig.ChannelName != "" { + slackMessage.Channel = notificationConfig.ChannelName + } + + if err := s.client.Notify(ctx, *notificationConfig, *slackMessage); err != nil { + if errors.As(err, new(retry.RetryableError)) { + return true, err + } else { + return false, err + } + } + + return false, nil +} + +func (s *PluginService) GetSystemDefaultTemplate() string { + return defaultAlertTemplateBody +} diff --git a/plugins/receivers/slack/notification_service_test.go b/plugins/receivers/slack/service_test.go similarity index 51% rename from plugins/receivers/slack/notification_service_test.go rename to plugins/receivers/slack/service_test.go index 38b184c4..0433ba66 100644 --- a/plugins/receivers/slack/notification_service_test.go +++ b/plugins/receivers/slack/service_test.go @@ -14,7 +14,76 @@ import ( "github.com/stretchr/testify/mock" ) -func TestNotificationService_Publish(t *testing.T) { +func TestService_BuildData(t *testing.T) { + type testCase struct { + Description string + Setup func(sc *mocks.SlackCaller, e *mocks.Encryptor) + Confs map[string]interface{} + Err error + } + var ( + ctx = context.TODO() + testCases = []testCase{ + { + Description: "should return error if configuration is invalid", + Setup: func(sc *mocks.SlackCaller, e *mocks.Encryptor) {}, + Confs: make(map[string]interface{}), + Err: errors.New("invalid slack receiver config, workspace: , token: "), + }, + { + Description: "should return error if failed to get workspace channels with slack client", + Setup: func(sc *mocks.SlackCaller, e *mocks.Encryptor) { + sc.EXPECT().GetWorkspaceChannels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("secret.MaskableString")).Return(nil, errors.New("some error")) + }, + Confs: map[string]interface{}{ + "token": secret.MaskableString("key"), + "workspace": "odpf", + }, + Err: errors.New("could not get channels: some error"), + }, + { + Description: "should return nil error if success populating receiver.Receiver", + Setup: func(sc *mocks.SlackCaller, e *mocks.Encryptor) { + sc.EXPECT().GetWorkspaceChannels(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("secret.MaskableString")).Return([]slack.Channel{ + { + ID: "id", + Name: "name", + }, + }, nil) + }, + Confs: map[string]interface{}{ + "token": secret.MaskableString("key"), + "workspace": "odpf", + }, + }, + } + ) + + for _, tc := range testCases { + t.Run(tc.Description, func(t *testing.T) { + var ( + slackClientMock = new(mocks.SlackCaller) + encryptorMock = new(mocks.Encryptor) + ) + + svc := slack.NewPluginService(slack.AppConfig{}, encryptorMock, slack.WithSlackClient(slackClientMock)) + + tc.Setup(slackClientMock, encryptorMock) + + _, err := svc.BuildData(ctx, tc.Confs) + if tc.Err != err { + if tc.Err.Error() != err.Error() { + t.Fatalf("got error %s, expected was %s", err.Error(), tc.Err.Error()) + } + } + + slackClientMock.AssertExpectations(t) + encryptorMock.AssertExpectations(t) + }) + } +} + +func TestService_Send(t *testing.T) { tests := []struct { name string setup func(*mocks.SlackCaller) @@ -81,21 +150,21 @@ func TestNotificationService_Publish(t *testing.T) { tt.setup(mockSlackClient) } - s := slack.NewNotificationService(mockSlackClient, nil) + s := slack.NewPluginService(slack.AppConfig{}, nil, slack.WithSlackClient(mockSlackClient)) - got, err := s.Publish(context.Background(), tt.notificationMessage) + got, err := s.Send(context.Background(), tt.notificationMessage) if (err != nil) != tt.wantErr { - t.Errorf("NotificationService.Publish() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Service.Publish() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.wantRetryable { - t.Errorf("NotificationService.Publish() = %v, want %v", got, tt.wantRetryable) + t.Errorf("Service.Publish() = %v, want %v", got, tt.wantRetryable) } }) } } -func TestNotificationService_PreHookTransformConfigs(t *testing.T) { +func TestService_PreHookQueueTransformConfigs(t *testing.T) { tests := []struct { name string setup func(*mocks.Encryptor) @@ -153,20 +222,20 @@ func TestNotificationService_PreHookTransformConfigs(t *testing.T) { tt.setup(mockEncryptor) } - s := slack.NewNotificationService(nil, mockEncryptor) - got, err := s.PreHookTransformConfigs(context.TODO(), tt.notificationConfigMap) + s := slack.NewPluginService(slack.AppConfig{}, mockEncryptor) + got, err := s.PreHookQueueTransformConfigs(context.TODO(), tt.notificationConfigMap) if (err != nil) != tt.wantErr { - t.Errorf("NotificationService.PreHookTransformConfigs() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Service.PreHookQueueTransformConfigs() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NotificationService.PreHookTransformConfigs() = %v, want %v", got, tt.want) + t.Errorf("Service.PreHookQueueTransformConfigs() = %v, want %v", got, tt.want) } }) } } -func TestNotificationService_PostHookTransformConfigs(t *testing.T) { +func TestService_PostHookQueueTransformConfigs(t *testing.T) { tests := []struct { name string setup func(*mocks.Encryptor) @@ -197,149 +266,7 @@ func TestNotificationService_PostHookTransformConfigs(t *testing.T) { wantErr: true, }, { - name: "should return encrypted slack token if succeed", - - notificationConfigMap: map[string]interface{}{ - "workspace": "a workspace", - "token": secret.MaskableString("a token"), - "channel_name": "channel", - }, - setup: func(e *mocks.Encryptor) { - e.EXPECT().Decrypt(mock.AnythingOfType("secret.MaskableString")).Return(secret.MaskableString("maskable-token"), nil) - }, - want: map[string]interface{}{ - "workspace": "a workspace", - "token": secret.MaskableString("maskable-token"), - "channel_name": "channel", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockEncryptor = new(mocks.Encryptor) - ) - - if tt.setup != nil { - tt.setup(mockEncryptor) - } - - s := slack.NewNotificationService(nil, mockEncryptor) - got, err := s.PostHookTransformConfigs(context.TODO(), tt.notificationConfigMap) - if (err != nil) != tt.wantErr { - t.Errorf("NotificationService.PostHookTransformConfigs() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NotificationService.PostHookTransformConfigs() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSlackNotificationService_PreHookTransformConfigs(t *testing.T) { - tests := []struct { - name string - setup func(*mocks.Encryptor) - notificationConfigMap map[string]interface{} - want map[string]interface{} - wantErr bool - }{ - { - name: "should return error if failed to parse configmap to notification config", - notificationConfigMap: nil, - wantErr: true, - }, - { - name: "should return error if validate notification config failed", - notificationConfigMap: map[string]interface{}{ - "token": 123, - }, - wantErr: true, - }, - { - name: "should return error if slack token encryption failed", - notificationConfigMap: map[string]interface{}{ - "token": secret.MaskableString("a token"), - }, - setup: func(e *mocks.Encryptor) { - e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return("", errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return encrypted slack token if succeed", - - notificationConfigMap: map[string]interface{}{ - "workspace": "a workspace", - "token": secret.MaskableString("a token"), - "channel_name": "channel", - }, - setup: func(e *mocks.Encryptor) { - e.EXPECT().Encrypt(mock.AnythingOfType("secret.MaskableString")).Return(secret.MaskableString("maskable-token"), nil) - }, - want: map[string]interface{}{ - "workspace": "a workspace", - "token": secret.MaskableString("maskable-token"), - "channel_name": "channel", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var ( - mockEncryptor = new(mocks.Encryptor) - ) - - if tt.setup != nil { - tt.setup(mockEncryptor) - } - - s := slack.NewNotificationService(nil, mockEncryptor) - got, err := s.PreHookTransformConfigs(context.TODO(), tt.notificationConfigMap) - if (err != nil) != tt.wantErr { - t.Errorf("SlackNotificationService.PreHookTransformConfigs() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("SlackNotificationService.PreHookTransformConfigs() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSlackNotificationService_PostHookTransformConfigs(t *testing.T) { - tests := []struct { - name string - setup func(*mocks.Encryptor) - notificationConfigMap map[string]interface{} - want map[string]interface{} - wantErr bool - }{ - { - name: "should return error if failed to parse configmap to notification config", - notificationConfigMap: nil, - wantErr: true, - }, - { - name: "should return error if validate notification config failed", - notificationConfigMap: map[string]interface{}{ - "token": 123, - }, - wantErr: true, - }, - { - name: "should return error if slack token decryption failed", - notificationConfigMap: map[string]interface{}{ - "token": secret.MaskableString("a token"), - }, - setup: func(e *mocks.Encryptor) { - e.EXPECT().Decrypt(mock.AnythingOfType("secret.MaskableString")).Return("", errors.New("some error")) - }, - wantErr: true, - }, - { - name: "should return encrypted slack token if succeed", + name: "should return decrypted slack token if succeed", notificationConfigMap: map[string]interface{}{ "workspace": "a workspace", @@ -366,14 +293,14 @@ func TestSlackNotificationService_PostHookTransformConfigs(t *testing.T) { tt.setup(mockEncryptor) } - s := slack.NewNotificationService(nil, mockEncryptor) - got, err := s.PostHookTransformConfigs(context.TODO(), tt.notificationConfigMap) + s := slack.NewPluginService(slack.AppConfig{}, mockEncryptor) + got, err := s.PostHookQueueTransformConfigs(context.TODO(), tt.notificationConfigMap) if (err != nil) != tt.wantErr { - t.Errorf("SlackNotificationService.PostHookTransformConfigs() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Service.PostHookQueueTransformConfigs() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("SlackNotificationService.PostHookTransformConfigs() = %v, want %v", got, tt.want) + t.Errorf("Service.PostHookQueueTransformConfigs() = %v, want %v", got, tt.want) } }) } diff --git a/plugins/receivers/slack/template.go b/plugins/receivers/slack/template.go index e31b6050..9539cc1d 100644 --- a/plugins/receivers/slack/template.go +++ b/plugins/receivers/slack/template.go @@ -3,6 +3,6 @@ package slack import _ "embed" var ( - //go:embed config/default_cortex_alert_template_body.goyaml - defaultCortexAlertTemplateBody string + //go:embed config/default_alert_template_body.goyaml + defaultAlertTemplateBody string ) diff --git a/proto/odpf/siren/v1beta1/siren.pb.go b/proto/odpf/siren/v1beta1/siren.pb.go index f440df54..e65e0dd4 100644 --- a/proto/odpf/siren/v1beta1/siren.pb.go +++ b/proto/odpf/siren/v1beta1/siren.pb.go @@ -1326,8 +1326,8 @@ type ReceiverMetadata struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Configuration map[string]string `protobuf:"bytes,4,rep,name=configuration,proto3" json:"configuration,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Configuration *structpb.Struct `protobuf:"bytes,4,opt,name=configuration,proto3" json:"configuration,omitempty"` } func (x *ReceiverMetadata) Reset() { @@ -1369,7 +1369,7 @@ func (x *ReceiverMetadata) GetId() uint64 { return 0 } -func (x *ReceiverMetadata) GetConfiguration() map[string]string { +func (x *ReceiverMetadata) GetConfiguration() *structpb.Struct { if x != nil { return x.Configuration } @@ -2912,22 +2912,18 @@ func (x *ListAlertsResponse) GetAlerts() []*Alert { return nil } -type CortexAlert struct { +type CreateAlertsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Annotations map[string]string `protobuf:"bytes,1,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` - StartsAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=starts_at,json=startsAt,proto3" json:"starts_at,omitempty"` - EndsAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=ends_at,json=endsAt,proto3" json:"ends_at,omitempty"` - GeneratorUrl string `protobuf:"bytes,6,opt,name=generator_url,json=generatorUrl,proto3" json:"generator_url,omitempty"` - Fingerprint string `protobuf:"bytes,7,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"` + ProviderType string `protobuf:"bytes,1,opt,name=provider_type,json=providerType,proto3" json:"provider_type,omitempty"` + ProviderId uint64 `protobuf:"varint,2,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` + Body *structpb.Struct `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` } -func (x *CortexAlert) Reset() { - *x = CortexAlert{} +func (x *CreateAlertsRequest) Reset() { + *x = CreateAlertsRequest{} if protoimpl.UnsafeEnabled { mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2935,13 +2931,13 @@ func (x *CortexAlert) Reset() { } } -func (x *CortexAlert) String() string { +func (x *CreateAlertsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CortexAlert) ProtoMessage() {} +func (*CreateAlertsRequest) ProtoMessage() {} -func (x *CortexAlert) ProtoReflect() protoreflect.Message { +func (x *CreateAlertsRequest) ProtoReflect() protoreflect.Message { mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2953,140 +2949,33 @@ func (x *CortexAlert) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CortexAlert.ProtoReflect.Descriptor instead. -func (*CortexAlert) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateAlertsRequest.ProtoReflect.Descriptor instead. +func (*CreateAlertsRequest) Descriptor() ([]byte, []int) { return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{50} } -func (x *CortexAlert) GetAnnotations() map[string]string { +func (x *CreateAlertsRequest) GetProviderType() string { if x != nil { - return x.Annotations - } - return nil -} - -func (x *CortexAlert) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *CortexAlert) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *CortexAlert) GetStartsAt() *timestamppb.Timestamp { - if x != nil { - return x.StartsAt - } - return nil -} - -func (x *CortexAlert) GetEndsAt() *timestamppb.Timestamp { - if x != nil { - return x.EndsAt - } - return nil -} - -func (x *CortexAlert) GetGeneratorUrl() string { - if x != nil { - return x.GeneratorUrl + return x.ProviderType } return "" } -func (x *CortexAlert) GetFingerprint() string { - if x != nil { - return x.Fingerprint - } - return "" -} - -type CreateCortexAlertsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ProviderId uint64 `protobuf:"varint,1,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` - Alerts []*CortexAlert `protobuf:"bytes,2,rep,name=alerts,proto3" json:"alerts,omitempty"` - GroupKey string `protobuf:"bytes,3,opt,name=group_key,json=groupKey,proto3" json:"group_key,omitempty"` - ExternalUrl string `protobuf:"bytes,4,opt,name=external_url,json=externalUrl,proto3" json:"external_url,omitempty"` - Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` -} - -func (x *CreateCortexAlertsRequest) Reset() { - *x = CreateCortexAlertsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[51] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateCortexAlertsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateCortexAlertsRequest) ProtoMessage() {} - -func (x *CreateCortexAlertsRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[51] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateCortexAlertsRequest.ProtoReflect.Descriptor instead. -func (*CreateCortexAlertsRequest) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{51} -} - -func (x *CreateCortexAlertsRequest) GetProviderId() uint64 { +func (x *CreateAlertsRequest) GetProviderId() uint64 { if x != nil { return x.ProviderId } return 0 } -func (x *CreateCortexAlertsRequest) GetAlerts() []*CortexAlert { +func (x *CreateAlertsRequest) GetBody() *structpb.Struct { if x != nil { - return x.Alerts + return x.Body } return nil } -func (x *CreateCortexAlertsRequest) GetGroupKey() string { - if x != nil { - return x.GroupKey - } - return "" -} - -func (x *CreateCortexAlertsRequest) GetExternalUrl() string { - if x != nil { - return x.ExternalUrl - } - return "" -} - -func (x *CreateCortexAlertsRequest) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - -type CreateCortexAlertsResponse struct { +type CreateAlertsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -3094,23 +2983,23 @@ type CreateCortexAlertsResponse struct { Alerts []*Alert `protobuf:"bytes,1,rep,name=alerts,proto3" json:"alerts,omitempty"` } -func (x *CreateCortexAlertsResponse) Reset() { - *x = CreateCortexAlertsResponse{} +func (x *CreateAlertsResponse) Reset() { + *x = CreateAlertsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[52] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *CreateCortexAlertsResponse) String() string { +func (x *CreateAlertsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateCortexAlertsResponse) ProtoMessage() {} +func (*CreateAlertsResponse) ProtoMessage() {} -func (x *CreateCortexAlertsResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[52] +func (x *CreateAlertsResponse) ProtoReflect() protoreflect.Message { + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3121,12 +3010,12 @@ func (x *CreateCortexAlertsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateCortexAlertsResponse.ProtoReflect.Descriptor instead. -func (*CreateCortexAlertsResponse) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{52} +// Deprecated: Use CreateAlertsResponse.ProtoReflect.Descriptor instead. +func (*CreateAlertsResponse) Descriptor() ([]byte, []int) { + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{51} } -func (x *CreateCortexAlertsResponse) GetAlerts() []*Alert { +func (x *CreateAlertsResponse) GetAlerts() []*Alert { if x != nil { return x.Alerts } @@ -3147,7 +3036,7 @@ type Annotations struct { func (x *Annotations) Reset() { *x = Annotations{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[53] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3160,7 +3049,7 @@ func (x *Annotations) String() string { func (*Annotations) ProtoMessage() {} func (x *Annotations) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[53] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3173,7 +3062,7 @@ func (x *Annotations) ProtoReflect() protoreflect.Message { // Deprecated: Use Annotations.ProtoReflect.Descriptor instead. func (*Annotations) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{53} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{52} } func (x *Annotations) GetMetricName() string { @@ -3215,7 +3104,7 @@ type Labels struct { func (x *Labels) Reset() { *x = Labels{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[54] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3228,7 +3117,7 @@ func (x *Labels) String() string { func (*Labels) ProtoMessage() {} func (x *Labels) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[54] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3241,7 +3130,7 @@ func (x *Labels) ProtoReflect() protoreflect.Message { // Deprecated: Use Labels.ProtoReflect.Descriptor instead. func (*Labels) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{54} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{53} } func (x *Labels) GetSeverity() string { @@ -3271,7 +3160,7 @@ type Rule struct { func (x *Rule) Reset() { *x = Rule{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[55] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3284,7 +3173,7 @@ func (x *Rule) String() string { func (*Rule) ProtoMessage() {} func (x *Rule) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[55] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3297,7 +3186,7 @@ func (x *Rule) ProtoReflect() protoreflect.Message { // Deprecated: Use Rule.ProtoReflect.Descriptor instead. func (*Rule) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{55} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{54} } func (x *Rule) GetId() uint64 { @@ -3384,7 +3273,7 @@ type Variables struct { func (x *Variables) Reset() { *x = Variables{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[56] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3397,7 +3286,7 @@ func (x *Variables) String() string { func (*Variables) ProtoMessage() {} func (x *Variables) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[56] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3410,7 +3299,7 @@ func (x *Variables) ProtoReflect() protoreflect.Message { // Deprecated: Use Variables.ProtoReflect.Descriptor instead. func (*Variables) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{56} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{55} } func (x *Variables) GetName() string { @@ -3456,7 +3345,7 @@ type ListRulesRequest struct { func (x *ListRulesRequest) Reset() { *x = ListRulesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[57] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3469,7 +3358,7 @@ func (x *ListRulesRequest) String() string { func (*ListRulesRequest) ProtoMessage() {} func (x *ListRulesRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[57] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3482,7 +3371,7 @@ func (x *ListRulesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRulesRequest.ProtoReflect.Descriptor instead. func (*ListRulesRequest) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{57} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{56} } func (x *ListRulesRequest) GetName() string { @@ -3531,7 +3420,7 @@ type ListRulesResponse struct { func (x *ListRulesResponse) Reset() { *x = ListRulesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[58] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3544,7 +3433,7 @@ func (x *ListRulesResponse) String() string { func (*ListRulesResponse) ProtoMessage() {} func (x *ListRulesResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[58] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3557,7 +3446,7 @@ func (x *ListRulesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRulesResponse.ProtoReflect.Descriptor instead. func (*ListRulesResponse) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{58} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{57} } func (x *ListRulesResponse) GetRules() []*Rule { @@ -3583,7 +3472,7 @@ type UpdateRuleRequest struct { func (x *UpdateRuleRequest) Reset() { *x = UpdateRuleRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[59] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3596,7 +3485,7 @@ func (x *UpdateRuleRequest) String() string { func (*UpdateRuleRequest) ProtoMessage() {} func (x *UpdateRuleRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[59] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3609,7 +3498,7 @@ func (x *UpdateRuleRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRuleRequest.ProtoReflect.Descriptor instead. func (*UpdateRuleRequest) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{59} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{58} } func (x *UpdateRuleRequest) GetEnabled() bool { @@ -3665,7 +3554,7 @@ type UpdateRuleResponse struct { func (x *UpdateRuleResponse) Reset() { *x = UpdateRuleResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[60] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3678,7 +3567,7 @@ func (x *UpdateRuleResponse) String() string { func (*UpdateRuleResponse) ProtoMessage() {} func (x *UpdateRuleResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[60] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3691,7 +3580,7 @@ func (x *UpdateRuleResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRuleResponse.ProtoReflect.Descriptor instead. func (*UpdateRuleResponse) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{60} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{59} } func (x *UpdateRuleResponse) GetId() uint64 { @@ -3715,7 +3604,7 @@ type TemplateVariables struct { func (x *TemplateVariables) Reset() { *x = TemplateVariables{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[61] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3728,7 +3617,7 @@ func (x *TemplateVariables) String() string { func (*TemplateVariables) ProtoMessage() {} func (x *TemplateVariables) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[61] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3741,7 +3630,7 @@ func (x *TemplateVariables) ProtoReflect() protoreflect.Message { // Deprecated: Use TemplateVariables.ProtoReflect.Descriptor instead. func (*TemplateVariables) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{61} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{60} } func (x *TemplateVariables) GetName() string { @@ -3789,7 +3678,7 @@ type Template struct { func (x *Template) Reset() { *x = Template{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[62] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3802,7 +3691,7 @@ func (x *Template) String() string { func (*Template) ProtoMessage() {} func (x *Template) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[62] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3815,7 +3704,7 @@ func (x *Template) ProtoReflect() protoreflect.Message { // Deprecated: Use Template.ProtoReflect.Descriptor instead. func (*Template) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{62} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{61} } func (x *Template) GetId() uint64 { @@ -3878,7 +3767,7 @@ type ListTemplatesRequest struct { func (x *ListTemplatesRequest) Reset() { *x = ListTemplatesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[63] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3891,7 +3780,7 @@ func (x *ListTemplatesRequest) String() string { func (*ListTemplatesRequest) ProtoMessage() {} func (x *ListTemplatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[63] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3904,7 +3793,7 @@ func (x *ListTemplatesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTemplatesRequest.ProtoReflect.Descriptor instead. func (*ListTemplatesRequest) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{63} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{62} } func (x *ListTemplatesRequest) GetTag() string { @@ -3925,7 +3814,7 @@ type ListTemplatesResponse struct { func (x *ListTemplatesResponse) Reset() { *x = ListTemplatesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[64] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3938,7 +3827,7 @@ func (x *ListTemplatesResponse) String() string { func (*ListTemplatesResponse) ProtoMessage() {} func (x *ListTemplatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[64] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3951,7 +3840,7 @@ func (x *ListTemplatesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTemplatesResponse.ProtoReflect.Descriptor instead. func (*ListTemplatesResponse) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{64} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{63} } func (x *ListTemplatesResponse) GetTemplates() []*Template { @@ -3976,7 +3865,7 @@ type UpsertTemplateRequest struct { func (x *UpsertTemplateRequest) Reset() { *x = UpsertTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[65] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3989,7 +3878,7 @@ func (x *UpsertTemplateRequest) String() string { func (*UpsertTemplateRequest) ProtoMessage() {} func (x *UpsertTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[65] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4002,7 +3891,7 @@ func (x *UpsertTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertTemplateRequest.ProtoReflect.Descriptor instead. func (*UpsertTemplateRequest) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{65} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{64} } func (x *UpsertTemplateRequest) GetId() uint64 { @@ -4051,7 +3940,7 @@ type UpsertTemplateResponse struct { func (x *UpsertTemplateResponse) Reset() { *x = UpsertTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[66] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4064,7 +3953,7 @@ func (x *UpsertTemplateResponse) String() string { func (*UpsertTemplateResponse) ProtoMessage() {} func (x *UpsertTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[66] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4077,7 +3966,7 @@ func (x *UpsertTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertTemplateResponse.ProtoReflect.Descriptor instead. func (*UpsertTemplateResponse) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{66} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{65} } func (x *UpsertTemplateResponse) GetId() uint64 { @@ -4098,7 +3987,7 @@ type GetTemplateRequest struct { func (x *GetTemplateRequest) Reset() { *x = GetTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[67] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4111,7 +4000,7 @@ func (x *GetTemplateRequest) String() string { func (*GetTemplateRequest) ProtoMessage() {} func (x *GetTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[67] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4124,7 +4013,7 @@ func (x *GetTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTemplateRequest.ProtoReflect.Descriptor instead. func (*GetTemplateRequest) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{67} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{66} } func (x *GetTemplateRequest) GetName() string { @@ -4145,7 +4034,7 @@ type GetTemplateResponse struct { func (x *GetTemplateResponse) Reset() { *x = GetTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[68] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4158,7 +4047,7 @@ func (x *GetTemplateResponse) String() string { func (*GetTemplateResponse) ProtoMessage() {} func (x *GetTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[68] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4171,7 +4060,7 @@ func (x *GetTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTemplateResponse.ProtoReflect.Descriptor instead. func (*GetTemplateResponse) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{68} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{67} } func (x *GetTemplateResponse) GetTemplate() *Template { @@ -4192,7 +4081,7 @@ type DeleteTemplateRequest struct { func (x *DeleteTemplateRequest) Reset() { *x = DeleteTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[69] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4205,7 +4094,7 @@ func (x *DeleteTemplateRequest) String() string { func (*DeleteTemplateRequest) ProtoMessage() {} func (x *DeleteTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[69] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4218,7 +4107,7 @@ func (x *DeleteTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTemplateRequest.ProtoReflect.Descriptor instead. func (*DeleteTemplateRequest) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{69} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{68} } func (x *DeleteTemplateRequest) GetName() string { @@ -4237,7 +4126,7 @@ type DeleteTemplateResponse struct { func (x *DeleteTemplateResponse) Reset() { *x = DeleteTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[70] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4250,7 +4139,7 @@ func (x *DeleteTemplateResponse) String() string { func (*DeleteTemplateResponse) ProtoMessage() {} func (x *DeleteTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[70] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4263,7 +4152,7 @@ func (x *DeleteTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTemplateResponse.ProtoReflect.Descriptor instead. func (*DeleteTemplateResponse) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{70} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{69} } type RenderTemplateRequest struct { @@ -4278,7 +4167,7 @@ type RenderTemplateRequest struct { func (x *RenderTemplateRequest) Reset() { *x = RenderTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[71] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4291,7 +4180,7 @@ func (x *RenderTemplateRequest) String() string { func (*RenderTemplateRequest) ProtoMessage() {} func (x *RenderTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[71] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4304,7 +4193,7 @@ func (x *RenderTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RenderTemplateRequest.ProtoReflect.Descriptor instead. func (*RenderTemplateRequest) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{71} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{70} } func (x *RenderTemplateRequest) GetName() string { @@ -4332,7 +4221,7 @@ type RenderTemplateResponse struct { func (x *RenderTemplateResponse) Reset() { *x = RenderTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[72] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4345,7 +4234,7 @@ func (x *RenderTemplateResponse) String() string { func (*RenderTemplateResponse) ProtoMessage() {} func (x *RenderTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[72] + mi := &file_odpf_siren_v1beta1_siren_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4358,7 +4247,7 @@ func (x *RenderTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RenderTemplateResponse.ProtoReflect.Descriptor instead. func (*RenderTemplateResponse) Descriptor() ([]byte, []int) { - return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{72} + return file_odpf_siren_v1beta1_siren_proto_rawDescGZIP(), []int{71} } func (x *RenderTemplateResponse) GetBody() string { @@ -4579,86 +4468,14 @@ var file_odpf_siren_v1beta1_siren_proto_rawDesc = []byte{ 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x19, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x01, 0x0a, 0x10, 0x52, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x5d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, - 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x40, - 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x9e, 0x03, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x29, 0x0a, 0x03, 0x75, 0x72, 0x6e, 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, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x1c, 0x0a, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x42, 0x0a, 0x09, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x41, - 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, - 0x68, 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, 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, 0x22, 0x1a, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x63, 0x0a, - 0x19, 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, 0x12, 0x46, 0x0a, 0x0d, 0x73, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0xb2, 0x02, 0x0a, 0x19, 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, - 0x12, 0x29, 0x0a, 0x03, 0x75, 0x72, 0x6e, 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, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x42, 0x0a, 0x09, 0x72, 0x65, 0x63, - 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, - 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x04, 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, 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, 0x2e, 0x4d, 0x61, 0x74, 0x63, - 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6d, 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, 0x22, 0x2c, 0x0a, 0x1a, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x28, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, - 0x5f, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x73, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0xc2, 0x02, 0x0a, 0x19, 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, 0x12, 0x0e, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x61, 0x0a, 0x10, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3d, + 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 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, 0x0d, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x9e, 0x03, + 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x03, 0x75, 0x72, 0x6e, 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, @@ -4668,708 +4485,737 @@ var file_odpf_siren_v1beta1_siren_proto_rawDesc = []byte{ 0x76, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x05, 0x6d, - 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x6f, 0x64, 0x70, + 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x05, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6d, 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, 0x22, 0x2c, 0x0a, 0x1a, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x2b, 0x0a, 0x19, 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, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, - 0x22, 0x1c, 0x0a, 0x1a, 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, 0xa3, - 0x03, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x72, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, - 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, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, - 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x74, + 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 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, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 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, 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, 0x22, 0x1a, + 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x63, 0x0a, 0x19, 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, 0x12, 0x46, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0xb2, 0x02, 0x0a, 0x19, 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, 0x12, 0x29, 0x0a, + 0x03, 0x75, 0x72, 0x6e, 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, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x42, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x64, 0x70, 0x66, + 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x05, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x18, 0x04, 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, 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, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x05, 0x6d, 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, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x53, 0x0a, 0x15, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x72, 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, 0x52, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, - 0x73, 0x22, 0xa4, 0x02, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xfa, 0x42, 0x15, 0x72, 0x13, - 0x32, 0x11, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2e, 0x2d, - 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x4d, 0x0a, - 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x0e, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x0e, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x39, 0x0a, - 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 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, 0x28, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x1a, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, - 0x69, 0x64, 0x22, 0x24, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, + 0x69, 0x64, 0x22, 0x28, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5f, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc2, 0x02, + 0x0a, 0x19, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x03, 0x75, + 0x72, 0x6e, 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, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x42, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, + 0x68, 0x18, 0x05, 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, 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, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x05, 0x6d, 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, 0x22, 0x2c, 0x0a, 0x1a, 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, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x2b, 0x0a, 0x19, 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, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, + 0x1a, 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, 0xa3, 0x03, 0x0a, 0x08, + 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x40, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x28, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x2e, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 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, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 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, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, + 0x70, 0x64, 0x61, 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, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 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, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x53, 0x0a, 0x15, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 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, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x22, 0xa4, + 0x02, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xfa, 0x42, 0x15, 0x72, 0x13, 0x32, 0x11, 0x5e, + 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2e, 0x2d, 0x5d, 0x2b, 0x24, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x4d, 0x0a, 0x06, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x0e, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 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, 0x28, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x38, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 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, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, - 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x22, 0xb4, 0x02, 0x0a, 0x15, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x2c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x18, 0xfa, 0x42, 0x15, 0x72, 0x13, 0x32, 0x11, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, - 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2e, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x4d, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 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, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, + 0x24, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x08, + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 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, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x08, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x22, 0xb4, 0x02, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 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, 0x28, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, - 0x65, 0x72, 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, 0x27, 0x0a, 0x15, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x2c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, + 0xfa, 0x42, 0x15, 0x72, 0x13, 0x32, 0x11, 0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, + 0x2d, 0x39, 0x5f, 0x2e, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x4d, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x35, 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, 0x2e, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 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, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 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, 0x28, 0x0a, + 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 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, 0x27, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5a, 0x0a, 0x15, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, - 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5a, 0x0a, - 0x15, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x18, 0x02, 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, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x90, 0x02, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, - 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, - 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, - 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 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, 0x0b, 0x74, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x65, 0x64, 0x41, 0x74, 0x22, 0xd1, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, - 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 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, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x47, 0x0a, 0x12, 0x4c, 0x69, - 0x73, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x31, 0x0a, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x52, 0x06, 0x61, 0x6c, 0x65, - 0x72, 0x74, 0x73, 0x22, 0xee, 0x03, 0x0a, 0x0b, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x41, 0x6c, - 0x65, 0x72, 0x74, 0x12, 0x52, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, - 0x72, 0x74, 0x65, 0x78, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, - 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x5f, 0x61, - 0x74, 0x18, 0x04, 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, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x41, 0x74, 0x12, 0x33, 0x0a, - 0x07, 0x65, 0x6e, 0x64, 0x73, 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, 0x06, 0x65, 0x6e, 0x64, 0x73, - 0x41, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, - 0x75, 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x67, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, - 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, - 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 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, 0xcf, 0x01, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, - 0x6f, 0x72, 0x74, 0x65, 0x78, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x41, - 0x6c, 0x65, 0x72, 0x74, 0x52, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x55, 0x72, 0x6c, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x4f, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, - 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x52, - 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x0b, 0x41, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x22, 0x24, 0x0a, 0x06, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x0a, - 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x22, 0x88, 0x03, 0x0a, 0x04, 0x52, 0x75, - 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x76, 0x61, 0x72, - 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, - 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x09, 0x76, 0x61, 0x72, - 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, + 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, 0x07, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x90, 0x02, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, + 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x72, 0x75, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, - 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x09, 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, 0x36, 0x0a, 0x12, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, + 0x64, 0x41, 0x74, 0x22, 0xd1, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x65, 0x72, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x3c, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 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, + 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, + 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x47, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, + 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x52, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, + 0x22, 0x88, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2b, + 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 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, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x49, 0x0a, 0x14, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x52, 0x06, + 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x0b, 0x41, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x22, 0x24, 0x0a, 0x06, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x22, 0x88, 0x03, 0x0a, 0x04, 0x52, 0x75, 0x6c, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x64, + 0x70, 0x66, 0x2e, 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, + 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, 0x36, 0x0a, 0x12, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x32, 0x02, 0x28, 0x00, + 0x52, 0x11, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x22, 0x84, 0x01, 0x0a, 0x09, 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, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 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, 0xae, 0x01, 0x0a, 0x10, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x73, 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, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x32, 0x02, 0x28, - 0x00, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x22, 0x84, 0x01, 0x0a, 0x09, 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, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 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, 0xae, 0x01, 0x0a, 0x10, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x73, 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, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, - 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x43, 0x0a, 0x11, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 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, 0x05, 0x72, 0x75, 0x6c, 0x65, - 0x73, 0x22, 0xbd, 0x02, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x12, 0x36, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 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, 0x09, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x33, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x04, 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, 0x08, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, - 0x73, 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 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, 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, + 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x43, 0x0a, 0x11, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2e, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 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, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, + 0x22, 0xbd, 0x02, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x12, 0x36, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 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, + 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x09, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 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, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x33, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x04, 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, 0x08, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x64, 0x70, 0x66, 0x2e, 0x73, + 0x69, 0x72, 0x65, 0x6e, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 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, + 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, - 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, 0x32, 0xc9, 0x28, 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, + 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, 0x32, 0xbc, 0x28, 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, 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, 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, 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, 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, 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, + 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, 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, 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, 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, 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, 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, + 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, 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, 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, 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, + 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, 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, 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, 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, + 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, 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, 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, + 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, 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, 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, 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, 0xc4, 0x01, 0x0a, 0x12, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x41, 0x6c, 0x65, 0x72, 0x74, - 0x73, 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, 0x43, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 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, 0x43, 0x6f, 0x72, 0x74, - 0x65, 0x78, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x4f, 0x92, 0x41, 0x1d, 0x0a, 0x05, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x12, 0x14, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x20, 0x61, 0x6c, 0x65, 0x72, - 0x74, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a, 0x22, 0x24, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 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, + 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, - 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, 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, + 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, 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, + 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, 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, 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, - 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, + 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, 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, 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, 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, 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, - 0x42, 0xb2, 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, - 0x54, 0x12, 0x4f, 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, 0x05, 0x30, 0x2e, 0x34, - 0x2e, 0x30, 0x2a, 0x01, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 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, 0x42, 0xb2, 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, 0x54, 0x12, 0x4f, 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, 0x05, 0x30, + 0x2e, 0x34, 0x2e, 0x30, 0x2a, 0x01, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5384,7 +5230,7 @@ func file_odpf_siren_v1beta1_siren_proto_rawDescGZIP() []byte { return file_odpf_siren_v1beta1_siren_proto_rawDescData } -var file_odpf_siren_v1beta1_siren_proto_msgTypes = make([]protoimpl.MessageInfo, 89) +var file_odpf_siren_v1beta1_siren_proto_msgTypes = make([]protoimpl.MessageInfo, 85) var file_odpf_siren_v1beta1_siren_proto_goTypes = []interface{}{ (*Provider)(nil), // 0: odpf.siren.v1beta1.Provider (*ListProvidersRequest)(nil), // 1: odpf.siren.v1beta1.ListProvidersRequest @@ -5436,179 +5282,171 @@ var file_odpf_siren_v1beta1_siren_proto_goTypes = []interface{}{ (*Alert)(nil), // 47: odpf.siren.v1beta1.Alert (*ListAlertsRequest)(nil), // 48: odpf.siren.v1beta1.ListAlertsRequest (*ListAlertsResponse)(nil), // 49: odpf.siren.v1beta1.ListAlertsResponse - (*CortexAlert)(nil), // 50: odpf.siren.v1beta1.CortexAlert - (*CreateCortexAlertsRequest)(nil), // 51: odpf.siren.v1beta1.CreateCortexAlertsRequest - (*CreateCortexAlertsResponse)(nil), // 52: odpf.siren.v1beta1.CreateCortexAlertsResponse - (*Annotations)(nil), // 53: odpf.siren.v1beta1.Annotations - (*Labels)(nil), // 54: odpf.siren.v1beta1.Labels - (*Rule)(nil), // 55: odpf.siren.v1beta1.Rule - (*Variables)(nil), // 56: odpf.siren.v1beta1.Variables - (*ListRulesRequest)(nil), // 57: odpf.siren.v1beta1.ListRulesRequest - (*ListRulesResponse)(nil), // 58: odpf.siren.v1beta1.ListRulesResponse - (*UpdateRuleRequest)(nil), // 59: odpf.siren.v1beta1.UpdateRuleRequest - (*UpdateRuleResponse)(nil), // 60: odpf.siren.v1beta1.UpdateRuleResponse - (*TemplateVariables)(nil), // 61: odpf.siren.v1beta1.TemplateVariables - (*Template)(nil), // 62: odpf.siren.v1beta1.Template - (*ListTemplatesRequest)(nil), // 63: odpf.siren.v1beta1.ListTemplatesRequest - (*ListTemplatesResponse)(nil), // 64: odpf.siren.v1beta1.ListTemplatesResponse - (*UpsertTemplateRequest)(nil), // 65: odpf.siren.v1beta1.UpsertTemplateRequest - (*UpsertTemplateResponse)(nil), // 66: odpf.siren.v1beta1.UpsertTemplateResponse - (*GetTemplateRequest)(nil), // 67: odpf.siren.v1beta1.GetTemplateRequest - (*GetTemplateResponse)(nil), // 68: odpf.siren.v1beta1.GetTemplateResponse - (*DeleteTemplateRequest)(nil), // 69: odpf.siren.v1beta1.DeleteTemplateRequest - (*DeleteTemplateResponse)(nil), // 70: odpf.siren.v1beta1.DeleteTemplateResponse - (*RenderTemplateRequest)(nil), // 71: odpf.siren.v1beta1.RenderTemplateRequest - (*RenderTemplateResponse)(nil), // 72: odpf.siren.v1beta1.RenderTemplateResponse - nil, // 73: odpf.siren.v1beta1.Provider.LabelsEntry - nil, // 74: odpf.siren.v1beta1.CreateProviderRequest.LabelsEntry - nil, // 75: odpf.siren.v1beta1.UpdateProviderRequest.LabelsEntry - nil, // 76: odpf.siren.v1beta1.Namespace.LabelsEntry - nil, // 77: odpf.siren.v1beta1.CreateNamespaceRequest.LabelsEntry - nil, // 78: odpf.siren.v1beta1.UpdateNamespaceRequest.LabelsEntry - nil, // 79: odpf.siren.v1beta1.ReceiverMetadata.ConfigurationEntry - nil, // 80: odpf.siren.v1beta1.Subscription.MatchEntry - nil, // 81: odpf.siren.v1beta1.CreateSubscriptionRequest.MatchEntry - nil, // 82: odpf.siren.v1beta1.UpdateSubscriptionRequest.MatchEntry - nil, // 83: odpf.siren.v1beta1.Receiver.LabelsEntry - nil, // 84: odpf.siren.v1beta1.CreateReceiverRequest.LabelsEntry - nil, // 85: odpf.siren.v1beta1.UpdateReceiverRequest.LabelsEntry - nil, // 86: odpf.siren.v1beta1.CortexAlert.AnnotationsEntry - nil, // 87: odpf.siren.v1beta1.CortexAlert.LabelsEntry - nil, // 88: odpf.siren.v1beta1.RenderTemplateRequest.VariablesEntry - (*structpb.Struct)(nil), // 89: google.protobuf.Struct - (*timestamppb.Timestamp)(nil), // 90: google.protobuf.Timestamp + (*CreateAlertsRequest)(nil), // 50: odpf.siren.v1beta1.CreateAlertsRequest + (*CreateAlertsResponse)(nil), // 51: odpf.siren.v1beta1.CreateAlertsResponse + (*Annotations)(nil), // 52: odpf.siren.v1beta1.Annotations + (*Labels)(nil), // 53: odpf.siren.v1beta1.Labels + (*Rule)(nil), // 54: odpf.siren.v1beta1.Rule + (*Variables)(nil), // 55: odpf.siren.v1beta1.Variables + (*ListRulesRequest)(nil), // 56: odpf.siren.v1beta1.ListRulesRequest + (*ListRulesResponse)(nil), // 57: odpf.siren.v1beta1.ListRulesResponse + (*UpdateRuleRequest)(nil), // 58: odpf.siren.v1beta1.UpdateRuleRequest + (*UpdateRuleResponse)(nil), // 59: odpf.siren.v1beta1.UpdateRuleResponse + (*TemplateVariables)(nil), // 60: odpf.siren.v1beta1.TemplateVariables + (*Template)(nil), // 61: odpf.siren.v1beta1.Template + (*ListTemplatesRequest)(nil), // 62: odpf.siren.v1beta1.ListTemplatesRequest + (*ListTemplatesResponse)(nil), // 63: odpf.siren.v1beta1.ListTemplatesResponse + (*UpsertTemplateRequest)(nil), // 64: odpf.siren.v1beta1.UpsertTemplateRequest + (*UpsertTemplateResponse)(nil), // 65: odpf.siren.v1beta1.UpsertTemplateResponse + (*GetTemplateRequest)(nil), // 66: odpf.siren.v1beta1.GetTemplateRequest + (*GetTemplateResponse)(nil), // 67: odpf.siren.v1beta1.GetTemplateResponse + (*DeleteTemplateRequest)(nil), // 68: odpf.siren.v1beta1.DeleteTemplateRequest + (*DeleteTemplateResponse)(nil), // 69: odpf.siren.v1beta1.DeleteTemplateResponse + (*RenderTemplateRequest)(nil), // 70: odpf.siren.v1beta1.RenderTemplateRequest + (*RenderTemplateResponse)(nil), // 71: odpf.siren.v1beta1.RenderTemplateResponse + nil, // 72: odpf.siren.v1beta1.Provider.LabelsEntry + nil, // 73: odpf.siren.v1beta1.CreateProviderRequest.LabelsEntry + nil, // 74: odpf.siren.v1beta1.UpdateProviderRequest.LabelsEntry + nil, // 75: odpf.siren.v1beta1.Namespace.LabelsEntry + nil, // 76: odpf.siren.v1beta1.CreateNamespaceRequest.LabelsEntry + nil, // 77: odpf.siren.v1beta1.UpdateNamespaceRequest.LabelsEntry + nil, // 78: odpf.siren.v1beta1.Subscription.MatchEntry + nil, // 79: odpf.siren.v1beta1.CreateSubscriptionRequest.MatchEntry + nil, // 80: odpf.siren.v1beta1.UpdateSubscriptionRequest.MatchEntry + nil, // 81: odpf.siren.v1beta1.Receiver.LabelsEntry + nil, // 82: odpf.siren.v1beta1.CreateReceiverRequest.LabelsEntry + nil, // 83: odpf.siren.v1beta1.UpdateReceiverRequest.LabelsEntry + nil, // 84: odpf.siren.v1beta1.RenderTemplateRequest.VariablesEntry + (*structpb.Struct)(nil), // 85: google.protobuf.Struct + (*timestamppb.Timestamp)(nil), // 86: google.protobuf.Timestamp } var file_odpf_siren_v1beta1_siren_proto_depIdxs = []int32{ - 89, // 0: odpf.siren.v1beta1.Provider.credentials:type_name -> google.protobuf.Struct - 73, // 1: odpf.siren.v1beta1.Provider.labels:type_name -> odpf.siren.v1beta1.Provider.LabelsEntry - 90, // 2: odpf.siren.v1beta1.Provider.created_at:type_name -> google.protobuf.Timestamp - 90, // 3: odpf.siren.v1beta1.Provider.updated_at:type_name -> google.protobuf.Timestamp + 85, // 0: odpf.siren.v1beta1.Provider.credentials:type_name -> google.protobuf.Struct + 72, // 1: odpf.siren.v1beta1.Provider.labels:type_name -> odpf.siren.v1beta1.Provider.LabelsEntry + 86, // 2: odpf.siren.v1beta1.Provider.created_at:type_name -> google.protobuf.Timestamp + 86, // 3: odpf.siren.v1beta1.Provider.updated_at:type_name -> google.protobuf.Timestamp 0, // 4: odpf.siren.v1beta1.ListProvidersResponse.providers:type_name -> odpf.siren.v1beta1.Provider - 89, // 5: odpf.siren.v1beta1.CreateProviderRequest.credentials:type_name -> google.protobuf.Struct - 74, // 6: odpf.siren.v1beta1.CreateProviderRequest.labels:type_name -> odpf.siren.v1beta1.CreateProviderRequest.LabelsEntry + 85, // 5: odpf.siren.v1beta1.CreateProviderRequest.credentials:type_name -> google.protobuf.Struct + 73, // 6: odpf.siren.v1beta1.CreateProviderRequest.labels:type_name -> odpf.siren.v1beta1.CreateProviderRequest.LabelsEntry 0, // 7: odpf.siren.v1beta1.GetProviderResponse.provider:type_name -> odpf.siren.v1beta1.Provider - 89, // 8: odpf.siren.v1beta1.UpdateProviderRequest.credentials:type_name -> google.protobuf.Struct - 75, // 9: odpf.siren.v1beta1.UpdateProviderRequest.labels:type_name -> odpf.siren.v1beta1.UpdateProviderRequest.LabelsEntry - 89, // 10: odpf.siren.v1beta1.Namespace.credentials:type_name -> google.protobuf.Struct - 76, // 11: odpf.siren.v1beta1.Namespace.labels:type_name -> odpf.siren.v1beta1.Namespace.LabelsEntry - 90, // 12: odpf.siren.v1beta1.Namespace.created_at:type_name -> google.protobuf.Timestamp - 90, // 13: odpf.siren.v1beta1.Namespace.updated_at:type_name -> google.protobuf.Timestamp + 85, // 8: odpf.siren.v1beta1.UpdateProviderRequest.credentials:type_name -> google.protobuf.Struct + 74, // 9: odpf.siren.v1beta1.UpdateProviderRequest.labels:type_name -> odpf.siren.v1beta1.UpdateProviderRequest.LabelsEntry + 85, // 10: odpf.siren.v1beta1.Namespace.credentials:type_name -> google.protobuf.Struct + 75, // 11: odpf.siren.v1beta1.Namespace.labels:type_name -> odpf.siren.v1beta1.Namespace.LabelsEntry + 86, // 12: odpf.siren.v1beta1.Namespace.created_at:type_name -> google.protobuf.Timestamp + 86, // 13: odpf.siren.v1beta1.Namespace.updated_at:type_name -> google.protobuf.Timestamp 11, // 14: odpf.siren.v1beta1.ListNamespacesResponse.namespaces:type_name -> odpf.siren.v1beta1.Namespace - 89, // 15: odpf.siren.v1beta1.CreateNamespaceRequest.credentials:type_name -> google.protobuf.Struct - 77, // 16: odpf.siren.v1beta1.CreateNamespaceRequest.labels:type_name -> odpf.siren.v1beta1.CreateNamespaceRequest.LabelsEntry - 90, // 17: odpf.siren.v1beta1.CreateNamespaceRequest.created_at:type_name -> google.protobuf.Timestamp - 90, // 18: odpf.siren.v1beta1.CreateNamespaceRequest.updated_at:type_name -> google.protobuf.Timestamp + 85, // 15: odpf.siren.v1beta1.CreateNamespaceRequest.credentials:type_name -> google.protobuf.Struct + 76, // 16: odpf.siren.v1beta1.CreateNamespaceRequest.labels:type_name -> odpf.siren.v1beta1.CreateNamespaceRequest.LabelsEntry + 86, // 17: odpf.siren.v1beta1.CreateNamespaceRequest.created_at:type_name -> google.protobuf.Timestamp + 86, // 18: odpf.siren.v1beta1.CreateNamespaceRequest.updated_at:type_name -> google.protobuf.Timestamp 11, // 19: odpf.siren.v1beta1.GetNamespaceResponse.namespace:type_name -> odpf.siren.v1beta1.Namespace - 89, // 20: odpf.siren.v1beta1.UpdateNamespaceRequest.credentials:type_name -> google.protobuf.Struct - 78, // 21: odpf.siren.v1beta1.UpdateNamespaceRequest.labels:type_name -> odpf.siren.v1beta1.UpdateNamespaceRequest.LabelsEntry - 79, // 22: odpf.siren.v1beta1.ReceiverMetadata.configuration:type_name -> odpf.siren.v1beta1.ReceiverMetadata.ConfigurationEntry + 85, // 20: odpf.siren.v1beta1.UpdateNamespaceRequest.credentials:type_name -> google.protobuf.Struct + 77, // 21: odpf.siren.v1beta1.UpdateNamespaceRequest.labels:type_name -> odpf.siren.v1beta1.UpdateNamespaceRequest.LabelsEntry + 85, // 22: odpf.siren.v1beta1.ReceiverMetadata.configuration:type_name -> google.protobuf.Struct 22, // 23: odpf.siren.v1beta1.Subscription.receivers:type_name -> odpf.siren.v1beta1.ReceiverMetadata - 80, // 24: odpf.siren.v1beta1.Subscription.match:type_name -> odpf.siren.v1beta1.Subscription.MatchEntry - 90, // 25: odpf.siren.v1beta1.Subscription.created_at:type_name -> google.protobuf.Timestamp - 90, // 26: odpf.siren.v1beta1.Subscription.updated_at:type_name -> google.protobuf.Timestamp + 78, // 24: odpf.siren.v1beta1.Subscription.match:type_name -> odpf.siren.v1beta1.Subscription.MatchEntry + 86, // 25: odpf.siren.v1beta1.Subscription.created_at:type_name -> google.protobuf.Timestamp + 86, // 26: odpf.siren.v1beta1.Subscription.updated_at:type_name -> google.protobuf.Timestamp 23, // 27: odpf.siren.v1beta1.ListSubscriptionsResponse.subscriptions:type_name -> odpf.siren.v1beta1.Subscription 22, // 28: odpf.siren.v1beta1.CreateSubscriptionRequest.receivers:type_name -> odpf.siren.v1beta1.ReceiverMetadata - 81, // 29: odpf.siren.v1beta1.CreateSubscriptionRequest.match:type_name -> odpf.siren.v1beta1.CreateSubscriptionRequest.MatchEntry + 79, // 29: odpf.siren.v1beta1.CreateSubscriptionRequest.match:type_name -> odpf.siren.v1beta1.CreateSubscriptionRequest.MatchEntry 23, // 30: odpf.siren.v1beta1.GetSubscriptionResponse.subscription:type_name -> odpf.siren.v1beta1.Subscription 22, // 31: odpf.siren.v1beta1.UpdateSubscriptionRequest.receivers:type_name -> odpf.siren.v1beta1.ReceiverMetadata - 82, // 32: odpf.siren.v1beta1.UpdateSubscriptionRequest.match:type_name -> odpf.siren.v1beta1.UpdateSubscriptionRequest.MatchEntry - 83, // 33: odpf.siren.v1beta1.Receiver.labels:type_name -> odpf.siren.v1beta1.Receiver.LabelsEntry - 89, // 34: odpf.siren.v1beta1.Receiver.configurations:type_name -> google.protobuf.Struct - 89, // 35: odpf.siren.v1beta1.Receiver.data:type_name -> google.protobuf.Struct - 90, // 36: odpf.siren.v1beta1.Receiver.created_at:type_name -> google.protobuf.Timestamp - 90, // 37: odpf.siren.v1beta1.Receiver.updated_at:type_name -> google.protobuf.Timestamp + 80, // 32: odpf.siren.v1beta1.UpdateSubscriptionRequest.match:type_name -> odpf.siren.v1beta1.UpdateSubscriptionRequest.MatchEntry + 81, // 33: odpf.siren.v1beta1.Receiver.labels:type_name -> odpf.siren.v1beta1.Receiver.LabelsEntry + 85, // 34: odpf.siren.v1beta1.Receiver.configurations:type_name -> google.protobuf.Struct + 85, // 35: odpf.siren.v1beta1.Receiver.data:type_name -> google.protobuf.Struct + 86, // 36: odpf.siren.v1beta1.Receiver.created_at:type_name -> google.protobuf.Timestamp + 86, // 37: odpf.siren.v1beta1.Receiver.updated_at:type_name -> google.protobuf.Timestamp 34, // 38: odpf.siren.v1beta1.ListReceiversResponse.receivers:type_name -> odpf.siren.v1beta1.Receiver - 84, // 39: odpf.siren.v1beta1.CreateReceiverRequest.labels:type_name -> odpf.siren.v1beta1.CreateReceiverRequest.LabelsEntry - 89, // 40: odpf.siren.v1beta1.CreateReceiverRequest.configurations:type_name -> google.protobuf.Struct + 82, // 39: odpf.siren.v1beta1.CreateReceiverRequest.labels:type_name -> odpf.siren.v1beta1.CreateReceiverRequest.LabelsEntry + 85, // 40: odpf.siren.v1beta1.CreateReceiverRequest.configurations:type_name -> google.protobuf.Struct 34, // 41: odpf.siren.v1beta1.GetReceiverResponse.receiver:type_name -> odpf.siren.v1beta1.Receiver - 85, // 42: odpf.siren.v1beta1.UpdateReceiverRequest.labels:type_name -> odpf.siren.v1beta1.UpdateReceiverRequest.LabelsEntry - 89, // 43: odpf.siren.v1beta1.UpdateReceiverRequest.configurations:type_name -> google.protobuf.Struct - 89, // 44: odpf.siren.v1beta1.NotifyReceiverRequest.payload:type_name -> google.protobuf.Struct - 90, // 45: odpf.siren.v1beta1.Alert.triggered_at:type_name -> google.protobuf.Timestamp + 83, // 42: odpf.siren.v1beta1.UpdateReceiverRequest.labels:type_name -> odpf.siren.v1beta1.UpdateReceiverRequest.LabelsEntry + 85, // 43: odpf.siren.v1beta1.UpdateReceiverRequest.configurations:type_name -> google.protobuf.Struct + 85, // 44: odpf.siren.v1beta1.NotifyReceiverRequest.payload:type_name -> google.protobuf.Struct + 86, // 45: odpf.siren.v1beta1.Alert.triggered_at:type_name -> google.protobuf.Timestamp 47, // 46: odpf.siren.v1beta1.ListAlertsResponse.alerts:type_name -> odpf.siren.v1beta1.Alert - 86, // 47: odpf.siren.v1beta1.CortexAlert.annotations:type_name -> odpf.siren.v1beta1.CortexAlert.AnnotationsEntry - 87, // 48: odpf.siren.v1beta1.CortexAlert.labels:type_name -> odpf.siren.v1beta1.CortexAlert.LabelsEntry - 90, // 49: odpf.siren.v1beta1.CortexAlert.starts_at:type_name -> google.protobuf.Timestamp - 90, // 50: odpf.siren.v1beta1.CortexAlert.ends_at:type_name -> google.protobuf.Timestamp - 50, // 51: odpf.siren.v1beta1.CreateCortexAlertsRequest.alerts:type_name -> odpf.siren.v1beta1.CortexAlert - 47, // 52: odpf.siren.v1beta1.CreateCortexAlertsResponse.alerts:type_name -> odpf.siren.v1beta1.Alert - 56, // 53: odpf.siren.v1beta1.Rule.variables:type_name -> odpf.siren.v1beta1.Variables - 90, // 54: odpf.siren.v1beta1.Rule.created_at:type_name -> google.protobuf.Timestamp - 90, // 55: odpf.siren.v1beta1.Rule.updated_at:type_name -> google.protobuf.Timestamp - 55, // 56: odpf.siren.v1beta1.ListRulesResponse.rules:type_name -> odpf.siren.v1beta1.Rule - 56, // 57: odpf.siren.v1beta1.UpdateRuleRequest.variables:type_name -> odpf.siren.v1beta1.Variables - 90, // 58: odpf.siren.v1beta1.Template.created_at:type_name -> google.protobuf.Timestamp - 90, // 59: odpf.siren.v1beta1.Template.updated_at:type_name -> google.protobuf.Timestamp - 61, // 60: odpf.siren.v1beta1.Template.variables:type_name -> odpf.siren.v1beta1.TemplateVariables - 62, // 61: odpf.siren.v1beta1.ListTemplatesResponse.templates:type_name -> odpf.siren.v1beta1.Template - 61, // 62: odpf.siren.v1beta1.UpsertTemplateRequest.variables:type_name -> odpf.siren.v1beta1.TemplateVariables - 62, // 63: odpf.siren.v1beta1.GetTemplateResponse.template:type_name -> odpf.siren.v1beta1.Template - 88, // 64: odpf.siren.v1beta1.RenderTemplateRequest.variables:type_name -> odpf.siren.v1beta1.RenderTemplateRequest.VariablesEntry - 1, // 65: odpf.siren.v1beta1.SirenService.ListProviders:input_type -> odpf.siren.v1beta1.ListProvidersRequest - 3, // 66: odpf.siren.v1beta1.SirenService.CreateProvider:input_type -> odpf.siren.v1beta1.CreateProviderRequest - 5, // 67: odpf.siren.v1beta1.SirenService.GetProvider:input_type -> odpf.siren.v1beta1.GetProviderRequest - 7, // 68: odpf.siren.v1beta1.SirenService.UpdateProvider:input_type -> odpf.siren.v1beta1.UpdateProviderRequest - 9, // 69: odpf.siren.v1beta1.SirenService.DeleteProvider:input_type -> odpf.siren.v1beta1.DeleteProviderRequest - 45, // 70: odpf.siren.v1beta1.SirenService.NotifyReceiver:input_type -> odpf.siren.v1beta1.NotifyReceiverRequest - 12, // 71: odpf.siren.v1beta1.SirenService.ListNamespaces:input_type -> odpf.siren.v1beta1.ListNamespacesRequest - 14, // 72: odpf.siren.v1beta1.SirenService.CreateNamespace:input_type -> odpf.siren.v1beta1.CreateNamespaceRequest - 16, // 73: odpf.siren.v1beta1.SirenService.GetNamespace:input_type -> odpf.siren.v1beta1.GetNamespaceRequest - 18, // 74: odpf.siren.v1beta1.SirenService.UpdateNamespace:input_type -> odpf.siren.v1beta1.UpdateNamespaceRequest - 20, // 75: odpf.siren.v1beta1.SirenService.DeleteNamespace:input_type -> odpf.siren.v1beta1.DeleteNamespaceRequest - 24, // 76: odpf.siren.v1beta1.SirenService.ListSubscriptions:input_type -> odpf.siren.v1beta1.ListSubscriptionsRequest - 26, // 77: odpf.siren.v1beta1.SirenService.CreateSubscription:input_type -> odpf.siren.v1beta1.CreateSubscriptionRequest - 28, // 78: odpf.siren.v1beta1.SirenService.GetSubscription:input_type -> odpf.siren.v1beta1.GetSubscriptionRequest - 30, // 79: odpf.siren.v1beta1.SirenService.UpdateSubscription:input_type -> odpf.siren.v1beta1.UpdateSubscriptionRequest - 32, // 80: odpf.siren.v1beta1.SirenService.DeleteSubscription:input_type -> odpf.siren.v1beta1.DeleteSubscriptionRequest - 35, // 81: odpf.siren.v1beta1.SirenService.ListReceivers:input_type -> odpf.siren.v1beta1.ListReceiversRequest - 37, // 82: odpf.siren.v1beta1.SirenService.CreateReceiver:input_type -> odpf.siren.v1beta1.CreateReceiverRequest - 39, // 83: odpf.siren.v1beta1.SirenService.GetReceiver:input_type -> odpf.siren.v1beta1.GetReceiverRequest - 41, // 84: odpf.siren.v1beta1.SirenService.UpdateReceiver:input_type -> odpf.siren.v1beta1.UpdateReceiverRequest - 43, // 85: odpf.siren.v1beta1.SirenService.DeleteReceiver:input_type -> odpf.siren.v1beta1.DeleteReceiverRequest - 48, // 86: odpf.siren.v1beta1.SirenService.ListAlerts:input_type -> odpf.siren.v1beta1.ListAlertsRequest - 51, // 87: odpf.siren.v1beta1.SirenService.CreateCortexAlerts:input_type -> odpf.siren.v1beta1.CreateCortexAlertsRequest - 57, // 88: odpf.siren.v1beta1.SirenService.ListRules:input_type -> odpf.siren.v1beta1.ListRulesRequest - 59, // 89: odpf.siren.v1beta1.SirenService.UpdateRule:input_type -> odpf.siren.v1beta1.UpdateRuleRequest - 63, // 90: odpf.siren.v1beta1.SirenService.ListTemplates:input_type -> odpf.siren.v1beta1.ListTemplatesRequest - 67, // 91: odpf.siren.v1beta1.SirenService.GetTemplate:input_type -> odpf.siren.v1beta1.GetTemplateRequest - 65, // 92: odpf.siren.v1beta1.SirenService.UpsertTemplate:input_type -> odpf.siren.v1beta1.UpsertTemplateRequest - 69, // 93: odpf.siren.v1beta1.SirenService.DeleteTemplate:input_type -> odpf.siren.v1beta1.DeleteTemplateRequest - 71, // 94: odpf.siren.v1beta1.SirenService.RenderTemplate:input_type -> odpf.siren.v1beta1.RenderTemplateRequest - 2, // 95: odpf.siren.v1beta1.SirenService.ListProviders:output_type -> odpf.siren.v1beta1.ListProvidersResponse - 4, // 96: odpf.siren.v1beta1.SirenService.CreateProvider:output_type -> odpf.siren.v1beta1.CreateProviderResponse - 6, // 97: odpf.siren.v1beta1.SirenService.GetProvider:output_type -> odpf.siren.v1beta1.GetProviderResponse - 8, // 98: odpf.siren.v1beta1.SirenService.UpdateProvider:output_type -> odpf.siren.v1beta1.UpdateProviderResponse - 10, // 99: odpf.siren.v1beta1.SirenService.DeleteProvider:output_type -> odpf.siren.v1beta1.DeleteProviderResponse - 46, // 100: odpf.siren.v1beta1.SirenService.NotifyReceiver:output_type -> odpf.siren.v1beta1.NotifyReceiverResponse - 13, // 101: odpf.siren.v1beta1.SirenService.ListNamespaces:output_type -> odpf.siren.v1beta1.ListNamespacesResponse - 15, // 102: odpf.siren.v1beta1.SirenService.CreateNamespace:output_type -> odpf.siren.v1beta1.CreateNamespaceResponse - 17, // 103: odpf.siren.v1beta1.SirenService.GetNamespace:output_type -> odpf.siren.v1beta1.GetNamespaceResponse - 19, // 104: odpf.siren.v1beta1.SirenService.UpdateNamespace:output_type -> odpf.siren.v1beta1.UpdateNamespaceResponse - 21, // 105: odpf.siren.v1beta1.SirenService.DeleteNamespace:output_type -> odpf.siren.v1beta1.DeleteNamespaceResponse - 25, // 106: odpf.siren.v1beta1.SirenService.ListSubscriptions:output_type -> odpf.siren.v1beta1.ListSubscriptionsResponse - 27, // 107: odpf.siren.v1beta1.SirenService.CreateSubscription:output_type -> odpf.siren.v1beta1.CreateSubscriptionResponse - 29, // 108: odpf.siren.v1beta1.SirenService.GetSubscription:output_type -> odpf.siren.v1beta1.GetSubscriptionResponse - 31, // 109: odpf.siren.v1beta1.SirenService.UpdateSubscription:output_type -> odpf.siren.v1beta1.UpdateSubscriptionResponse - 33, // 110: odpf.siren.v1beta1.SirenService.DeleteSubscription:output_type -> odpf.siren.v1beta1.DeleteSubscriptionResponse - 36, // 111: odpf.siren.v1beta1.SirenService.ListReceivers:output_type -> odpf.siren.v1beta1.ListReceiversResponse - 38, // 112: odpf.siren.v1beta1.SirenService.CreateReceiver:output_type -> odpf.siren.v1beta1.CreateReceiverResponse - 40, // 113: odpf.siren.v1beta1.SirenService.GetReceiver:output_type -> odpf.siren.v1beta1.GetReceiverResponse - 42, // 114: odpf.siren.v1beta1.SirenService.UpdateReceiver:output_type -> odpf.siren.v1beta1.UpdateReceiverResponse - 44, // 115: odpf.siren.v1beta1.SirenService.DeleteReceiver:output_type -> odpf.siren.v1beta1.DeleteReceiverResponse - 49, // 116: odpf.siren.v1beta1.SirenService.ListAlerts:output_type -> odpf.siren.v1beta1.ListAlertsResponse - 52, // 117: odpf.siren.v1beta1.SirenService.CreateCortexAlerts:output_type -> odpf.siren.v1beta1.CreateCortexAlertsResponse - 58, // 118: odpf.siren.v1beta1.SirenService.ListRules:output_type -> odpf.siren.v1beta1.ListRulesResponse - 60, // 119: odpf.siren.v1beta1.SirenService.UpdateRule:output_type -> odpf.siren.v1beta1.UpdateRuleResponse - 64, // 120: odpf.siren.v1beta1.SirenService.ListTemplates:output_type -> odpf.siren.v1beta1.ListTemplatesResponse - 68, // 121: odpf.siren.v1beta1.SirenService.GetTemplate:output_type -> odpf.siren.v1beta1.GetTemplateResponse - 66, // 122: odpf.siren.v1beta1.SirenService.UpsertTemplate:output_type -> odpf.siren.v1beta1.UpsertTemplateResponse - 70, // 123: odpf.siren.v1beta1.SirenService.DeleteTemplate:output_type -> odpf.siren.v1beta1.DeleteTemplateResponse - 72, // 124: odpf.siren.v1beta1.SirenService.RenderTemplate:output_type -> odpf.siren.v1beta1.RenderTemplateResponse - 95, // [95:125] is the sub-list for method output_type - 65, // [65:95] is the sub-list for method input_type - 65, // [65:65] is the sub-list for extension type_name - 65, // [65:65] is the sub-list for extension extendee - 0, // [0:65] is the sub-list for field type_name + 85, // 47: odpf.siren.v1beta1.CreateAlertsRequest.body:type_name -> google.protobuf.Struct + 47, // 48: odpf.siren.v1beta1.CreateAlertsResponse.alerts:type_name -> odpf.siren.v1beta1.Alert + 55, // 49: odpf.siren.v1beta1.Rule.variables:type_name -> odpf.siren.v1beta1.Variables + 86, // 50: odpf.siren.v1beta1.Rule.created_at:type_name -> google.protobuf.Timestamp + 86, // 51: odpf.siren.v1beta1.Rule.updated_at:type_name -> google.protobuf.Timestamp + 54, // 52: odpf.siren.v1beta1.ListRulesResponse.rules:type_name -> odpf.siren.v1beta1.Rule + 55, // 53: odpf.siren.v1beta1.UpdateRuleRequest.variables:type_name -> odpf.siren.v1beta1.Variables + 86, // 54: odpf.siren.v1beta1.Template.created_at:type_name -> google.protobuf.Timestamp + 86, // 55: odpf.siren.v1beta1.Template.updated_at:type_name -> google.protobuf.Timestamp + 60, // 56: odpf.siren.v1beta1.Template.variables:type_name -> odpf.siren.v1beta1.TemplateVariables + 61, // 57: odpf.siren.v1beta1.ListTemplatesResponse.templates:type_name -> odpf.siren.v1beta1.Template + 60, // 58: odpf.siren.v1beta1.UpsertTemplateRequest.variables:type_name -> odpf.siren.v1beta1.TemplateVariables + 61, // 59: odpf.siren.v1beta1.GetTemplateResponse.template:type_name -> odpf.siren.v1beta1.Template + 84, // 60: odpf.siren.v1beta1.RenderTemplateRequest.variables:type_name -> odpf.siren.v1beta1.RenderTemplateRequest.VariablesEntry + 1, // 61: odpf.siren.v1beta1.SirenService.ListProviders:input_type -> odpf.siren.v1beta1.ListProvidersRequest + 3, // 62: odpf.siren.v1beta1.SirenService.CreateProvider:input_type -> odpf.siren.v1beta1.CreateProviderRequest + 5, // 63: odpf.siren.v1beta1.SirenService.GetProvider:input_type -> odpf.siren.v1beta1.GetProviderRequest + 7, // 64: odpf.siren.v1beta1.SirenService.UpdateProvider:input_type -> odpf.siren.v1beta1.UpdateProviderRequest + 9, // 65: odpf.siren.v1beta1.SirenService.DeleteProvider:input_type -> odpf.siren.v1beta1.DeleteProviderRequest + 45, // 66: odpf.siren.v1beta1.SirenService.NotifyReceiver:input_type -> odpf.siren.v1beta1.NotifyReceiverRequest + 12, // 67: odpf.siren.v1beta1.SirenService.ListNamespaces:input_type -> odpf.siren.v1beta1.ListNamespacesRequest + 14, // 68: odpf.siren.v1beta1.SirenService.CreateNamespace:input_type -> odpf.siren.v1beta1.CreateNamespaceRequest + 16, // 69: odpf.siren.v1beta1.SirenService.GetNamespace:input_type -> odpf.siren.v1beta1.GetNamespaceRequest + 18, // 70: odpf.siren.v1beta1.SirenService.UpdateNamespace:input_type -> odpf.siren.v1beta1.UpdateNamespaceRequest + 20, // 71: odpf.siren.v1beta1.SirenService.DeleteNamespace:input_type -> odpf.siren.v1beta1.DeleteNamespaceRequest + 24, // 72: odpf.siren.v1beta1.SirenService.ListSubscriptions:input_type -> odpf.siren.v1beta1.ListSubscriptionsRequest + 26, // 73: odpf.siren.v1beta1.SirenService.CreateSubscription:input_type -> odpf.siren.v1beta1.CreateSubscriptionRequest + 28, // 74: odpf.siren.v1beta1.SirenService.GetSubscription:input_type -> odpf.siren.v1beta1.GetSubscriptionRequest + 30, // 75: odpf.siren.v1beta1.SirenService.UpdateSubscription:input_type -> odpf.siren.v1beta1.UpdateSubscriptionRequest + 32, // 76: odpf.siren.v1beta1.SirenService.DeleteSubscription:input_type -> odpf.siren.v1beta1.DeleteSubscriptionRequest + 35, // 77: odpf.siren.v1beta1.SirenService.ListReceivers:input_type -> odpf.siren.v1beta1.ListReceiversRequest + 37, // 78: odpf.siren.v1beta1.SirenService.CreateReceiver:input_type -> odpf.siren.v1beta1.CreateReceiverRequest + 39, // 79: odpf.siren.v1beta1.SirenService.GetReceiver:input_type -> odpf.siren.v1beta1.GetReceiverRequest + 41, // 80: odpf.siren.v1beta1.SirenService.UpdateReceiver:input_type -> odpf.siren.v1beta1.UpdateReceiverRequest + 43, // 81: odpf.siren.v1beta1.SirenService.DeleteReceiver:input_type -> odpf.siren.v1beta1.DeleteReceiverRequest + 48, // 82: odpf.siren.v1beta1.SirenService.ListAlerts:input_type -> odpf.siren.v1beta1.ListAlertsRequest + 50, // 83: odpf.siren.v1beta1.SirenService.CreateAlerts:input_type -> odpf.siren.v1beta1.CreateAlertsRequest + 56, // 84: odpf.siren.v1beta1.SirenService.ListRules:input_type -> odpf.siren.v1beta1.ListRulesRequest + 58, // 85: odpf.siren.v1beta1.SirenService.UpdateRule:input_type -> odpf.siren.v1beta1.UpdateRuleRequest + 62, // 86: odpf.siren.v1beta1.SirenService.ListTemplates:input_type -> odpf.siren.v1beta1.ListTemplatesRequest + 66, // 87: odpf.siren.v1beta1.SirenService.GetTemplate:input_type -> odpf.siren.v1beta1.GetTemplateRequest + 64, // 88: odpf.siren.v1beta1.SirenService.UpsertTemplate:input_type -> odpf.siren.v1beta1.UpsertTemplateRequest + 68, // 89: odpf.siren.v1beta1.SirenService.DeleteTemplate:input_type -> odpf.siren.v1beta1.DeleteTemplateRequest + 70, // 90: odpf.siren.v1beta1.SirenService.RenderTemplate:input_type -> odpf.siren.v1beta1.RenderTemplateRequest + 2, // 91: odpf.siren.v1beta1.SirenService.ListProviders:output_type -> odpf.siren.v1beta1.ListProvidersResponse + 4, // 92: odpf.siren.v1beta1.SirenService.CreateProvider:output_type -> odpf.siren.v1beta1.CreateProviderResponse + 6, // 93: odpf.siren.v1beta1.SirenService.GetProvider:output_type -> odpf.siren.v1beta1.GetProviderResponse + 8, // 94: odpf.siren.v1beta1.SirenService.UpdateProvider:output_type -> odpf.siren.v1beta1.UpdateProviderResponse + 10, // 95: odpf.siren.v1beta1.SirenService.DeleteProvider:output_type -> odpf.siren.v1beta1.DeleteProviderResponse + 46, // 96: odpf.siren.v1beta1.SirenService.NotifyReceiver:output_type -> odpf.siren.v1beta1.NotifyReceiverResponse + 13, // 97: odpf.siren.v1beta1.SirenService.ListNamespaces:output_type -> odpf.siren.v1beta1.ListNamespacesResponse + 15, // 98: odpf.siren.v1beta1.SirenService.CreateNamespace:output_type -> odpf.siren.v1beta1.CreateNamespaceResponse + 17, // 99: odpf.siren.v1beta1.SirenService.GetNamespace:output_type -> odpf.siren.v1beta1.GetNamespaceResponse + 19, // 100: odpf.siren.v1beta1.SirenService.UpdateNamespace:output_type -> odpf.siren.v1beta1.UpdateNamespaceResponse + 21, // 101: odpf.siren.v1beta1.SirenService.DeleteNamespace:output_type -> odpf.siren.v1beta1.DeleteNamespaceResponse + 25, // 102: odpf.siren.v1beta1.SirenService.ListSubscriptions:output_type -> odpf.siren.v1beta1.ListSubscriptionsResponse + 27, // 103: odpf.siren.v1beta1.SirenService.CreateSubscription:output_type -> odpf.siren.v1beta1.CreateSubscriptionResponse + 29, // 104: odpf.siren.v1beta1.SirenService.GetSubscription:output_type -> odpf.siren.v1beta1.GetSubscriptionResponse + 31, // 105: odpf.siren.v1beta1.SirenService.UpdateSubscription:output_type -> odpf.siren.v1beta1.UpdateSubscriptionResponse + 33, // 106: odpf.siren.v1beta1.SirenService.DeleteSubscription:output_type -> odpf.siren.v1beta1.DeleteSubscriptionResponse + 36, // 107: odpf.siren.v1beta1.SirenService.ListReceivers:output_type -> odpf.siren.v1beta1.ListReceiversResponse + 38, // 108: odpf.siren.v1beta1.SirenService.CreateReceiver:output_type -> odpf.siren.v1beta1.CreateReceiverResponse + 40, // 109: odpf.siren.v1beta1.SirenService.GetReceiver:output_type -> odpf.siren.v1beta1.GetReceiverResponse + 42, // 110: odpf.siren.v1beta1.SirenService.UpdateReceiver:output_type -> odpf.siren.v1beta1.UpdateReceiverResponse + 44, // 111: odpf.siren.v1beta1.SirenService.DeleteReceiver:output_type -> odpf.siren.v1beta1.DeleteReceiverResponse + 49, // 112: odpf.siren.v1beta1.SirenService.ListAlerts:output_type -> odpf.siren.v1beta1.ListAlertsResponse + 51, // 113: odpf.siren.v1beta1.SirenService.CreateAlerts:output_type -> odpf.siren.v1beta1.CreateAlertsResponse + 57, // 114: odpf.siren.v1beta1.SirenService.ListRules:output_type -> odpf.siren.v1beta1.ListRulesResponse + 59, // 115: odpf.siren.v1beta1.SirenService.UpdateRule:output_type -> odpf.siren.v1beta1.UpdateRuleResponse + 63, // 116: odpf.siren.v1beta1.SirenService.ListTemplates:output_type -> odpf.siren.v1beta1.ListTemplatesResponse + 67, // 117: odpf.siren.v1beta1.SirenService.GetTemplate:output_type -> odpf.siren.v1beta1.GetTemplateResponse + 65, // 118: odpf.siren.v1beta1.SirenService.UpsertTemplate:output_type -> odpf.siren.v1beta1.UpsertTemplateResponse + 69, // 119: odpf.siren.v1beta1.SirenService.DeleteTemplate:output_type -> odpf.siren.v1beta1.DeleteTemplateResponse + 71, // 120: odpf.siren.v1beta1.SirenService.RenderTemplate:output_type -> odpf.siren.v1beta1.RenderTemplateResponse + 91, // [91:121] is the sub-list for method output_type + 61, // [61:91] is the sub-list for method input_type + 61, // [61:61] is the sub-list for extension type_name + 61, // [61:61] is the sub-list for extension extendee + 0, // [0:61] is the sub-list for field type_name } func init() { file_odpf_siren_v1beta1_siren_proto_init() } @@ -6218,7 +6056,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { } } file_odpf_siren_v1beta1_siren_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CortexAlert); i { + switch v := v.(*CreateAlertsRequest); i { case 0: return &v.state case 1: @@ -6230,7 +6068,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { } } file_odpf_siren_v1beta1_siren_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateCortexAlertsRequest); i { + switch v := v.(*CreateAlertsResponse); i { case 0: return &v.state case 1: @@ -6242,18 +6080,6 @@ func file_odpf_siren_v1beta1_siren_proto_init() { } } file_odpf_siren_v1beta1_siren_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateCortexAlertsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_odpf_siren_v1beta1_siren_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Annotations); i { case 0: return &v.state @@ -6265,7 +6091,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Labels); i { case 0: return &v.state @@ -6277,7 +6103,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Rule); i { case 0: return &v.state @@ -6289,7 +6115,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Variables); i { case 0: return &v.state @@ -6301,7 +6127,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListRulesRequest); i { case 0: return &v.state @@ -6313,7 +6139,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListRulesResponse); i { case 0: return &v.state @@ -6325,7 +6151,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpdateRuleRequest); i { case 0: return &v.state @@ -6337,7 +6163,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpdateRuleResponse); i { case 0: return &v.state @@ -6349,7 +6175,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TemplateVariables); i { case 0: return &v.state @@ -6361,7 +6187,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Template); i { case 0: return &v.state @@ -6373,7 +6199,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListTemplatesRequest); i { case 0: return &v.state @@ -6385,7 +6211,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListTemplatesResponse); i { case 0: return &v.state @@ -6397,7 +6223,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpsertTemplateRequest); i { case 0: return &v.state @@ -6409,7 +6235,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpsertTemplateResponse); i { case 0: return &v.state @@ -6421,7 +6247,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetTemplateRequest); i { case 0: return &v.state @@ -6433,7 +6259,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetTemplateResponse); i { case 0: return &v.state @@ -6445,7 +6271,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteTemplateRequest); i { case 0: return &v.state @@ -6457,7 +6283,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteTemplateResponse); i { case 0: return &v.state @@ -6469,7 +6295,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RenderTemplateRequest); i { case 0: return &v.state @@ -6481,7 +6307,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { return nil } } - file_odpf_siren_v1beta1_siren_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { + file_odpf_siren_v1beta1_siren_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RenderTemplateResponse); i { case 0: return &v.state @@ -6500,7 +6326,7 @@ func file_odpf_siren_v1beta1_siren_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_odpf_siren_v1beta1_siren_proto_rawDesc, NumEnums: 0, - NumMessages: 89, + NumMessages: 85, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/odpf/siren/v1beta1/siren.pb.gw.go b/proto/odpf/siren/v1beta1/siren.pb.gw.go index 629a635b..316fc4dd 100644 --- a/proto/odpf/siren/v1beta1/siren.pb.gw.go +++ b/proto/odpf/siren/v1beta1/siren.pb.gw.go @@ -1103,15 +1103,15 @@ func local_request_SirenService_ListAlerts_0(ctx context.Context, marshaler runt } -func request_SirenService_CreateCortexAlerts_0(ctx context.Context, marshaler runtime.Marshaler, client SirenServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq CreateCortexAlertsRequest +func request_SirenService_CreateAlerts_0(ctx context.Context, marshaler runtime.Marshaler, client SirenServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateAlertsRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Body); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1122,6 +1122,16 @@ func request_SirenService_CreateCortexAlerts_0(ctx context.Context, marshaler ru _ = err ) + val, ok = pathParams["provider_type"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_type") + } + + protoReq.ProviderType, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_type", err) + } + val, ok = pathParams["provider_id"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_id") @@ -1132,20 +1142,20 @@ func request_SirenService_CreateCortexAlerts_0(ctx context.Context, marshaler ru return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_id", err) } - msg, err := client.CreateCortexAlerts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.CreateAlerts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_SirenService_CreateCortexAlerts_0(ctx context.Context, marshaler runtime.Marshaler, server SirenServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq CreateCortexAlertsRequest +func local_request_SirenService_CreateAlerts_0(ctx context.Context, marshaler runtime.Marshaler, server SirenServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateAlertsRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Body); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1156,6 +1166,16 @@ func local_request_SirenService_CreateCortexAlerts_0(ctx context.Context, marsha _ = err ) + val, ok = pathParams["provider_type"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_type") + } + + protoReq.ProviderType, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_type", err) + } + val, ok = pathParams["provider_id"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_id") @@ -1166,7 +1186,7 @@ func local_request_SirenService_CreateCortexAlerts_0(ctx context.Context, marsha return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_id", err) } - msg, err := server.CreateCortexAlerts(ctx, &protoReq) + msg, err := server.CreateAlerts(ctx, &protoReq) return msg, metadata, err } @@ -2039,7 +2059,7 @@ func RegisterSirenServiceHandlerServer(ctx context.Context, mux *runtime.ServeMu }) - mux.Handle("POST", pattern_SirenService_CreateCortexAlerts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_SirenService_CreateAlerts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -2047,12 +2067,12 @@ func RegisterSirenServiceHandlerServer(ctx context.Context, mux *runtime.ServeMu inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.siren.v1beta1.SirenService/CreateCortexAlerts", runtime.WithHTTPPathPattern("/v1beta1/alerts/cortex/{provider_id}")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/odpf.siren.v1beta1.SirenService/CreateAlerts", runtime.WithHTTPPathPattern("/v1beta1/alerts/{provider_type}/{provider_id}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_SirenService_CreateCortexAlerts_0(annotatedContext, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_SirenService_CreateAlerts_0(annotatedContext, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { @@ -2060,7 +2080,7 @@ func RegisterSirenServiceHandlerServer(ctx context.Context, mux *runtime.ServeMu return } - forward_SirenService_CreateCortexAlerts_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_SirenService_CreateAlerts_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -2764,25 +2784,25 @@ func RegisterSirenServiceHandlerClient(ctx context.Context, mux *runtime.ServeMu }) - mux.Handle("POST", pattern_SirenService_CreateCortexAlerts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_SirenService_CreateAlerts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/odpf.siren.v1beta1.SirenService/CreateCortexAlerts", runtime.WithHTTPPathPattern("/v1beta1/alerts/cortex/{provider_id}")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/odpf.siren.v1beta1.SirenService/CreateAlerts", runtime.WithHTTPPathPattern("/v1beta1/alerts/{provider_type}/{provider_id}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_SirenService_CreateCortexAlerts_0(annotatedContext, inboundMarshaler, client, req, pathParams) + resp, md, err := request_SirenService_CreateAlerts_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_SirenService_CreateCortexAlerts_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_SirenService_CreateAlerts_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -2988,7 +3008,7 @@ var ( pattern_SirenService_ListAlerts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 1, 0, 4, 1, 5, 3}, []string{"v1beta1", "alerts", "provider_type", "provider_id"}, "")) - pattern_SirenService_CreateCortexAlerts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1beta1", "alerts", "cortex", "provider_id"}, "")) + pattern_SirenService_CreateAlerts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 1, 0, 4, 1, 5, 3}, []string{"v1beta1", "alerts", "provider_type", "provider_id"}, "")) pattern_SirenService_ListRules_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1beta1", "rules"}, "")) @@ -3050,7 +3070,7 @@ var ( forward_SirenService_ListAlerts_0 = runtime.ForwardResponseMessage - forward_SirenService_CreateCortexAlerts_0 = runtime.ForwardResponseMessage + forward_SirenService_CreateAlerts_0 = runtime.ForwardResponseMessage forward_SirenService_ListRules_0 = runtime.ForwardResponseMessage diff --git a/proto/odpf/siren/v1beta1/siren.pb.validate.go b/proto/odpf/siren/v1beta1/siren.pb.validate.go index d906ec8c..3896051e 100644 --- a/proto/odpf/siren/v1beta1/siren.pb.validate.go +++ b/proto/odpf/siren/v1beta1/siren.pb.validate.go @@ -2931,7 +2931,34 @@ func (m *ReceiverMetadata) validate(all bool) error { // no validation rules for Id - // no validation rules for Configuration + if all { + switch v := interface{}(m.GetConfiguration()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ReceiverMetadataValidationError{ + field: "Configuration", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ReceiverMetadataValidationError{ + field: "Configuration", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetConfiguration()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ReceiverMetadataValidationError{ + field: "Configuration", + reason: "embedded message failed validation", + cause: err, + } + } + } if len(errors) > 0 { return ReceiverMetadataMultiError(errors) @@ -6431,249 +6458,74 @@ var _ interface { ErrorName() string } = ListAlertsResponseValidationError{} -// Validate checks the field values on CortexAlert with the rules defined in -// the proto definition for this message. If any rules are violated, the first -// error encountered is returned, or nil if there are no violations. -func (m *CortexAlert) Validate() error { +// Validate checks the field values on CreateAlertsRequest with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *CreateAlertsRequest) Validate() error { return m.validate(false) } -// ValidateAll checks the field values on CortexAlert with the rules defined in -// the proto definition for this message. If any rules are violated, the -// result is a list of violation errors wrapped in CortexAlertMultiError, or -// nil if none found. -func (m *CortexAlert) ValidateAll() error { +// ValidateAll checks the field values on CreateAlertsRequest with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// CreateAlertsRequestMultiError, or nil if none found. +func (m *CreateAlertsRequest) ValidateAll() error { return m.validate(true) } -func (m *CortexAlert) validate(all bool) error { +func (m *CreateAlertsRequest) validate(all bool) error { if m == nil { return nil } var errors []error - // no validation rules for Annotations - - // no validation rules for Labels - - // no validation rules for Status + // no validation rules for ProviderType - if all { - switch v := interface{}(m.GetStartsAt()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, CortexAlertValidationError{ - field: "StartsAt", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, CortexAlertValidationError{ - field: "StartsAt", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetStartsAt()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return CortexAlertValidationError{ - field: "StartsAt", - reason: "embedded message failed validation", - cause: err, - } - } - } + // no validation rules for ProviderId if all { - switch v := interface{}(m.GetEndsAt()).(type) { + switch v := interface{}(m.GetBody()).(type) { case interface{ ValidateAll() error }: if err := v.ValidateAll(); err != nil { - errors = append(errors, CortexAlertValidationError{ - field: "EndsAt", + errors = append(errors, CreateAlertsRequestValidationError{ + field: "Body", reason: "embedded message failed validation", cause: err, }) } case interface{ Validate() error }: if err := v.Validate(); err != nil { - errors = append(errors, CortexAlertValidationError{ - field: "EndsAt", + errors = append(errors, CreateAlertsRequestValidationError{ + field: "Body", reason: "embedded message failed validation", cause: err, }) } } - } else if v, ok := interface{}(m.GetEndsAt()).(interface{ Validate() error }); ok { + } else if v, ok := interface{}(m.GetBody()).(interface{ Validate() error }); ok { if err := v.Validate(); err != nil { - return CortexAlertValidationError{ - field: "EndsAt", + return CreateAlertsRequestValidationError{ + field: "Body", reason: "embedded message failed validation", cause: err, } } } - // no validation rules for GeneratorUrl - - // no validation rules for Fingerprint - if len(errors) > 0 { - return CortexAlertMultiError(errors) + return CreateAlertsRequestMultiError(errors) } return nil } -// CortexAlertMultiError is an error wrapping multiple validation errors -// returned by CortexAlert.ValidateAll() if the designated constraints aren't met. -type CortexAlertMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m CortexAlertMultiError) Error() string { - var msgs []string - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m CortexAlertMultiError) AllErrors() []error { return m } - -// CortexAlertValidationError is the validation error returned by -// CortexAlert.Validate if the designated constraints aren't met. -type CortexAlertValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e CortexAlertValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e CortexAlertValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e CortexAlertValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e CortexAlertValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e CortexAlertValidationError) ErrorName() string { return "CortexAlertValidationError" } - -// Error satisfies the builtin error interface -func (e CortexAlertValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sCortexAlert.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = CortexAlertValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = CortexAlertValidationError{} - -// Validate checks the field values on CreateCortexAlertsRequest with the rules -// defined in the proto definition for this message. If any rules are -// violated, the first error encountered is returned, or nil if there are no violations. -func (m *CreateCortexAlertsRequest) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on CreateCortexAlertsRequest with the -// rules defined in the proto definition for this message. If any rules are -// violated, the result is a list of violation errors wrapped in -// CreateCortexAlertsRequestMultiError, or nil if none found. -func (m *CreateCortexAlertsRequest) ValidateAll() error { - return m.validate(true) -} - -func (m *CreateCortexAlertsRequest) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - // no validation rules for ProviderId - - for idx, item := range m.GetAlerts() { - _, _ = idx, item - - if all { - switch v := interface{}(item).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, CreateCortexAlertsRequestValidationError{ - field: fmt.Sprintf("Alerts[%v]", idx), - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, CreateCortexAlertsRequestValidationError{ - field: fmt.Sprintf("Alerts[%v]", idx), - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return CreateCortexAlertsRequestValidationError{ - field: fmt.Sprintf("Alerts[%v]", idx), - reason: "embedded message failed validation", - cause: err, - } - } - } - - } - - // no validation rules for GroupKey - - // no validation rules for ExternalUrl - - // no validation rules for Version - - if len(errors) > 0 { - return CreateCortexAlertsRequestMultiError(errors) - } - return nil -} - -// CreateCortexAlertsRequestMultiError is an error wrapping multiple validation -// errors returned by CreateCortexAlertsRequest.ValidateAll() if the -// designated constraints aren't met. -type CreateCortexAlertsRequestMultiError []error +// CreateAlertsRequestMultiError is an error wrapping multiple validation +// errors returned by CreateAlertsRequest.ValidateAll() if the designated +// constraints aren't met. +type CreateAlertsRequestMultiError []error // Error returns a concatenation of all the error messages it wraps. -func (m CreateCortexAlertsRequestMultiError) Error() string { +func (m CreateAlertsRequestMultiError) Error() string { var msgs []string for _, err := range m { msgs = append(msgs, err.Error()) @@ -6682,11 +6534,11 @@ func (m CreateCortexAlertsRequestMultiError) Error() string { } // AllErrors returns a list of validation violation errors. -func (m CreateCortexAlertsRequestMultiError) AllErrors() []error { return m } +func (m CreateAlertsRequestMultiError) AllErrors() []error { return m } -// CreateCortexAlertsRequestValidationError is the validation error returned by -// CreateCortexAlertsRequest.Validate if the designated constraints aren't met. -type CreateCortexAlertsRequestValidationError struct { +// CreateAlertsRequestValidationError is the validation error returned by +// CreateAlertsRequest.Validate if the designated constraints aren't met. +type CreateAlertsRequestValidationError struct { field string reason string cause error @@ -6694,24 +6546,24 @@ type CreateCortexAlertsRequestValidationError struct { } // Field function returns field value. -func (e CreateCortexAlertsRequestValidationError) Field() string { return e.field } +func (e CreateAlertsRequestValidationError) Field() string { return e.field } // Reason function returns reason value. -func (e CreateCortexAlertsRequestValidationError) Reason() string { return e.reason } +func (e CreateAlertsRequestValidationError) Reason() string { return e.reason } // Cause function returns cause value. -func (e CreateCortexAlertsRequestValidationError) Cause() error { return e.cause } +func (e CreateAlertsRequestValidationError) Cause() error { return e.cause } // Key function returns key value. -func (e CreateCortexAlertsRequestValidationError) Key() bool { return e.key } +func (e CreateAlertsRequestValidationError) Key() bool { return e.key } // ErrorName returns error name. -func (e CreateCortexAlertsRequestValidationError) ErrorName() string { - return "CreateCortexAlertsRequestValidationError" +func (e CreateAlertsRequestValidationError) ErrorName() string { + return "CreateAlertsRequestValidationError" } // Error satisfies the builtin error interface -func (e CreateCortexAlertsRequestValidationError) Error() string { +func (e CreateAlertsRequestValidationError) Error() string { cause := "" if e.cause != nil { cause = fmt.Sprintf(" | caused by: %v", e.cause) @@ -6723,14 +6575,14 @@ func (e CreateCortexAlertsRequestValidationError) Error() string { } return fmt.Sprintf( - "invalid %sCreateCortexAlertsRequest.%s: %s%s", + "invalid %sCreateAlertsRequest.%s: %s%s", key, e.field, e.reason, cause) } -var _ error = CreateCortexAlertsRequestValidationError{} +var _ error = CreateAlertsRequestValidationError{} var _ interface { Field() string @@ -6738,24 +6590,24 @@ var _ interface { Key() bool Cause() error ErrorName() string -} = CreateCortexAlertsRequestValidationError{} +} = CreateAlertsRequestValidationError{} -// Validate checks the field values on CreateCortexAlertsResponse with the -// rules defined in the proto definition for this message. If any rules are +// Validate checks the field values on CreateAlertsResponse with the rules +// defined in the proto definition for this message. If any rules are // violated, the first error encountered is returned, or nil if there are no violations. -func (m *CreateCortexAlertsResponse) Validate() error { +func (m *CreateAlertsResponse) Validate() error { return m.validate(false) } -// ValidateAll checks the field values on CreateCortexAlertsResponse with the -// rules defined in the proto definition for this message. If any rules are +// ValidateAll checks the field values on CreateAlertsResponse with the rules +// defined in the proto definition for this message. If any rules are // violated, the result is a list of violation errors wrapped in -// CreateCortexAlertsResponseMultiError, or nil if none found. -func (m *CreateCortexAlertsResponse) ValidateAll() error { +// CreateAlertsResponseMultiError, or nil if none found. +func (m *CreateAlertsResponse) ValidateAll() error { return m.validate(true) } -func (m *CreateCortexAlertsResponse) validate(all bool) error { +func (m *CreateAlertsResponse) validate(all bool) error { if m == nil { return nil } @@ -6769,7 +6621,7 @@ func (m *CreateCortexAlertsResponse) validate(all bool) error { switch v := interface{}(item).(type) { case interface{ ValidateAll() error }: if err := v.ValidateAll(); err != nil { - errors = append(errors, CreateCortexAlertsResponseValidationError{ + errors = append(errors, CreateAlertsResponseValidationError{ field: fmt.Sprintf("Alerts[%v]", idx), reason: "embedded message failed validation", cause: err, @@ -6777,7 +6629,7 @@ func (m *CreateCortexAlertsResponse) validate(all bool) error { } case interface{ Validate() error }: if err := v.Validate(); err != nil { - errors = append(errors, CreateCortexAlertsResponseValidationError{ + errors = append(errors, CreateAlertsResponseValidationError{ field: fmt.Sprintf("Alerts[%v]", idx), reason: "embedded message failed validation", cause: err, @@ -6786,7 +6638,7 @@ func (m *CreateCortexAlertsResponse) validate(all bool) error { } } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { if err := v.Validate(); err != nil { - return CreateCortexAlertsResponseValidationError{ + return CreateAlertsResponseValidationError{ field: fmt.Sprintf("Alerts[%v]", idx), reason: "embedded message failed validation", cause: err, @@ -6797,18 +6649,18 @@ func (m *CreateCortexAlertsResponse) validate(all bool) error { } if len(errors) > 0 { - return CreateCortexAlertsResponseMultiError(errors) + return CreateAlertsResponseMultiError(errors) } return nil } -// CreateCortexAlertsResponseMultiError is an error wrapping multiple -// validation errors returned by CreateCortexAlertsResponse.ValidateAll() if -// the designated constraints aren't met. -type CreateCortexAlertsResponseMultiError []error +// CreateAlertsResponseMultiError is an error wrapping multiple validation +// errors returned by CreateAlertsResponse.ValidateAll() if the designated +// constraints aren't met. +type CreateAlertsResponseMultiError []error // Error returns a concatenation of all the error messages it wraps. -func (m CreateCortexAlertsResponseMultiError) Error() string { +func (m CreateAlertsResponseMultiError) Error() string { var msgs []string for _, err := range m { msgs = append(msgs, err.Error()) @@ -6817,11 +6669,11 @@ func (m CreateCortexAlertsResponseMultiError) Error() string { } // AllErrors returns a list of validation violation errors. -func (m CreateCortexAlertsResponseMultiError) AllErrors() []error { return m } +func (m CreateAlertsResponseMultiError) AllErrors() []error { return m } -// CreateCortexAlertsResponseValidationError is the validation error returned -// by CreateCortexAlertsResponse.Validate if the designated constraints aren't met. -type CreateCortexAlertsResponseValidationError struct { +// CreateAlertsResponseValidationError is the validation error returned by +// CreateAlertsResponse.Validate if the designated constraints aren't met. +type CreateAlertsResponseValidationError struct { field string reason string cause error @@ -6829,24 +6681,24 @@ type CreateCortexAlertsResponseValidationError struct { } // Field function returns field value. -func (e CreateCortexAlertsResponseValidationError) Field() string { return e.field } +func (e CreateAlertsResponseValidationError) Field() string { return e.field } // Reason function returns reason value. -func (e CreateCortexAlertsResponseValidationError) Reason() string { return e.reason } +func (e CreateAlertsResponseValidationError) Reason() string { return e.reason } // Cause function returns cause value. -func (e CreateCortexAlertsResponseValidationError) Cause() error { return e.cause } +func (e CreateAlertsResponseValidationError) Cause() error { return e.cause } // Key function returns key value. -func (e CreateCortexAlertsResponseValidationError) Key() bool { return e.key } +func (e CreateAlertsResponseValidationError) Key() bool { return e.key } // ErrorName returns error name. -func (e CreateCortexAlertsResponseValidationError) ErrorName() string { - return "CreateCortexAlertsResponseValidationError" +func (e CreateAlertsResponseValidationError) ErrorName() string { + return "CreateAlertsResponseValidationError" } // Error satisfies the builtin error interface -func (e CreateCortexAlertsResponseValidationError) Error() string { +func (e CreateAlertsResponseValidationError) Error() string { cause := "" if e.cause != nil { cause = fmt.Sprintf(" | caused by: %v", e.cause) @@ -6858,14 +6710,14 @@ func (e CreateCortexAlertsResponseValidationError) Error() string { } return fmt.Sprintf( - "invalid %sCreateCortexAlertsResponse.%s: %s%s", + "invalid %sCreateAlertsResponse.%s: %s%s", key, e.field, e.reason, cause) } -var _ error = CreateCortexAlertsResponseValidationError{} +var _ error = CreateAlertsResponseValidationError{} var _ interface { Field() string @@ -6873,7 +6725,7 @@ var _ interface { Key() bool Cause() error ErrorName() string -} = CreateCortexAlertsResponseValidationError{} +} = CreateAlertsResponseValidationError{} // Validate checks the field values on Annotations with the rules defined in // the proto definition for this message. If any rules are violated, the first diff --git a/proto/odpf/siren/v1beta1/siren_grpc.pb.go b/proto/odpf/siren/v1beta1/siren_grpc.pb.go index 56c3b32f..34b0c557 100644 --- a/proto/odpf/siren/v1beta1/siren_grpc.pb.go +++ b/proto/odpf/siren/v1beta1/siren_grpc.pb.go @@ -40,7 +40,7 @@ type SirenServiceClient interface { UpdateReceiver(ctx context.Context, in *UpdateReceiverRequest, opts ...grpc.CallOption) (*UpdateReceiverResponse, error) DeleteReceiver(ctx context.Context, in *DeleteReceiverRequest, opts ...grpc.CallOption) (*DeleteReceiverResponse, error) ListAlerts(ctx context.Context, in *ListAlertsRequest, opts ...grpc.CallOption) (*ListAlertsResponse, error) - CreateCortexAlerts(ctx context.Context, in *CreateCortexAlertsRequest, opts ...grpc.CallOption) (*CreateCortexAlertsResponse, error) + CreateAlerts(ctx context.Context, in *CreateAlertsRequest, opts ...grpc.CallOption) (*CreateAlertsResponse, error) ListRules(ctx context.Context, in *ListRulesRequest, opts ...grpc.CallOption) (*ListRulesResponse, error) UpdateRule(ctx context.Context, in *UpdateRuleRequest, opts ...grpc.CallOption) (*UpdateRuleResponse, error) ListTemplates(ctx context.Context, in *ListTemplatesRequest, opts ...grpc.CallOption) (*ListTemplatesResponse, error) @@ -256,9 +256,9 @@ func (c *sirenServiceClient) ListAlerts(ctx context.Context, in *ListAlertsReque return out, nil } -func (c *sirenServiceClient) CreateCortexAlerts(ctx context.Context, in *CreateCortexAlertsRequest, opts ...grpc.CallOption) (*CreateCortexAlertsResponse, error) { - out := new(CreateCortexAlertsResponse) - err := c.cc.Invoke(ctx, "/odpf.siren.v1beta1.SirenService/CreateCortexAlerts", in, out, opts...) +func (c *sirenServiceClient) CreateAlerts(ctx context.Context, in *CreateAlertsRequest, opts ...grpc.CallOption) (*CreateAlertsResponse, error) { + out := new(CreateAlertsResponse) + err := c.cc.Invoke(ctx, "/odpf.siren.v1beta1.SirenService/CreateAlerts", in, out, opts...) if err != nil { return nil, err } @@ -354,7 +354,7 @@ type SirenServiceServer interface { UpdateReceiver(context.Context, *UpdateReceiverRequest) (*UpdateReceiverResponse, error) DeleteReceiver(context.Context, *DeleteReceiverRequest) (*DeleteReceiverResponse, error) ListAlerts(context.Context, *ListAlertsRequest) (*ListAlertsResponse, error) - CreateCortexAlerts(context.Context, *CreateCortexAlertsRequest) (*CreateCortexAlertsResponse, error) + CreateAlerts(context.Context, *CreateAlertsRequest) (*CreateAlertsResponse, error) ListRules(context.Context, *ListRulesRequest) (*ListRulesResponse, error) UpdateRule(context.Context, *UpdateRuleRequest) (*UpdateRuleResponse, error) ListTemplates(context.Context, *ListTemplatesRequest) (*ListTemplatesResponse, error) @@ -435,8 +435,8 @@ func (UnimplementedSirenServiceServer) DeleteReceiver(context.Context, *DeleteRe func (UnimplementedSirenServiceServer) ListAlerts(context.Context, *ListAlertsRequest) (*ListAlertsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListAlerts not implemented") } -func (UnimplementedSirenServiceServer) CreateCortexAlerts(context.Context, *CreateCortexAlertsRequest) (*CreateCortexAlertsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateCortexAlerts not implemented") +func (UnimplementedSirenServiceServer) CreateAlerts(context.Context, *CreateAlertsRequest) (*CreateAlertsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateAlerts not implemented") } func (UnimplementedSirenServiceServer) ListRules(context.Context, *ListRulesRequest) (*ListRulesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListRules not implemented") @@ -868,20 +868,20 @@ func _SirenService_ListAlerts_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _SirenService_CreateCortexAlerts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateCortexAlertsRequest) +func _SirenService_CreateAlerts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAlertsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(SirenServiceServer).CreateCortexAlerts(ctx, in) + return srv.(SirenServiceServer).CreateAlerts(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/odpf.siren.v1beta1.SirenService/CreateCortexAlerts", + FullMethod: "/odpf.siren.v1beta1.SirenService/CreateAlerts", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SirenServiceServer).CreateCortexAlerts(ctx, req.(*CreateCortexAlertsRequest)) + return srv.(SirenServiceServer).CreateAlerts(ctx, req.(*CreateAlertsRequest)) } return interceptor(ctx, in, info, handler) } @@ -1108,8 +1108,8 @@ var SirenService_ServiceDesc = grpc.ServiceDesc{ Handler: _SirenService_ListAlerts_Handler, }, { - MethodName: "CreateCortexAlerts", - Handler: _SirenService_CreateCortexAlerts_Handler, + MethodName: "CreateAlerts", + Handler: _SirenService_CreateAlerts_Handler, }, { MethodName: "ListRules", diff --git a/proto/siren.swagger.yaml b/proto/siren.swagger.yaml index 46ee3507..9ede0e34 100644 --- a/proto/siren.swagger.yaml +++ b/proto/siren.swagger.yaml @@ -53,20 +53,23 @@ paths: format: uint64 tags: - Alert - /v1beta1/alerts/cortex/{provider_id}: post: - summary: create cortex alerts - operationId: SirenService_CreateCortexAlerts + summary: create alerts + operationId: SirenService_CreateAlerts responses: "200": description: A successful response. schema: - $ref: '#/definitions/CreateCortexAlertsResponse' + $ref: '#/definitions/CreateAlertsResponse' default: description: An unexpected error response. schema: $ref: '#/definitions/Status' parameters: + - name: provider_type + in: path + required: true + type: string - name: provider_id in: path required: true @@ -77,17 +80,6 @@ paths: required: true schema: type: object - properties: - alerts: - type: array - items: - $ref: '#/definitions/CortexAlert' - external_url: - type: string - group_key: - type: string - version: - type: string tags: - Alert /v1beta1/namespaces: @@ -777,30 +769,7 @@ definitions: '@type': type: string additionalProperties: {} - CortexAlert: - type: object - properties: - annotations: - type: object - additionalProperties: - type: string - ends_at: - type: string - format: date-time - fingerprint: - type: string - generator_url: - type: string - labels: - type: object - additionalProperties: - type: string - starts_at: - type: string - format: date-time - status: - type: string - CreateCortexAlertsResponse: + CreateAlertsResponse: type: object properties: alerts: @@ -1077,8 +1046,6 @@ definitions: properties: configuration: type: object - additionalProperties: - type: string id: type: string format: uint64 diff --git a/test/e2e_test/cortex_alerting_test.go b/test/e2e_test/cortex_alerting_test.go new file mode 100644 index 00000000..0272a4f5 --- /dev/null +++ b/test/e2e_test/cortex_alerting_test.go @@ -0,0 +1,174 @@ +package e2e_test + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/mcuadros/go-defaults" + "github.com/odpf/siren/config" + "github.com/odpf/siren/internal/server" + sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" + "github.com/stretchr/testify/suite" + "google.golang.org/protobuf/types/known/structpb" +) + +type CortexAlertingTestSuite struct { + suite.Suite + client sirenv1beta1.SirenServiceClient + cancelClient func() + appConfig *config.Config + testBench *CortexTest +} + +func (s *CortexAlertingTestSuite) SetupTest() { + apiPort, err := getFreePort() + s.Require().Nil(err) + + s.appConfig = &config.Config{ + Log: config.Log{ + Level: "debug", + }, + Service: server.Config{ + Port: apiPort, + EncryptionKey: testEncryptionKey, + }, + } + + defaults.SetDefaults(s.appConfig) + + s.testBench, err = InitCortexEnvironment(s.appConfig) + s.Require().NoError(err) + + // setup custom cortex config + s.appConfig.Providers.Cortex.WebhookBaseAPI = fmt.Sprintf("http://host.docker.internal:%d/v1beta1/alerts/cortex", apiPort) + s.appConfig.Providers.Cortex.GroupWaitDuration = "1s" + + // enable worker + s.appConfig.Notification.MessageHandler.Enabled = true + s.appConfig.Notification.DLQHandler.Enabled = true + + StartSiren(*s.appConfig) + + ctx := context.Background() + s.client, s.cancelClient, err = CreateClient(ctx, fmt.Sprintf("localhost:%d", apiPort)) + s.Require().NoError(err) + + _, err = s.client.CreateProvider(ctx, &sirenv1beta1.CreateProviderRequest{ + Host: fmt.Sprintf("http://%s", s.testBench.NginxHost), + Urn: "cortex-test", + Name: "cortex-test", + Type: "cortex", + }) + s.Require().NoError(err) +} + +func (s *CortexAlertingTestSuite) TearDownTest() { + s.cancelClient() + // Clean tests + err := s.testBench.CleanUp() + s.Require().NoError(err) +} + +func (s *CortexAlertingTestSuite) TestSendingNotification() { + ctx := context.Background() + + s.Run("Triggering alert with matching subscription labels should trigger notification", func() { + _, err := s.client.CreateNamespace(ctx, &sirenv1beta1.CreateNamespaceRequest{ + Name: "new-odpf-1", + Urn: "new-odpf-1", + Provider: 1, + Credentials: nil, + Labels: map[string]string{ + "key1": "value1", + }, + }) + s.Require().NoError(err) + + // add receiver odpf-http + triggerAlertBody := ` + [ + { + "state": "firing", + "value": 1, + "labels": { + "severity": "WARNING", + "team": "odpf", + "service": "some-service", + "environment": "integration" + }, + "annotations": { + "resource": "test_alert", + "metricName": "test_alert", + "metricValue": "1", + "template": "alert_test" + } + } + ]` + + 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) + + expectedBody := `{"environment":"integration","generatorUrl":"","groupKey":"{}:{severity=\"WARNING\"}","id":"cortex-39255dc96d0f642c","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"}` + s.Assert().Equal(expectedBody, string(body)) + close(waitChan) + })) + s.Require().Nil(err) + defer testServer.Close() + + configs, err := structpb.NewStruct(map[string]interface{}{ + "url": testServer.URL, + }) + s.Require().NoError(err) + _, err = s.client.CreateReceiver(ctx, &sirenv1beta1.CreateReceiverRequest{ + Name: "odpf-http", + Type: "http", + Labels: map[string]string{ + "entity": "odpf", + "kind": "http", + }, + Configurations: configs, + }) + s.Require().NoError(err) + + _, err = s.client.CreateSubscription(ctx, &sirenv1beta1.CreateSubscriptionRequest{ + Urn: "subscribe-http-one", + Namespace: 1, + Receivers: []*sirenv1beta1.ReceiverMetadata{ + { + Id: 1, + }, + }, + Match: map[string]string{ + "team": "odpf", + "service": "some-service", + "environment": "integration", + }, + }) + s.Require().NoError(err) + + for { + bodyBytes, err := triggerCortexAlert(s.testBench.NginxHost, "new-odpf-1", triggerAlertBody) + s.Assert().NoError(err) + if err != nil { + break + } + + if string(bodyBytes) != "the Alertmanager is not configured\n" { + break + } + } + + <-waitChan + }) +} + +func TestCortexAlertingTestSuite(t *testing.T) { + suite.Run(t, new(CortexAlertingTestSuite)) +} diff --git a/test/e2e_test/cortex_helper_test.go b/test/e2e_test/cortex_helper_test.go index ac0274f0..fa42be21 100644 --- a/test/e2e_test/cortex_helper_test.go +++ b/test/e2e_test/cortex_helper_test.go @@ -1,6 +1,7 @@ package e2e_test import ( + "bytes" "context" "fmt" "io" @@ -15,6 +16,7 @@ import ( "github.com/odpf/siren/internal/store/postgres/migrations" "github.com/odpf/siren/plugins/providers/cortex" sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" + "github.com/odpf/siren/test/e2e_test/dockertestx" "github.com/ory/dockertest/v3/docker" "github.com/stretchr/testify/suite" "google.golang.org/protobuf/types/known/structpb" @@ -22,9 +24,8 @@ import ( type CortexTest struct { PGConfig db.Config - CortexConfig cortex.Config - CortexAMHost string - CortexAllHost string + CortexConfig cortex.AppConfig + NginxHost string bridgeNetworkName string pool *dockertest.Pool network *docker.Network @@ -79,8 +80,14 @@ func bootstrapCortexTestData(s *suite.Suite, ctx context.Context, client sirenv1 s.Require().Equal(1, len(rRes.GetReceivers())) } -func fetchCortexRules(cortexHost string) ([]byte, error) { - resp, err := http.Get(fmt.Sprintf("http://%s/api/v1/rules", cortexHost)) +func fetchCortexRules(cortexHost, tenant string) ([]byte, error) { + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1/rules", cortexHost), nil) + if err != nil { + return nil, err + } + req.Header.Set("X-Scope-OrgID", tenant) + + resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } @@ -93,8 +100,34 @@ func fetchCortexRules(cortexHost string) ([]byte, error) { return bodyBytes, nil } -func fetchCortexAlertmanagerConfig(cortexAMHost string) ([]byte, error) { - resp, err := http.Get(fmt.Sprintf("http://%s/api/v1/alerts", cortexAMHost)) +func fetchCortexAlertmanagerConfig(cortexAMHost, tenant string) ([]byte, error) { + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1/alerts", cortexAMHost), nil) + if err != nil { + return nil, err + } + req.Header.Set("X-Scope-OrgID", tenant) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return bodyBytes, nil +} + +func triggerCortexAlert(cortexAMHost, tenant, bodyJson string) ([]byte, error) { + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s/api/prom/alertmanager/api/v1/alerts", cortexAMHost), bytes.NewBufferString(bodyJson)) + if err != nil { + return nil, err + } + req.Header.Set("X-Scope-OrgID", tenant) + + resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } @@ -175,7 +208,7 @@ func InitCortexEnvironment(appConfig *config.Config) (*CortexTest, error) { if err != nil { return nil, err } - ct.CortexAMHost = dockerCortexAM.GetExternalHost() + ct.resources = append(ct.resources, dockerCortexAM.GetResource()) logger.Info("cortex-am is up") @@ -191,10 +224,27 @@ func InitCortexEnvironment(appConfig *config.Config) (*CortexTest, error) { return nil, err } - ct.CortexAllHost = dockerCortexAll.GetExternalHost() ct.resources = append(ct.resources, dockerCortexAll.GetResource()) logger.Info("cortex-all is up") + dockerNginx, err := dockertestx.CreateNginx( + dockertestx.NginxWithDockerNetwork(ct.network), + dockertestx.NginxWithDockerPool(ct.pool), + dockertestx.NginxWithPresetConfig("cortex"), + dockertestx.NginxWithExposedPort("9009"), + dockertestx.NginxWithConfigVariables(map[string]string{ + "ExposedPort": "9009", + "RulerHost": dockerCortexAll.GetInternalHost(), + "AlertManagerHost": dockerCortexAM.GetInternalHost(), + }), + ) + if err != nil { + return nil, err + } + + ct.NginxHost = dockerNginx.GetExternalHost() + ct.resources = append(ct.resources, dockerNginx.GetResource()) + ct.PGConfig = db.Config{ Driver: "postgres", URL: dockerPG.GetExternalConnString(), diff --git a/test/e2e_test/cortex_namespace_test.go b/test/e2e_test/cortex_namespace_test.go new file mode 100644 index 00000000..98637c8c --- /dev/null +++ b/test/e2e_test/cortex_namespace_test.go @@ -0,0 +1,93 @@ +package e2e_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/mcuadros/go-defaults" + "github.com/odpf/siren/config" + "github.com/odpf/siren/internal/server" + sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" + "github.com/stretchr/testify/suite" +) + +type CortexNamespaceTestSuite struct { + suite.Suite + client sirenv1beta1.SirenServiceClient + cancelClient func() + appConfig *config.Config + testBench *CortexTest +} + +func (s *CortexNamespaceTestSuite) SetupTest() { + apiPort, err := getFreePort() + s.Require().Nil(err) + + s.appConfig = &config.Config{ + Log: config.Log{ + Level: "debug", + }, + Service: server.Config{ + Port: apiPort, + EncryptionKey: testEncryptionKey, + }, + } + + defaults.SetDefaults(s.appConfig) + + s.testBench, err = InitCortexEnvironment(s.appConfig) + s.Require().NoError(err) + + s.appConfig.Providers.Cortex.WebhookBaseAPI = "http://host.docker.internal:8080/v1beta1/alerts/cortex" + StartSiren(*s.appConfig) + + ctx := context.Background() + s.client, s.cancelClient, err = CreateClient(ctx, fmt.Sprintf("localhost:%d", apiPort)) + s.Require().NoError(err) + + _, err = s.client.CreateProvider(ctx, &sirenv1beta1.CreateProviderRequest{ + Host: fmt.Sprintf("http://%s", s.testBench.NginxHost), + Urn: "cortex-test", + Name: "cortex-test", + Type: "cortex", + }) + s.Require().NoError(err) +} + +func (s *CortexNamespaceTestSuite) TearDownTest() { + s.cancelClient() + // Clean tests + err := s.testBench.CleanUp() + s.Require().NoError(err) +} + +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() { + _, err := s.client.CreateNamespace(ctx, &sirenv1beta1.CreateNamespaceRequest{ + Name: "new-odpf-1", + Urn: "new-odpf-1", + Provider: 1, + Credentials: nil, + Labels: map[string]string{ + "key1": "value1", + }, + }) + s.Require().NoError(err) + + bodyBytes, err := fetchCortexAlertmanagerConfig(s.testBench.NginxHost, "new-odpf-1") + s.Require().NoError(err) + + expectedScenarioCortexAM, err := os.ReadFile("testdata/cortex/expected-cortexamconfig-scenario.yaml") + s.Require().NoError(err) + + s.Assert().Empty(diffYaml(bodyBytes, expectedScenarioCortexAM)) + }) +} + +func TestCortexNamespaceTestSuite(t *testing.T) { + suite.Run(t, new(CortexNamespaceTestSuite)) +} diff --git a/test/e2e_test/cortex_rule_test.go b/test/e2e_test/cortex_rule_test.go index 8bcd6f09..d9c76c63 100644 --- a/test/e2e_test/cortex_rule_test.go +++ b/test/e2e_test/cortex_rule_test.go @@ -6,6 +6,7 @@ import ( "os" "testing" + "github.com/mcuadros/go-defaults" "github.com/odpf/siren/config" "github.com/odpf/siren/internal/server" sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" @@ -37,18 +38,20 @@ func (s *CortexRuleTestSuite) SetupTest() { }, } + defaults.SetDefaults(s.appConfig) + s.testBench, err = InitCortexEnvironment(s.appConfig) s.Require().NoError(err) - // override address to use ruler (all) - s.appConfig.Cortex.Address = fmt.Sprintf("http://%s", s.testBench.CortexAllHost) + s.appConfig.Providers.Cortex.WebhookBaseAPI = fmt.Sprintf("http://host.docker.internal:%d/v1beta1/alerts/cortex", apiPort) + s.appConfig.Providers.Cortex.GroupWaitDuration = "1s" StartSiren(*s.appConfig) ctx := context.Background() s.client, s.cancelClient, err = CreateClient(ctx, fmt.Sprintf("localhost:%d", apiPort)) s.Require().NoError(err) - bootstrapCortexTestData(&s.Suite, ctx, s.client, s.appConfig.Cortex.Address) + bootstrapCortexTestData(&s.Suite, ctx, s.client, s.testBench.NginxHost) } func (s *CortexRuleTestSuite) TearDownTest() { @@ -70,7 +73,7 @@ func (s *CortexRuleTestSuite) TestRules() { err = uploadRule(ctx, s.client, "testdata/cortex/rule-sample-scenario-1.yaml") s.Require().NoError(err) - bodyBytes, err := fetchCortexRules(s.testBench.CortexAllHost) + bodyBytes, err := fetchCortexRules(s.testBench.NginxHost, "fake") s.Require().NoError(err) expectedScenarioCortexRule, err := os.ReadFile("testdata/cortex/expected-cortexrule-scenario-1.yaml") s.Require().NoError(err) @@ -82,7 +85,7 @@ func (s *CortexRuleTestSuite) TestRules() { err := uploadRule(ctx, s.client, "testdata/cortex/rule-sample-scenario-2.yaml") s.Require().NoError(err) - bodyBytes, err := fetchCortexRules(s.testBench.CortexAllHost) + bodyBytes, err := fetchCortexRules(s.testBench.NginxHost, "fake") s.Require().NoError(err) expectedScenarioCortexRule, err := os.ReadFile("testdata/cortex/expected-cortexrule-scenario-2.yaml") s.Require().NoError(err) diff --git a/test/e2e_test/cortex_subscription_test.go b/test/e2e_test/cortex_subscription_test.go deleted file mode 100644 index a36f516e..00000000 --- a/test/e2e_test/cortex_subscription_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package e2e_test - -import ( - "context" - "fmt" - "os" - "testing" - - "github.com/odpf/siren/config" - "github.com/odpf/siren/internal/server" - sirenv1beta1 "github.com/odpf/siren/proto/odpf/siren/v1beta1" - "github.com/stretchr/testify/suite" -) - -type CortexSubscriptionTestSuite struct { - suite.Suite - client sirenv1beta1.SirenServiceClient - cancelClient func() - appConfig *config.Config - testBench *CortexTest -} - -func (s *CortexSubscriptionTestSuite) SetupTest() { - - apiPort, err := getFreePort() - s.Require().Nil(err) - - s.appConfig = &config.Config{ - Log: config.Log{ - Level: "debug", - }, - Service: server.Config{ - Port: apiPort, - EncryptionKey: testEncryptionKey, - }, - } - - s.testBench, err = InitCortexEnvironment(s.appConfig) - s.Require().NoError(err) - - // override address to use alertmanager only - s.appConfig.Cortex.Address = fmt.Sprintf("http://%s", s.testBench.CortexAMHost) - StartSiren(*s.appConfig) - - ctx := context.Background() - s.client, s.cancelClient, err = CreateClient(ctx, fmt.Sprintf("localhost:%d", apiPort)) - s.Require().NoError(err) - - bootstrapCortexTestData(&s.Suite, ctx, s.client, s.appConfig.Cortex.Address) -} - -func (s *CortexSubscriptionTestSuite) TearDownTest() { - s.cancelClient() - // Clean tests - err := s.testBench.CleanUp() - s.Require().NoError(err) -} - -func (s *CortexSubscriptionTestSuite) TestSubscriptions() { - ctx := context.Background() - - s.Run("1. initial state has no alert subscriptions, add a subscription to odpf-http should return `testdata/cortex/expected-cortexamconfig-scenario-1.yaml`", func() { - _, err := s.client.CreateSubscription(ctx, &sirenv1beta1.CreateSubscriptionRequest{ - Urn: "subscribe-http-one", - Namespace: 1, - Receivers: []*sirenv1beta1.ReceiverMetadata{ - { - Id: 1, - }, - }, - Match: map[string]string{ - "team": "odpf-platform", - "environment": "integration", - }, - }) - s.Require().NoError(err) - - bodyBytes, err := fetchCortexAlertmanagerConfig(s.testBench.CortexAMHost) - s.Require().NoError(err) - - expectedScenarioCortexAM, err := os.ReadFile("testdata/cortex/expected-cortexamconfig-scenario-1.yaml") - s.Require().NoError(err) - - s.Assert().Empty(diffYaml(bodyBytes, expectedScenarioCortexAM)) - }) - - s.Run("2. initial state `testdata/cortex/expected-cortexamconfig-scenario-1.yaml`, updating subscription, should return `testdata/cortex/expected-cortexamconfig-scenario-1-updated.yaml`", func() { - _, err := s.client.UpdateSubscription(ctx, &sirenv1beta1.UpdateSubscriptionRequest{ - Id: 1, - Urn: "subscribe-http-one-updated", - Namespace: 1, - Receivers: []*sirenv1beta1.ReceiverMetadata{ - { - Id: 1, - }, - }, - Match: map[string]string{ - "team": "odpf-platform-updated", - "environment": "integration-updated", - }, - }) - s.Require().NoError(err) - - bodyBytes, err := fetchCortexAlertmanagerConfig(s.testBench.CortexAMHost) - s.Require().NoError(err) - - expectedScenarioCortexAM, err := os.ReadFile("testdata/cortex/expected-cortexamconfig-scenario-1-updated.yaml") - s.Require().NoError(err) - - s.Assert().Empty(diffYaml(bodyBytes, expectedScenarioCortexAM)) - }) - - s.Run("3. `testdata/cortex/expected-cortexamconfig-scenario-1-updated.yaml`, updating subscription, should return `testdata/cortex/expected-cortexamconfig-scenario-2.yaml`", func() { - _, err := s.client.DeleteSubscription(ctx, &sirenv1beta1.DeleteSubscriptionRequest{ - Id: 1, - }) - s.Require().NoError(err) - - bodyBytes, err := fetchCortexAlertmanagerConfig(s.testBench.CortexAMHost) - s.Require().NoError(err) - - expectedScenarioCortexAM, err := os.ReadFile("testdata/cortex/expected-cortexamconfig-scenario-2.yaml") - s.Require().NoError(err) - - s.Assert().Empty(diffYaml(bodyBytes, expectedScenarioCortexAM)) - }) -} - -func TestCortexSubscriptionTestSuite(t *testing.T) { - suite.Run(t, new(CortexSubscriptionTestSuite)) -} diff --git a/test/e2e_test/dockertestx/configs/nginx/cortex_nginx.conf b/test/e2e_test/dockertestx/configs/nginx/cortex_nginx.conf new file mode 100644 index 00000000..6298ae27 --- /dev/null +++ b/test/e2e_test/dockertestx/configs/nginx/cortex_nginx.conf @@ -0,0 +1,93 @@ +worker_processes 1; +error_log /dev/stderr; +pid /tmp/nginx.pid; +worker_rlimit_nofile 8192; + +events { + worker_connections 1024; +} + + +http { + client_max_body_size 5M; + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] $status ' + '"$request" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for" $http_x_scope_orgid'; + access_log /dev/stderr main; + sendfile on; + tcp_nopush on; + resolver 127.0.0.11 ipv6=off; + + server { + listen {{.ExposedPort}}; + proxy_connect_timeout 300s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + proxy_http_version 1.1; + + location = /healthz { + return 200 'alive'; + } + + # Distributor Config + location = /ring { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + location = /all_user_stats { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + location = /api/prom/push { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + ## New Remote write API. Ref: https://cortexmetrics.io/docs/api/#remote-write + location = /api/v1/push { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + + # Alertmanager Config + location ~ /api/prom/alertmanager/.* { + proxy_pass http://{{.AlertManagerHost}}$request_uri; + } + + location ~ /api/v1/alerts { + proxy_pass http://{{.AlertManagerHost}}$request_uri; + } + + location ~ /multitenant_alertmanager/status { + proxy_pass http://{{.AlertManagerHost}}$request_uri; + } + + # Ruler Config + location ~ /api/v1/rules { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + location ~ /ruler/ring { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + # Config Config + location ~ /api/prom/configs/.* { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + # Query Config + location ~ /api/prom/.* { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + ## New Query frontend APIs as per https://cortexmetrics.io/docs/api/#querier--query-frontend + location ~ ^/prometheus/api/v1/(read|metadata|labels|series|query_range|query) { + proxy_pass http://{{.RulerHost}}$request_uri; + } + + location ~ /prometheus/api/v1/label/.* { + proxy_pass http://{{.RulerHost}}$request_uri; + } + } +} \ No newline at end of file diff --git a/test/e2e_test/dockertestx/nginx.go b/test/e2e_test/dockertestx/nginx.go new file mode 100644 index 00000000..9c5f0db4 --- /dev/null +++ b/test/e2e_test/dockertestx/nginx.go @@ -0,0 +1,247 @@ +package dockertestx + +import ( + "bytes" + _ "embed" + "fmt" + "io/fs" + "net/http" + "os" + "path" + "text/template" + "time" + + "github.com/google/uuid" + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" +) + +const ( + nginxDefaultHealthEndpoint = "/healthz" + nginxDefaultExposedPort = "8080" + nginxDefaultVersionTag = "1.23" +) + +var ( + //go:embed configs/nginx/cortex_nginx.conf + NginxCortexConfig string +) + +type dockerNginxOption func(dc *dockerNginx) + +// NginxWithHealthEndpoint is an option to assign health endpoint +func NginxWithHealthEndpoint(healthEndpoint string) dockerNginxOption { + return func(dc *dockerNginx) { + dc.healthEndpoint = healthEndpoint + } +} + +// NginxWithDockerNetwork is an option to assign docker network +func NginxWithDockerNetwork(network *docker.Network) dockerNginxOption { + return func(dc *dockerNginx) { + dc.network = network + } +} + +// NginxWithVersionTag is an option to assign version tag +// of a `nginx` image +func NginxWithVersionTag(versionTag string) dockerNginxOption { + return func(dc *dockerNginx) { + dc.versionTag = versionTag + } +} + +// NginxWithDockerPool is an option to assign docker pool +func NginxWithDockerPool(pool *dockertest.Pool) dockerNginxOption { + return func(dc *dockerNginx) { + dc.pool = pool + } +} + +// NginxWithDockerPool is an option to assign docker pool +func NginxWithExposedPort(port string) dockerNginxOption { + return func(dc *dockerNginx) { + dc.exposedPort = port + } +} + +func NginxWithPresetConfig(presetConfig string) dockerNginxOption { + return func(dc *dockerNginx) { + dc.presetConfig = presetConfig + } +} + +func NginxWithConfigVariables(cv map[string]string) dockerNginxOption { + return func(dc *dockerNginx) { + dc.configVariables = cv + } +} + +type dockerNginx struct { + network *docker.Network + pool *dockertest.Pool + exposedPort string + internalHost string + externalHost string + presetConfig string + versionTag string + healthEndpoint string + configVariables map[string]string + dockertestResource *dockertest.Resource +} + +// CreateNginx is a function to create a dockerized nginx +func CreateNginx(opts ...dockerNginxOption) (*dockerNginx, error) { + var ( + err error + dc = &dockerNginx{} + ) + + for _, opt := range opts { + opt(dc) + } + + name := fmt.Sprintf("nginx-%s", uuid.New().String()) + + if dc.pool == nil { + dc.pool, err = dockertest.NewPool("") + if err != nil { + return nil, fmt.Errorf("could not create dockertest pool: %w", err) + } + } + + if dc.versionTag == "" { + dc.versionTag = nginxDefaultVersionTag + } + + if dc.exposedPort == "" { + dc.exposedPort = nginxDefaultExposedPort + } + + if dc.healthEndpoint == "" { + dc.healthEndpoint = nginxDefaultHealthEndpoint + } + + runOpts := &dockertest.RunOptions{ + Name: name, + Repository: "nginx", + Tag: dc.versionTag, + ExposedPorts: []string{fmt.Sprintf("%s/tcp", dc.exposedPort)}, + } + + if dc.network != nil { + runOpts.NetworkID = dc.network.ID + } + + var confString string + switch dc.presetConfig { + case "cortex": + confString = NginxCortexConfig + } + + tmpl := template.New("nginx-config") + parsedTemplate, err := tmpl.Parse(confString) + if err != nil { + return nil, err + } + var generatedConf bytes.Buffer + err = parsedTemplate.Execute(&generatedConf, dc.configVariables) + if err != nil { + // it is unlikely that the code returns error here + return nil, err + } + confString = generatedConf.String() + + pwd, err := os.Getwd() + if err != nil { + return nil, err + } + + var ( + confDestinationFolder = fmt.Sprintf("%s/tmp/dockertest-configs/nginx", pwd) + ) + + foldersPath := []string{confDestinationFolder} + for _, fp := range foldersPath { + if _, err := os.Stat(fp); os.IsNotExist(err) { + if err := os.MkdirAll(fp, 0777); err != nil { + return nil, err + } + } + } + + if err := os.WriteFile(path.Join(confDestinationFolder, "nginx.conf"), []byte(confString), fs.ModePerm); err != nil { + return nil, err + } + + dc.dockertestResource, err = dc.pool.RunWithOptions( + runOpts, + func(config *docker.HostConfig) { + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } + config.Mounts = []docker.HostMount{ + { + Target: "/etc/nginx/nginx.conf", + Source: path.Join(confDestinationFolder, "nginx.conf"), + Type: "bind", + }, + } + }, + ) + if err != nil { + return nil, err + } + + externalPort := dc.dockertestResource.GetPort(fmt.Sprintf("%s/tcp", dc.exposedPort)) + dc.internalHost = fmt.Sprintf("%s:%s", name, dc.exposedPort) + dc.externalHost = fmt.Sprintf("localhost:%s", externalPort) + + if err = dc.dockertestResource.Expire(120); err != nil { + return nil, err + } + + // exponential backoff-retry, because the application in the container might not be ready to accept connections yet + dc.pool.MaxWait = 60 * time.Second + + if err = dc.pool.Retry(func() error { + httpClient := &http.Client{} + res, err := httpClient.Get(fmt.Sprintf("http://localhost:%s%s", externalPort, dc.healthEndpoint)) + if err != nil { + return err + } + + if res.StatusCode != 200 { + return fmt.Errorf("nginx server return status %d", res.StatusCode) + } + + return nil + }); err != nil { + err = fmt.Errorf("could not connect to docker: %w", err) + return nil, fmt.Errorf("could not connect to docker: %w", err) + } + + return dc, nil +} + +// GetPool returns docker pool +func (dc *dockerNginx) GetPool() *dockertest.Pool { + return dc.pool +} + +// GetResource returns docker resource +func (dc *dockerNginx) GetResource() *dockertest.Resource { + return dc.dockertestResource +} + +// GetInternalHost returns internal hostname and port +// e.g. internal-xxxxxx:8080 +func (dc *dockerNginx) GetInternalHost() string { + return dc.internalHost +} + +// GetExternalHost returns localhost and port +// e.g. localhost:51113 +func (dc *dockerNginx) GetExternalHost() string { + return dc.externalHost +} diff --git a/test/e2e_test/helper_test.go b/test/e2e_test/helper_test.go index 94ca2b1c..25bc5b58 100644 --- a/test/e2e_test/helper_test.go +++ b/test/e2e_test/helper_test.go @@ -20,10 +20,6 @@ import ( "gopkg.in/yaml.v2" ) -var ( - SERVER_HOST = "http://localhost:8080" -) - func uploadTemplate(ctx context.Context, cl sirenv1beta1.SirenServiceClient, filePath string) error { yamlFile, err := os.ReadFile(filePath) if err != nil { diff --git a/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-1-updated.yaml b/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-1-updated.yaml deleted file mode 100644 index 95aecc47..00000000 --- a/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-1-updated.yaml +++ /dev/null @@ -1,89 +0,0 @@ -template_files: - helper.tmpl: |- - {{define "__alert_severity_prefix_emoji" -}} - {{if ne .Status "firing" -}} - :white_check_mark: - {{- else if eq .CommonLabels.severity "CRITICAL" -}} - :fire: - {{- else if eq .CommonLabels.severity "WARNING" -}} - :warning: - {{- else -}} - :question: - {{- end}} - {{- end}} - {{ define "slack.pretext" -}} - {{- template "__alert_severity_prefix_emoji" . }} [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] - {{- if eq .Status "resolved" }} ~[{{ .CommonLabels.severity | toUpper }}]~ - {{- else }} *[{{ .CommonLabels.severity | toUpper }}]* - {{- end}} {{ .CommonLabels.alertname }} - {{- end }} - {{define "slack.color" -}} - {{if eq .Status "firing" -}} - {{if eq .CommonLabels.severity "WARNING" -}} - warning - {{- else if eq .CommonLabels.severity "CRITICAL" -}} - danger - {{- else -}} - #439FE0 - {{- end -}} - {{else -}} - good - {{- end}} - {{- end}} - {{ define "slack.title" -}} - {{$first := true}}{{ range .CommonLabels.SortedPairs}}{{if $first}}{{$first = false}}{{else}}{{` | `}}{{end}}{{ .Value }}{{end }} - {{- end }} - {{ define "slack.body"}} - {{ range .Alerts -}} - {{ .Annotations.summary }} - {{ end }} - {{ end}} - {{ define "alertmanager_root_url" }}http://localhost:8080{{ end }} - {{ define "slack.warning.name" }}{{ end }} - {{ define "slack.warning.webhook_url" }}Not Supported: https://github.com/prometheus/alertmanager/issues/2207{{ end }} - {{ define "slack.critical.name" }}{{ end }} - {{ define "slack.critical.webhook_url" }}Not Supported: https://github.com/prometheus/alertmanager/issues/2207{{ end }} - {{ define "pagerduty.service_key" }}xyz{{ end }} - {{ define "slack.default.name" }}none{{ end }} - {{ define "silence_url" -}} - {{ template "alertmanager_root_url" }}/api/prom/alertmanager/#/silences/new?filter={{ `{` | urlquery -}} - {{$first := true}}{{range .CommonLabels.SortedPairs }}{{if $first}}{{$first = false}}{{else}}{{`,` | urlquery }}{{end -}} - {{ .Name | urlquery }}{{ `="` | urlquery }}{{ .Value | urlquery }}{{ `"` | urlquery }}{{ end }} - {{- `}` | urlquery }} - {{- end }} - {{define "slack.dashboard"}} - {{- if (index .Alerts 0).Annotations.dashboard}}{{(index .Alerts 0).Annotations.dashboard}}{{else}}https://radar.golabs.io{{end}} - {{- end -}} - {{define "slack.runbook"}} - {{- if (index .Alerts 0).Annotations.playbook}}{{(index .Alerts 0).Annotations.playbook}}{{end}} - {{- end -}} -alertmanager_config: |- - templates: - - 'helper.tmpl' - global: - pagerduty_url: https://events.pagerduty.com/v2/enqueue - resolve_timeout: 5m - slack_api_url: https://slack.com/api/chat.postMessage - receivers: - - name: default - - name: http_subscribe-http-one-updated_receiverId_1_idx_0 - webhook_configs: - - url: 'http://fake-webhook-endpoint.odpf.io' - route: - group_by: - - alertname - - severity - - owner - - service_name - - time_stamp - - identifier - group_wait: 30s - group_interval: 30m - repeat_interval: 4h - receiver: default - routes: - - receiver: http_subscribe-http-one-updated_receiverId_1_idx_0 - match: - environment: integration-updated - team: odpf-platform-updated - continue: true diff --git a/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-1.yaml b/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-1.yaml deleted file mode 100644 index aa3b2200..00000000 --- a/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-1.yaml +++ /dev/null @@ -1,89 +0,0 @@ -template_files: - helper.tmpl: |- - {{define "__alert_severity_prefix_emoji" -}} - {{if ne .Status "firing" -}} - :white_check_mark: - {{- else if eq .CommonLabels.severity "CRITICAL" -}} - :fire: - {{- else if eq .CommonLabels.severity "WARNING" -}} - :warning: - {{- else -}} - :question: - {{- end}} - {{- end}} - {{ define "slack.pretext" -}} - {{- template "__alert_severity_prefix_emoji" . }} [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] - {{- if eq .Status "resolved" }} ~[{{ .CommonLabels.severity | toUpper }}]~ - {{- else }} *[{{ .CommonLabels.severity | toUpper }}]* - {{- end}} {{ .CommonLabels.alertname }} - {{- end }} - {{define "slack.color" -}} - {{if eq .Status "firing" -}} - {{if eq .CommonLabels.severity "WARNING" -}} - warning - {{- else if eq .CommonLabels.severity "CRITICAL" -}} - danger - {{- else -}} - #439FE0 - {{- end -}} - {{else -}} - good - {{- end}} - {{- end}} - {{ define "slack.title" -}} - {{$first := true}}{{ range .CommonLabels.SortedPairs}}{{if $first}}{{$first = false}}{{else}}{{` | `}}{{end}}{{ .Value }}{{end }} - {{- end }} - {{ define "slack.body"}} - {{ range .Alerts -}} - {{ .Annotations.summary }} - {{ end }} - {{ end}} - {{ define "alertmanager_root_url" }}http://localhost:8080{{ end }} - {{ define "slack.warning.name" }}{{ end }} - {{ define "slack.warning.webhook_url" }}Not Supported: https://github.com/prometheus/alertmanager/issues/2207{{ end }} - {{ define "slack.critical.name" }}{{ end }} - {{ define "slack.critical.webhook_url" }}Not Supported: https://github.com/prometheus/alertmanager/issues/2207{{ end }} - {{ define "pagerduty.service_key" }}xyz{{ end }} - {{ define "slack.default.name" }}none{{ end }} - {{ define "silence_url" -}} - {{ template "alertmanager_root_url" }}/api/prom/alertmanager/#/silences/new?filter={{ `{` | urlquery -}} - {{$first := true}}{{range .CommonLabels.SortedPairs }}{{if $first}}{{$first = false}}{{else}}{{`,` | urlquery }}{{end -}} - {{ .Name | urlquery }}{{ `="` | urlquery }}{{ .Value | urlquery }}{{ `"` | urlquery }}{{ end }} - {{- `}` | urlquery }} - {{- end }} - {{define "slack.dashboard"}} - {{- if (index .Alerts 0).Annotations.dashboard}}{{(index .Alerts 0).Annotations.dashboard}}{{else}}https://radar.golabs.io{{end}} - {{- end -}} - {{define "slack.runbook"}} - {{- if (index .Alerts 0).Annotations.playbook}}{{(index .Alerts 0).Annotations.playbook}}{{end}} - {{- end -}} -alertmanager_config: |- - templates: - - 'helper.tmpl' - global: - pagerduty_url: https://events.pagerduty.com/v2/enqueue - resolve_timeout: 5m - slack_api_url: https://slack.com/api/chat.postMessage - receivers: - - name: default - - name: http_subscribe-http-one_receiverId_1_idx_0 - webhook_configs: - - url: 'http://fake-webhook-endpoint.odpf.io' - route: - group_by: - - alertname - - severity - - owner - - service_name - - time_stamp - - identifier - group_wait: 30s - group_interval: 30m - repeat_interval: 4h - receiver: default - routes: - - receiver: http_subscribe-http-one_receiverId_1_idx_0 - match: - environment: integration - team: odpf-platform - continue: true diff --git a/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-2.yaml b/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-2.yaml deleted file mode 100644 index 8e3264a6..00000000 --- a/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario-2.yaml +++ /dev/null @@ -1,81 +0,0 @@ -template_files: - helper.tmpl: |- - {{define "__alert_severity_prefix_emoji" -}} - {{if ne .Status "firing" -}} - :white_check_mark: - {{- else if eq .CommonLabels.severity "CRITICAL" -}} - :fire: - {{- else if eq .CommonLabels.severity "WARNING" -}} - :warning: - {{- else -}} - :question: - {{- end}} - {{- end}} - {{ define "slack.pretext" -}} - {{- template "__alert_severity_prefix_emoji" . }} [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] - {{- if eq .Status "resolved" }} ~[{{ .CommonLabels.severity | toUpper }}]~ - {{- else }} *[{{ .CommonLabels.severity | toUpper }}]* - {{- end}} {{ .CommonLabels.alertname }} - {{- end }} - {{define "slack.color" -}} - {{if eq .Status "firing" -}} - {{if eq .CommonLabels.severity "WARNING" -}} - warning - {{- else if eq .CommonLabels.severity "CRITICAL" -}} - danger - {{- else -}} - #439FE0 - {{- end -}} - {{else -}} - good - {{- end}} - {{- end}} - {{ define "slack.title" -}} - {{$first := true}}{{ range .CommonLabels.SortedPairs}}{{if $first}}{{$first = false}}{{else}}{{` | `}}{{end}}{{ .Value }}{{end }} - {{- end }} - {{ define "slack.body"}} - {{ range .Alerts -}} - {{ .Annotations.summary }} - {{ end }} - {{ end}} - {{ define "alertmanager_root_url" }}http://localhost:8080{{ end }} - {{ define "slack.warning.name" }}{{ end }} - {{ define "slack.warning.webhook_url" }}Not Supported: https://github.com/prometheus/alertmanager/issues/2207{{ end }} - {{ define "slack.critical.name" }}{{ end }} - {{ define "slack.critical.webhook_url" }}Not Supported: https://github.com/prometheus/alertmanager/issues/2207{{ end }} - {{ define "pagerduty.service_key" }}xyz{{ end }} - {{ define "slack.default.name" }}none{{ end }} - {{ define "silence_url" -}} - {{ template "alertmanager_root_url" }}/api/prom/alertmanager/#/silences/new?filter={{ `{` | urlquery -}} - {{$first := true}}{{range .CommonLabels.SortedPairs }}{{if $first}}{{$first = false}}{{else}}{{`,` | urlquery }}{{end -}} - {{ .Name | urlquery }}{{ `="` | urlquery }}{{ .Value | urlquery }}{{ `"` | urlquery }}{{ end }} - {{- `}` | urlquery }} - {{- end }} - {{define "slack.dashboard"}} - {{- if (index .Alerts 0).Annotations.dashboard}}{{(index .Alerts 0).Annotations.dashboard}}{{else}}https://radar.golabs.io{{end}} - {{- end -}} - {{define "slack.runbook"}} - {{- if (index .Alerts 0).Annotations.playbook}}{{(index .Alerts 0).Annotations.playbook}}{{end}} - {{- end -}} -alertmanager_config: |- - templates: - - 'helper.tmpl' - global: - pagerduty_url: https://events.pagerduty.com/v2/enqueue - resolve_timeout: 5m - slack_api_url: https://slack.com/api/chat.postMessage - receivers: - - name: default - route: - group_by: - - alertname - - severity - - owner - - service_name - - time_stamp - - identifier - group_wait: 30s - group_interval: 30m - repeat_interval: 4h - receiver: default - routes: diff --git a/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario.yaml b/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario.yaml new file mode 100644 index 00000000..3cac5a6b --- /dev/null +++ b/test/e2e_test/testdata/cortex/expected-cortexamconfig-scenario.yaml @@ -0,0 +1,82 @@ +template_files: + helper.tmpl: |- + {{define "__alert_severity_prefix_emoji" -}} + {{if ne .Status "firing" -}} + :white_check_mark: + {{- else if eq .CommonLabels.severity "CRITICAL" -}} + :fire: + {{- else if eq .CommonLabels.severity "WARNING" -}} + :warning: + {{- else -}} + :question: + {{- end}} + {{- end}} + {{ define "slack.pretext" -}} + {{- template "__alert_severity_prefix_emoji" . }} [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] + {{- if eq .Status "resolved" }} ~[{{ .CommonLabels.severity | toUpper }}]~ + {{- else }} *[{{ .CommonLabels.severity | toUpper }}]* + {{- end}} {{ .CommonLabels.alertname }} + {{- end }} + {{define "slack.color" -}} + {{if eq .Status "firing" -}} + {{if eq .CommonLabels.severity "WARNING" -}} + warning + {{- else if eq .CommonLabels.severity "CRITICAL" -}} + danger + {{- else -}} + #439FE0 + {{- end -}} + {{else -}} + good + {{- end}} + {{- end}} + {{ define "slack.title" -}} + {{$first := true}}{{ range .CommonLabels.SortedPairs}}{{if $first}}{{$first = false}}{{else}}{{` | `}}{{end}}{{ .Value }}{{end }} + {{- end }} + {{ define "slack.body"}} + {{ range .Alerts -}} + {{ .Annotations.summary }} + {{ end }} + {{ end}} + {{ define "alertmanager_root_url" }}http://localhost:8080{{ end }} + {{ define "slack.warning.name" }}{{ end }} + {{ define "slack.warning.webhook_url" }}Not Supported: https://github.com/prometheus/alertmanager/issues/2207{{ end }} + {{ define "slack.critical.name" }}{{ end }} + {{ define "slack.critical.webhook_url" }}Not Supported: https://github.com/prometheus/alertmanager/issues/2207{{ end }} + {{ define "pagerduty.service_key" }}xyz{{ end }} + {{ define "slack.default.name" }}none{{ end }} + {{ define "silence_url" -}} + {{ template "alertmanager_root_url" }}/api/prom/alertmanager/#/silences/new?filter={{ `{` | urlquery -}} + {{$first := true}}{{range .CommonLabels.SortedPairs }}{{if $first}}{{$first = false}}{{else}}{{`,` | urlquery }}{{end -}} + {{ .Name | urlquery }}{{ `="` | urlquery }}{{ .Value | urlquery }}{{ `"` | urlquery }}{{ end }} + {{- `}` | urlquery }} + {{- end }} + {{define "slack.dashboard"}} + {{- if (index .Alerts 0).Annotations.dashboard}}{{(index .Alerts 0).Annotations.dashboard}}{{else}}https://radar.golabs.io{{end}} + {{- end -}} + {{define "slack.runbook"}} + {{- if (index .Alerts 0).Annotations.playbook}}{{(index .Alerts 0).Annotations.playbook}}{{end}} + {{- end -}} +alertmanager_config: |- + templates: + - 'helper.tmpl' + global: + pagerduty_url: https://events.pagerduty.com/v2/enqueue + resolve_timeout: 5m + slack_api_url: https://slack.com/api/chat.postMessage + receivers: + - name: default + webhook_configs: + - url: 'http://host.docker.internal:8080/v1beta1/alerts/cortex/1' + route: + receiver: default + group_by: + - alertname + - severity + - owner + - service_name + - time_stamp + - identifier + group_wait: 30s + group_interval: 30m + repeat_interval: 4h