INFRA-3012 : Houston topic changes according to new construct (#415)
* INFRA-3012 : Houston title changes according to new construct * INFRA-3012 : add titkle change for resolve and duplicate case * INFRA-3012 : Failing tests fix * INFRA-3012 : Added migration script for backfilling
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
INSERT INTO incident_products (incident_entity_id, product_entity_id)
|
||||
SELECT i.id, 9
|
||||
FROM incident i
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM incident_products ip
|
||||
WHERE ip.incident_entity_id = i.id
|
||||
);
|
||||
|
||||
|
||||
UPDATE incident set reporting_team_id = 85 where reporting_team_id is NULL;
|
||||
|
||||
COMMIT;
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"houston/model/severity"
|
||||
"houston/model/team"
|
||||
"houston/pkg/slackbot"
|
||||
"houston/service/incident/impl"
|
||||
slackService "houston/service/slack"
|
||||
|
||||
"github.com/slack-go/slack"
|
||||
"github.com/slack-go/slack/socketmode"
|
||||
@@ -15,21 +17,22 @@ import (
|
||||
)
|
||||
|
||||
type IncidentUpdateTitleAction struct {
|
||||
client *socketmode.Client
|
||||
|
||||
client *socketmode.Client
|
||||
incidentService *incident.Repository
|
||||
teamService *team.Repository
|
||||
severityService *severity.Repository
|
||||
slackbotClient *slackbot.Client
|
||||
slackService slackService.ISlackService
|
||||
}
|
||||
|
||||
func NewIncidentUpdateTitleAction(client *socketmode.Client, incidentService *incident.Repository, teamService *team.Repository, severityService *severity.Repository, slackbotClient *slackbot.Client) *IncidentUpdateTitleAction {
|
||||
func NewIncidentUpdateTitleAction(client *socketmode.Client, incidentService *incident.Repository, teamService *team.Repository, severityService *severity.Repository, slackbotClient *slackbot.Client, service slackService.ISlackService) *IncidentUpdateTitleAction {
|
||||
return &IncidentUpdateTitleAction{
|
||||
client: client,
|
||||
incidentService: incidentService,
|
||||
teamService: teamService,
|
||||
severityService: severityService,
|
||||
slackbotClient: slackbotClient,
|
||||
slackService: service,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,25 +86,13 @@ func (itp *IncidentUpdateTitleAction) IncidentUpdateTitle(callback slack.Interac
|
||||
zap.String("user_id", user.ID), zap.Error(err))
|
||||
return
|
||||
}
|
||||
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> *>* `houston set title to %s`", user.ID, incidentEntity.Title), false)
|
||||
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> *>* `houston set title to %s`", user.ID, incidentEntity.Title), false)
|
||||
_, _, errMessage := itp.client.PostMessage(callback.View.PrivateMetadata, msgOption)
|
||||
if errMessage != nil {
|
||||
logger.Error("post response failed for IncidentUpdateTitle", zap.Error(errMessage))
|
||||
return
|
||||
}
|
||||
|
||||
result, err := itp.incidentService.FindIncidentSeverityTeamJoin(incidentEntity.SlackChannel)
|
||||
if err != nil {
|
||||
logger.Error("query failed for FindIncidentSeverityTeamJoin", zap.Error(errMessage))
|
||||
return
|
||||
}
|
||||
msgOption = slack.MsgOptionText(fmt.Sprintf("set the channel topic: %s : %s %s | %s", result.TeamName, result.SeverityName, incidentEntity.IncidentName, incidentEntity.Title), false)
|
||||
_, _, errMessage = itp.client.PostMessage(callback.View.PrivateMetadata, msgOption)
|
||||
if errMessage != nil {
|
||||
logger.Error("post response failed for IncidentUpdateTitle", zap.Error(errMessage))
|
||||
return
|
||||
}
|
||||
|
||||
severityEntity, err := itp.severityService.FindSeverityById(incidentEntity.SeverityId)
|
||||
if err != nil {
|
||||
logger.Error("error in fetching severity in incident update type action", zap.String("channel", incidentEntity.SlackChannel),
|
||||
@@ -113,6 +104,17 @@ func (itp *IncidentUpdateTitleAction) IncidentUpdateTitle(callback slack.Interac
|
||||
return
|
||||
}
|
||||
|
||||
statusEntity, err := itp.incidentService.FindIncidentStatusById(incidentEntity.Status)
|
||||
if err != nil {
|
||||
logger.Error("error in fetching status in incident update type action", zap.String("channel", incidentEntity.SlackChannel),
|
||||
zap.Uint("incident_id", incidentEntity.ID), zap.Error(err))
|
||||
return
|
||||
} else if statusEntity == nil {
|
||||
logger.Info("status not found in incident update type action", zap.String("channel", incidentEntity.SlackChannel),
|
||||
zap.Uint("incident_id", incidentEntity.ID))
|
||||
return
|
||||
}
|
||||
|
||||
teamEntity, err := itp.teamService.FindTeamById(incidentEntity.TeamId)
|
||||
if err != nil {
|
||||
logger.Error("FindTeamEntityById error",
|
||||
@@ -126,8 +128,32 @@ func (itp *IncidentUpdateTitleAction) IncidentUpdateTitle(callback slack.Interac
|
||||
return
|
||||
}
|
||||
|
||||
topic := fmt.Sprintf("%s-%s(%s) Incident-%d | %s", teamEntity.Name, severityEntity.Name, severityEntity.Description, incidentEntity.ID, incidentEntity.Title)
|
||||
itp.slackbotClient.SetChannelTopic(callback.View.PrivateMetadata, topic)
|
||||
productNames := make([]string, 0)
|
||||
for _, product := range incidentEntity.Products {
|
||||
productNames = append(productNames, product.Name)
|
||||
}
|
||||
|
||||
channelTopic := impl.ChannelTopic{
|
||||
ProductNames: productNames,
|
||||
ReportingTeamName: incidentEntity.ReportingTeam.Name,
|
||||
ResponderTeamName: teamEntity.Name,
|
||||
SeverityName: severityEntity.Name,
|
||||
StatusName: statusEntity.Name,
|
||||
Title: incidentEntity.Title,
|
||||
}
|
||||
|
||||
msgOption = slack.MsgOptionText(fmt.Sprintf("set the channel topic: %s", channelTopic.String()), false)
|
||||
_, _, errMessage = itp.client.PostMessage(callback.View.PrivateMetadata, msgOption)
|
||||
if errMessage != nil {
|
||||
logger.Error("post response failed for IncidentUpdateTitle", zap.Error(errMessage))
|
||||
return
|
||||
}
|
||||
|
||||
err = itp.slackService.SetTopicOfConversationByChannelId(incidentEntity.SlackChannel, incidentEntity.IncidentName, channelTopic.String())
|
||||
if err != nil {
|
||||
logger.Error("error in setting channel topic", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
var payload interface{}
|
||||
itp.client.Ack(*request, payload)
|
||||
|
||||
@@ -92,7 +92,7 @@ func NewBlockActionProcessor(
|
||||
incidentUpdateSeverityAction: action.NewIncidentUpdateSeverityAction(socketModeClient, incidentRepository,
|
||||
severityService, teamService, slackbotClient, incidentServiceV2),
|
||||
incidentUpdateTitleAction: action.NewIncidentUpdateTitleAction(socketModeClient, incidentRepository,
|
||||
teamService, severityService, slackbotClient),
|
||||
teamService, severityService, slackbotClient, slackService),
|
||||
incidentUpdateDescriptionAction: action.NewIncidentUpdateDescriptionAction(socketModeClient, incidentRepository),
|
||||
incidentDuplicateAction: action.NewDuplicateIncidentProcessor(socketModeClient, incidentRepository,
|
||||
tagService, teamService, severityService, incidentServiceV2),
|
||||
@@ -266,7 +266,7 @@ func NewViewSubmissionProcessor(
|
||||
updateIncidentAction: action.NewIncidentUpdateAction(socketModeClient, incidentRepository,
|
||||
tagService, teamService, severityService, incidentServiceV2),
|
||||
incidentUpdateTitleAction: action.NewIncidentUpdateTitleAction(socketModeClient, incidentRepository,
|
||||
teamService, severityService, slackbotClient),
|
||||
teamService, severityService, slackbotClient, slackService),
|
||||
incidentUpdateDescriptionAction: action.NewIncidentUpdateDescriptionAction(socketModeClient, incidentRepository),
|
||||
incidentUpdateSeverityAction: action.NewIncidentUpdateSeverityAction(socketModeClient, incidentRepository,
|
||||
severityService, teamService, slackbotClient, incidentServiceV2),
|
||||
|
||||
@@ -309,7 +309,7 @@ func (r *Repository) GetOpenIncidents(limit int) (*[]IncidentSeverityTeamDTO, er
|
||||
func (r *Repository) FindIncidentByChannelId(channelId string) (*IncidentEntity, error) {
|
||||
var incidentEntity IncidentEntity
|
||||
|
||||
result := r.gormClient.Preload("Products").Find(&incidentEntity, "slack_channel = ?", channelId)
|
||||
result := r.gormClient.Preload("Products").Preload("ReportingTeam").Find(&incidentEntity, "slack_channel = ?", channelId)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
@@ -324,7 +324,7 @@ func (r *Repository) FindIncidentByChannelId(channelId string) (*IncidentEntity,
|
||||
func (r *Repository) FindIncidentById(Id uint) (*IncidentEntity, error) {
|
||||
var incidentEntity IncidentEntity
|
||||
|
||||
result := r.gormClient.Preload("Products").Find(&incidentEntity, "id = ?", Id)
|
||||
result := r.gormClient.Preload("Products").Preload("ReportingTeam").Find(&incidentEntity, "id = ?", Id)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
29
service/incident/impl/channel_topic.go
Normal file
29
service/incident/impl/channel_topic.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package impl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ChannelTopic struct {
|
||||
ProductNames []string
|
||||
ReportingTeamName string
|
||||
ResponderTeamName string
|
||||
SeverityName string
|
||||
StatusName string
|
||||
Title string
|
||||
}
|
||||
|
||||
func (td *ChannelTopic) String() string {
|
||||
var productNameCommaSeparated string
|
||||
if len(td.ProductNames) > 1 {
|
||||
productNameCommaSeparated = fmt.Sprintf(strings.Join(td.ProductNames[:len(td.ProductNames)-1], ", ") + " & " + td.ProductNames[len(td.ProductNames)-1])
|
||||
} else if len(td.ProductNames) == 1 {
|
||||
productNameCommaSeparated = td.ProductNames[0]
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s || %s (%s) || %s -> %s || %s",
|
||||
productNameCommaSeparated, td.SeverityName, td.StatusName,
|
||||
td.ReportingTeamName, td.ResponderTeamName, td.Title,
|
||||
)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"houston/mocks"
|
||||
"houston/model/incident"
|
||||
"houston/model/incident_channel"
|
||||
"houston/model/product"
|
||||
"houston/model/severity"
|
||||
"houston/model/team"
|
||||
teamUserModel "houston/model/teamUser"
|
||||
@@ -38,6 +39,7 @@ type IncidentServiceSuite struct {
|
||||
calendarService mocks.ICalendarServiceMock
|
||||
rcaService mocks.IRcaServiceMock
|
||||
severityService mocks.ISeverityServiceMock
|
||||
teamServiceV2 mocks.ITeamServiceV2Mock
|
||||
mockDirectory string
|
||||
mockPngName string
|
||||
mockCsvName string
|
||||
@@ -122,6 +124,10 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_Success() {
|
||||
|
||||
suite.teamUserService.GetTeamUsersForGivenSeverityMock.Return([]teamUserModel.TeamUserDTO{tu}, nil)
|
||||
suite.teamUserService.GetTeamUsersWithMinimumSeverityIdLessThanOrEqualToGivenSeverityMock.Return([]teamUserModel.TeamUserDTO{tu}, nil)
|
||||
suite.teamServiceV2.GetTeamByIdMock.Return(GetMockTeamWithId(1), nil)
|
||||
suite.severityService.FindSeverityByIdMock.Return(GetMockSeverityDTOWitId(mockIncident.SeverityId), nil)
|
||||
suite.slackService.SetTopicOfConversationByChannelIdMock.Return(nil)
|
||||
suite.incidentRepository.UpdateIncidentWithAssociationsMock.Return(nil)
|
||||
_, err := suite.incidentService.UpdateIncident(
|
||||
service.UpdateIncidentRequest{
|
||||
Id: suite.mockIncidentId,
|
||||
@@ -129,6 +135,7 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_Success() {
|
||||
SeverityId: "3",
|
||||
TeamId: "2",
|
||||
MetaData: mockMetaData,
|
||||
ProductIDs: []uint{1, 2},
|
||||
},
|
||||
suite.mockUserEmail,
|
||||
)
|
||||
@@ -310,6 +317,8 @@ func (suite *IncidentServiceSuite) Test_UpdateIncident_SlackError() {
|
||||
|
||||
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,
|
||||
@@ -378,6 +387,7 @@ func GetMockIncident() *incident.IncidentEntity {
|
||||
RCA: "",
|
||||
ConferenceId: "",
|
||||
ConferenceLink: "",
|
||||
Products: make([]product.ProductEntity, 1),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +428,15 @@ func GetMockSeverityWithId(severityId uint) *severity.SeverityEntity {
|
||||
}
|
||||
}
|
||||
|
||||
func GetMockSeverityDTOWitId(severityId uint) *severity.SeverityDTO {
|
||||
return &severity.SeverityDTO{
|
||||
ID: severityId,
|
||||
Name: "Mock Severity",
|
||||
Description: "Mock Description",
|
||||
SlackUserIds: pq.StringArray{"1", "2"},
|
||||
}
|
||||
}
|
||||
|
||||
func GetMockStatusWithId(statusId uint) *incident.IncidentStatusEntity {
|
||||
return &incident.IncidentStatusEntity{
|
||||
Model: gorm.Model{
|
||||
@@ -498,6 +517,7 @@ func (suite *IncidentServiceSuite) SetupTest() {
|
||||
suite.updatedTeamId = 2
|
||||
suite.teamUserService = *mocks.NewITeamUserServiceMock(suite.T())
|
||||
suite.severityService = *mocks.NewISeverityServiceMock(suite.T())
|
||||
suite.teamServiceV2 = *mocks.NewITeamServiceV2Mock(suite.T())
|
||||
suite.incidentService = &IncidentServiceV2{
|
||||
db: nil,
|
||||
slackService: &suite.slackService,
|
||||
@@ -513,6 +533,7 @@ func (suite *IncidentServiceSuite) SetupTest() {
|
||||
rcaService: &suite.rcaService,
|
||||
teamUserService: &suite.teamUserService,
|
||||
severityService: &suite.severityService,
|
||||
teamServiceV2: &suite.teamServiceV2,
|
||||
}
|
||||
|
||||
suite.mockDirectory = "test_file"
|
||||
|
||||
@@ -156,7 +156,7 @@ const logTag = "[create-incident]"
|
||||
const updateLogTag = "[update-incident-v2]"
|
||||
const resolveLogTag = "[resolve-incident]"
|
||||
const updateSeveritySlackActionCount = 6
|
||||
const updateStatusSlackActionCount = 2
|
||||
const updateStatusSlackActionCount = 3
|
||||
const updateTeamSlackActionCount = 5
|
||||
|
||||
/*
|
||||
@@ -1480,8 +1480,17 @@ func (i *IncidentServiceV2) UpdateSeverityWorkflow(
|
||||
})
|
||||
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
err := i.slackService.SetChannelTopicByChannelId(
|
||||
incidentEntity.SlackChannel, incidentEntity.IncidentName, teamEntity, severityEntity, incidentEntity,
|
||||
channelTopic := ChannelTopic{
|
||||
ProductNames: getProductNamesFromProducts(incidentEntity.Products),
|
||||
ReportingTeamName: incidentEntity.ReportingTeam.Name,
|
||||
ResponderTeamName: teamEntity.Name,
|
||||
SeverityName: severityEntity.Name,
|
||||
StatusName: incidentStatusEntity.Name,
|
||||
Title: incidentEntity.Title,
|
||||
}
|
||||
err := i.slackService.SetTopicOfConversationByChannelId(
|
||||
incidentEntity.SlackChannel, incidentEntity.IncidentName,
|
||||
channelTopic.String(),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
@@ -1630,6 +1639,28 @@ func (i *IncidentServiceV2) UpdateStatusWorkflow(
|
||||
processUpdateMessage(incidentEntity, teamEntity, severityEntity, incidentStatus, incidentChannels, i)
|
||||
})
|
||||
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
channelTopic := ChannelTopic{
|
||||
ProductNames: getProductNamesFromProducts(incidentEntity.Products),
|
||||
ReportingTeamName: incidentEntity.ReportingTeam.Name,
|
||||
ResponderTeamName: teamEntity.Name,
|
||||
SeverityName: severityEntity.Name,
|
||||
StatusName: incidentStatus.Name,
|
||||
Title: incidentEntity.Title,
|
||||
}
|
||||
err := i.slackService.SetTopicOfConversationByChannelId(
|
||||
incidentEntity.SlackChannel, incidentEntity.IncidentName,
|
||||
channelTopic.String(),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
fmt.Sprintf("%s %d error while setting channel topic", updateLogTag, incidentEntity.TeamId),
|
||||
zap.Error(err),
|
||||
)
|
||||
slackErrors = append(slackErrors, err)
|
||||
}
|
||||
})
|
||||
|
||||
waitGroup.Wait()
|
||||
if slackErrors != nil && len(slackErrors) != 0 {
|
||||
return slackErrors[0]
|
||||
@@ -1843,19 +1874,24 @@ func (i *IncidentServiceV2) UpdateTeamIdWorkflow(
|
||||
slackErrors = append(slackErrors, err)
|
||||
}
|
||||
|
||||
channelTopic := ChannelTopic{
|
||||
ProductNames: getProductNamesFromProducts(incidentEntity.Products),
|
||||
ReportingTeamName: incidentEntity.ReportingTeam.Name,
|
||||
ResponderTeamName: teamEntity.Name,
|
||||
SeverityName: severityEntity.Name,
|
||||
StatusName: incidentStatus.Name,
|
||||
Title: incidentEntity.Title,
|
||||
}
|
||||
|
||||
var waitGroup sync.WaitGroup
|
||||
|
||||
waitGroup.Add(updateTeamSlackActionCount)
|
||||
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
txt := fmt.Sprintf(
|
||||
"<@%s> *>* set the channel topic: *%s · %s (%s) %s* | %s",
|
||||
"<@%s> *>* set the channel topic: %s",
|
||||
userId,
|
||||
teamEntity.Name,
|
||||
severityEntity.Name,
|
||||
severityEntity.Description,
|
||||
incidentEntity.IncidentName,
|
||||
incidentEntity.Title,
|
||||
channelTopic.String(),
|
||||
)
|
||||
att := slackUtil.Attachment{
|
||||
Text: txt,
|
||||
@@ -1905,8 +1941,9 @@ func (i *IncidentServiceV2) UpdateTeamIdWorkflow(
|
||||
})
|
||||
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
err := i.slackService.SetChannelTopicByChannelId(
|
||||
incidentEntity.SlackChannel, incidentEntity.IncidentName, teamEntity, severityEntity, incidentEntity,
|
||||
err := i.slackService.SetTopicOfConversationByChannelId(
|
||||
incidentEntity.SlackChannel, incidentEntity.IncidentName,
|
||||
channelTopic.String(),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
@@ -1935,7 +1972,7 @@ func (i *IncidentServiceV2) UpdateProductIdWorkflow(
|
||||
) error {
|
||||
var slackErrors []error
|
||||
var waitGroup sync.WaitGroup
|
||||
waitGroup.Add(2)
|
||||
waitGroup.Add(3)
|
||||
|
||||
incidentEntity, err := i.incidentRepository.FindIncidentById(incidentID)
|
||||
if err != nil {
|
||||
@@ -1943,10 +1980,7 @@ func (i *IncidentServiceV2) UpdateProductIdWorkflow(
|
||||
return err
|
||||
}
|
||||
|
||||
var productNames []string
|
||||
for _, p := range incidentEntity.Products {
|
||||
productNames = append(productNames, p.Name)
|
||||
}
|
||||
productNames := getProductNamesFromProducts(incidentEntity.Products)
|
||||
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
txt := fmt.Sprintf(
|
||||
@@ -1967,6 +2001,28 @@ func (i *IncidentServiceV2) UpdateProductIdWorkflow(
|
||||
processUpdateMessage(incidentEntity, teamEntity, severityEntity, incidentStatus, incidentChannels, i)
|
||||
})
|
||||
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
channelTopic := ChannelTopic{
|
||||
ProductNames: productNames,
|
||||
ReportingTeamName: incidentEntity.ReportingTeam.Name,
|
||||
ResponderTeamName: teamEntity.Name,
|
||||
SeverityName: severityEntity.Name,
|
||||
StatusName: incidentStatus.Name,
|
||||
Title: incidentEntity.Title,
|
||||
}
|
||||
err := i.slackService.SetTopicOfConversationByChannelId(
|
||||
incidentEntity.SlackChannel, incidentEntity.IncidentName,
|
||||
channelTopic.String(),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
fmt.Sprintf("%s %d error while setting channel topic", updateLogTag, incidentEntity.TeamId),
|
||||
zap.Error(err),
|
||||
)
|
||||
slackErrors = append(slackErrors, err)
|
||||
}
|
||||
})
|
||||
|
||||
waitGroup.Wait()
|
||||
if slackErrors != nil && len(slackErrors) != 0 {
|
||||
return slackErrors[0]
|
||||
@@ -2214,3 +2270,11 @@ func (i *IncidentServiceV2) SendAlert(incidentEntity *incident.IncidentEntity) {
|
||||
logger.Error(fmt.Sprintf("Error occurred while sending alert for incident: %d", incidentEntity.ID))
|
||||
}
|
||||
}
|
||||
|
||||
func getProductNamesFromProducts(products []product.ProductEntity) []string {
|
||||
var productNames []string
|
||||
for _, product := range products {
|
||||
productNames = append(productNames, product.Name)
|
||||
}
|
||||
return productNames
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const postUpdateResolveStatusActionCount = 3
|
||||
const postUpdateResolveStatusActionCount = 4
|
||||
|
||||
func (i *IncidentServiceV2) DuplicateUpdateIncidentStatus(duplicateOfID uint, userID string, incidentEntity *incident.IncidentEntity) error {
|
||||
channelID := incidentEntity.SlackChannel
|
||||
@@ -53,7 +53,7 @@ func (i *IncidentServiceV2) DuplicateUpdateIncidentStatus(duplicateOfID uint, us
|
||||
return customErrors.NewDataAccessError(fmt.Sprintf("Failed to update incident with id %d", incidentId))
|
||||
}
|
||||
|
||||
i.DuplicateUpdateStatusWorkFlow(channelID, originalIncidentChannel, incidentEntity.SeverityId)
|
||||
i.DuplicateUpdateStatusWorkFlow(channelID, originalIncidentChannel, incidentEntity)
|
||||
return nil
|
||||
}
|
||||
func (i *IncidentServiceV2) validateDuplicateIdAndGetChannelId(duplicateOfID uint, incidentId uint) (string, error) {
|
||||
@@ -70,11 +70,11 @@ func (i *IncidentServiceV2) validateDuplicateIdAndGetChannelId(duplicateOfID uin
|
||||
}
|
||||
return originalIncident.SlackChannel, nil
|
||||
}
|
||||
func (i *IncidentServiceV2) DuplicateUpdateStatusWorkFlow(currentChannel, originalIncidentChannel string, severityId uint) {
|
||||
func (i *IncidentServiceV2) DuplicateUpdateStatusWorkFlow(currentChannel, originalIncidentChannel string, incidentEntity *incident.IncidentEntity) {
|
||||
var waitGroup sync.WaitGroup
|
||||
waitGroup.Add(1)
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
msg, err := houstonSlackUtil.GetArchivalTimeOfIncidentChannel(incident.Duplicated, int(severityId))
|
||||
msg, err := houstonSlackUtil.GetArchivalTimeOfIncidentChannel(incident.Duplicated, int(incidentEntity.SeverityId))
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s Failed to get archiving time for incident channel %s", updateLogTag, currentChannel), zap.Error(err))
|
||||
metrics.PublishHoustonFlowFailureMetrics(DUPLICATE_INCIDENT, err.Error())
|
||||
@@ -94,6 +94,36 @@ func (i *IncidentServiceV2) DuplicateUpdateStatusWorkFlow(currentChannel, origin
|
||||
metrics.PublishHoustonFlowFailureMetrics(DUPLICATE_INCIDENT, err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
waitGroup.Add(1)
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
responderTeam, err := i.teamServiceV2.GetTeamById(incidentEntity.TeamId)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s Failed to get team with id %d", updateLogTag, incidentEntity.TeamId), zap.Error(err))
|
||||
metrics.PublishHoustonFlowFailureMetrics(DUPLICATE_INCIDENT, err.Error())
|
||||
return
|
||||
}
|
||||
severity, err := i.severityService.FindSeverityById(incidentEntity.SeverityId)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s Failed to get severity with id %d", updateLogTag, incidentEntity.SeverityId), zap.Error(err))
|
||||
metrics.PublishHoustonFlowFailureMetrics(DUPLICATE_INCIDENT, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
channelTopic := ChannelTopic{
|
||||
ProductNames: getProductNamesFromProducts(incidentEntity.Products),
|
||||
ReportingTeamName: incidentEntity.ReportingTeam.Name,
|
||||
ResponderTeamName: responderTeam.Name,
|
||||
SeverityName: severity.Name,
|
||||
StatusName: incident.Duplicated,
|
||||
Title: incidentEntity.Title,
|
||||
}
|
||||
err = i.slackService.SetTopicOfConversationByChannelId(currentChannel, incidentEntity.IncidentName, channelTopic.String())
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s Failed to set channel topic for channel %s", updateLogTag, currentChannel), zap.Error(err))
|
||||
metrics.PublishHoustonFlowFailureMetrics(DUPLICATE_INCIDENT, err.Error())
|
||||
}
|
||||
})
|
||||
waitGroup.Wait()
|
||||
}
|
||||
|
||||
@@ -714,6 +744,35 @@ func (i *IncidentServiceV2) postUpdateResolveStatusFlow(incidentEntity *incident
|
||||
}
|
||||
})
|
||||
|
||||
go util.ExecuteConcurrentAction(&waitGroup, func() {
|
||||
responderTeam, err := i.teamServiceV2.GetTeamById(incidentEntity.TeamId)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s Failed to get team with id %d", updateLogTag, incidentEntity.TeamId), zap.Error(err))
|
||||
postResolveErrors.AddErrors(err)
|
||||
return
|
||||
}
|
||||
severity, err := i.severityService.FindSeverityById(incidentEntity.SeverityId)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s Failed to get severity with id %d", updateLogTag, incidentEntity.SeverityId), zap.Error(err))
|
||||
postResolveErrors.AddErrors(err)
|
||||
return
|
||||
}
|
||||
|
||||
channelTopic := ChannelTopic{
|
||||
ProductNames: getProductNamesFromProducts(incidentEntity.Products),
|
||||
ReportingTeamName: incidentEntity.ReportingTeam.Name,
|
||||
ResponderTeamName: responderTeam.Name,
|
||||
SeverityName: severity.Name,
|
||||
StatusName: incident.Resolved,
|
||||
Title: incidentEntity.Title,
|
||||
}
|
||||
err = i.slackService.SetTopicOfConversationByChannelId(incidentEntity.SlackChannel, incidentEntity.IncidentName, channelTopic.String())
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s Failed to set channel topic for channel %s", updateLogTag, incidentEntity.SlackChannel), zap.Error(err))
|
||||
postResolveErrors.AddErrors(err)
|
||||
}
|
||||
})
|
||||
|
||||
go i.DeleteConferenceEvent(incidentEntity)
|
||||
|
||||
go i.processIncidentRCAFlow(incidentEntity)
|
||||
|
||||
@@ -67,6 +67,9 @@ func (suite *IncidentServiceSuite) TestDuplicateUpdateIncidentStatus_Success() {
|
||||
suite.incidentRepository.FindIncidentByIdMock.When(mockOriginalIncident.ID).Then(mockOriginalIncident, nil)
|
||||
suite.incidentRepository.UpdateIncidentMock.When(mockCurrentIncident).Then(nil)
|
||||
suite.slackService.PostMessageByChannelIDMock.Return("", nil)
|
||||
suite.teamServiceV2.GetTeamByIdMock.Return(GetMockTeamWithId(1), nil)
|
||||
suite.severityService.FindSeverityByIdMock.Return(GetMockSeverityDTOWitId(mockCurrentIncident.SeverityId), nil)
|
||||
suite.slackService.SetTopicOfConversationByChannelIdMock.Return(nil)
|
||||
err := suite.incidentService.DuplicateUpdateIncidentStatus(mockOriginalIncident.ID, "userID", mockCurrentIncident)
|
||||
suite.NoError(err)
|
||||
}
|
||||
@@ -546,6 +549,9 @@ func (suite *IncidentServiceSuite) TestResolveIncident_PostResolveFlowErrorCase(
|
||||
suite.slackService.UpdateMessageWithAttachmentsMock.Return(errors.New("Slack Error"))
|
||||
suite.calendarService.DeleteEventMock.Return(nil)
|
||||
suite.rcaService.SendConversationDataForGeneratingRCAMock.Return(nil)
|
||||
suite.teamServiceV2.GetTeamByIdMock.Return(GetMockTeamWithId(1), nil)
|
||||
suite.severityService.FindSeverityByIdMock.Return(GetMockSeverityDTOWitId(mockIncident.SeverityId), nil)
|
||||
suite.slackService.SetTopicOfConversationByChannelIdMock.Return(nil)
|
||||
err := suite.incidentService.ResolveIncident(sampleRequest, "")
|
||||
suite.Error(err, "service must throw error")
|
||||
}
|
||||
@@ -586,6 +592,9 @@ func (suite *IncidentServiceSuite) TestResolveIncident_HappyFlow() {
|
||||
suite.slackService.UpdateMessageWithAttachmentsMock.Return(nil)
|
||||
suite.calendarService.DeleteEventMock.Return(nil)
|
||||
suite.rcaService.SendConversationDataForGeneratingRCAMock.Return(nil)
|
||||
suite.teamServiceV2.GetTeamByIdMock.Return(GetMockTeamWithId(1), nil)
|
||||
suite.severityService.FindSeverityByIdMock.Return(GetMockSeverityDTOWitId(mockIncident.SeverityId), nil)
|
||||
suite.slackService.SetTopicOfConversationByChannelIdMock.Return(nil)
|
||||
err := suite.incidentService.ResolveIncident(sampleRequest, "")
|
||||
suite.NoError(err, "No error must be thrown")
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"houston/model/team"
|
||||
user2 "houston/model/user"
|
||||
incidentService "houston/service/incident"
|
||||
incidentServiceImpl "houston/service/incident/impl"
|
||||
"houston/service/products"
|
||||
"houston/service/productsTeams"
|
||||
incidentRequest "houston/service/request/incident"
|
||||
@@ -222,7 +223,7 @@ func (i *incidentOrchestratorImpl) CreateIncident(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = i.setChannelTopic(slackChannel, channelTopic, incidentEntity)
|
||||
err = i.setChannelTopic(slackChannel, channelTopic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -318,13 +319,13 @@ func (i *incidentOrchestratorImpl) getDefaultTeam() (*team.TeamDTO, error) {
|
||||
|
||||
func (i *incidentOrchestratorImpl) buildCreateIncidentDTO(
|
||||
createIncRequest *incidentRequest.CreateIncidentRequestV3,
|
||||
) (*incident.CreateIncidentDTO, *channelTopic, error) {
|
||||
) (*incident.CreateIncidentDTO, *incidentServiceImpl.ChannelTopic, error) {
|
||||
var (
|
||||
createIncidentDTO incident.CreateIncidentDTO
|
||||
err error
|
||||
wg sync.WaitGroup
|
||||
mutex sync.Mutex
|
||||
channelTopic = &channelTopic{}
|
||||
channelTopic = &incidentServiceImpl.ChannelTopic{}
|
||||
)
|
||||
|
||||
wg.Add(5)
|
||||
@@ -337,6 +338,7 @@ func (i *incidentOrchestratorImpl) buildCreateIncidentDTO(
|
||||
}
|
||||
mutex.Lock()
|
||||
createIncidentDTO.ReportingTeamID = &reportingTeam.ID
|
||||
channelTopic.ReportingTeamName = reportingTeam.Name
|
||||
mutex.Unlock()
|
||||
}()
|
||||
|
||||
@@ -347,8 +349,8 @@ func (i *incidentOrchestratorImpl) buildCreateIncidentDTO(
|
||||
return
|
||||
}
|
||||
mutex.Lock()
|
||||
channelTopic.teamName = responderTeam.Name
|
||||
createIncidentDTO.TeamId = fmt.Sprintf("%d", responderTeam.ID)
|
||||
channelTopic.ResponderTeamName = responderTeam.Name
|
||||
mutex.Unlock()
|
||||
}()
|
||||
|
||||
@@ -359,9 +361,8 @@ func (i *incidentOrchestratorImpl) buildCreateIncidentDTO(
|
||||
return
|
||||
}
|
||||
mutex.Lock()
|
||||
channelTopic.severityName = severityDTO.Name
|
||||
channelTopic.severityDescription = severityDTO.Description
|
||||
createIncidentDTO.Severity = fmt.Sprintf("%d", severityDTO.ID)
|
||||
channelTopic.SeverityName = severityDTO.Name
|
||||
mutex.Unlock()
|
||||
}()
|
||||
|
||||
@@ -375,8 +376,13 @@ func (i *incidentOrchestratorImpl) buildCreateIncidentDTO(
|
||||
err = fmt.Errorf("error in fetching product details")
|
||||
return
|
||||
}
|
||||
var productNames []string
|
||||
for _, product := range productDTOs {
|
||||
productNames = append(productNames, product.ProductName)
|
||||
}
|
||||
mutex.Lock()
|
||||
createIncidentDTO.ProductIds = createIncRequest.ProductIds
|
||||
channelTopic.ProductNames = productNames
|
||||
mutex.Unlock()
|
||||
}()
|
||||
|
||||
@@ -394,8 +400,10 @@ func (i *incidentOrchestratorImpl) buildCreateIncidentDTO(
|
||||
}
|
||||
|
||||
createIncidentDTO.Title = createIncRequest.Title
|
||||
channelTopic.Title = createIncRequest.Title
|
||||
createIncidentDTO.Description = createIncRequest.Description
|
||||
createIncidentDTO.Status = incident.Investigating
|
||||
channelTopic.StatusName = string(incident.Investigating)
|
||||
createIncidentDTO.UpdatedBy = createIncidentDTO.CreatedBy
|
||||
createIncidentDTO.StartTime = time.Now()
|
||||
createIncidentDTO.EnableReminder = false
|
||||
@@ -410,7 +418,7 @@ func (i *incidentOrchestratorImpl) buildCreateIncidentDTO(
|
||||
|
||||
func (i *incidentOrchestratorImpl) createIncidentEntityTransactionally(
|
||||
request *incidentRequest.CreateIncidentRequestV3, tx *gorm.DB,
|
||||
) (*incident.IncidentEntity, *channelTopic, error) {
|
||||
) (*incident.IncidentEntity, *incidentServiceImpl.ChannelTopic, error) {
|
||||
createIncidentDTO, channelTopic, err := i.buildCreateIncidentDTO(request)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -442,10 +450,8 @@ func (i *incidentOrchestratorImpl) createSlackChannelTransactionally(
|
||||
}
|
||||
|
||||
func (i *incidentOrchestratorImpl) setChannelTopic(
|
||||
channel *slack2.Channel, channelTopic *channelTopic, incidentEntity *incident.IncidentEntity,
|
||||
channel *slack2.Channel, channelTopic *incidentServiceImpl.ChannelTopic,
|
||||
) error {
|
||||
channelTopic.incidentTitle = incidentEntity.Title
|
||||
channelTopic.incidentID = incidentEntity.ID
|
||||
err := i.slackService.SetTopicOfConversation(channel, channelTopic.String())
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
@@ -471,25 +477,6 @@ func (i *incidentOrchestratorImpl) updateSlackDetailsIntoIncidentEntity(channel
|
||||
return nil
|
||||
}
|
||||
|
||||
type channelTopic struct {
|
||||
teamName string
|
||||
severityName string
|
||||
severityDescription string
|
||||
incidentID uint
|
||||
incidentTitle string
|
||||
}
|
||||
|
||||
func (td *channelTopic) String() string {
|
||||
return fmt.Sprintf(
|
||||
"%s-%s(%s) Incident-%d | %s",
|
||||
td.teamName,
|
||||
td.severityName,
|
||||
td.severityDescription,
|
||||
td.incidentID,
|
||||
td.incidentTitle,
|
||||
)
|
||||
}
|
||||
|
||||
type sortListOfTeamDTOByName []team.TeamDTO
|
||||
|
||||
func (t sortListOfTeamDTOByName) Len() int {
|
||||
|
||||
@@ -178,10 +178,13 @@ func (s *SlackService) SetChannelTopic(
|
||||
}
|
||||
|
||||
func (s *SlackService) SetTopicOfConversation(channel *slack.Channel, topic string) error {
|
||||
return s.setTopicOfConversation(channel.ID, channel.Name, topic)
|
||||
return s.SetTopicOfConversationByChannelId(channel.ID, channel.Name, topic)
|
||||
}
|
||||
|
||||
func (s *SlackService) setTopicOfConversation(channelID, channelName string, topic string) error {
|
||||
func (s *SlackService) SetTopicOfConversationByChannelId(channelID, channelName string, topic string) error {
|
||||
if len(topic) > channelTopicLengthLimit {
|
||||
topic = topic[:maxAllowedTopicIndex] + "..."
|
||||
}
|
||||
_, err := s.SocketModeClientWrapper.SetTopicOfConversation(channelID, topic)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("%s set topic on slack channel failed", logTag)
|
||||
@@ -209,7 +212,7 @@ func (s *SlackService) SetChannelTopicByChannelId(
|
||||
incidentEntity.Title,
|
||||
)
|
||||
|
||||
return s.setTopicOfConversation(channelID, channelName, topic)
|
||||
return s.SetTopicOfConversationByChannelId(channelID, channelName, topic)
|
||||
}
|
||||
|
||||
func (s *SlackService) GetUserByEmailOrID(userEmailOrSlackID string) (*slack.User, error) {
|
||||
@@ -666,3 +669,5 @@ const (
|
||||
)
|
||||
|
||||
const userOptionLimit = 200
|
||||
const channelTopicLengthLimit = 250
|
||||
const maxAllowedTopicIndex = 247
|
||||
|
||||
@@ -35,6 +35,7 @@ type ISlackService interface {
|
||||
UpdateMessageWithAttachments(channelID string, timestamp string, options slack.Attachment) error
|
||||
PostMessageWithAttachments(channelID string, attachment slack.Attachment) (string, error)
|
||||
SetTopicOfConversation(channel *slack.Channel, topic string) error
|
||||
SetTopicOfConversationByChannelId(channelId, channelName, topic string) error
|
||||
SetChannelTopicByChannelId(
|
||||
channelID string,
|
||||
channelName string,
|
||||
|
||||
Reference in New Issue
Block a user