diff --git a/Makefile b/Makefile index fbe7ac7..0d37938 100644 --- a/Makefile +++ b/Makefile @@ -58,3 +58,4 @@ generatemocks: cd $(CURDIR)/model/incident && minimock -i IIncidentRepository -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/pkg/monitoringService && minimock -i MonitoringServiceActions -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/service/krakatoa && minimock -i IKrakatoaService -s _mock.go -o $(CURDIR)/mocks + cd $(CURDIR)/pkg/socketModeClient && minimock -i ISocketModeClientWrapper -s _mock.go -o $(CURDIR)/mocks diff --git a/appcontext/app.go b/appcontext/app.go index b09ebbd..2a6a802 100644 --- a/appcontext/app.go +++ b/appcontext/app.go @@ -47,7 +47,7 @@ func InitializeServices() { logRepo: logRepo, teamRepo: teamRepo, severityRepo: severityRepo, - incidentRepo: initIncidentRepo(severityRepo, logRepo, teamRepo, slaService.SocketModeClient), + incidentRepo: initIncidentRepo(severityRepo, logRepo, teamRepo, slaService.SocketModeClientWrapper.GetClient()), slackService: slaService, incidentService: initIncidentService(), tagRepo: initTagRepo(), diff --git a/pkg/socketModeClient/socket_mode_client_wrapper.go b/pkg/socketModeClient/socket_mode_client_wrapper.go new file mode 100644 index 0000000..1f80762 --- /dev/null +++ b/pkg/socketModeClient/socket_mode_client_wrapper.go @@ -0,0 +1,123 @@ +package socketModeClient + +import ( + "github.com/slack-go/slack" + "github.com/slack-go/slack/socketmode" +) + +type SocketModeClientWrapper struct { + SocketModeClient *socketmode.Client +} + +func NewSocketModeClientWrapper(socketModeClient *socketmode.Client) *SocketModeClientWrapper { + return &SocketModeClientWrapper{socketModeClient} +} + +func (wrapper *SocketModeClientWrapper) PostMessage( + channelID string, + options ...slack.MsgOption, +) (string, string, error) { + return wrapper.SocketModeClient.PostMessage(channelID, options...) +} + +func (wrapper *SocketModeClientWrapper) UpdateMessage( + channelID string, + timestamp string, + options ...slack.MsgOption, +) (string, string, string, error) { + return wrapper.SocketModeClient.UpdateMessage(channelID, timestamp, options...) +} + +func (wrapper *SocketModeClientWrapper) UploadFile( + params slack.FileUploadParameters, +) (file *slack.File, err error) { + return wrapper.SocketModeClient.UploadFile(params) +} + +func (wrapper *SocketModeClientWrapper) AddBookmark( + channelID string, + params slack.AddBookmarkParameters, +) (slack.Bookmark, error) { + return wrapper.SocketModeClient.AddBookmark(channelID, params) +} + +func (wrapper *SocketModeClientWrapper) PostEphemeral( + channelID string, + userID string, + options ...slack.MsgOption, +) (string, error) { + return wrapper.SocketModeClient.PostEphemeral(channelID, userID, options...) +} + +func (wrapper *SocketModeClientWrapper) GetUsersInfo( + users ...string, +) (*[]slack.User, error) { + return wrapper.SocketModeClient.GetUsersInfo(users...) +} + +func (wrapper *SocketModeClientWrapper) GetUserByEmail( + email string, +) (*slack.User, error) { + return wrapper.SocketModeClient.GetUserByEmail(email) +} + +func (wrapper *SocketModeClientWrapper) CreateConversation( + params slack.CreateConversationParams, +) (*slack.Channel, error) { + return wrapper.SocketModeClient.CreateConversation(params) +} + +func (wrapper *SocketModeClientWrapper) GetConversationInfo( + input *slack.GetConversationInfoInput, +) (*slack.Channel, error) { + return wrapper.SocketModeClient.GetConversationInfo(input) +} + +func (wrapper *SocketModeClientWrapper) GetConversationHistory( + params *slack.GetConversationHistoryParameters, +) (*slack.GetConversationHistoryResponse, error) { + return wrapper.SocketModeClient.GetConversationHistory(params) +} + +func (wrapper *SocketModeClientWrapper) GetConversationReplies( + params *slack.GetConversationRepliesParameters, +) (msgs []slack.Message, hasMore bool, nextCursor string, err error) { + return wrapper.SocketModeClient.GetConversationReplies(params) +} + +func (wrapper *SocketModeClientWrapper) SetTopicOfConversation( + channelID string, + topic string, +) (*slack.Channel, error) { + return wrapper.SocketModeClient.SetTopicOfConversation(channelID, topic) +} + +func (wrapper *SocketModeClientWrapper) GetUsersInConversation( + params *slack.GetUsersInConversationParameters, +) ([]string, string, error) { + return wrapper.SocketModeClient.GetUsersInConversation(params) +} + +func (wrapper *SocketModeClientWrapper) InviteUsersToConversation( + channelID string, + users ...string, +) (*slack.Channel, error) { + return wrapper.SocketModeClient.InviteUsersToConversation(channelID, users...) +} + +func (wrapper *SocketModeClientWrapper) OpenView( + triggerID string, + view slack.ModalViewRequest, +) (*slack.ViewResponse, error) { + return wrapper.SocketModeClient.OpenView(triggerID, view) +} + +func (wrapper *SocketModeClientWrapper) AckRequest( + request socketmode.Request, payload ...interface{}, +) { + wrapper.SocketModeClient.Ack(request, payload...) +} + +func (wrapper *SocketModeClientWrapper) GetClient() *socketmode.Client { + return wrapper.SocketModeClient +} diff --git a/pkg/socketModeClient/socket_mode_client_wrapper_interface.go b/pkg/socketModeClient/socket_mode_client_wrapper_interface.go new file mode 100644 index 0000000..e1d1cc1 --- /dev/null +++ b/pkg/socketModeClient/socket_mode_client_wrapper_interface.go @@ -0,0 +1,26 @@ +package socketModeClient + +import ( + "github.com/slack-go/slack" + "github.com/slack-go/slack/socketmode" +) + +type ISocketModeClientWrapper interface { + PostMessage(channelID string, options ...slack.MsgOption) (string, string, error) + UpdateMessage(channelID string, timestamp string, options ...slack.MsgOption) (string, string, string, error) + UploadFile(params slack.FileUploadParameters) (*slack.File, error) + AddBookmark(channelID string, params slack.AddBookmarkParameters) (slack.Bookmark, error) + PostEphemeral(channelID string, userID string, options ...slack.MsgOption) (string, error) + GetUsersInfo(users ...string) (*[]slack.User, error) + GetUserByEmail(email string) (*slack.User, error) + CreateConversation(params slack.CreateConversationParams) (*slack.Channel, error) + GetConversationInfo(input *slack.GetConversationInfoInput) (*slack.Channel, error) + GetConversationHistory(params *slack.GetConversationHistoryParameters) (*slack.GetConversationHistoryResponse, error) + GetConversationReplies(params *slack.GetConversationRepliesParameters) ([]slack.Message, bool, string, error) + SetTopicOfConversation(channelID string, topic string) (*slack.Channel, error) + GetUsersInConversation(params *slack.GetUsersInConversationParameters) ([]string, string, error) + InviteUsersToConversation(channelID string, users ...string) (*slack.Channel, error) + OpenView(triggerID string, view slack.ModalViewRequest) (*slack.ViewResponse, error) + AckRequest(request socketmode.Request, payload ...interface{}) + GetClient() *socketmode.Client +} diff --git a/service/incident/incident_service_v2.go b/service/incident/incident_service_v2.go index a8fd574..22b13d3 100644 --- a/service/incident/incident_service_v2.go +++ b/service/incident/incident_service_v2.go @@ -54,7 +54,7 @@ func NewIncidentServiceV2(db *gorm.DB) *IncidentServiceV2 { userRepository := user.NewUserRepository(db) slackService := slack.NewSlackService() incidentRepository := incident.NewIncidentRepository( - db, severityRepository, logRepository, teamRepository, slackService.SocketModeClient, + db, severityRepository, logRepository, teamRepository, slackService.SocketModeClientWrapper.GetClient(), ) incidentChannelService := incidentChannel.NewIncidentChannelService(db) krakatoaService := krakatoa.NewKrakatoaService() diff --git a/service/slack/slack_service.go b/service/slack/slack_service.go index 859afc5..1210883 100644 --- a/service/slack/slack_service.go +++ b/service/slack/slack_service.go @@ -12,26 +12,28 @@ import ( "houston/model/incident" "houston/model/severity" "houston/model/team" + "houston/pkg/socketModeClient" service "houston/service/response" - "os" "strconv" "strings" "sync" ) type SlackService struct { - SocketModeClient *socketmode.Client + SocketModeClientWrapper socketModeClient.ISocketModeClientWrapper } func NewSlackService() *SlackService { - return &SlackService{createSocketModeClient()} + return &SlackService{ + socketModeClient.NewSocketModeClientWrapper(createSocketModeClient()), + } } const logTag = "[slack-service]" // SendDM - Send a direct message to the recipient func (s *SlackService) SendDM(recipientID string, messageText string) error { - _, _, err := s.SocketModeClient.PostMessage("@"+recipientID, slack.MsgOptionText(messageText, false)) + _, _, err := s.SocketModeClientWrapper.PostMessage("@"+recipientID, slack.MsgOptionText(messageText, false)) if err != nil { logger.Error(fmt.Sprintf("%s error in sending DM to the recipientID: %s: %v", logTag, recipientID, err)) return err @@ -43,7 +45,7 @@ func (s *SlackService) SendDM(recipientID string, messageText string) error { func (s *SlackService) PostMessage(message string, escape bool, channel *slack.Channel) (string, error) { msgOption := slack.MsgOptionText(message, escape) - _, timeStamp, err := s.SocketModeClient.PostMessage(channel.ID, msgOption) + _, timeStamp, err := s.SocketModeClientWrapper.PostMessage(channel.ID, msgOption) if err != nil { e := fmt.Sprintf("%s Failed to post message into channel: %s", logTag, channel.Name) logger.Error(e, zap.Error(err)) @@ -69,7 +71,7 @@ func (s *SlackService) UploadFilesToChannel(filePaths []string, channel string) } func (s *SlackService) UploadFileByPath(filePath string, channel string) (file *slack.File, err error) { - file, err = s.SocketModeClient.UploadFile(slack.FileUploadParameters{ + file, err = s.SocketModeClientWrapper.UploadFile(slack.FileUploadParameters{ File: filePath, Channels: []string{channel}, }) @@ -85,7 +87,7 @@ func (s *SlackService) UploadFileByPath(filePath string, channel string) (file * func (s *SlackService) AddBookmark(bookmark string, channel *slack.Channel, title string) { bookmarkParam := slack.AddBookmarkParameters{Link: bookmark, Title: title} - _, err := s.SocketModeClient.AddBookmark(channel.ID, bookmarkParam) + _, err := s.SocketModeClientWrapper.AddBookmark(channel.ID, bookmarkParam) if err != nil { e := fmt.Sprintf("%s Failed to add book mark into channel: %s", logTag, channel.Name) logger.Error(e, zap.Error(err)) @@ -93,7 +95,7 @@ func (s *SlackService) AddBookmark(bookmark string, channel *slack.Channel, titl } func (s *SlackService) PostMessageOption(channelID string, messageOption slack.MsgOption) (string, error) { - _, timeStamp, err := s.SocketModeClient.PostMessage(channelID, messageOption) + _, timeStamp, err := s.SocketModeClientWrapper.PostMessage(channelID, messageOption) if err != nil { e := fmt.Sprintf("%s Failed to post message into channelID: %s", logTag, channelID) logger.Error(e, zap.Error(err)) @@ -104,7 +106,7 @@ func (s *SlackService) PostMessageOption(channelID string, messageOption slack.M func (s *SlackService) PostMessageByChannelID(message string, escape bool, channelID string) (string, error) { msgOption := slack.MsgOptionText(message, escape) - _, timeStamp, err := s.SocketModeClient.PostMessage(channelID, msgOption) + _, timeStamp, err := s.SocketModeClientWrapper.PostMessage(channelID, msgOption) if err != nil { e := fmt.Sprintf("%s Failed to post message into channel: %s", logTag, channelID) logger.Error(e, zap.Error(err)) @@ -114,7 +116,7 @@ func (s *SlackService) PostMessageByChannelID(message string, escape bool, chann } func (s *SlackService) PostMessageWithAttachments(channelID string, attachment slack.Attachment) (string, error) { - _, timeStamp, err := s.SocketModeClient.PostMessage(channelID, slack.MsgOptionAttachments(attachment)) + _, timeStamp, err := s.SocketModeClientWrapper.PostMessage(channelID, slack.MsgOptionAttachments(attachment)) if err != nil { e := fmt.Sprintf("%s Failed to post message into channel: %s with attachments: %v", logTag, channelID, attachment) logger.Error(e, zap.Error(err)) @@ -124,13 +126,13 @@ func (s *SlackService) PostMessageWithAttachments(channelID string, attachment s } func (s *SlackService) UpdateMessageWithAttachments(channelID string, timestamp string, options slack.Attachment) error { - _, _, _, err := s.SocketModeClient.UpdateMessage(channelID, timestamp, slack.MsgOptionAttachments(options)) + _, _, _, err := s.SocketModeClientWrapper.UpdateMessage(channelID, timestamp, slack.MsgOptionAttachments(options)) return err } func (s *SlackService) PostEphemeralByChannelID(message string, userID string, escape bool, channelID string) error { msgOption := slack.MsgOptionText(message, escape) - _, err := s.SocketModeClient.PostEphemeral(channelID, userID, msgOption) + _, err := s.SocketModeClientWrapper.PostEphemeral(channelID, userID, msgOption) if err != nil { e := fmt.Sprintf("%s Failed to post message into channel: %s", logTag, channelID) logger.Error(e, zap.Error(err)) @@ -142,7 +144,7 @@ func (s *SlackService) PostEphemeralByChannelID(message string, userID string, e func (s *SlackService) PostMessageBlocks(channelID string, blocks slack.Blocks, color string) (string, error) { att := slack.Attachment{Blocks: blocks, Color: color} messageOption := slack.MsgOptionAttachments(att) - _, timestamp, err := s.SocketModeClient.PostMessage(channelID, messageOption) + _, timestamp, err := s.SocketModeClientWrapper.PostMessage(channelID, messageOption) if err != nil { return "", err } @@ -150,7 +152,7 @@ func (s *SlackService) PostMessageBlocks(channelID string, blocks slack.Blocks, } func (s *SlackService) GetConversationReplies(channelID string, timeStamp string, limit int) (msgs []slack.Message, hasMore bool, nextCursor string, err error) { - return s.SocketModeClient.GetConversationReplies( + return s.SocketModeClientWrapper.GetConversationReplies( &slack.GetConversationRepliesParameters{ChannelID: channelID, Timestamp: timeStamp, Limit: limit}, ) } @@ -186,7 +188,7 @@ func (s *SlackService) SetChannelTopicByChannelId( incidentEntity.Title, ) - _, err := s.SocketModeClient.SetTopicOfConversation(channelID, topic) + _, err := s.SocketModeClientWrapper.SetTopicOfConversation(channelID, topic) if err != nil { e := fmt.Sprintf("%s set topic on slack channel failed", logTag) logger.Error(e, zap.String("channel_id", channelName), zap.Error(err)) @@ -213,7 +215,7 @@ func (s *SlackService) GetUserByEmailOrID(userEmailOrSlackID string) (*slack.Use } func (s *SlackService) GetUserByEmail(userEmail string) (*slack.User, error) { - slackUser, err := s.SocketModeClient.GetUserByEmail(userEmail) + slackUser, err := s.SocketModeClientWrapper.GetUserByEmail(userEmail) if err != nil { e := fmt.Sprintf("%s error in getting user by email: %s", logTag, userEmail) logger.Error(e, zap.Error(err)) @@ -223,7 +225,7 @@ func (s *SlackService) GetUserByEmail(userEmail string) (*slack.User, error) { } func (s *SlackService) GetUserBySlackID(slackUserID string) (*slack.User, error) { - slackUser, err := s.SocketModeClient.GetUsersInfo(slackUserID) + slackUser, err := s.SocketModeClientWrapper.GetUsersInfo(slackUserID) if err != nil { e := fmt.Sprintf("%s error in getting user by ID: %s", logTag, slackUserID) logger.Error(e, zap.Error(err)) @@ -257,7 +259,7 @@ func (s *SlackService) IsASlackUser(slackIdOrEmail string) (bool, *slack.User) { } func (s *SlackService) CreateSlackChannel(incidentId uint) (*slack.Channel, error) { - channel, err := createChannel(getIncidentChannelName(incidentId), s.SocketModeClient) + channel, err := createChannel(getIncidentChannelName(incidentId), s.SocketModeClientWrapper) if err != nil { return nil, fmt.Errorf("%s failed to create Slack Channel for incident %d. error: %+v", logTag, incidentId, err) } @@ -273,7 +275,7 @@ func (s *SlackService) InviteUsersToConversation(channelId string, userIds ...st failedToAddUsers = append(failedToAddUsers, userId) } else { allUserNames = append(allUserNames, userInfo.Profile.RealName) - _, err := s.SocketModeClient.InviteUsersToConversation(channelId, userInfo.ID) + _, err := s.SocketModeClientWrapper.InviteUsersToConversation(channelId, userInfo.ID) if err != nil { failedToAddUsers = append(failedToAddUsers, userInfo.ID) } @@ -309,13 +311,13 @@ func getIncidentChannelName(incidentID uint) string { return channelPrefix + strconv.Itoa(int(incidentID)) } -func createChannel(channelName string, socketModeClient *socketmode.Client) (*slack.Channel, error) { +func createChannel(channelName string, socketModeWrapper socketModeClient.ISocketModeClientWrapper) (*slack.Channel, error) { request := slack.CreateConversationParams{ ChannelName: channelName, IsPrivate: false, } - channel, err := socketModeClient.CreateConversation(request) + channel, err := socketModeWrapper.CreateConversation(request) if err != nil { e := fmt.Sprintf("%s failed to create slack channel with name: %s", logTag, channelName) logger.Error(e, zap.Error(err)) @@ -330,23 +332,23 @@ func createSocketModeClient() *socketmode.Client { appToken := viper.GetString("houston.slack.app.token") if appToken == "" { logger.Error("HOUSTON_SLACK_APP_TOKEN must be set.") - os.Exit(1) + return nil } if !strings.HasPrefix(appToken, "xapp-") { logger.Error("HOUSTON_SLACK_APP_TOKEN must have the prefix \"xapp-\".") - os.Exit(1) + return nil } botToken := viper.GetString("houston.slack.bot.token") if botToken == "" { logger.Error("HOUSTON_SLACK_BOT_TOKEN must be set.") - os.Exit(1) + return nil } if !strings.HasPrefix(botToken, "xoxb-") { logger.Error("HOUSTON_SLACK_BOT_TOKEN must have the prefix \"xoxb-\".") - os.Exit(1) + return nil } api := slack.New( @@ -414,7 +416,7 @@ func (s *SlackService) GetChannelConversationHistory(channelId string) ([]slack. Limit: 400, } for hasMore { - conversation, err := s.SocketModeClient.GetConversationHistory(¶ms) + conversation, err := s.SocketModeClientWrapper.GetConversationHistory(¶ms) if err != nil { logger.Error(fmt.Sprintf("failed while fetching conversation history of channel id: %v", channelId), zap.Error(err)) return messages, err @@ -428,7 +430,7 @@ func (s *SlackService) GetChannelConversationHistory(channelId string) ([]slack. // GetAllUsersInConversation - gets list of members in a particular Slack channel func (s *SlackService) GetAllUsersInConversation(channelId string) ([]string, error) { - users, _, err := s.SocketModeClient.GetUsersInConversation(&slack.GetUsersInConversationParameters{ + users, _, err := s.SocketModeClientWrapper.GetUsersInConversation(&slack.GetUsersInConversationParameters{ ChannelID: channelId, }) if err != nil { @@ -464,7 +466,7 @@ func (s *SlackService) GetUsersInfo(users ...string) *[]slack.User { splittingSize := 30 splitUsersList := splitUsers(users, splittingSize) for usersList := range splitUsersList { - usersInfoResponse, err := s.SocketModeClient.GetUsersInfo(splitUsersList[usersList]...) + usersInfoResponse, err := s.SocketModeClientWrapper.GetUsersInfo(splitUsersList[usersList]...) if err != nil { logger.Error(fmt.Sprintf("%s failed to get users info for [%+v]. %+v", logTag, splitUsersList[usersList], err)) } else { @@ -514,11 +516,11 @@ func convertToConversationResponse(message slack.Message) service.ConversationRe func (s *SlackService) AckRequest(request socketmode.Request) { var payload interface{} - s.SocketModeClient.Ack(request, payload) + s.SocketModeClientWrapper.AckRequest(request, payload) } func (s *SlackService) OpenView(callback slack.InteractionCallback, modalRequest slack.ModalViewRequest, action string) { - _, err := s.SocketModeClient.OpenView(callback.TriggerID, modalRequest) + _, err := s.SocketModeClientWrapper.OpenView(callback.TriggerID, modalRequest) if err != nil { logger.Error(fmt.Sprintf("houston slackbot open view command for %s failed.", action), zap.String("trigger_id", callback.TriggerID), zap.String("channel_id", callback.Channel.ID), zap.Error(err)) @@ -527,7 +529,7 @@ func (s *SlackService) OpenView(callback slack.InteractionCallback, modalRequest } func (s *SlackService) GetConversationInfo(channelId string) (*slack.Channel, error) { - channel, err := s.SocketModeClient.GetConversationInfo(&slack.GetConversationInfoInput{ + channel, err := s.SocketModeClientWrapper.GetConversationInfo(&slack.GetConversationInfoInput{ ChannelID: channelId, }) if err != nil {