INFRA-3151 : Add unarchival listener to add houston bot to incident slack channel (#416)
* INFRA-3151 : Add unarchival listener to add houston bot to incident slack channel * INFRA-3151 : review comments * INFRA-3151 : format fix
This commit is contained in:
@@ -33,14 +33,15 @@ import (
|
||||
)
|
||||
|
||||
type slackHandler struct {
|
||||
socketModeClient *socketmode.Client
|
||||
slashCommandProcessor *processor.SlashCommandProcessor
|
||||
commandProcessor processor.CommandProcessor
|
||||
memberJoinCallbackProcessor *processor.MemberJoinedCallbackEventProcessor
|
||||
blockActionProcessor *processor.BlockActionProcessor
|
||||
viewSubmissionProcessor *processor.ViewSubmissionProcessor
|
||||
userChangeEventProcessor *processor.UserChangeEventProcessor
|
||||
houstonCommandResolver *resolver.HoustonCommandResolver
|
||||
socketModeClient *socketmode.Client
|
||||
slashCommandProcessor *processor.SlashCommandProcessor
|
||||
commandProcessor processor.CommandProcessor
|
||||
memberJoinCallbackProcessor *processor.MemberJoinedCallbackEventProcessor
|
||||
channelUnarchivalEventProcessor *processor.ChannelUnarchivalEventProcessor
|
||||
blockActionProcessor *processor.BlockActionProcessor
|
||||
viewSubmissionProcessor *processor.ViewSubmissionProcessor
|
||||
userChangeEventProcessor *processor.UserChangeEventProcessor
|
||||
houstonCommandResolver *resolver.HoustonCommandResolver
|
||||
}
|
||||
|
||||
func NewSlackHandler(
|
||||
@@ -74,6 +75,7 @@ func NewSlackHandler(
|
||||
memberJoinCallbackProcessor: processor.NewMemberJoinedCallbackEventProcessor(
|
||||
socketModeClient, incidentService, teamService, severityService,
|
||||
),
|
||||
channelUnarchivalEventProcessor: processor.NewChannelUnarchivalEventProcessor(),
|
||||
blockActionProcessor: processor.NewBlockActionProcessor(
|
||||
socketModeClient,
|
||||
incidentService,
|
||||
@@ -149,6 +151,10 @@ func (sh *slackHandler) HoustonConnect() {
|
||||
switch innerEvent.Type {
|
||||
case util.UserChangeEvent:
|
||||
sh.userChangeEventProcessor.ProcessCommand(ev, evt.Request)
|
||||
case util.ChannelUnarchiveEvent:
|
||||
sh.channelUnarchivalEventProcessor.ProcessCommand(
|
||||
innerEvent.Data.(*slackevents.ChannelUnarchiveEvent), *evt.Request, sh.socketModeClient,
|
||||
)
|
||||
}
|
||||
|
||||
switch innerEventData := innerEvent.Data.(type) {
|
||||
|
||||
@@ -49,6 +49,7 @@ const (
|
||||
|
||||
const (
|
||||
UserChangeEvent = "user_change"
|
||||
ChannelUnarchiveEvent = "channel_unarchive"
|
||||
DeactivatedUserName = "Deactivated User"
|
||||
IsArchivedError = "is_archived"
|
||||
NotInChannelError = "not_in_channel"
|
||||
|
||||
33
internal/processor/channel_unarchive_event_processor.go
Normal file
33
internal/processor/channel_unarchive_event_processor.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/slack-go/slack/slackevents"
|
||||
"github.com/slack-go/slack/socketmode"
|
||||
"houston/appcontext"
|
||||
"houston/common/metrics"
|
||||
"houston/logger"
|
||||
"houston/service/incident"
|
||||
)
|
||||
|
||||
type ChannelUnarchivalEventProcessor struct {
|
||||
incidentService incident.IIncidentService
|
||||
}
|
||||
|
||||
func NewChannelUnarchivalEventProcessor() *ChannelUnarchivalEventProcessor {
|
||||
return &ChannelUnarchivalEventProcessor{
|
||||
incidentService: appcontext.GetIncidentService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (processor *ChannelUnarchivalEventProcessor) ProcessCommand(
|
||||
event *slackevents.ChannelUnarchiveEvent, request socketmode.Request, client *socketmode.Client,
|
||||
) {
|
||||
err := processor.incidentService.AddBotUponChannelUnarchival(event.Channel)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprint("Error in processing channel unarchival: ", err))
|
||||
metrics.PublishHoustonFlowFailureMetrics("CHANNEL_UNARCHIVAL_EVENT", err.Error())
|
||||
}
|
||||
|
||||
client.Ack(request)
|
||||
}
|
||||
@@ -61,6 +61,24 @@ func (r *Repository) GetUnArchivedChannelsForTerminalIncidents() ([]IncidentChan
|
||||
return incidentChannels, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetIncidentChannelBySlackChannelId(channelId string) (*IncidentChannelEntity, error) {
|
||||
var incidentChannel *IncidentChannelEntity
|
||||
|
||||
query := r.gormClient.
|
||||
Table("incident_channel").
|
||||
Where("incident_channel.slack_channel = ?", channelId).
|
||||
Joins("JOIN incident ON incident_channel.slack_channel = incident.slack_channel")
|
||||
|
||||
if err := query.First(&incidentChannel).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return incidentChannel, nil
|
||||
}
|
||||
|
||||
func (r *Repository) UpdateIncidentChannelArchivalStatuses(incidentChannelIds []uint, archivalStatus bool) error {
|
||||
err := r.gormClient.
|
||||
Model(&IncidentChannelEntity{}).
|
||||
|
||||
@@ -3,5 +3,6 @@ package incident_channel
|
||||
type IncidentChannelRepositoryInterface interface {
|
||||
GetIncidentChannels(incidentID uint) ([]IncidentChannelEntity, error)
|
||||
GetUnArchivedChannelsForTerminalIncidents() ([]IncidentChannelEntity, error)
|
||||
GetIncidentChannelBySlackChannelId(channelId string) (*IncidentChannelEntity, error)
|
||||
UpdateIncidentChannelArchivalStatuses(incidentChannelIds []uint, archivalStatus bool) error
|
||||
}
|
||||
|
||||
25
service/incident/impl/channel_unarchival.go
Normal file
25
service/incident/impl/channel_unarchival.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package impl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"houston/logger"
|
||||
)
|
||||
|
||||
func (i *IncidentServiceV2) AddBotUponChannelUnarchival(channelID string) error {
|
||||
const logTag = "[incident-service-add-bot-upon-channel-unarchival]"
|
||||
logger.Info(fmt.Sprintf("%s received request to add bot upon channel unarchival for channel: %s", logTag, channelID))
|
||||
|
||||
isIncidentChannel := i.incidentChannelService.IsIncidentChannel(channelID)
|
||||
if !isIncidentChannel {
|
||||
logger.Info(fmt.Sprintf("%s Channel: %s is not an incident channel", logTag, channelID))
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _, _, err := i.slackService.JoinConversation(channelID)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s failed to join channel %s %v", logTag, channelID, err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
23
service/incident/impl/channel_unarchival_test.go
Normal file
23
service/incident/impl/channel_unarchival_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package impl
|
||||
|
||||
import "errors"
|
||||
|
||||
func (suite *IncidentServiceSuite) Test_AddBotUponChannelUnarchival_NonIncidentChannelCase() {
|
||||
suite.incidentChannelService.IsIncidentChannelMock.Return(false)
|
||||
err := suite.incidentService.AddBotUponChannelUnarchival("123")
|
||||
suite.NoError(err, "service must not return error")
|
||||
}
|
||||
|
||||
func (suite *IncidentServiceSuite) Test_AddBotUponChannelUnarchival_JoinConversationFailureCase() {
|
||||
suite.incidentChannelService.IsIncidentChannelMock.Return(true)
|
||||
suite.slackService.JoinConversationMock.Return(nil, "", []string{}, errors.New("error"))
|
||||
err := suite.incidentService.AddBotUponChannelUnarchival("123")
|
||||
suite.Error(err, "service must return error")
|
||||
}
|
||||
|
||||
func (suite *IncidentServiceSuite) Test_AddBotUponChannelUnarchival_SuccessCase() {
|
||||
suite.incidentChannelService.IsIncidentChannelMock.Return(true)
|
||||
suite.slackService.JoinConversationMock.Return(nil, "", []string{}, nil)
|
||||
err := suite.incidentService.AddBotUponChannelUnarchival("123")
|
||||
suite.NoError(err, "service must not return error")
|
||||
}
|
||||
@@ -40,4 +40,5 @@ type IIncidentService interface {
|
||||
requestType string,
|
||||
) error
|
||||
FetchIncidentsApproachingSlaBreach() ([]incident.IncidentDTO, error)
|
||||
AddBotUponChannelUnarchival(channelID string) error
|
||||
}
|
||||
|
||||
@@ -42,3 +42,21 @@ func (i *IncidentChannelService) GetIncidentChannels(
|
||||
}
|
||||
return incidentChannelEntities, nil
|
||||
}
|
||||
|
||||
func (i *IncidentChannelService) getIncidentChannelBySlackChannelId(channelId string) (*incident_channel.IncidentChannelEntity, error) {
|
||||
incidentChannel, err := i.incidentChannelRepository.GetIncidentChannelBySlackChannelId(channelId)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to get incident channel for channel id %s %v", channelId, err))
|
||||
return nil, err
|
||||
}
|
||||
return incidentChannel, nil
|
||||
}
|
||||
|
||||
func (i *IncidentChannelService) IsIncidentChannel(channelID string) bool {
|
||||
incidentChannel, err := i.getIncidentChannelBySlackChannelId(channelID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return incidentChannel != nil
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@ import "houston/model/incident_channel"
|
||||
type IIncidentChannelService interface {
|
||||
GetIncidentChannels(incidentID uint) ([]incident_channel.IncidentChannelEntity, error)
|
||||
ArchiveIncidentChannels() error
|
||||
IsIncidentChannel(channelID string) bool
|
||||
}
|
||||
|
||||
@@ -34,6 +34,28 @@ func (suite *IncidentChannelServiceSuite) Test_GetIncidentChannels_SuccessCase()
|
||||
suite.Equal(incidentChannels, result)
|
||||
}
|
||||
|
||||
func (suite *IncidentChannelServiceSuite) Test_IsIncidentChannel_RepositoryFailureCase() {
|
||||
channelId := "123"
|
||||
suite.incidentChannelRepository.GetIncidentChannelBySlackChannelIdMock.Return(nil, errors.New("error"))
|
||||
result := suite.incidentChannelService.IsIncidentChannel(channelId)
|
||||
suite.False(result, "should return false")
|
||||
}
|
||||
|
||||
func (suite *IncidentChannelServiceSuite) Test_IsIncidentChannel_NilIncidentChannelCase() {
|
||||
channelId := "123"
|
||||
suite.incidentChannelRepository.GetIncidentChannelBySlackChannelIdMock.Return(nil, nil)
|
||||
result := suite.incidentChannelService.IsIncidentChannel(channelId)
|
||||
suite.False(result, "should return false")
|
||||
}
|
||||
|
||||
func (suite *IncidentChannelServiceSuite) Test_IsIncidentChannel_ValidIncidentChannelCase() {
|
||||
channelId := "123"
|
||||
incidentChannel := GetMockIncidentChannel(1, 1, channelId, false)
|
||||
suite.incidentChannelRepository.GetIncidentChannelBySlackChannelIdMock.Return(&incidentChannel, nil)
|
||||
result := suite.incidentChannelService.IsIncidentChannel(channelId)
|
||||
suite.True(result, "should return true")
|
||||
}
|
||||
|
||||
func GetMockIncidentChannel(id, incidentId uint, slackChannel string, isArchived bool) incident_channel.IncidentChannelEntity {
|
||||
incidentChannel := incident_channel.IncidentChannelEntity{
|
||||
IncidentId: incidentId,
|
||||
|
||||
@@ -50,4 +50,5 @@ type ISlackService interface {
|
||||
PostDivider(channelId string) error
|
||||
ArchiveConversation(channelId string) error
|
||||
GetAllUsers() ([]slack.User, error)
|
||||
JoinConversation(channelID string) (*slack.Channel, string, []string, error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user