* INFRA-2829 | Implemented transaction in create incident flow * INFRA-2829 | created util func for rollback * INFRA-2829 | removed redundant cod to create slack channel
796 lines
28 KiB
Go
796 lines
28 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"houston/appcontext"
|
|
"houston/common/util"
|
|
slackUtil "houston/common/util/slack"
|
|
"houston/internal/processor/action"
|
|
"houston/internal/processor/action/view"
|
|
logger "houston/logger"
|
|
"houston/model/incident"
|
|
"houston/model/log"
|
|
"houston/model/severity"
|
|
"houston/model/team"
|
|
"houston/model/user"
|
|
"houston/pkg/slackbot"
|
|
incidentV2 "houston/service/incident"
|
|
"houston/service/incident/impl"
|
|
rcaService "houston/service/rca/impl"
|
|
request "houston/service/request"
|
|
incidentRequest "houston/service/request/incident"
|
|
service "houston/service/response"
|
|
common "houston/service/response/common"
|
|
slack2 "houston/service/slack"
|
|
utils "houston/service/utils"
|
|
"math"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/slack-go/slack"
|
|
"github.com/slack-go/slack/socketmode"
|
|
"github.com/spf13/viper"
|
|
"github.com/thoas/go-funk"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type incidentService struct {
|
|
gin *gin.Engine
|
|
db *gorm.DB
|
|
socketModeClient *socketmode.Client
|
|
teamRepository *team.Repository
|
|
severityRepository *severity.Repository
|
|
incidentRepository *incident.Repository
|
|
userRepository *user.Repository
|
|
messageUpdateAction *action.IncidentChannelMessageUpdateAction
|
|
slackbotClient *slackbot.Client
|
|
slackService slack2.ISlackService
|
|
incidentServiceV2 *impl.IncidentServiceV2
|
|
rcaService *rcaService.RcaService
|
|
}
|
|
|
|
func NewIncidentService(gin *gin.Engine, db *gorm.DB, socketModeClient *socketmode.Client) *incidentService {
|
|
severityRepository := severity.NewSeverityRepository(db)
|
|
logRepository := log.NewLogRepository(db)
|
|
teamRepository := team.NewTeamRepository(db, logRepository)
|
|
incidentRepository := incident.NewIncidentRepository(db, severityRepository, logRepository, teamRepository, socketModeClient)
|
|
userRepository := user.NewUserRepository(db)
|
|
messageUpdateAction := action.NewIncidentChannelMessageUpdateAction(
|
|
socketModeClient, incidentRepository, teamRepository, severityRepository)
|
|
slackBot := slackbot.NewSlackClient(socketModeClient)
|
|
incidentServiceV2 := impl.NewIncidentServiceV2(db)
|
|
rcaService := appcontext.GetRCAService()
|
|
return &incidentService{
|
|
gin: gin,
|
|
db: db,
|
|
socketModeClient: socketModeClient,
|
|
teamRepository: teamRepository,
|
|
severityRepository: severityRepository,
|
|
incidentRepository: incidentRepository,
|
|
userRepository: userRepository,
|
|
messageUpdateAction: messageUpdateAction,
|
|
slackbotClient: slackBot,
|
|
slackService: appcontext.GetSlackService(),
|
|
incidentServiceV2: incidentServiceV2,
|
|
rcaService: rcaService,
|
|
}
|
|
}
|
|
|
|
func (i *incidentService) GetIncidents(c *gin.Context) {
|
|
IncidentId := c.Param("id")
|
|
TeamIds := c.Query("team_ids")
|
|
SeverityIds := c.Query("severity_ids")
|
|
Status := c.Query("statuses")
|
|
IncidentName := c.Query("incident_name")
|
|
From := c.Query("from")
|
|
To := c.Query("to")
|
|
|
|
if IncidentId != "" {
|
|
numIncidentId, err := strconv.Atoi(IncidentId)
|
|
if err != nil {
|
|
logger.Error("error in converting string to int", zap.String("IncidentId", IncidentId), zap.Error(err))
|
|
c.JSON(http.StatusBadGateway, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
}
|
|
incidentEntity, err := i.incidentRepository.FindIncidentById(uint(numIncidentId))
|
|
if err != nil {
|
|
logger.Error("error in finding incident by id", zap.Int("numIncidentId", numIncidentId))
|
|
c.JSON(http.StatusBadGateway, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
}
|
|
incidentResponse, _ := i.rcaService.GetIncidentResponseWithRCALink(incidentEntity)
|
|
c.JSON(http.StatusOK, common.SuccessResponse(incidentResponse, http.StatusOK))
|
|
return
|
|
}
|
|
|
|
pageSize, pageNumber, err :=
|
|
utils.ValidatePage(c.Query("page_size"), c.Query("page_number"))
|
|
if err != nil {
|
|
logger.Error("error in query parameters", zap.Int64("page_size", pageSize),
|
|
zap.Int64("page_number", pageNumber), zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
incidents, totalElements, err := i.GetAllIncidents(request.IncidentFilters{
|
|
TeamIds: TeamIds,
|
|
SeverityIds: SeverityIds,
|
|
StatusIds: Status,
|
|
PageSize: pageSize,
|
|
PageNumber: pageNumber,
|
|
IncidentName: IncidentName,
|
|
From: From,
|
|
To: To,
|
|
}, i.incidentRepository)
|
|
if err != nil {
|
|
logger.Info("error in fetching incidents", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
incidentResponses, err := i.GetIncidentResponseFromIncidentEntity(
|
|
incidents, i.incidentRepository, i.severityRepository, i.teamRepository)
|
|
if err != nil {
|
|
logger.Error("error in GetIncidentResponseFromIncidentEntity", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
page := common.Page{
|
|
PageSize: pageSize,
|
|
PageNumber: pageNumber,
|
|
TotalElements: totalElements,
|
|
}
|
|
|
|
c.JSON(http.StatusOK, common.SuccessPaginatedResponse(incidentResponses, common.Page{
|
|
PageSize: page.PageSize,
|
|
TotalPages: int64(math.Ceil(float64(page.TotalElements) / float64(pageSize))),
|
|
PageNumber: pageNumber,
|
|
TotalElements: page.TotalElements,
|
|
}, http.StatusOK))
|
|
}
|
|
|
|
func (i *incidentService) GetIncidentResponseFromIncidentEntity(
|
|
incidents []incident.IncidentEntity, incidentRepository *incident.Repository, severityRepository *severity.Repository,
|
|
teamRepository *team.Repository) ([]service.IncidentResponse, error) {
|
|
|
|
teams, err := teamRepository.GetAllActiveTeams()
|
|
if err != nil {
|
|
logger.Error("error in fetching teams", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
severities, err := severityRepository.GetAllActiveSeverity()
|
|
if err != nil {
|
|
logger.Error("error in fetching severities", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
incidentStatuses, err := incidentRepository.FetchAllIncidentStatuses()
|
|
if err != nil {
|
|
logger.Error("error in fetching incidentStatuses", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
userEmailMappings, err := i.GetUserIdAndIdentityMappingOfAllIncidents(incidents)
|
|
if err != nil {
|
|
logger.Error("error in fetching user emails mapping by ids", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
var incidentResponses []service.IncidentResponse = []service.IncidentResponse{}
|
|
for incidentIndex := range incidents {
|
|
incidentResponse, _ := i.rcaService.GetIncidentResponseWithRCALink(&incidents[incidentIndex])
|
|
incidentResponses = append(incidentResponses, incidentResponse)
|
|
for _, t := range *teams {
|
|
if t.ID == incidents[incidentIndex].TeamId {
|
|
incidentResponses[incidentIndex].TeamName = t.Name
|
|
}
|
|
}
|
|
|
|
for _, s := range *severities {
|
|
if s.ID == incidents[incidentIndex].SeverityId {
|
|
incidentResponses[incidentIndex].SeverityName = s.Name
|
|
}
|
|
}
|
|
|
|
for _, is := range *incidentStatuses {
|
|
if is.ID == incidents[incidentIndex].Status {
|
|
incidentResponses[incidentIndex].StatusName = is.Name
|
|
}
|
|
}
|
|
if userEmailMappings[incidents[incidentIndex].CreatedBy] != "" {
|
|
incidentResponses[incidentIndex].CreatedBy = userEmailMappings[incidents[incidentIndex].CreatedBy]
|
|
} else {
|
|
incidentResponses[incidentIndex].CreatedBy = incidents[incidentIndex].CreatedBy
|
|
}
|
|
|
|
if userEmailMappings[incidents[incidentIndex].UpdatedBy] != "" {
|
|
incidentResponses[incidentIndex].UpdatedBy = userEmailMappings[incidents[incidentIndex].UpdatedBy]
|
|
} else {
|
|
incidentResponses[incidentIndex].UpdatedBy = incidents[incidentIndex].UpdatedBy
|
|
}
|
|
}
|
|
return incidentResponses, nil
|
|
}
|
|
|
|
func (i *incidentService) GetUserIdAndIdentityMappingOfAllIncidents(incidents []incident.IncidentEntity) (
|
|
map[string]string, error) {
|
|
listOfUserIds := funk.Map(incidents, func(incd incident.IncidentEntity) string {
|
|
return incd.CreatedBy
|
|
}).([]string)
|
|
listOfUserIds = append(listOfUserIds, funk.Map(incidents, func(incd incident.IncidentEntity) string {
|
|
return incd.UpdatedBy
|
|
}).([]string)...)
|
|
userIdAndIdentityMapping, err := i.slackbotClient.GetUserEmailsOrNameByIds(listOfUserIds...)
|
|
if err != nil {
|
|
logger.Error("error in fetching user emails by ids", zap.Any("listOfUserIds", listOfUserIds), zap.Error(err))
|
|
return map[string]string{}, err
|
|
}
|
|
return userIdAndIdentityMapping, nil
|
|
}
|
|
|
|
func (i *incidentService) GetAllIncidents(incidentFilters request.IncidentFilters, incidentRepository *incident.Repository) ([]incident.IncidentEntity, int, error) {
|
|
var TeamIds []uint
|
|
var SeverityIds []uint
|
|
var StatusIds []uint
|
|
if incidentFilters.TeamIds != "" {
|
|
TeamIds = i.SplitStringAndGetUintArray(incidentFilters.TeamIds)
|
|
}
|
|
if incidentFilters.SeverityIds != "" {
|
|
SeverityIds = i.SplitStringAndGetUintArray(incidentFilters.SeverityIds)
|
|
}
|
|
if incidentFilters.StatusIds != "" {
|
|
StatusIds = i.SplitStringAndGetUintArray(incidentFilters.StatusIds)
|
|
}
|
|
return incidentRepository.FetchAllIncidentsPaginated(
|
|
TeamIds, SeverityIds, StatusIds, incidentFilters.PageNumber, incidentFilters.PageSize, incidentFilters.IncidentName, incidentFilters.From, incidentFilters.To)
|
|
}
|
|
|
|
func (i *incidentService) SplitStringAndGetUintArray(str string) []uint {
|
|
uintsStr := strings.Split(str, ",")
|
|
|
|
var uints []uint
|
|
for _, uintStr := range uintsStr {
|
|
uintVal, err := strconv.ParseUint(uintStr, 10, 64)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
uints = append(uints, uint(uintVal))
|
|
}
|
|
return uints
|
|
}
|
|
|
|
func (i *incidentService) GetIncidentHeader(c *gin.Context) {
|
|
var incidentHeaderResponse service.IncidentHeaderResponse
|
|
severityEntities, err := i.severityRepository.GetAllActiveSeverity()
|
|
if err != nil {
|
|
logger.Error("error in fetching severities", zap.Error(err))
|
|
}
|
|
for _, severity := range *severityEntities {
|
|
severityResponse := service.IncidentHeaderOption{
|
|
Value: severity.ID,
|
|
Label: severity.Name,
|
|
}
|
|
incidentHeaderResponse.Severities = append(incidentHeaderResponse.Severities, severityResponse)
|
|
}
|
|
|
|
incidentStatusEntities, err := i.incidentRepository.FetchAllIncidentStatuses()
|
|
if err != nil {
|
|
logger.Error("error in fetching incident statuses", zap.Error(err))
|
|
}
|
|
for _, incidentStatus := range *incidentStatusEntities {
|
|
incidentStatusResponse := service.IncidentHeaderOption{
|
|
Value: incidentStatus.ID,
|
|
Label: incidentStatus.Name,
|
|
}
|
|
incidentHeaderResponse.IncidentStatuses =
|
|
append(incidentHeaderResponse.IncidentStatuses, incidentStatusResponse)
|
|
}
|
|
|
|
teamEntities, err := i.teamRepository.GetAllActiveTeams()
|
|
if err != nil {
|
|
logger.Error("error in fetching severities", zap.Error(err))
|
|
}
|
|
for _, team := range *teamEntities {
|
|
teamResponse := service.IncidentHeaderOption{
|
|
Value: team.ID,
|
|
Label: team.Name,
|
|
}
|
|
incidentHeaderResponse.Teams = append(incidentHeaderResponse.Teams, teamResponse)
|
|
}
|
|
c.JSON(http.StatusMultiStatus, common.SuccessResponse(incidentHeaderResponse, http.StatusMultiStatus))
|
|
}
|
|
|
|
func (i *incidentService) GetTeamIncidents(c *gin.Context) {
|
|
var Statuses string
|
|
InputStatuses := c.Query("statuses")
|
|
if InputStatuses == "" {
|
|
incidentStatuses, err := i.incidentRepository.FetchAllIncidentStatuses()
|
|
if err != nil {
|
|
logger.Error("error in fetching incident statuses", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
for _, incidentStatus := range *incidentStatuses {
|
|
if incidentStatus.IsTerminalStatus != true {
|
|
Statuses += fmt.Sprintf("%d,", incidentStatus.ID)
|
|
}
|
|
}
|
|
Statuses = Statuses[:len(Statuses)-1]
|
|
}
|
|
incidents, _, err := i.GetAllIncidents(request.IncidentFilters{
|
|
TeamIds: c.Param("teamId"),
|
|
StatusIds: Statuses,
|
|
PageNumber: 0,
|
|
PageSize: viper.GetInt64("CRM_TEAM_INCIDENT_COUNT"),
|
|
}, i.incidentRepository)
|
|
if err != nil {
|
|
logger.Error("error in fetching incidents", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, common.SuccessResponse(incidents, http.StatusOK))
|
|
}
|
|
|
|
func (i *incidentService) CreateIncident(c *gin.Context) {
|
|
|
|
var createIncRequest incidentRequest.CreateIncidentRequest
|
|
if err := c.ShouldBindJSON(&createIncRequest); err != nil {
|
|
c.JSON(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
if err := utils.ValidateCreateIncidentRequest(createIncRequest); err != nil {
|
|
logger.Error("[Create incident Api] Error while creating incident", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
incidentDTO, err := i.buildCreateIncidentDTO(createIncRequest)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, err)
|
|
}
|
|
|
|
// Save the incident to the database
|
|
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
|
|
}
|
|
|
|
teamEntity, severityEntity, incidentStatusEntity, err := i.getTeamAndSeverityAndStatus(
|
|
incidentEntity.TeamId,
|
|
incidentEntity.SeverityId,
|
|
incidentEntity.Status,
|
|
)
|
|
if err != nil {
|
|
logger.Error("[CIP] failed while getting team, severity and status", zap.Error(err))
|
|
tx.Rollback()
|
|
return
|
|
}
|
|
|
|
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(channel.ID, incidentEntity, teamEntity,
|
|
severityEntity, incidentStatusEntity)
|
|
if err != nil {
|
|
logger.Error("[Create incident] error while posting incident summary", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
// Check if the user is a slack user and get the slack user id
|
|
isSlackUser, slackUser := i.userRepository.IsAHoustonUser(incidentEntity.CreatedBy)
|
|
|
|
if isSlackUser {
|
|
//Add user who created the incident
|
|
i.slackbotClient.InviteUsersToConversation(incidentEntity.SlackChannel, slackUser.SlackUserId)
|
|
}
|
|
// add default users to the incident
|
|
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(channel.ID, severityEntity, teamEntity, i.slackbotClient, i.socketModeClient)
|
|
|
|
err = util.AssignResponderToIncident(
|
|
i.incidentRepository,
|
|
incidentEntity,
|
|
teamEntity, severityEntity,
|
|
i.socketModeClient,
|
|
incidentEntity.CreatedBy,
|
|
)
|
|
if err != nil {
|
|
logger.Error("[Create incident] Error while assigning responder to the incident ", zap.Error(err))
|
|
}
|
|
|
|
err = incidentV2.PostIncidentSLAMessageToChannel(incidentEntity, i.severityRepository, i.socketModeClient)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
go i.incidentServiceV2.HandleKrakatoaWorkflow(incidentEntity)
|
|
|
|
incidentResponse := service.ConvertToIncidentResponse(*incidentEntity)
|
|
c.JSON(http.StatusOK, common.SuccessResponse(incidentResponse, http.StatusOK))
|
|
}
|
|
|
|
func (i *incidentService) InvitePseOnCallPersonToIncident(channelId, ts string) {
|
|
go func() {
|
|
time.Sleep(3 * time.Second)
|
|
msg, _, _, _ := i.socketModeClient.GetConversationReplies(&slack.GetConversationRepliesParameters{
|
|
ChannelID: channelId,
|
|
Timestamp: ts,
|
|
Limit: 2,
|
|
},
|
|
)
|
|
if len(msg) > 1 {
|
|
//User id needs to sliced from `<@XXXXXXXXXXXX>` format to `XXXXXXXXXXXX`
|
|
i.socketModeClient.InviteUsersToConversation(channelId, msg[1].Text[2:13])
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (i *incidentService) addDefaultUsersToIncident(channelId string, teamEntity *team.TeamEntity,
|
|
severityEntity *severity.SeverityEntity) error {
|
|
var userIdList []string
|
|
userIdList = append(append(userIdList, teamEntity.SlackUserIds...), severityEntity.SlackUserIds...)
|
|
|
|
for _, o := range userIdList {
|
|
i.slackbotClient.InviteUsersToConversation(channelId, o)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *incidentService) postIncidentSummary(incidentChannelID string,
|
|
incidentEntity *incident.IncidentEntity, teamEntity *team.TeamEntity, severityEntity *severity.SeverityEntity,
|
|
incidentStatusEntity *incident.IncidentStatusEntity) (*string, error) {
|
|
// Post incident summary to Blaze Group channel and incident channel
|
|
blocks := view.IncidentSummarySection(incidentEntity, teamEntity, severityEntity, incidentStatusEntity)
|
|
color := util.GetColorBySeverity(incidentEntity.SeverityId)
|
|
att := slack.Attachment{Blocks: blocks, Color: color}
|
|
_, timestamp, err := i.socketModeClient.PostMessage(incidentChannelID, slack.MsgOptionAttachments(att))
|
|
if incidentEntity.MetaData != nil {
|
|
msgOption, noMetaDataError := slackUtil.BuildSlackTextMessageFromMetaData(incidentEntity.MetaData, true)
|
|
if noMetaDataError == nil {
|
|
_, _, err = i.socketModeClient.PostMessage(incidentChannelID, msgOption)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
if err == nil {
|
|
i.incidentRepository.CreateIncidentChannelEntry(&incident.CreateIncidentChannelEntry{
|
|
SlackChannel: incidentChannelID,
|
|
MessageTimeStamp: timestamp,
|
|
IncidentId: incidentEntity.ID,
|
|
})
|
|
} else {
|
|
return nil, fmt.Errorf("[CIP] error in saving message %v", err)
|
|
}
|
|
return ×tamp, nil
|
|
}
|
|
|
|
func (i *incidentService) getTeamAndSeverityAndStatus(teamId, severityId, status uint) (*team.TeamEntity,
|
|
*severity.SeverityEntity, *incident.IncidentStatusEntity, error) {
|
|
teamEntity, err := i.teamRepository.FindTeamById(teamId)
|
|
if err != nil || teamEntity == nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
severityEntity, err := i.severityRepository.FindSeverityById(severityId)
|
|
if err != nil || severityEntity == nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
incidentStatusEntity, err := i.incidentRepository.FindIncidentStatusById(status)
|
|
if err != nil || incidentStatusEntity == nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
return teamEntity, severityEntity, incidentStatusEntity, nil
|
|
}
|
|
|
|
func (i *incidentService) buildCreateIncidentDTO(createIncRequest incidentRequest.CreateIncidentRequest) (*incident.CreateIncidentDTO, error) {
|
|
var createIncidentRequest incident.CreateIncidentDTO
|
|
teamEntity, err := i.teamRepository.FindTeamByTeamName(createIncRequest.TeamName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
severityEntity, err := i.severityRepository.FindSeverityByName(createIncRequest.SeverityName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var rawJson, _ = json.Marshal([]incidentRequest.CreateIncidentMetaData{createIncRequest.MetaData})
|
|
createdBy := slackUtil.GetSlackUserIdFromEmail(createIncRequest.CreatedBy, i.slackbotClient)
|
|
|
|
createIncidentRequest.Title = createIncRequest.Title
|
|
createIncidentRequest.Description = createIncRequest.Description
|
|
createIncidentRequest.Severity = fmt.Sprintf("%d", severityEntity.ID)
|
|
createIncidentRequest.TeamId = fmt.Sprintf("%d", teamEntity.ID)
|
|
createIncidentRequest.Status = incident.Investigating
|
|
createIncidentRequest.CreatedBy = createdBy
|
|
createIncidentRequest.UpdatedBy = createdBy
|
|
createIncidentRequest.StartTime = time.Now()
|
|
createIncidentRequest.EnableReminder = false
|
|
createIncidentRequest.MetaData = rawJson
|
|
return &createIncidentRequest, nil
|
|
}
|
|
|
|
func (i *incidentService) UpdateIncident(c *gin.Context) {
|
|
userEmail := c.GetHeader("X-User-Email")
|
|
|
|
var updateIncidentRequest request.UpdateIncidentRequest
|
|
if err := c.ShouldBindJSON(&updateIncidentRequest); err != nil {
|
|
c.JSON(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
err := utils.ValidateUpdateIncidentRequest(updateIncidentRequest, userEmail)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
incidentEntity, err := i.incidentRepository.FindIncidentById(updateIncidentRequest.Id)
|
|
if err != nil {
|
|
logger.Error("error in fetching incident", zap.Any("incidentId", updateIncidentRequest.Id), zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
if incidentEntity == nil {
|
|
logger.Info("incident not found", zap.Any("incidentId", updateIncidentRequest.Id))
|
|
c.JSON(http.StatusBadRequest, common.ErrorResponse(errors.New(fmt.Sprintf("incident with id: %v not found", updateIncidentRequest.Id)), http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
userInfo, err := i.slackbotClient.GetUserByEmail(userEmail)
|
|
if err != nil {
|
|
logger.Error(fmt.Sprintf("error in fetching user by email: %v, hence using user email as user Id", userEmail), zap.String("userEmail", userEmail), zap.Error(err))
|
|
userInfo = &slack.User{
|
|
ID: userEmail,
|
|
}
|
|
}
|
|
|
|
err = i.UpdateSeverityId(updateIncidentRequest, userInfo.ID, incidentEntity)
|
|
if err != nil {
|
|
logger.Error("error in updating severity", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
err = i.UpdateStatus(updateIncidentRequest, userInfo.ID, incidentEntity.SlackChannel, incidentEntity)
|
|
if err != nil {
|
|
logger.Error("error in updating status", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
err = i.UpdateTeamId(updateIncidentRequest, userInfo.ID, incidentEntity)
|
|
if err != nil {
|
|
logger.Error("error in updating teamId", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
err = i.UpdateMetaData(updateIncidentRequest, incidentEntity, userInfo.ID)
|
|
if err != nil {
|
|
logger.Error("error in updating metadata", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
return
|
|
}
|
|
|
|
incidentEntity.UpdatedAt = time.Now()
|
|
incidentEntity.UpdatedBy = userInfo.ID
|
|
i.incidentRepository.UpdateIncident(incidentEntity)
|
|
|
|
i.messageUpdateAction.ProcessAction(incidentEntity.SlackChannel)
|
|
|
|
if updateIncidentRequest.SeverityId != "" || updateIncidentRequest.TeamId != "" {
|
|
teamEntity, err := i.teamRepository.FindTeamById(incidentEntity.TeamId)
|
|
if err != nil {
|
|
logger.Error(fmt.Sprintf("error in fetching team by id: %v", incidentEntity.TeamId), zap.Error(err))
|
|
}
|
|
|
|
incidentSeverityEntity, err := i.severityRepository.FindIncidentSeverityEntityById(int(incidentEntity.SeverityId))
|
|
if err != nil {
|
|
logger.Error(fmt.Sprintf("error in fetching severity by id: %v", incidentEntity.SeverityId), zap.Error(err))
|
|
}
|
|
|
|
topic := fmt.Sprintf("%s-%s(%s) Incident-%d | %s", teamEntity.Name, incidentSeverityEntity.Name, incidentSeverityEntity.Description, incidentEntity.ID, incidentEntity.Title)
|
|
i.socketModeClient.SetTopicOfConversation(incidentEntity.SlackChannel, topic)
|
|
if !util.IsBlank(updateIncidentRequest.TeamId) {
|
|
go i.incidentServiceV2.HandleKrakatoaWorkflow(incidentEntity)
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, common.SuccessResponse(incidentEntity.SlackChannel, http.StatusOK))
|
|
}
|
|
|
|
func (i *incidentService) UpdateSeverityId(
|
|
updateIncidentRequest request.UpdateIncidentRequest, userId string, incidentEntity *incident.IncidentEntity) error {
|
|
if updateIncidentRequest.SeverityId != "" {
|
|
num, err := strconv.ParseUint(updateIncidentRequest.SeverityId, 10, 64)
|
|
if err != nil {
|
|
logger.Error("error in string to int conversion",
|
|
zap.String("SeverityId", updateIncidentRequest.SeverityId), zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
severityEntity, err := i.severityRepository.FindSeverityById(uint(num))
|
|
if err != nil {
|
|
logger.Error("error in fetching severity", zap.Any("severity", incidentEntity.SeverityId), zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
if updateIncidentRequest.SeverityId != "" {
|
|
num, err := strconv.ParseUint(updateIncidentRequest.SeverityId, 10, 64)
|
|
if err != nil {
|
|
logger.Error("error in string to int conversion",
|
|
zap.String("SeverityId", updateIncidentRequest.SeverityId), zap.Error(err))
|
|
return err
|
|
}
|
|
if incidentEntity.SeverityId != uint(num) {
|
|
incidentEntity.SeverityId = uint(num)
|
|
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla)
|
|
errMessage := util.PostIncidentSeverityUpdateMessage(userId, severityEntity.Name, incidentEntity.SlackChannel, i.socketModeClient)
|
|
if errMessage != nil {
|
|
logger.Error("post response failed for IncidentUpdateSeverity", zap.Error(errMessage))
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *incidentService) UpdateStatus(
|
|
updateIncidentRequest request.UpdateIncidentRequest, userId, slackChannel string,
|
|
incidentEntity *incident.IncidentEntity) error {
|
|
if updateIncidentRequest.Status != "" {
|
|
num, err := strconv.ParseUint(updateIncidentRequest.Status, 10, 64)
|
|
if err != nil {
|
|
logger.Error("error in string to int conversion",
|
|
zap.String("Status", updateIncidentRequest.Status), zap.Error(err))
|
|
return err
|
|
}
|
|
if incidentEntity.Status != uint(num) {
|
|
incidentEntity.Status = uint(num)
|
|
incidentStatus, _ := i.incidentRepository.FindIncidentStatusById(incidentEntity.Status)
|
|
errMessage := util.PostIncidentStatusUpdateMessage(userId, incidentStatus.Name, slackChannel, i.socketModeClient)
|
|
if errMessage != nil {
|
|
logger.Error("post response failed for IncidentUpdateStatus", zap.Error(errMessage))
|
|
return err
|
|
}
|
|
if incidentStatus.IsTerminalStatus {
|
|
now := time.Now()
|
|
incidentEntity.EndTime = &now
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *incidentService) UpdateTeamId(
|
|
updateIncidentRequest request.UpdateIncidentRequest, userId string, incidentEntity *incident.IncidentEntity) error {
|
|
if updateIncidentRequest.TeamId != "" {
|
|
severityEntity, err := i.severityRepository.FindSeverityById(incidentEntity.SeverityId)
|
|
if err != nil {
|
|
logger.Error("error in fetching severity", zap.Any("severity", incidentEntity.SeverityId), zap.Error(err))
|
|
return err
|
|
}
|
|
if updateIncidentRequest.TeamId != "" {
|
|
num, err := strconv.ParseUint(updateIncidentRequest.TeamId, 10, 64)
|
|
if err != nil {
|
|
logger.Error("error in string to int conversion",
|
|
zap.String("TeamId", updateIncidentRequest.TeamId), zap.Error(err))
|
|
return err
|
|
}
|
|
if incidentEntity.TeamId != uint(num) {
|
|
incidentEntity.TeamId = uint(num)
|
|
teamEntity, err := i.teamRepository.FindTeamById(incidentEntity.TeamId)
|
|
if err != nil {
|
|
logger.Error("error in fetching team by id",
|
|
zap.String("TeamId", updateIncidentRequest.TeamId), zap.Error(err))
|
|
return err
|
|
}
|
|
errMessage := util.PostIncidentTypeUpdateMessage(
|
|
userId, teamEntity.Name, severityEntity.Name, severityEntity.Description,
|
|
incidentEntity.IncidentName, incidentEntity.Title, incidentEntity.SlackChannel, i.socketModeClient)
|
|
if errMessage != nil {
|
|
logger.Error("post response failed for IncidentUpdateType", zap.Error(errMessage))
|
|
return err
|
|
}
|
|
err = i.addDefaultUsersToIncident(incidentEntity.SlackChannel, teamEntity, severityEntity)
|
|
if err != nil {
|
|
logger.Error("[Update Incident] error while adding default users to incident", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
util.TagPseOrDevOncallToIncident(
|
|
incidentEntity.SlackChannel,
|
|
severityEntity,
|
|
teamEntity,
|
|
i.slackbotClient,
|
|
i.socketModeClient,
|
|
)
|
|
|
|
err = util.AssignResponderToIncident(
|
|
i.incidentRepository,
|
|
incidentEntity,
|
|
teamEntity,
|
|
severityEntity,
|
|
i.socketModeClient,
|
|
incidentEntity.UpdatedBy,
|
|
)
|
|
if err != nil {
|
|
logger.Error("[Update Incident][update team id] error while assigning responder to the incident ", zap.Error(err))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *incidentService) UpdateMetaData(updateIncidentRequest request.UpdateIncidentRequest, incidentEntity *incident.IncidentEntity, userId string) error {
|
|
metaData := incidentRequest.CreateIncidentMetaData{}
|
|
if updateIncidentRequest.MetaData != metaData {
|
|
metaData.CrmTicketCreationTime = updateIncidentRequest.MetaData.CrmTicketCreationTime
|
|
metaData.CustomerId = updateIncidentRequest.MetaData.CustomerId
|
|
metaData.PhoneNumber = updateIncidentRequest.MetaData.PhoneNumber
|
|
metaData.TicketId = updateIncidentRequest.MetaData.TicketId
|
|
metaData.AgentName = updateIncidentRequest.MetaData.AgentName
|
|
metaData.TicketGroup = updateIncidentRequest.MetaData.TicketGroup
|
|
var incidentMetadata []incidentRequest.CreateIncidentMetaData
|
|
|
|
if incidentEntity.MetaData != nil {
|
|
err := json.Unmarshal(incidentEntity.MetaData, &incidentMetadata)
|
|
if err != nil {
|
|
logger.Error("error occurred while converting incident metadata to json", zap.Error(err))
|
|
return err
|
|
}
|
|
}
|
|
incidentMetadata = append(incidentMetadata, metaData)
|
|
var err error
|
|
incidentEntity.MetaData, err = json.Marshal(incidentMetadata)
|
|
if err != nil {
|
|
logger.Error("error occurred while converting incident metadata to json", zap.Error(err))
|
|
return err
|
|
}
|
|
errMessage := slackUtil.PostIncidentCustomerDataUpdateMessage(updateIncidentRequest.MetaData, userId, incidentEntity.SlackChannel, i.socketModeClient)
|
|
if errMessage != nil {
|
|
logger.Error("post response failed for IncidentUpdateCustomerData", zap.Error(errMessage))
|
|
return errMessage
|
|
}
|
|
}
|
|
return nil
|
|
}
|