TP-63844 | Update channel name logic (#426)

* TP-63844 | Update channel name logic
This commit is contained in:
Sriram Bhargav
2024-06-06 17:15:10 +05:30
committed by GitHub
parent dbf408380f
commit 4950c48038
13 changed files with 251 additions and 34 deletions

View File

@@ -108,6 +108,7 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_Success() {
suite.slackService.InviteUsersToConversationMock.Return(nil)
suite.slackService.GetConversationRepliesMock.Return([]slack.Message{}, false, "", nil)
suite.slackService.RenameSlackChannelMock.Return(nil)
suite.incidentRepository.UpsertIncidentRoleMock.Return(nil)
@@ -204,7 +205,7 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_InvalidStatus() {
mockIncident := GetMockIncident()
mockUser := GetMockUser()
mockTeam := GetMockTeamWithId(suite.previousTeamId)
mockSeverirty := GetMockSeverityWithId(suite.previousSeverityId)
mockSeverity := GetMockSeverityWithId(suite.previousSeverityId)
suite.incidentRepository.FindIncidentByIdMock.When(suite.mockIncidentId).
Then(mockIncident, nil)
@@ -213,7 +214,7 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_InvalidStatus() {
suite.teamRepository.FindTeamByIdMock.Return(mockTeam, nil)
suite.severityRepository.FindSeverityByIdMock.Return(mockSeverirty, nil)
suite.severityRepository.FindSeverityByIdMock.Return(mockSeverity, nil)
suite.incidentRepository.FindIncidentStatusByIdMock.Return(nil, errors.New("record not found"))
@@ -231,7 +232,7 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_InvalidChannel() {
mockIncident := GetMockIncident()
mockUser := GetMockUser()
mockTeam := GetMockTeamWithId(suite.previousTeamId)
mockSeverirty := GetMockSeverityWithId(suite.previousSeverityId)
mockSeverity := GetMockSeverityWithId(suite.previousSeverityId)
mockStatus := GetMockStatusWithId(suite.previousStatusId)
suite.incidentRepository.FindIncidentByIdMock.When(suite.mockIncidentId).
@@ -241,7 +242,7 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_InvalidChannel() {
suite.teamRepository.FindTeamByIdMock.Return(mockTeam, nil)
suite.severityRepository.FindSeverityByIdMock.Return(mockSeverirty, nil)
suite.severityRepository.FindSeverityByIdMock.Return(mockSeverity, nil)
suite.incidentRepository.FindIncidentStatusByIdMock.Return(mockStatus, nil)
@@ -261,7 +262,7 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_DBError() {
mockIncident := GetMockIncident()
mockUser := GetMockUser()
mockTeam := GetMockTeamWithId(suite.previousTeamId)
mockSeverirty := GetMockSeverityWithId(suite.previousSeverityId)
mockSeverity := GetMockSeverityWithId(suite.previousSeverityId)
mockStatus := GetMockStatusWithId(suite.previousStatusId)
mockChannels := GetMockChannels()
@@ -272,7 +273,7 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_DBError() {
suite.teamRepository.FindTeamByIdMock.Return(mockTeam, nil)
suite.severityRepository.FindSeverityByIdMock.Return(mockSeverirty, nil)
suite.severityRepository.FindSeverityByIdMock.Return(mockSeverity, nil)
suite.incidentRepository.FindIncidentStatusByIdMock.Return(mockStatus, nil)
@@ -313,6 +314,49 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_SlackError() {
suite.incidentRepository.UpdateIncidentMock.Return(nil)
suite.slackService.RenameSlackChannelMock.Return(nil)
suite.slackService.PostMessageByChannelIDMock.Return("", errors.New("Could not post message to slack channel"))
suite.slackService.UpdateMessageWithAttachmentsMock.Return(errors.New("Could not update message"))
suite.slackService.SetTopicOfConversationByChannelIdMock.Return(errors.New("Could not set topic of conversation"))
_, err := suite.incidentService.UpdateIncident(
service.UpdateIncidentRequest{
Id: suite.mockIncidentId,
Status: "2",
},
suite.mockUserEmail,
)
suite.Error(err, "update incident should fail when slack throws an error")
}
func (suite *IncidentServiceSuite) Test_UpdateIncident_ChannelRenameError() {
mockIncident := GetMockIncident()
mockUser := GetMockUser()
mockTeam := GetMockTeamWithId(suite.previousTeamId)
mockSeverity := GetMockSeverityWithId(suite.previousSeverityId)
mockStatus := GetMockStatusWithId(suite.previousStatusId)
mockChannels := GetMockChannels()
suite.incidentRepository.FindIncidentByIdMock.When(suite.mockIncidentId).
Then(mockIncident, nil)
suite.slackService.GetUserByEmailOrIDMock.When(suite.mockUserEmail).
Then(mockUser, nil)
suite.teamRepository.FindTeamByIdMock.Return(mockTeam, nil)
suite.severityRepository.FindSeverityByIdMock.Return(mockSeverity, nil)
suite.incidentRepository.FindIncidentStatusByIdMock.Return(mockStatus, nil)
suite.incidentChannelService.GetIncidentChannelsMock.Return(mockChannels, nil)
suite.incidentRepository.UpdateIncidentMock.Return(nil)
suite.slackService.RenameSlackChannelMock.Return(errors.New("could not rename slack channel"))
suite.slackService.PostMessageByChannelIDMock.Return("", errors.New("Could not post message to slack channel"))
suite.slackService.UpdateMessageWithAttachmentsMock.Return(errors.New("Could not update message"))

View File

@@ -59,6 +59,7 @@ import (
teamUserServiceImpl "houston/service/teamUser/impl"
teamUserSeverityServiceImpl "houston/service/teamUserSeverity/impl"
userService "houston/service/user"
utils "houston/service/utils"
"math"
"net/http"
"reflect"
@@ -155,8 +156,8 @@ func NewIncidentServiceV2(db *gorm.DB) *IncidentServiceV2 {
const logTag = "[create-incident]"
const updateLogTag = "[update-incident-v2]"
const resolveLogTag = "[resolve-incident]"
const updateSeveritySlackActionCount = 6
const updateStatusSlackActionCount = 3
const updateSeveritySlackActionCount = 7
const updateStatusSlackActionCount = 4
const updateTeamSlackActionCount = 5
/*
@@ -1372,7 +1373,13 @@ func (i *IncidentServiceV2) UpdateSeverityId(
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla)
incidentEntity.UpdatedAt = time.Now()
incidentEntity.UpdatedBy = userId
err := i.incidentRepository.UpdateIncidentWithJustification(incidentEntity, request.Justification)
incidentEntity.IncidentName = utils.ConstructIncidentChannelName(
incidentEntity.ID,
incidentEntity.SeverityId,
incidentStatusEntity.Name,
)
err = i.incidentRepository.UpdateIncidentWithJustification(incidentEntity, request.Justification)
if err != nil {
logger.Error(fmt.Sprintf("%s error in committing update to DB", updateLogTag), zap.Error(err))
return err
@@ -1479,6 +1486,14 @@ func (i *IncidentServiceV2) UpdateSeverityWorkflow(
processUpdateMessage(incidentEntity, teamEntity, severityEntity, incidentStatusEntity, incidentChannels, i)
})
go util.ExecuteConcurrentAction(&waitGroup, func() {
err = i.slackService.RenameSlackChannel(incidentEntity.SlackChannel, incidentEntity.IncidentName)
if err != nil {
logger.Error("error in renaming slack channel", zap.Error(err))
slackErrors = append(slackErrors, err)
}
})
go util.ExecuteConcurrentAction(&waitGroup, func() {
channelTopic := ChannelTopic{
ProductNames: getProductNamesFromProducts(incidentEntity.Products),
@@ -1533,11 +1548,18 @@ func (i *IncidentServiceV2) UpdateStatus(
if incidentEntity.Status != uint(statusID) {
currentStatus, _ := i.incidentRepository.FindIncidentStatusById(incidentEntity.Status)
statusToBeUpdated, _ := i.incidentRepository.FindIncidentStatusById(uint(statusID))
if statusToBeUpdated == nil {
logger.Error(fmt.Sprintf("%s no status found for status id %s", updateLogTag, request.Status),
zap.String("Status", request.Status), zap.Error(err))
return fmt.Errorf("Invalid status ID: %s", request.Status)
}
incidentEntity.IncidentName = utils.ConstructIncidentChannelName(
incidentEntity.ID,
incidentEntity.SeverityId,
statusToBeUpdated.Name,
)
switch uint(statusID) {
case incident.DuplicateId:
{
@@ -1639,6 +1661,14 @@ func (i *IncidentServiceV2) UpdateStatusWorkflow(
processUpdateMessage(incidentEntity, teamEntity, severityEntity, incidentStatus, incidentChannels, i)
})
go util.ExecuteConcurrentAction(&waitGroup, func() {
err := i.slackService.RenameSlackChannel(incidentEntity.SlackChannel, incidentEntity.IncidentName)
if err != nil {
logger.Error(fmt.Sprintf("%s error in renaming slack channel", updateLogTag), zap.Error(err))
slackErrors = append(slackErrors, err)
}
})
go util.ExecuteConcurrentAction(&waitGroup, func() {
channelTopic := ChannelTopic{
ProductNames: getProductNamesFromProducts(incidentEntity.Products),

View File

@@ -17,13 +17,14 @@ import (
tagModel "houston/model/tag"
rcaService "houston/service/rca/impl"
service "houston/service/request"
utils "houston/service/utils"
"strconv"
"strings"
"sync"
"time"
)
const postUpdateResolveStatusActionCount = 4
const postUpdateResolveStatusActionCount = 5
func (i *IncidentServiceV2) DuplicateUpdateIncidentStatus(duplicateOfID uint, userID string, incidentEntity *incident.IncidentEntity) error {
channelID := incidentEntity.SlackChannel
@@ -46,6 +47,12 @@ func (i *IncidentServiceV2) DuplicateUpdateIncidentStatus(duplicateOfID uint, us
metrics.PublishHoustonFlowFailureMetrics(DUPLICATE_INCIDENT, postErr.Error())
return customErrors.NewSlackError("Failed to post message in slack channel")
}
incidentEntity.IncidentName = utils.ConstructIncidentChannelName(
incidentEntity.ID,
incidentEntity.SeverityId,
incident.Duplicated,
)
err = i.incidentRepository.UpdateIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("%s Failed to update incident with id %d", updateLogTag, incidentId), zap.Error(err))
@@ -95,6 +102,15 @@ func (i *IncidentServiceV2) DuplicateUpdateStatusWorkFlow(currentChannel, origin
}
})
waitGroup.Add(1)
go util.ExecuteConcurrentAction(&waitGroup, func() {
err := i.slackService.RenameSlackChannel(incidentEntity.SlackChannel, incidentEntity.IncidentName)
if err != nil {
logger.Error(fmt.Sprintf("%s error in renaming slack channel", updateLogTag), zap.Error(err))
metrics.PublishHoustonFlowFailureMetrics(DUPLICATE_INCIDENT, err.Error())
}
})
waitGroup.Add(1)
go util.ExecuteConcurrentAction(&waitGroup, func() {
responderTeam, err := i.teamServiceV2.GetTeamById(incidentEntity.TeamId)
@@ -611,6 +627,12 @@ func (i *IncidentServiceV2) updateIncidentResolveStatus(incidentEntity *incident
endTime := time.Now()
incidentEntity.Status = incidentStatusEntity.ID
incidentEntity.EndTime = &endTime
incidentEntity.IncidentName = utils.ConstructIncidentChannelName(
incidentEntity.ID,
incidentEntity.SeverityId,
incident.Resolved,
)
err = i.commitIncidentEntity(incidentEntity, userId)
if err != nil {
logger.Error(fmt.Sprintf("%s Error while updating incident status to resolved", resolveLogTag), zap.Error(err))
@@ -773,6 +795,14 @@ func (i *IncidentServiceV2) postUpdateResolveStatusFlow(incidentEntity *incident
}
})
go util.ExecuteConcurrentAction(&waitGroup, func() {
err := i.slackService.RenameSlackChannel(incidentEntity.SlackChannel, incidentEntity.IncidentName)
if err != nil {
logger.Error(fmt.Sprintf("%s error in renaming slack channel", updateLogTag), zap.Error(err))
postResolveErrors.AddErrors(err)
}
})
go i.DeleteConferenceEvent(incidentEntity)
go i.processIncidentRCAFlow(incidentEntity)

View File

@@ -69,6 +69,7 @@ func (suite *IncidentServiceSuite) TestDuplicateUpdateIncidentStatus_Success() {
suite.slackService.PostMessageByChannelIDMock.Return("", nil)
suite.teamServiceV2.GetTeamByIdMock.Return(GetMockTeamWithId(1), nil)
suite.severityService.FindSeverityByIdMock.Return(GetMockSeverityDTOWitId(mockCurrentIncident.SeverityId), nil)
suite.slackService.RenameSlackChannelMock.Return(nil)
suite.slackService.SetTopicOfConversationByChannelIdMock.Return(nil)
err := suite.incidentService.DuplicateUpdateIncidentStatus(mockOriginalIncident.ID, "userID", mockCurrentIncident)
suite.NoError(err)
@@ -542,6 +543,7 @@ func (suite *IncidentServiceSuite) TestResolveIncident_PostResolveFlowErrorCase(
suite.tagService.GetMandatoryActiveTagsMock.Return(GetMockActiveMandatoryTags(), nil)
suite.incidentRepository.GetIncidentTagsByTagIdsMock.Return(mockIncidentTag, nil)
suite.slackService.PostMessageOptionMock.Return("", errors.New("Slack Error"))
suite.slackService.RenameSlackChannelMock.Return(nil)
suite.teamRepository.FindTeamByIdMock.Return(GetMockTeamWithId(1), nil)
suite.severityRepository.FindSeverityByIdMock.Return(GetMockSeverityWithId(3), nil)
suite.incidentRepository.FindIncidentStatusByIdMock.Return(GetMockIncidentStatus(), nil)
@@ -591,6 +593,7 @@ func (suite *IncidentServiceSuite) TestResolveIncident_HappyFlow() {
suite.incidentChannelService.GetIncidentChannelsMock.Return(GetMockChannels(), nil)
suite.slackService.UpdateMessageWithAttachmentsMock.Return(nil)
suite.calendarService.DeleteEventMock.Return(nil)
suite.slackService.RenameSlackChannelMock.Return(nil)
suite.rcaService.SendConversationDataForGeneratingRCAMock.Return(nil)
suite.teamServiceV2.GetTeamByIdMock.Return(GetMockTeamWithId(1), nil)
suite.severityService.FindSeverityByIdMock.Return(GetMockSeverityDTOWitId(mockIncident.SeverityId), nil)

View File

@@ -24,6 +24,7 @@ import (
"houston/service/teamService"
"houston/service/teamUser"
"houston/service/user"
utils "houston/service/utils"
"sort"
"sync"
"time"
@@ -211,7 +212,13 @@ func (i *incidentOrchestratorImpl) CreateIncident(
tx.Rollback()
return nil, err
}
slackChannel, err := i.createSlackChannelTransactionally(incidentEntity.ID, tx, incidentEntity.IncidentName)
slackChannel, err := i.createSlackChannelTransactionally(
incidentEntity.ID,
incidentEntity.IncidentName,
utils.ConstructIncidentChannelName(
incidentEntity.ID, request.SeverityID, string(incident.Investigating),
),
)
if err != nil {
tx.Rollback()
return nil, err
@@ -432,10 +439,8 @@ func (i *incidentOrchestratorImpl) createIncidentEntityTransactionally(
return entity, channelTopic, nil
}
func (i *incidentOrchestratorImpl) createSlackChannelTransactionally(
incidentID uint, tx *gorm.DB, incidentName string,
) (*slack2.Channel, error) {
channel, err := i.slackService.CreateSlackChannel(incidentID)
func (i *incidentOrchestratorImpl) createSlackChannelTransactionally(incidentID uint, incidentName string, channelName string) (*slack2.Channel, error) {
channel, err := i.slackService.CreateSlackChannel(incidentID, channelName)
if err != nil {
logger.Error(
fmt.Sprintf("%s [%s] Error while crating slack channel", logTag, incidentName),

View File

@@ -15,7 +15,6 @@ import (
"houston/model/team"
"houston/pkg/socketModeClient"
service "houston/service/response"
"strconv"
"strings"
"sync"
)
@@ -274,8 +273,8 @@ func (s *SlackService) IsASlackUser(slackIdOrEmail string) (bool, *slack.User) {
return true, user
}
func (s *SlackService) CreateSlackChannel(incidentId uint) (*slack.Channel, error) {
channel, err := createChannel(getIncidentChannelName(incidentId), s.SocketModeClientWrapper)
func (s *SlackService) CreateSlackChannel(incidentId uint, channelName string) (*slack.Channel, error) {
channel, err := createChannel(channelName, s.SocketModeClientWrapper)
if err != nil {
return nil, fmt.Errorf("%s failed to create Slack Channel for incident %d. error: %+v", logTag, incidentId, err)
}
@@ -314,19 +313,6 @@ func (s *SlackService) InviteUsersToConversation(channelId string, userIds ...st
return nil
}
func getIncidentChannelName(incidentID uint) string {
var channelPrefix string
env := viper.GetString("env")
if env == "prod" {
channelPrefix = "_houston-"
} else if env == "qa" {
channelPrefix = "_test-issue-"
} else {
channelPrefix = "_dev-issue-"
}
return channelPrefix + strconv.Itoa(int(incidentID))
}
func createChannel(channelName string, socketModeWrapper socketModeClient.ISocketModeClientWrapper) (*slack.Channel, error) {
request := slack.CreateConversationParams{
ChannelName: channelName,
@@ -601,6 +587,12 @@ func (s *SlackService) GetConversationInfo(channelId string) (*slack.Channel, er
return channel, nil
}
func (s *SlackService) RenameSlackChannel(channelId string, channelName string) error {
_, err := s.SocketModeClientWrapper.RenameConversation(channelId,
channelName)
return err
}
func (s *SlackService) PostDivider(channelId string) error {
blocks := slack.Blocks{
BlockSet: []slack.Block{

View File

@@ -25,7 +25,7 @@ type ISlackService interface {
GetUserBySlackID(slackUserID string) (*slack.User, error)
GetSlackUserIdOrEmail(email string) string
IsASlackUser(slackIdOrEmail string) (bool, *slack.User)
CreateSlackChannel(incidentId uint) (*slack.Channel, error)
CreateSlackChannel(incidentId uint, channelName string) (*slack.Channel, error)
InviteUsersToConversation(channelId string, userIds ...string) error
GetSlackConversationHistoryWithReplies(channelId string) ([]service.ConversationResponse, error)
GetChannelConversationHistory(channelId string) ([]slack.Message, error)
@@ -46,6 +46,7 @@ type ISlackService interface {
UploadFilesToChannel(files []string, channel string)
UploadFileByPath(filePath string, channels string) (file *slack.File, err error)
GetConversationInfo(channelId string) (*slack.Channel, error)
RenameSlackChannel(channelId string, channelName string) error
AckRequest(request socketmode.Request)
CheckUserInConversation(userID, channelID string) (bool, error)
PostDivider(channelId string) error

View File

@@ -314,7 +314,7 @@ func (suite *SlackServiceSuite) Test_GetUsersInfo_UsersMoreThanSplitSize() {
func (suite *SlackServiceSuite) Test_CreateSlackChannel() {
suite.SocketModeClientWrapper.CreateConversationMock.Return(GetMockChannel(), nil)
channel, err := suite.SlackService.CreateSlackChannel(1)
channel, err := suite.SlackService.CreateSlackChannel(1, "TestSlackChannel")
suite.NoError(err, "service must not throw error")
suite.NotNil(channel, "channel must not be nil")
}
@@ -667,6 +667,12 @@ func (suite *SlackServiceSuite) Test_ArchiveConversation_SuccessCase() {
}
func (suite *SlackServiceSuite) Test_RenameSlackChannel_SuccessCase() {
suite.SocketModeClientWrapper.RenameConversationMock.Return(GetMockChannel(), nil)
err := suite.SlackService.RenameSlackChannel("testSlackChannelID", "RenamedSlackChannel")
suite.NoError(err, "service must not throw error")
}
func GetMockMessageText() string {
return "Hello World!"
}

View File

@@ -0,0 +1,37 @@
package service
import (
"fmt"
"github.com/spf13/viper"
"strconv"
)
func appendEnvPostfixToChannel(channelName string) string {
var envPostfix string
env := viper.GetString("env")
if env == "prod" {
envPostfix = "houston"
} else if env == "qa" {
envPostfix = "test-issue"
} else {
envPostfix = "dev-issue"
}
return fmt.Sprintf("%s-%s", channelName, envPostfix)
}
func ConstructIncidentChannelName(incidentId uint, severityId uint, status string) string {
var severityStatusPrefix string
var channelPrefix = "_"
switch status {
case "Investigating", "Identified":
severityStatusPrefix = "i"
case "Monitoring":
severityStatusPrefix = "m"
case "Resolved":
severityStatusPrefix = "r"
case "Duplicated":
severityStatusPrefix = "rd"
}
var channelName = fmt.Sprintf("%s%s%d-%s", channelPrefix, severityStatusPrefix, severityId-1, strconv.Itoa(int(incidentId)))
return appendEnvPostfixToChannel(channelName)
}

View File

@@ -0,0 +1,62 @@
package service
import (
"github.com/spf13/viper"
"github.com/stretchr/testify/suite"
"houston/logger"
"testing"
)
type SlackChannelNameUtilsSuite struct {
suite.Suite
}
func (suite *SlackChannelNameUtilsSuite) Test_Investigating_Channel_Name_SuccessCase() {
viper.Set("env", "prod")
channelName := ConstructIncidentChannelName(15300, 1, "Investigating")
suite.Equal("_i0-15300-houston", channelName)
}
func (suite *SlackChannelNameUtilsSuite) Test_Identified_Channel_Name_SuccessCase() {
viper.Set("env", "prod")
channelName := ConstructIncidentChannelName(15399, 1, "Identified")
suite.Equal("_i0-15399-houston", channelName)
}
func (suite *SlackChannelNameUtilsSuite) Test_Monitoring_Channel_Name_SuccessCase() {
viper.Set("env", "dev")
channelName := ConstructIncidentChannelName(15229, 1, "Monitoring")
suite.Equal("_m0-15229-dev-issue", channelName)
}
func (suite *SlackChannelNameUtilsSuite) Test_Resolved_Channel_Name_SuccessCase() {
viper.Set("env", "qa")
channelName := ConstructIncidentChannelName(15398, 1, "Resolved")
suite.Equal("_r0-15398-test-issue", channelName)
}
func (suite *SlackChannelNameUtilsSuite) Test_Duplicated_Channel_Name_SuccessCase() {
viper.Set("env", "prod")
channelName := ConstructIncidentChannelName(15400, 1, "Duplicated")
suite.Equal("_rd0-15400-houston", channelName)
}
func (suite *SlackChannelNameUtilsSuite) Test_Duplicated_Channel_Name_SuccessCase_Sev1() {
viper.Set("env", "prod")
channelName := ConstructIncidentChannelName(15400, 2, "Duplicated")
suite.Equal("_rd1-15400-houston", channelName)
}
func (suite *SlackChannelNameUtilsSuite) Test_Monitoring_Channel_Name_SuccessCase_Sev3() {
viper.Set("env", "dev")
channelName := ConstructIncidentChannelName(15229, 4, "Monitoring")
suite.Equal("_m3-15229-dev-issue", channelName)
}
func (suite *SlackChannelNameUtilsSuite) SetupTest() {
logger.InitLogger()
}
func TestSlackUtilsSuite(t *testing.T) {
suite.Run(t, new(SlackChannelNameUtilsSuite))
}