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:
Shashank Shekhar
2024-02-16 14:47:22 +05:30
committed by GitHub
parent ede0b1a60c
commit 36d590221c
7 changed files with 62 additions and 80 deletions

View File

@@ -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{}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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

View File

@@ -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)