INFRA-2829 | Implemented transaction in create incident flow (#371)
* INFRA-2829 | Implemented transaction in create incident flow * INFRA-2829 | created util func for rollback * INFRA-2829 | removed redundant cod to create slack channel
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/slack-go/slack/socketmode"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/exp/slices"
|
||||
"gorm.io/gorm"
|
||||
"houston/logger"
|
||||
"houston/model/incident"
|
||||
"houston/model/severity"
|
||||
@@ -34,6 +35,13 @@ func SplitUntilWord(input, stopWord string) (string, string) {
|
||||
return strings.TrimSpace(input[:stopIndex]), strings.TrimSpace(input[stopIndex+len(stopWord):])
|
||||
}
|
||||
|
||||
func RollbackTransaction(tx *gorm.DB) {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func RemoveDuplicate[T string | int](sliceList []T) []T {
|
||||
allKeys := make(map[T]bool)
|
||||
list := []T{}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
"houston/appcontext"
|
||||
"houston/common/metrics"
|
||||
"houston/common/util"
|
||||
"houston/internal/processor/action/view"
|
||||
@@ -53,15 +54,29 @@ func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessing(callback s
|
||||
logger.Info("[CIP] incident request created", zap.Any("request", createIncidentRequest))
|
||||
|
||||
// Save the incident to the database
|
||||
incidentEntity, err := isp.incidentRepository.CreateIncidentEntity(createIncidentRequest)
|
||||
tx := isp.db.Begin()
|
||||
defer util.RollbackTransaction(tx)
|
||||
incidentEntity, err := isp.incidentRepository.CreateIncidentEntity(createIncidentRequest, tx)
|
||||
if err != nil {
|
||||
logger.Error("[CIP] Error while creating incident", zap.Error(err))
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
channelID, err := isp.createSlackChannel(incidentEntity)
|
||||
channel, err := appcontext.GetSlackService().CreateSlackChannel(incidentEntity.ID)
|
||||
if err != nil {
|
||||
logger.Error("[CIP] Error while creating incident channel", zap.Error(err))
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
incidentEntity.SlackChannel = channel.ID
|
||||
incidentEntity.IncidentName = channel.Name
|
||||
err = isp.incidentRepository.UpdateIncident(incidentEntity)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("[CIP] failed to update the slack channel name for incident-id: %v", incidentEntity.ID))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,11 +89,11 @@ func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessing(callback s
|
||||
|
||||
topic := fmt.Sprintf("%s-%s(%s) Incident-%d | %s", teamEntity.Name, severityEntity.Name, severityEntity.Description, incidentEntity.ID, incidentEntity.Title)
|
||||
|
||||
isp.slackbotClient.SetChannelTopic(*channelID, topic)
|
||||
isp.slackbotClient.SetChannelTopic(channel.ID, topic)
|
||||
|
||||
go func() {
|
||||
// Post incident summary to Blaze Group channel and incident channel
|
||||
_, err := isp.postIncidentSummary(callback.View.PrivateMetadata, *channelID, incidentEntity, teamEntity,
|
||||
_, err := isp.postIncidentSummary(callback.View.PrivateMetadata, channel.ID, incidentEntity, teamEntity,
|
||||
severityEntity, incidentStatusEntity)
|
||||
if err != nil {
|
||||
logger.Error("[CIP] error while posting incident summary", zap.Error(err))
|
||||
@@ -87,13 +102,13 @@ func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessing(callback s
|
||||
//Add user who created the incident
|
||||
isp.slackbotClient.InviteUsersToConversation(incidentEntity.SlackChannel, incidentEntity.CreatedBy)
|
||||
// add default users to the incident
|
||||
err = isp.addDefaultUsersToIncident(*channelID, teamEntity, severityEntity)
|
||||
err = isp.addDefaultUsersToIncident(channel.ID, teamEntity, severityEntity)
|
||||
if err != nil {
|
||||
logger.Error("[CIP] error while adding default users to incident", zap.Error(err))
|
||||
}
|
||||
|
||||
if len(strings.TrimSpace(teamEntity.OncallHandle)) > 0 {
|
||||
incidentHelper.TagPseOrDevOncallToIncident(*channelID, severityEntity, teamEntity, isp.slackbotClient, isp.client)
|
||||
incidentHelper.TagPseOrDevOncallToIncident(channel.ID, severityEntity, teamEntity, isp.slackbotClient, isp.client)
|
||||
}
|
||||
|
||||
err = incidentHelper.AssignResponderToIncident(
|
||||
@@ -125,12 +140,12 @@ func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessing(callback s
|
||||
msgUpdate.ProcessAction(incidentEntity.SlackChannel)
|
||||
blazeMessageUpdate.ProcessAction(callback.Channel.ID)
|
||||
bookmarkParam := slack.AddBookmarkParameters{Link: calendarEvent.ConferenceLink, Title: calendarService.GetConferenceTitle()}
|
||||
_, err := isp.client.AddBookmark(*channelID, bookmarkParam)
|
||||
_, err := isp.client.AddBookmark(channel.ID, bookmarkParam)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Unable to add conference link as bookmark for channel %s due to error: %s", incidentEntity.SlackChannel, err.Error()))
|
||||
}
|
||||
msgOption := slack.MsgOptionText(fmt.Sprintf(util.ConferenceMessage, calendarEvent.ConferenceLink), false)
|
||||
_, _, err = isp.client.PostMessage(*channelID, msgOption)
|
||||
_, _, err = isp.client.PostMessage(channel.ID, msgOption)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Unable to post message to channel %s due to error: %s", incidentEntity.SlackChannel, err.Error()))
|
||||
}
|
||||
@@ -184,28 +199,6 @@ func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessingV2(
|
||||
isp.client.Ack(*request, payload)
|
||||
}
|
||||
|
||||
func (isp *CreateIncidentAction) createSlackChannel(incidentEntity *incident.IncidentEntity) (*string, error) {
|
||||
var channelName string
|
||||
if viper.GetString("env") != "prod" {
|
||||
channelName = fmt.Sprintf("_test-issue-%d", incidentEntity.ID)
|
||||
} else {
|
||||
channelName = fmt.Sprintf("_houston-%d", incidentEntity.ID)
|
||||
}
|
||||
channelID, err := isp.slackbotClient.CreateChannel(channelName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
incidentEntity.SlackChannel = channelID
|
||||
incidentEntity.IncidentName = channelName
|
||||
err = isp.incidentRepository.UpdateIncident(incidentEntity)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("[CIP] failed to update the slack channel name for incident-id: %v", incidentEntity.ID))
|
||||
return nil, err
|
||||
}
|
||||
return &channelID, nil
|
||||
}
|
||||
|
||||
func (isp *CreateIncidentAction) addDefaultUsersToIncident(channelId string, teamEntity *team.TeamEntity,
|
||||
severityEntity *severity.SeverityEntity) error {
|
||||
var userIdList []string
|
||||
|
||||
@@ -49,7 +49,7 @@ func NewIncidentRepository(
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) CreateIncidentEntity(request *CreateIncidentDTO) (*IncidentEntity, error) {
|
||||
func (r *Repository) CreateIncidentEntity(request *CreateIncidentDTO, tx *gorm.DB) (*IncidentEntity, error) {
|
||||
severityId, err := strconv.Atoi(request.Severity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetch channel conversationInfo failed. err: %v", err)
|
||||
@@ -77,10 +77,7 @@ func (r *Repository) CreateIncidentEntity(request *CreateIncidentDTO) (*Incident
|
||||
MetaData: request.MetaData,
|
||||
}
|
||||
|
||||
result := r.gormClient.Create(incidentEntity)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
tx.Create(incidentEntity)
|
||||
|
||||
return incidentEntity, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package incident
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type IIncidentRepository interface {
|
||||
CreateIncidentEntity(request *CreateIncidentDTO) (*IncidentEntity, error)
|
||||
CreateIncidentEntity(request *CreateIncidentDTO, tx *gorm.DB) (*IncidentEntity, error)
|
||||
UpdateIncident(incidentEntity *IncidentEntity) error
|
||||
UpdateIncidentWithJustification(incidentEntity *IncidentEntity, justification string) error
|
||||
CreateIncidentTag(incidentId, tagId uint) (*IncidentTagEntity, error)
|
||||
|
||||
@@ -27,22 +27,6 @@ func (c *Client) FindParticipants(channelId string) ([]string, error) {
|
||||
return channelInfo, nil
|
||||
}
|
||||
|
||||
func (c *Client) CreateChannel(channelName string) (string, error) {
|
||||
request := slack.CreateConversationParams{
|
||||
ChannelName: channelName,
|
||||
IsPrivate: false,
|
||||
}
|
||||
|
||||
channel, err := c.socketModeClient.CreateConversation(request)
|
||||
if err != nil {
|
||||
logger.Error("create slackbot channel failed", zap.String("channel_name", channelName), zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
logger.Info("created slackbot channel successfully", zap.String("channel_name", channelName), zap.String("channel_id", channel.ID))
|
||||
return channel.ID, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetChannelTopic(channelId, topic string) (string, error) {
|
||||
channel, err := c.socketModeClient.SetTopicOfConversation(channelId, topic)
|
||||
if err != nil {
|
||||
|
||||
@@ -149,9 +149,12 @@ func (i *IncidentServiceV2) CreateIncident(
|
||||
logger.Info(fmt.Sprintf("%s CreateIncidentDTO created", logTag))
|
||||
|
||||
// Save the incident to the database
|
||||
incidentEntity, err := i.incidentRepository.CreateIncidentEntity(incidentDTO)
|
||||
tx := i.db.Begin()
|
||||
defer util.RollbackTransaction(tx)
|
||||
incidentEntity, err := i.incidentRepository.CreateIncidentEntity(incidentDTO, tx)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s Error while creating incident", logTag), zap.Error(err))
|
||||
tx.Rollback()
|
||||
return emptyResponse, err
|
||||
}
|
||||
incidentName := incidentEntity.IncidentName
|
||||
@@ -165,11 +168,13 @@ func (i *IncidentServiceV2) CreateIncident(
|
||||
fmt.Sprintf("%s [%s] Error while crating slack channel", logTag, incidentName),
|
||||
zap.Error(err),
|
||||
)
|
||||
tx.Rollback()
|
||||
return emptyResponse, err
|
||||
}
|
||||
logger.Info(fmt.Sprintf(
|
||||
"%s [%s] Slack channel created. Channel name is %s", logTag, incidentName, channel.Name),
|
||||
)
|
||||
tx.Commit()
|
||||
// Update channel details to incident entity
|
||||
incidentEntity.SlackChannel = channel.ID
|
||||
incidentEntity.IncidentName = channel.Name
|
||||
|
||||
@@ -357,9 +357,12 @@ func (i *incidentService) CreateIncident(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Save the incident to the database
|
||||
incidentEntity, err := i.incidentRepository.CreateIncidentEntity(incidentDTO)
|
||||
tx := i.db.Begin()
|
||||
defer util.RollbackTransaction(tx)
|
||||
incidentEntity, err := i.incidentRepository.CreateIncidentEntity(incidentDTO, tx)
|
||||
if err != nil {
|
||||
logger.Error("[Create incident Api] Error while creating incident", zap.Error(err))
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -370,17 +373,29 @@ func (i *incidentService) CreateIncident(c *gin.Context) {
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error("[CIP] failed while getting team, severity and status", zap.Error(err))
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
channelID, err := i.createSlackChannel(incidentEntity)
|
||||
channel, err := appcontext.GetSlackService().CreateSlackChannel(incidentEntity.ID)
|
||||
if err != nil {
|
||||
logger.Error("[Create incident] Error while creating incident channel", zap.Error(err))
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
incidentEntity.SlackChannel = channel.ID
|
||||
incidentEntity.IncidentName = channel.Name
|
||||
err = i.incidentRepository.UpdateIncident(incidentEntity)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("[CIP] failed to update the slack channel name for incident-id: %v", incidentEntity.ID))
|
||||
return
|
||||
}
|
||||
|
||||
// Post incident summary to Blaze Group channel and incident channel
|
||||
_, err = i.postIncidentSummary(*channelID, incidentEntity, teamEntity,
|
||||
_, err = i.postIncidentSummary(channel.ID, incidentEntity, teamEntity,
|
||||
severityEntity, incidentStatusEntity)
|
||||
if err != nil {
|
||||
logger.Error("[Create incident] error while posting incident summary", zap.Error(err))
|
||||
@@ -395,13 +410,13 @@ func (i *incidentService) CreateIncident(c *gin.Context) {
|
||||
i.slackbotClient.InviteUsersToConversation(incidentEntity.SlackChannel, slackUser.SlackUserId)
|
||||
}
|
||||
// add default users to the incident
|
||||
err = i.addDefaultUsersToIncident(*channelID, teamEntity, severityEntity)
|
||||
err = i.addDefaultUsersToIncident(channel.ID, teamEntity, severityEntity)
|
||||
if err != nil {
|
||||
logger.Error("[Create Incident] error while adding default users to incident", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
util.TagPseOrDevOncallToIncident(*channelID, severityEntity, teamEntity, i.slackbotClient, i.socketModeClient)
|
||||
util.TagPseOrDevOncallToIncident(channel.ID, severityEntity, teamEntity, i.slackbotClient, i.socketModeClient)
|
||||
|
||||
err = util.AssignResponderToIncident(
|
||||
i.incidentRepository,
|
||||
@@ -502,28 +517,6 @@ func (i *incidentService) getTeamAndSeverityAndStatus(teamId, severityId, status
|
||||
return teamEntity, severityEntity, incidentStatusEntity, nil
|
||||
}
|
||||
|
||||
func (i *incidentService) createSlackChannel(incidentEntity *incident.IncidentEntity) (*string, error) {
|
||||
var channelName string
|
||||
if viper.GetString("env") != "prod" {
|
||||
channelName = fmt.Sprintf("_test-issue-%d", incidentEntity.ID)
|
||||
} else {
|
||||
channelName = fmt.Sprintf("_houston-%d", incidentEntity.ID)
|
||||
}
|
||||
channelID, err := i.slackbotClient.CreateChannel(channelName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
incidentEntity.SlackChannel = channelID
|
||||
incidentEntity.IncidentName = channelName
|
||||
err = i.incidentRepository.UpdateIncident(incidentEntity)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("[Create Incident failed to update the slack channel name for incident-id: %v", incidentEntity.ID))
|
||||
return nil, err
|
||||
}
|
||||
return &channelID, nil
|
||||
}
|
||||
|
||||
func (i *incidentService) buildCreateIncidentDTO(createIncRequest incidentRequest.CreateIncidentRequest) (*incident.CreateIncidentDTO, error) {
|
||||
var createIncidentRequest incident.CreateIncidentDTO
|
||||
teamEntity, err := i.teamRepository.FindTeamByTeamName(createIncRequest.TeamName)
|
||||
|
||||
Reference in New Issue
Block a user