diff --git a/app/cli/cmd/integration_add.go b/app/cli/cmd/integration_add.go index 2181e71d4..8fe8a49b9 100644 --- a/app/cli/cmd/integration_add.go +++ b/app/cli/cmd/integration_add.go @@ -19,12 +19,15 @@ import ( "github.com/spf13/cobra" ) +var integrationDescription string + func newConfigIntegratioAddCmd() *cobra.Command { cmd := &cobra.Command{ Use: "add", Short: "Add integration", } + cmd.PersistentFlags().StringVar(&integrationDescription, "description", "", "integration registration description") cmd.AddCommand(newIntegrationAddDepTrackCmd()) return cmd } diff --git a/app/cli/cmd/integration_add_deptrack.go b/app/cli/cmd/integration_add_deptrack.go index 6700bd368..729585066 100644 --- a/app/cli/cmd/integration_add_deptrack.go +++ b/app/cli/cmd/integration_add_deptrack.go @@ -39,7 +39,7 @@ func newIntegrationAddDepTrackCmd() *cobra.Command { return fmt.Errorf("retrieving token from stdin: %w", err) } - res, err := action.NewIntegrationAddDeptrack(actionOpts).Run(instance, string(apiKey), allowAutoCreate) + res, err := action.NewIntegrationAddDeptrack(actionOpts).Run(instance, string(apiKey), integrationDescription, allowAutoCreate) if err != nil { return err } diff --git a/app/cli/cmd/integration_list.go b/app/cli/cmd/integration_list.go index f0391b1f1..37e75ae65 100644 --- a/app/cli/cmd/integration_list.go +++ b/app/cli/cmd/integration_list.go @@ -50,13 +50,13 @@ func integrationListTableOutput(items []*action.IntegrationItem) error { } t := newTableWriter() - t.AppendHeader(table.Row{"ID", "Name", "Config", "Created At"}) + t.AppendHeader(table.Row{"ID", "Description", "Kind", "Config", "Created At"}) for _, i := range items { var options []string for k, v := range i.Config { options = append(options, fmt.Sprintf("%s: %v", k, v)) } - t.AppendRow(table.Row{i.ID, i.Name, strings.Join(options, "\n"), i.CreatedAt.Format(time.RFC822)}) + t.AppendRow(table.Row{i.ID, i.Description, i.Kind, strings.Join(options, "\n"), i.CreatedAt.Format(time.RFC822)}) t.AppendSeparator() } diff --git a/app/cli/cmd/workflow_integration_list.go b/app/cli/cmd/workflow_integration_list.go index 452d05cb7..6c857c6ef 100644 --- a/app/cli/cmd/workflow_integration_list.go +++ b/app/cli/cmd/workflow_integration_list.go @@ -64,7 +64,7 @@ func integrationAttachmentListTableOutput(attachments []*action.IntegrationAttac } options = append(options, fmt.Sprintf("%s: %v", k, v)) } - t.AppendRow(table.Row{i.ID, integration.Name, strings.Join(options, "\n"), i.CreatedAt.Format(time.RFC822), wf.NamespacedName()}) + t.AppendRow(table.Row{i.ID, integration.Kind, strings.Join(options, "\n"), i.CreatedAt.Format(time.RFC822), wf.NamespacedName()}) t.AppendSeparator() } diff --git a/app/cli/internal/action/integration_add_deptrack.go b/app/cli/internal/action/integration_add_deptrack.go index e7efc93cf..a5b2b470d 100644 --- a/app/cli/internal/action/integration_add_deptrack.go +++ b/app/cli/internal/action/integration_add_deptrack.go @@ -32,7 +32,7 @@ func NewIntegrationAddDeptrack(cfg *ActionsOpts) *IntegrationAddDeptrack { return &IntegrationAddDeptrack{cfg} } -func (action *IntegrationAddDeptrack) Run(host, apiKey string, allowAutoProjectCreation bool) (*IntegrationItem, error) { +func (action *IntegrationAddDeptrack) Run(host, apiKey, description string, allowAutoProjectCreation bool) (*IntegrationItem, error) { client := pb.NewIntegrationsServiceClient(action.cfg.CPConnection) cdxRegistrationRequest := cxpb.RegistrationRequest{ ApiKey: apiKey, @@ -48,6 +48,7 @@ func (action *IntegrationAddDeptrack) Run(host, apiKey string, allowAutoProjectC i, err := client.Register(context.Background(), &pb.IntegrationsServiceRegisterRequest{ Kind: deptrack.ID, RegistrationConfig: anyConfig, + DisplayName: description, }) if err != nil { return nil, err diff --git a/app/cli/internal/action/integration_list.go b/app/cli/internal/action/integration_list.go index 8cbfd2198..6805ad252 100644 --- a/app/cli/internal/action/integration_list.go +++ b/app/cli/internal/action/integration_list.go @@ -29,10 +29,13 @@ type IntegrationList struct { } type IntegrationItem struct { - ID string `json:"id"` - Name string `json:"name"` - CreatedAt *time.Time `json:"createdAt"` - Config map[string]interface{} `json:"config"` + ID string `json:"id"` + // Integration backend kind, i.e slack, pagerduty, etc + Kind string `json:"name"` + // Integration description for display and differentiation purposes + Description string `json:"description"` + CreatedAt *time.Time `json:"createdAt"` + Config map[string]interface{} `json:"config"` } func NewIntegrationList(cfg *ActionsOpts) *IntegrationList { @@ -65,8 +68,9 @@ func pbIntegrationItemToAction(in *pb.IntegrationItem) (*IntegrationItem, error) } i := &IntegrationItem{ - Name: in.GetKind(), ID: in.GetId(), - CreatedAt: toTimePtr(in.GetCreatedAt().AsTime()), + Kind: in.GetKind(), ID: in.GetId(), + Description: in.GetDisplayName(), + CreatedAt: toTimePtr(in.GetCreatedAt().AsTime()), } // Old format does not include config so we skip it diff --git a/app/controlplane/api/controlplane/v1/integrations.pb.go b/app/controlplane/api/controlplane/v1/integrations.pb.go index 058c52627..87083efc1 100644 --- a/app/controlplane/api/controlplane/v1/integrations.pb.go +++ b/app/controlplane/api/controlplane/v1/integrations.pb.go @@ -44,7 +44,10 @@ type IntegrationsServiceRegisterRequest struct { unknownFields protoimpl.UnknownFields // Kind of integration to register + // This should match the ID of an existing integration Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + // Description of the registration, used for display purposes + DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // Associated registration configuration RegistrationConfig *anypb.Any `protobuf:"bytes,2,opt,name=registration_config,json=registrationConfig,proto3" json:"registration_config,omitempty"` } @@ -88,6 +91,13 @@ func (x *IntegrationsServiceRegisterRequest) GetKind() string { return "" } +func (x *IntegrationsServiceRegisterRequest) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + func (x *IntegrationsServiceRegisterRequest) GetRegistrationConfig() *anypb.Any { if x != nil { return x.RegistrationConfig @@ -522,9 +532,11 @@ type IntegrationItem struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` + // Description of the registration, used for display purposes + DisplayName string `protobuf:"bytes,4,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` // Arbitrary configuration for the integration Config []byte `protobuf:"bytes,5,opt,name=config,proto3" json:"config,omitempty"` } @@ -575,6 +587,13 @@ func (x *IntegrationItem) GetKind() string { return "" } +func (x *IntegrationItem) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + func (x *IntegrationItem) GetCreatedAt() *timestamppb.Timestamp { if x != nil { return x.CreatedAt @@ -768,145 +787,149 @@ var file_controlplane_v1_integrations_proto_rawDesc = []byte{ 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x92, 0x01, 0x0a, 0x22, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x6f, 0x74, 0x6f, 0x22, 0xb5, 0x01, 0x0a, 0x22, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x4f, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x08, 0xfa, 0x42, 0x05, 0xa2, - 0x01, 0x02, 0x08, 0x01, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x5f, 0x0a, 0x23, 0x49, 0x6e, 0x74, 0x65, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, - 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xcb, 0x01, 0x0a, 0x20, 0x49, 0x6e, + 0x01, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x13, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0xa2, 0x01, 0x02, 0x08, 0x01, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x5f, 0x0a, 0x23, 0x49, + 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xcb, 0x01, 0x0a, + 0x20, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x29, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, + 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0e, + 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0d, + 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x4b, 0x0a, + 0x11, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0xa2, 0x01, 0x02, 0x08, 0x01, 0x52, 0x10, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, + 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x67, 0x0a, 0x21, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, - 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0a, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0e, 0x69, 0x6e, 0x74, - 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0d, 0x69, 0x6e, 0x74, - 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x11, 0x61, 0x74, - 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0xa2, 0x01, 0x02, 0x08, 0x01, 0x52, 0x10, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x67, 0x0a, 0x21, 0x49, 0x6e, 0x74, 0x65, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x74, - 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x06, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, - 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, - 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x22, 0x20, 0x0a, 0x1e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x1f, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, - 0x3c, 0x0a, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x23, 0x0a, - 0x21, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x39, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, - 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x22, 0x5d, 0x0a, - 0x17, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x42, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, + 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x22, 0x20, 0x0a, 0x1e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x1f, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, - 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x88, 0x01, 0x0a, - 0x0f, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x18, 0x03, 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, - 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xfd, 0x01, 0x0a, 0x19, 0x49, 0x6e, 0x74, 0x65, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, - 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x02, 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, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x42, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x08, - 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x22, 0x3c, 0x0a, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xb0, 0x01, - 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x23, 0x0a, 0x21, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb0, 0x05, 0x0a, 0x13, 0x49, - 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x75, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x33, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x22, 0x3c, 0x0a, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x23, 0x0a, 0x21, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, + 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, + 0x22, 0x5d, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, + 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, + 0x65, 0x6e, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, + 0xab, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 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, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xfd, 0x01, + 0x0a, 0x19, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x74, 0x74, + 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 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, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x42, + 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x04, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x31, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, + 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, + 0x74, 0x65, 0x6d, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x22, 0x3c, 0x0a, + 0x20, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x06, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x12, - 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x06, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, - 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x41, - 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, - 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, - 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x23, 0x0a, 0x21, 0x49, + 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x32, 0xb0, 0x05, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x75, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x69, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x06, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x06, 0x41, + 0x74, 0x74, 0x61, 0x63, 0x68, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x74, + 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x06, + 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x74, 0x61, + 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, + 0x65, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, + 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/app/controlplane/api/controlplane/v1/integrations.pb.validate.go b/app/controlplane/api/controlplane/v1/integrations.pb.validate.go index 37cd0d841..655613b68 100644 --- a/app/controlplane/api/controlplane/v1/integrations.pb.validate.go +++ b/app/controlplane/api/controlplane/v1/integrations.pb.validate.go @@ -72,6 +72,8 @@ func (m *IntegrationsServiceRegisterRequest) validate(all bool) error { errors = append(errors, err) } + // no validation rules for DisplayName + if m.GetRegistrationConfig() == nil { err := IntegrationsServiceRegisterRequestValidationError{ field: "RegistrationConfig", @@ -1325,6 +1327,8 @@ func (m *IntegrationItem) validate(all bool) error { // no validation rules for Kind + // no validation rules for DisplayName + if all { switch v := interface{}(m.GetCreatedAt()).(type) { case interface{ ValidateAll() error }: diff --git a/app/controlplane/api/controlplane/v1/integrations.proto b/app/controlplane/api/controlplane/v1/integrations.proto index dbdc0e322..e4a6a52e5 100644 --- a/app/controlplane/api/controlplane/v1/integrations.proto +++ b/app/controlplane/api/controlplane/v1/integrations.proto @@ -39,7 +39,10 @@ service IntegrationsService { message IntegrationsServiceRegisterRequest { // Kind of integration to register + // This should match the ID of an existing integration string kind = 1 [(validate.rules).string.min_len = 1]; + // Description of the registration, used for display purposes + string display_name = 3; // Associated registration configuration google.protobuf.Any registration_config = 2 [(validate.rules).any.required = true]; } @@ -82,6 +85,8 @@ message ListAttachmentsResponse{ message IntegrationItem { string id = 1; string kind = 2; + // Description of the registration, used for display purposes + string display_name = 4; google.protobuf.Timestamp created_at = 3; // Arbitrary configuration for the integration bytes config = 5; diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/integrations.ts b/app/controlplane/api/gen/frontend/controlplane/v1/integrations.ts index 6ed7bee24..32170522a 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/integrations.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/integrations.ts @@ -9,8 +9,13 @@ import { WorkflowItem } from "./response_messages"; export const protobufPackage = "controlplane.v1"; export interface IntegrationsServiceRegisterRequest { - /** Kind of integration to register */ + /** + * Kind of integration to register + * This should match the ID of an existing integration + */ kind: string; + /** Description of the registration, used for display purposes */ + displayName: string; /** Associated registration configuration */ registrationConfig?: Any; } @@ -55,6 +60,8 @@ export interface ListAttachmentsResponse { export interface IntegrationItem { id: string; kind: string; + /** Description of the registration, used for display purposes */ + displayName: string; createdAt?: Date; /** Arbitrary configuration for the integration */ config: Uint8Array; @@ -77,7 +84,7 @@ export interface IntegrationsServiceDeleteResponse { } function createBaseIntegrationsServiceRegisterRequest(): IntegrationsServiceRegisterRequest { - return { kind: "", registrationConfig: undefined }; + return { kind: "", displayName: "", registrationConfig: undefined }; } export const IntegrationsServiceRegisterRequest = { @@ -85,6 +92,9 @@ export const IntegrationsServiceRegisterRequest = { if (message.kind !== "") { writer.uint32(10).string(message.kind); } + if (message.displayName !== "") { + writer.uint32(26).string(message.displayName); + } if (message.registrationConfig !== undefined) { Any.encode(message.registrationConfig, writer.uint32(18).fork()).ldelim(); } @@ -105,6 +115,13 @@ export const IntegrationsServiceRegisterRequest = { message.kind = reader.string(); continue; + case 3: + if (tag != 26) { + break; + } + + message.displayName = reader.string(); + continue; case 2: if (tag != 18) { break; @@ -124,6 +141,7 @@ export const IntegrationsServiceRegisterRequest = { fromJSON(object: any): IntegrationsServiceRegisterRequest { return { kind: isSet(object.kind) ? String(object.kind) : "", + displayName: isSet(object.displayName) ? String(object.displayName) : "", registrationConfig: isSet(object.registrationConfig) ? Any.fromJSON(object.registrationConfig) : undefined, }; }, @@ -131,6 +149,7 @@ export const IntegrationsServiceRegisterRequest = { toJSON(message: IntegrationsServiceRegisterRequest): unknown { const obj: any = {}; message.kind !== undefined && (obj.kind = message.kind); + message.displayName !== undefined && (obj.displayName = message.displayName); message.registrationConfig !== undefined && (obj.registrationConfig = message.registrationConfig ? Any.toJSON(message.registrationConfig) : undefined); return obj; @@ -147,6 +166,7 @@ export const IntegrationsServiceRegisterRequest = { ): IntegrationsServiceRegisterRequest { const message = createBaseIntegrationsServiceRegisterRequest(); message.kind = object.kind ?? ""; + message.displayName = object.displayName ?? ""; message.registrationConfig = (object.registrationConfig !== undefined && object.registrationConfig !== null) ? Any.fromPartial(object.registrationConfig) : undefined; @@ -703,7 +723,7 @@ export const ListAttachmentsResponse = { }; function createBaseIntegrationItem(): IntegrationItem { - return { id: "", kind: "", createdAt: undefined, config: new Uint8Array() }; + return { id: "", kind: "", displayName: "", createdAt: undefined, config: new Uint8Array() }; } export const IntegrationItem = { @@ -714,6 +734,9 @@ export const IntegrationItem = { if (message.kind !== "") { writer.uint32(18).string(message.kind); } + if (message.displayName !== "") { + writer.uint32(34).string(message.displayName); + } if (message.createdAt !== undefined) { Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(26).fork()).ldelim(); } @@ -744,6 +767,13 @@ export const IntegrationItem = { message.kind = reader.string(); continue; + case 4: + if (tag != 34) { + break; + } + + message.displayName = reader.string(); + continue; case 3: if (tag != 26) { break; @@ -771,6 +801,7 @@ export const IntegrationItem = { return { id: isSet(object.id) ? String(object.id) : "", kind: isSet(object.kind) ? String(object.kind) : "", + displayName: isSet(object.displayName) ? String(object.displayName) : "", createdAt: isSet(object.createdAt) ? fromJsonTimestamp(object.createdAt) : undefined, config: isSet(object.config) ? bytesFromBase64(object.config) : new Uint8Array(), }; @@ -780,6 +811,7 @@ export const IntegrationItem = { const obj: any = {}; message.id !== undefined && (obj.id = message.id); message.kind !== undefined && (obj.kind = message.kind); + message.displayName !== undefined && (obj.displayName = message.displayName); message.createdAt !== undefined && (obj.createdAt = message.createdAt.toISOString()); message.config !== undefined && (obj.config = base64FromBytes(message.config !== undefined ? message.config : new Uint8Array())); @@ -794,6 +826,7 @@ export const IntegrationItem = { const message = createBaseIntegrationItem(); message.id = object.id ?? ""; message.kind = object.kind ?? ""; + message.displayName = object.displayName ?? ""; message.createdAt = object.createdAt ?? undefined; message.config = object.config ?? new Uint8Array(); return message; diff --git a/app/controlplane/internal/biz/integration.go b/app/controlplane/internal/biz/integration.go index 003d2deb0..2eace3df0 100644 --- a/app/controlplane/internal/biz/integration.go +++ b/app/controlplane/internal/biz/integration.go @@ -38,12 +38,17 @@ type IntegrationAttachment struct { } type Integration struct { - ID uuid.UUID - Kind string - CreatedAt *time.Time - Config []byte + ID uuid.UUID + // Kind is the type of the integration, it matches the registered extension ID + Kind string + // DisplayName is a human readable description of the integration registration + // It helps to differentiate different instances of the same kind + DisplayName string + // Registration Configuration, usually JSON marshalled + Config []byte // Identifier to the external provider where any secret information is stored SecretName string + CreatedAt *time.Time } type IntegrationAndAttachment struct { @@ -51,8 +56,14 @@ type IntegrationAndAttachment struct { *IntegrationAttachment } +type IntegrationCreateOpts struct { + Kind, DisplayName, SecretName string + OrgID uuid.UUID + Config []byte +} + type IntegrationRepo interface { - Create(ctx context.Context, orgID uuid.UUID, kind string, secretID string, config []byte) (*Integration, error) + Create(ctx context.Context, opts *IntegrationCreateOpts) (*Integration, error) List(ctx context.Context, orgID uuid.UUID) ([]*Integration, error) FindByIDInOrg(ctx context.Context, orgID, ID uuid.UUID) (*Integration, error) SoftDelete(ctx context.Context, ID uuid.UUID) error @@ -90,7 +101,7 @@ func NewIntegrationUseCase(opts *NewIntegrationUseCaseOpts) *IntegrationUseCase } // Persist the secret and integration with its configuration in the database -func (uc *IntegrationUseCase) RegisterAndSave(ctx context.Context, orgID string, i sdk.FanOut, regConfig *anypb.Any) (*Integration, error) { +func (uc *IntegrationUseCase) RegisterAndSave(ctx context.Context, orgID, displayName string, i sdk.FanOut, regConfig *anypb.Any) (*Integration, error) { orgUUID, err := uuid.Parse(orgID) if err != nil { return nil, NewErrInvalidUUID(err) @@ -117,7 +128,10 @@ func (uc *IntegrationUseCase) RegisterAndSave(ctx context.Context, orgID string, } // Persist the integration configuration - return uc.integrationRepo.Create(ctx, orgUUID, i.Describe().ID, secretID, registrationResponse.Configuration) + return uc.integrationRepo.Create(ctx, &IntegrationCreateOpts{ + OrgID: orgUUID, Kind: i.Describe().ID, DisplayName: displayName, + SecretName: secretID, Config: registrationResponse.Configuration, + }) } type AttachOpts struct { diff --git a/app/controlplane/internal/biz/integration_test.go b/app/controlplane/internal/biz/integration_test.go index 108124563..81adc1323 100644 --- a/app/controlplane/internal/biz/integration_test.go +++ b/app/controlplane/internal/biz/integration_test.go @@ -35,6 +35,7 @@ import ( func (s *testSuite) TestCreate() { const kind = "my-integration" + const description = "my registration description" assert := assert.New(s.T()) // Mocked integration that will return both generic configuration and credentials @@ -47,9 +48,10 @@ func (s *testSuite) TestCreate() { Password: "key", URL: "host"}, }, nil) - got, err := s.Integration.RegisterAndSave(ctx, s.org.ID, integration, s.configAny) + got, err := s.Integration.RegisterAndSave(ctx, s.org.ID, description, integration, s.configAny) assert.NoError(err) assert.Equal(kind, got.Kind) + assert.Equal(description, got.DisplayName) // Check configuration was stored assert.Equal(s.config, got.Config) @@ -207,7 +209,7 @@ func (s *testSuite) SetupTest() { fanOut.On("Register", ctx, mock.Anything).Return(&sdk.RegistrationResponse{Configuration: s.config}, nil) s.fanOutIntegration = fanOut - s.integration, err = s.Integration.RegisterAndSave(ctx, s.org.ID, fanOut, s.configAny) + s.integration, err = s.Integration.RegisterAndSave(ctx, s.org.ID, "my integration instance", fanOut, s.configAny) assert.NoError(err) } diff --git a/app/controlplane/internal/biz/organization_integration_test.go b/app/controlplane/internal/biz/organization_integration_test.go index 1ec51cf57..47530badc 100644 --- a/app/controlplane/internal/biz/organization_integration_test.go +++ b/app/controlplane/internal/biz/organization_integration_test.go @@ -127,7 +127,7 @@ func (s *OrgIntegrationTestSuite) SetupTest() { configAny, err := anypb.New(config) assert.NoError(err) - _, err = s.Integration.RegisterAndSave(ctx, s.org.ID, integration, configAny) + _, err = s.Integration.RegisterAndSave(ctx, s.org.ID, "", integration, configAny) assert.NoError(err) // OCI repository diff --git a/app/controlplane/internal/data/ent/integration.go b/app/controlplane/internal/data/ent/integration.go index d0b13c60c..6287c651d 100644 --- a/app/controlplane/internal/data/ent/integration.go +++ b/app/controlplane/internal/data/ent/integration.go @@ -20,6 +20,8 @@ type Integration struct { ID uuid.UUID `json:"id,omitempty"` // Kind holds the value of the "kind" field. Kind string `json:"kind,omitempty"` + // DisplayName holds the value of the "display_name" field. + DisplayName string `json:"display_name,omitempty"` // SecretName holds the value of the "secret_name" field. SecretName string `json:"secret_name,omitempty"` // CreatedAt holds the value of the "created_at" field. @@ -74,7 +76,7 @@ func (*Integration) scanValues(columns []string) ([]any, error) { switch columns[i] { case integration.FieldConfiguration: values[i] = new([]byte) - case integration.FieldKind, integration.FieldSecretName: + case integration.FieldKind, integration.FieldDisplayName, integration.FieldSecretName: values[i] = new(sql.NullString) case integration.FieldCreatedAt, integration.FieldDeletedAt: values[i] = new(sql.NullTime) @@ -109,6 +111,12 @@ func (i *Integration) assignValues(columns []string, values []any) error { } else if value.Valid { i.Kind = value.String } + case integration.FieldDisplayName: + if value, ok := values[j].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field display_name", values[j]) + } else if value.Valid { + i.DisplayName = value.String + } case integration.FieldSecretName: if value, ok := values[j].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field secret_name", values[j]) @@ -181,6 +189,9 @@ func (i *Integration) String() string { builder.WriteString("kind=") builder.WriteString(i.Kind) builder.WriteString(", ") + builder.WriteString("display_name=") + builder.WriteString(i.DisplayName) + builder.WriteString(", ") builder.WriteString("secret_name=") builder.WriteString(i.SecretName) builder.WriteString(", ") diff --git a/app/controlplane/internal/data/ent/integration/integration.go b/app/controlplane/internal/data/ent/integration/integration.go index 3280cdbaf..97dd6ee2a 100644 --- a/app/controlplane/internal/data/ent/integration/integration.go +++ b/app/controlplane/internal/data/ent/integration/integration.go @@ -15,6 +15,8 @@ const ( FieldID = "id" // FieldKind holds the string denoting the kind field in the database. FieldKind = "kind" + // FieldDisplayName holds the string denoting the display_name field in the database. + FieldDisplayName = "display_name" // FieldSecretName holds the string denoting the secret_name field in the database. FieldSecretName = "secret_name" // FieldCreatedAt holds the string denoting the created_at field in the database. @@ -49,6 +51,7 @@ const ( var Columns = []string{ FieldID, FieldKind, + FieldDisplayName, FieldSecretName, FieldCreatedAt, FieldConfiguration, diff --git a/app/controlplane/internal/data/ent/integration/where.go b/app/controlplane/internal/data/ent/integration/where.go index 12b546024..ae0bf6c1f 100644 --- a/app/controlplane/internal/data/ent/integration/where.go +++ b/app/controlplane/internal/data/ent/integration/where.go @@ -61,6 +61,11 @@ func Kind(v string) predicate.Integration { return predicate.Integration(sql.FieldEQ(FieldKind, v)) } +// DisplayName applies equality check predicate on the "display_name" field. It's identical to DisplayNameEQ. +func DisplayName(v string) predicate.Integration { + return predicate.Integration(sql.FieldEQ(FieldDisplayName, v)) +} + // SecretName applies equality check predicate on the "secret_name" field. It's identical to SecretNameEQ. func SecretName(v string) predicate.Integration { return predicate.Integration(sql.FieldEQ(FieldSecretName, v)) @@ -146,6 +151,81 @@ func KindContainsFold(v string) predicate.Integration { return predicate.Integration(sql.FieldContainsFold(FieldKind, v)) } +// DisplayNameEQ applies the EQ predicate on the "display_name" field. +func DisplayNameEQ(v string) predicate.Integration { + return predicate.Integration(sql.FieldEQ(FieldDisplayName, v)) +} + +// DisplayNameNEQ applies the NEQ predicate on the "display_name" field. +func DisplayNameNEQ(v string) predicate.Integration { + return predicate.Integration(sql.FieldNEQ(FieldDisplayName, v)) +} + +// DisplayNameIn applies the In predicate on the "display_name" field. +func DisplayNameIn(vs ...string) predicate.Integration { + return predicate.Integration(sql.FieldIn(FieldDisplayName, vs...)) +} + +// DisplayNameNotIn applies the NotIn predicate on the "display_name" field. +func DisplayNameNotIn(vs ...string) predicate.Integration { + return predicate.Integration(sql.FieldNotIn(FieldDisplayName, vs...)) +} + +// DisplayNameGT applies the GT predicate on the "display_name" field. +func DisplayNameGT(v string) predicate.Integration { + return predicate.Integration(sql.FieldGT(FieldDisplayName, v)) +} + +// DisplayNameGTE applies the GTE predicate on the "display_name" field. +func DisplayNameGTE(v string) predicate.Integration { + return predicate.Integration(sql.FieldGTE(FieldDisplayName, v)) +} + +// DisplayNameLT applies the LT predicate on the "display_name" field. +func DisplayNameLT(v string) predicate.Integration { + return predicate.Integration(sql.FieldLT(FieldDisplayName, v)) +} + +// DisplayNameLTE applies the LTE predicate on the "display_name" field. +func DisplayNameLTE(v string) predicate.Integration { + return predicate.Integration(sql.FieldLTE(FieldDisplayName, v)) +} + +// DisplayNameContains applies the Contains predicate on the "display_name" field. +func DisplayNameContains(v string) predicate.Integration { + return predicate.Integration(sql.FieldContains(FieldDisplayName, v)) +} + +// DisplayNameHasPrefix applies the HasPrefix predicate on the "display_name" field. +func DisplayNameHasPrefix(v string) predicate.Integration { + return predicate.Integration(sql.FieldHasPrefix(FieldDisplayName, v)) +} + +// DisplayNameHasSuffix applies the HasSuffix predicate on the "display_name" field. +func DisplayNameHasSuffix(v string) predicate.Integration { + return predicate.Integration(sql.FieldHasSuffix(FieldDisplayName, v)) +} + +// DisplayNameIsNil applies the IsNil predicate on the "display_name" field. +func DisplayNameIsNil() predicate.Integration { + return predicate.Integration(sql.FieldIsNull(FieldDisplayName)) +} + +// DisplayNameNotNil applies the NotNil predicate on the "display_name" field. +func DisplayNameNotNil() predicate.Integration { + return predicate.Integration(sql.FieldNotNull(FieldDisplayName)) +} + +// DisplayNameEqualFold applies the EqualFold predicate on the "display_name" field. +func DisplayNameEqualFold(v string) predicate.Integration { + return predicate.Integration(sql.FieldEqualFold(FieldDisplayName, v)) +} + +// DisplayNameContainsFold applies the ContainsFold predicate on the "display_name" field. +func DisplayNameContainsFold(v string) predicate.Integration { + return predicate.Integration(sql.FieldContainsFold(FieldDisplayName, v)) +} + // SecretNameEQ applies the EQ predicate on the "secret_name" field. func SecretNameEQ(v string) predicate.Integration { return predicate.Integration(sql.FieldEQ(FieldSecretName, v)) diff --git a/app/controlplane/internal/data/ent/integration_create.go b/app/controlplane/internal/data/ent/integration_create.go index 2e274b463..8b880b22c 100644 --- a/app/controlplane/internal/data/ent/integration_create.go +++ b/app/controlplane/internal/data/ent/integration_create.go @@ -29,6 +29,20 @@ func (ic *IntegrationCreate) SetKind(s string) *IntegrationCreate { return ic } +// SetDisplayName sets the "display_name" field. +func (ic *IntegrationCreate) SetDisplayName(s string) *IntegrationCreate { + ic.mutation.SetDisplayName(s) + return ic +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (ic *IntegrationCreate) SetNillableDisplayName(s *string) *IntegrationCreate { + if s != nil { + ic.SetDisplayName(*s) + } + return ic +} + // SetSecretName sets the "secret_name" field. func (ic *IntegrationCreate) SetSecretName(s string) *IntegrationCreate { ic.mutation.SetSecretName(s) @@ -207,6 +221,10 @@ func (ic *IntegrationCreate) createSpec() (*Integration, *sqlgraph.CreateSpec) { _spec.SetField(integration.FieldKind, field.TypeString, value) _node.Kind = value } + if value, ok := ic.mutation.DisplayName(); ok { + _spec.SetField(integration.FieldDisplayName, field.TypeString, value) + _node.DisplayName = value + } if value, ok := ic.mutation.SecretName(); ok { _spec.SetField(integration.FieldSecretName, field.TypeString, value) _node.SecretName = value diff --git a/app/controlplane/internal/data/ent/integration_update.go b/app/controlplane/internal/data/ent/integration_update.go index 34e8b7420..5f4e75611 100644 --- a/app/controlplane/internal/data/ent/integration_update.go +++ b/app/controlplane/internal/data/ent/integration_update.go @@ -31,6 +31,26 @@ func (iu *IntegrationUpdate) Where(ps ...predicate.Integration) *IntegrationUpda return iu } +// SetDisplayName sets the "display_name" field. +func (iu *IntegrationUpdate) SetDisplayName(s string) *IntegrationUpdate { + iu.mutation.SetDisplayName(s) + return iu +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (iu *IntegrationUpdate) SetNillableDisplayName(s *string) *IntegrationUpdate { + if s != nil { + iu.SetDisplayName(*s) + } + return iu +} + +// ClearDisplayName clears the value of the "display_name" field. +func (iu *IntegrationUpdate) ClearDisplayName() *IntegrationUpdate { + iu.mutation.ClearDisplayName() + return iu +} + // SetConfiguration sets the "configuration" field. func (iu *IntegrationUpdate) SetConfiguration(b []byte) *IntegrationUpdate { iu.mutation.SetConfiguration(b) @@ -168,6 +188,12 @@ func (iu *IntegrationUpdate) sqlSave(ctx context.Context) (n int, err error) { } } } + if value, ok := iu.mutation.DisplayName(); ok { + _spec.SetField(integration.FieldDisplayName, field.TypeString, value) + } + if iu.mutation.DisplayNameCleared() { + _spec.ClearField(integration.FieldDisplayName, field.TypeString) + } if value, ok := iu.mutation.Configuration(); ok { _spec.SetField(integration.FieldConfiguration, field.TypeBytes, value) } @@ -289,6 +315,26 @@ type IntegrationUpdateOne struct { mutation *IntegrationMutation } +// SetDisplayName sets the "display_name" field. +func (iuo *IntegrationUpdateOne) SetDisplayName(s string) *IntegrationUpdateOne { + iuo.mutation.SetDisplayName(s) + return iuo +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (iuo *IntegrationUpdateOne) SetNillableDisplayName(s *string) *IntegrationUpdateOne { + if s != nil { + iuo.SetDisplayName(*s) + } + return iuo +} + +// ClearDisplayName clears the value of the "display_name" field. +func (iuo *IntegrationUpdateOne) ClearDisplayName() *IntegrationUpdateOne { + iuo.mutation.ClearDisplayName() + return iuo +} + // SetConfiguration sets the "configuration" field. func (iuo *IntegrationUpdateOne) SetConfiguration(b []byte) *IntegrationUpdateOne { iuo.mutation.SetConfiguration(b) @@ -456,6 +502,12 @@ func (iuo *IntegrationUpdateOne) sqlSave(ctx context.Context) (_node *Integratio } } } + if value, ok := iuo.mutation.DisplayName(); ok { + _spec.SetField(integration.FieldDisplayName, field.TypeString, value) + } + if iuo.mutation.DisplayNameCleared() { + _spec.ClearField(integration.FieldDisplayName, field.TypeString) + } if value, ok := iuo.mutation.Configuration(); ok { _spec.SetField(integration.FieldConfiguration, field.TypeBytes, value) } diff --git a/app/controlplane/internal/data/ent/migrate/schema.go b/app/controlplane/internal/data/ent/migrate/schema.go index 39a67a02c..4ba78bd50 100644 --- a/app/controlplane/internal/data/ent/migrate/schema.go +++ b/app/controlplane/internal/data/ent/migrate/schema.go @@ -12,6 +12,7 @@ var ( IntegrationsColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID, Unique: true}, {Name: "kind", Type: field.TypeString}, + {Name: "display_name", Type: field.TypeString, Nullable: true}, {Name: "secret_name", Type: field.TypeString}, {Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, {Name: "configuration", Type: field.TypeBytes, Nullable: true}, @@ -26,7 +27,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "integrations_organizations_integrations", - Columns: []*schema.Column{IntegrationsColumns[6]}, + Columns: []*schema.Column{IntegrationsColumns[7]}, RefColumns: []*schema.Column{OrganizationsColumns[0]}, OnDelete: schema.Cascade, }, diff --git a/app/controlplane/internal/data/ent/mutation.go b/app/controlplane/internal/data/ent/mutation.go index a6daf25b8..32fc66795 100644 --- a/app/controlplane/internal/data/ent/mutation.go +++ b/app/controlplane/internal/data/ent/mutation.go @@ -56,6 +56,7 @@ type IntegrationMutation struct { typ string id *uuid.UUID kind *string + display_name *string secret_name *string created_at *time.Time configuration *[]byte @@ -211,6 +212,55 @@ func (m *IntegrationMutation) ResetKind() { m.kind = nil } +// SetDisplayName sets the "display_name" field. +func (m *IntegrationMutation) SetDisplayName(s string) { + m.display_name = &s +} + +// DisplayName returns the value of the "display_name" field in the mutation. +func (m *IntegrationMutation) DisplayName() (r string, exists bool) { + v := m.display_name + if v == nil { + return + } + return *v, true +} + +// OldDisplayName returns the old "display_name" field's value of the Integration entity. +// If the Integration object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *IntegrationMutation) OldDisplayName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDisplayName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDisplayName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDisplayName: %w", err) + } + return oldValue.DisplayName, nil +} + +// ClearDisplayName clears the value of the "display_name" field. +func (m *IntegrationMutation) ClearDisplayName() { + m.display_name = nil + m.clearedFields[integration.FieldDisplayName] = struct{}{} +} + +// DisplayNameCleared returns if the "display_name" field was cleared in this mutation. +func (m *IntegrationMutation) DisplayNameCleared() bool { + _, ok := m.clearedFields[integration.FieldDisplayName] + return ok +} + +// ResetDisplayName resets all changes to the "display_name" field. +func (m *IntegrationMutation) ResetDisplayName() { + m.display_name = nil + delete(m.clearedFields, integration.FieldDisplayName) +} + // SetSecretName sets the "secret_name" field. func (m *IntegrationMutation) SetSecretName(s string) { m.secret_name = &s @@ -508,10 +558,13 @@ func (m *IntegrationMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *IntegrationMutation) Fields() []string { - fields := make([]string, 0, 5) + fields := make([]string, 0, 6) if m.kind != nil { fields = append(fields, integration.FieldKind) } + if m.display_name != nil { + fields = append(fields, integration.FieldDisplayName) + } if m.secret_name != nil { fields = append(fields, integration.FieldSecretName) } @@ -534,6 +587,8 @@ func (m *IntegrationMutation) Field(name string) (ent.Value, bool) { switch name { case integration.FieldKind: return m.Kind() + case integration.FieldDisplayName: + return m.DisplayName() case integration.FieldSecretName: return m.SecretName() case integration.FieldCreatedAt: @@ -553,6 +608,8 @@ func (m *IntegrationMutation) OldField(ctx context.Context, name string) (ent.Va switch name { case integration.FieldKind: return m.OldKind(ctx) + case integration.FieldDisplayName: + return m.OldDisplayName(ctx) case integration.FieldSecretName: return m.OldSecretName(ctx) case integration.FieldCreatedAt: @@ -577,6 +634,13 @@ func (m *IntegrationMutation) SetField(name string, value ent.Value) error { } m.SetKind(v) return nil + case integration.FieldDisplayName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDisplayName(v) + return nil case integration.FieldSecretName: v, ok := value.(string) if !ok { @@ -635,6 +699,9 @@ func (m *IntegrationMutation) AddField(name string, value ent.Value) error { // mutation. func (m *IntegrationMutation) ClearedFields() []string { var fields []string + if m.FieldCleared(integration.FieldDisplayName) { + fields = append(fields, integration.FieldDisplayName) + } if m.FieldCleared(integration.FieldConfiguration) { fields = append(fields, integration.FieldConfiguration) } @@ -655,6 +722,9 @@ func (m *IntegrationMutation) FieldCleared(name string) bool { // error if the field is not defined in the schema. func (m *IntegrationMutation) ClearField(name string) error { switch name { + case integration.FieldDisplayName: + m.ClearDisplayName() + return nil case integration.FieldConfiguration: m.ClearConfiguration() return nil @@ -672,6 +742,9 @@ func (m *IntegrationMutation) ResetField(name string) error { case integration.FieldKind: m.ResetKind() return nil + case integration.FieldDisplayName: + m.ResetDisplayName() + return nil case integration.FieldSecretName: m.ResetSecretName() return nil diff --git a/app/controlplane/internal/data/ent/runtime.go b/app/controlplane/internal/data/ent/runtime.go index f54f81164..472842d12 100644 --- a/app/controlplane/internal/data/ent/runtime.go +++ b/app/controlplane/internal/data/ent/runtime.go @@ -27,7 +27,7 @@ func init() { integrationFields := schema.Integration{}.Fields() _ = integrationFields // integrationDescCreatedAt is the schema descriptor for created_at field. - integrationDescCreatedAt := integrationFields[3].Descriptor() + integrationDescCreatedAt := integrationFields[4].Descriptor() // integration.DefaultCreatedAt holds the default value on creation for the created_at field. integration.DefaultCreatedAt = integrationDescCreatedAt.Default.(func() time.Time) // integrationDescID is the schema descriptor for id field. diff --git a/app/controlplane/internal/data/ent/schema-viz.html b/app/controlplane/internal/data/ent/schema-viz.html index 5ffd912fc..b87837d11 100644 --- a/app/controlplane/internal/data/ent/schema-viz.html +++ b/app/controlplane/internal/data/ent/schema-viz.html @@ -70,7 +70,7 @@ } - const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"Integration\",\"fields\":[{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"}]},{\"id\":\"OCIRepository\",\"fields\":[{\"name\":\"repo\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.OCIRepoValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation_ref\",\"type\":\"*biz.AttestationRef\"}]}],\"edges\":[{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"OCIRepository\",\"label\":\"oci_repositories\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"RobotAccount\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"}]}"); + const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"Integration\",\"fields\":[{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"display_name\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"}]},{\"id\":\"OCIRepository\",\"fields\":[{\"name\":\"repo\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.OCIRepoValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation_ref\",\"type\":\"*biz.AttestationRef\"}]}],\"edges\":[{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"OCIRepository\",\"label\":\"oci_repositories\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"RobotAccount\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"}]}"); const nodes = new vis.DataSet((entGraph.nodes || []).map(n => ({ id: n.id, diff --git a/app/controlplane/internal/data/ent/schema/integration.go b/app/controlplane/internal/data/ent/schema/integration.go index 137b3928f..23fbeeb76 100644 --- a/app/controlplane/internal/data/ent/schema/integration.go +++ b/app/controlplane/internal/data/ent/schema/integration.go @@ -33,6 +33,7 @@ func (Integration) Fields() []ent.Field { return []ent.Field{ field.UUID("id", uuid.UUID{}).Default(uuid.New).Unique().Immutable(), field.String("kind").Immutable(), + field.String("display_name").Optional(), field.String("secret_name").Immutable(), field.Time("created_at"). Default(time.Now). diff --git a/app/controlplane/internal/data/integration.go b/app/controlplane/internal/data/integration.go index e04712474..95b2ef457 100644 --- a/app/controlplane/internal/data/integration.go +++ b/app/controlplane/internal/data/integration.go @@ -39,12 +39,13 @@ func NewIntegrationRepo(data *Data, logger log.Logger) biz.IntegrationRepo { } } -func (r *IntegrationRepo) Create(ctx context.Context, orgID uuid.UUID, kind, secretName string, config []byte) (*biz.Integration, error) { +func (r *IntegrationRepo) Create(ctx context.Context, opts *biz.IntegrationCreateOpts) (*biz.Integration, error) { integration, err := r.data.db.Integration.Create(). - SetOrganizationID(orgID). - SetKind(kind). - SetSecretName(secretName). - SetConfiguration(config). + SetOrganizationID(opts.OrgID). + SetKind(opts.Kind). + SetDisplayName(opts.DisplayName). + SetSecretName(opts.SecretName). + SetConfiguration(opts.Config). Save(ctx) if err != nil { @@ -110,5 +111,12 @@ func entIntegrationToBiz(i *ent.Integration) *biz.Integration { return nil } - return &biz.Integration{ID: i.ID, Kind: i.Kind, CreatedAt: toTimePtr(i.CreatedAt), SecretName: i.SecretName, Config: i.Configuration} + return &biz.Integration{ + ID: i.ID, + Kind: i.Kind, + DisplayName: i.DisplayName, + CreatedAt: toTimePtr(i.CreatedAt), + SecretName: i.SecretName, + Config: i.Configuration, + } } diff --git a/app/controlplane/internal/dispatcher/dispatcher_test.go b/app/controlplane/internal/dispatcher/dispatcher_test.go index a4f6f3d0f..38aef862e 100644 --- a/app/controlplane/internal/dispatcher/dispatcher_test.go +++ b/app/controlplane/internal/dispatcher/dispatcher_test.go @@ -164,7 +164,7 @@ func (s *dispatcherTestSuite) SetupTest() { config, _ := anypb.New(&emptypb.Empty{}) s.cdxIntegrationBackend = &mockedIntegration{FanOutExtension: customImplementation, FanOutIntegration: b} - s.cdxIntegration, err = s.Integration.RegisterAndSave(ctx, s.org.ID, s.cdxIntegrationBackend, config) + s.cdxIntegration, err = s.Integration.RegisterAndSave(ctx, s.org.ID, "", s.cdxIntegrationBackend, config) require.NoError(s.T(), err) // Any material integration @@ -178,7 +178,7 @@ func (s *dispatcherTestSuite) SetupTest() { require.NoError(s.T(), err) s.anyIntegrationBackend = &mockedIntegration{FanOutExtension: customImplementation, FanOutIntegration: b} - s.anyIntegration, err = s.Integration.RegisterAndSave(ctx, s.org.ID, s.anyIntegrationBackend, config) + s.anyIntegration, err = s.Integration.RegisterAndSave(ctx, s.org.ID, "", s.anyIntegrationBackend, config) require.NoError(s.T(), err) // Attestation integration @@ -192,7 +192,7 @@ func (s *dispatcherTestSuite) SetupTest() { require.NoError(s.T(), err) s.ociIntegrationBackend = &mockedIntegration{FanOutExtension: customImplementation, FanOutIntegration: b} - s.ociIntegration, err = s.Integration.RegisterAndSave(ctx, s.org.ID, s.ociIntegrationBackend, config) + s.ociIntegration, err = s.Integration.RegisterAndSave(ctx, s.org.ID, "", s.ociIntegrationBackend, config) require.NoError(s.T(), err) // Attach all the integrations to the workflow diff --git a/app/controlplane/internal/service/integration.go b/app/controlplane/internal/service/integration.go index b6d2eb2c1..8ab943dbd 100644 --- a/app/controlplane/internal/service/integration.go +++ b/app/controlplane/internal/service/integration.go @@ -57,7 +57,7 @@ func (s *IntegrationsService) Register(ctx context.Context, req *pb.Integrations return nil, fmt.Errorf("loading integration: %w", err) } - i, err := s.integrationUC.RegisterAndSave(ctx, org.ID, integration, req.RegistrationConfig) + i, err := s.integrationUC.RegisterAndSave(ctx, org.ID, req.DisplayName, integration, req.RegistrationConfig) if err != nil { if biz.IsNotFound(err) { return nil, errors.NotFound("not found", err.Error()) @@ -193,7 +193,8 @@ func (s *IntegrationsService) Detach(ctx context.Context, req *pb.IntegrationsSe func bizIntegrationToPb(e *biz.Integration) *pb.IntegrationItem { return &pb.IntegrationItem{ Id: e.ID.String(), CreatedAt: timestamppb.New(*e.CreatedAt), - Kind: e.Kind, Config: e.Config, + DisplayName: e.DisplayName, + Kind: e.Kind, Config: e.Config, } }